Skip to content

Latest commit

 

History

History
218 lines (146 loc) · 10.3 KB

aes-256-gcm.md

File metadata and controls

218 lines (146 loc) · 10.3 KB

Authenticated Encryption with Additional Data using AES-GCM

Warning

WARNING: Despite being the most popular AEAD construction due to its use in TLS, safely using AES-GCM in a different context is tricky.

No more than ~ 350 GB of input data should be encrypted with a given key. This is for ~ 16 KB messages – Actual figures vary according to message sizes.

In addition, nonces are short and repeated nonces would totally destroy the security of this scheme. Nonces should thus come from atomic counters, which can be difficult to set up in a distributed environment.

Unless you absolutely need AES-GCM, use AEGIS-256 (crypto_aead_aegis256_*()) instead. It doesn’t have any of these limitations.

Or, if you don’t need to authenticate additional data, just stick to crypto_secretbox().

Example (combined mode)

#include <sodium.h>

#define MESSAGE (const unsigned char *) "test"
#define MESSAGE_LEN 4
#define ADDITIONAL_DATA (const unsigned char *) "123456"
#define ADDITIONAL_DATA_LEN 6

unsigned char nonce[crypto_aead_aes256gcm_NPUBBYTES];
unsigned char key[crypto_aead_aes256gcm_KEYBYTES];
unsigned char ciphertext[MESSAGE_LEN + crypto_aead_aes256gcm_ABYTES];
unsigned long long ciphertext_len;

sodium_init();
if (crypto_aead_aes256gcm_is_available() == 0) {
    abort(); /* Not available on this CPU */
}

crypto_aead_aes256gcm_keygen(key);
randombytes_buf(nonce, sizeof nonce);

crypto_aead_aes256gcm_encrypt(ciphertext, &ciphertext_len,
                              MESSAGE, MESSAGE_LEN,
                              ADDITIONAL_DATA, ADDITIONAL_DATA_LEN,
                              NULL, nonce, key);

unsigned char decrypted[MESSAGE_LEN];
unsigned long long decrypted_len;
if (ciphertext_len < crypto_aead_aes256gcm_ABYTES ||
    crypto_aead_aes256gcm_decrypt(decrypted, &decrypted_len,
                                  NULL,
                                  ciphertext, ciphertext_len,
                                  ADDITIONAL_DATA,
                                  ADDITIONAL_DATA_LEN,
                                  nonce, key) != 0) {
    /* message forged! */
}

Purpose

This operation:

  • Encrypts a message with a key and a nonce to keep it confidential
  • Computes an authentication tag. This tag is used to make sure that the message, as well as optional, non-confidential (non-encrypted) data, haven’t been tampered with.

A typical use case for additional data is to store protocol-specific metadata about the message, such as its length and encoding.

It can also be used as a MAC, with an empty message.

Decryption will never be performed, even partially, before verification.

When supported by the CPU, AES-GCM is the fastest AEAD cipher available in this library.

Limitations

The current implementation of this construction is hardware-accelerated and requires the Intel AES-NI extensions, or the ARM Crypto extensions.

Intel Westmere processors (introduced in 2010) and newer, as well as the vast majority of 64-bit ARM processors meet the requirements.

There are no plans to support non hardware-accelerated implementations of AES-GCM. This includes JavaScript and WebAssembly, where it can't be implemented efficiently and securely. If portability is a concern, use ChaCha20-Poly1305 instead.

Before using the functions below, hardware support for AES can be checked with:

int crypto_aead_aes256gcm_is_available(void);

The function returns 1 if the current CPU supports the AES256-GCM implementation, and 0 if it doesn’t.

The library must have been initialized with sodium_init() prior to calling this function.

Combined mode

In combined mode, the authentication tag is directly appended to the encrypted message. This is usually what you want.

int crypto_aead_aes256gcm_encrypt(unsigned char *c,
                                  unsigned long long *clen_p,
                                  const unsigned char *m,
                                  unsigned long long mlen,
                                  const unsigned char *ad,
                                  unsigned long long adlen,
                                  const unsigned char *nsec,
                                  const unsigned char *npub,
                                  const unsigned char *k);

The function crypto_aead_aes256gcm_encrypt() encrypts a message m whose length is mlen bytes using a secret key k (crypto_aead_aes256gcm_KEYBYTES bytes) and a public nonce npub (crypto_aead_aes256gcm_NPUBBYTES bytes).

The encrypted message, as well as a tag authenticating both the confidential message m and adlen bytes of non-confidential data ad, are put into c.

ad can also be a NULL pointer if no additional data are required.

At most mlen + crypto_aead_aes256gcm_ABYTES bytes are put into c, and the actual number of bytes is stored into clen if clen is not a NULL pointer.

nsec is not used by this particular construction and should always be NULL.

The function always returns 0.

The public nonce npub should never ever be reused with the same key. The recommended way to generate it is to use randombytes_buf() for the first message, and then to increment it for each subsequent message using the same key.

int crypto_aead_aes256gcm_decrypt(unsigned char *m,
                                  unsigned long long *mlen_p,
                                  unsigned char *nsec,
                                  const unsigned char *c,
                                  unsigned long long clen,
                                  const unsigned char *ad,
                                  unsigned long long adlen,
                                  const unsigned char *npub,
                                  const unsigned char *k);

The function crypto_aead_aes256gcm_decrypt() verifies that the ciphertext c (as produced by crypto_aead_aes256gcm_encrypt()), includes a valid tag using a secret key k, a public nonce npub, and additional data ad (adlen bytes). clen is the ciphertext length in bytes with the authenticator, so it has to be at least aead_aes256gcm_ABYTES.

ad can be a NULL pointer if no additional data are required.

nsec is not used by this particular construction and should always be NULL.

The function returns -1 if the verification fails.

If the verification succeeds, the function returns 0, puts the decrypted message into m and stores its actual number of bytes into mlen if mlen is not a NULL pointer.

At most clen - crypto_aead_aes256gcm_ABYTES bytes will be put into m.

Detached mode

Some applications may need to store the authentication tag and the encrypted message at different locations.

For this specific use case, “detached” variants of the functions above are available.

int crypto_aead_aes256gcm_encrypt_detached(unsigned char *c,
                                           unsigned char *mac,
                                           unsigned long long *maclen_p,
                                           const unsigned char *m,
                                           unsigned long long mlen,
                                           const unsigned char *ad,
                                           unsigned long long adlen,
                                           const unsigned char *nsec,
                                           const unsigned char *npub,
                                           const unsigned char *k);

crypto_aead_aes256gcm_encrypt_detached() encrypts a message m whose length is mlen bytes using a secret key k (crypto_aead_aes256gcm_KEYBYTES bytes) and a public nonce npub (crypto_aead_aes256gcm_NPUBBYTES bytes).

The encrypted message in put into c. A tag authenticating both the confidential message m and adlen bytes of non-confidential data ad is put into mac.

ad can also be a NULL pointer if no additional data are required.

crypto_aead_aes256gcm_ABYTES bytes are put into mac, and the actual number of bytes required for verification is stored into maclen_p, unless maclen_p is NULL pointer.

nsec is not used by this particular construction and should always be NULL.

The function always returns 0.

int crypto_aead_aes256gcm_decrypt_detached(unsigned char *m,
                                           unsigned char *nsec,
                                           const unsigned char *c,
                                           unsigned long long clen,
                                           const unsigned char *mac,
                                           const unsigned char *ad,
                                           unsigned long long adlen,
                                           const unsigned char *npub,
                                           const unsigned char *k);

The function crypto_aead_aes256gcm_decrypt_detached() verifies that the tag mac is valid for the ciphertext c using a secret key k, a public nonce npub, and additional data ad (adlen bytes).

clen is the ciphertext length in bytes.

ad can be a NULL pointer if no additional data are required.

nsec is not used by this particular construction and should always be NULL.

The function returns -1 if the verification fails.

If the verification succeeds, the function returns 0, and puts the decrypted message into m, whose length is equal to the length of the ciphertext.

void crypto_aead_aes256gcm_keygen(unsigned char k[crypto_aead_aes256gcm_KEYBYTES]);

This helper function introduced in libsodium 1.0.12 creates a random key k.

It is equivalent to calling randombytes_buf() but improves code clarity and can prevent misuse by ensuring that the provided key length is always be correct.

Constants

  • crypto_aead_aes256gcm_KEYBYTES
  • crypto_aead_aes256gcm_NPUBBYTES
  • crypto_aead_aes256gcm_ABYTES

Notes

The nonce is 96 bits long. In order to prevent nonce reuse, if a key is being reused, it is recommended to increment the previous nonce instead of generating a random nonce for each message.

To prevent nonce reuse in a client-server protocol, either use different keys for each direction, or make sure that a bit is masked in one direction, and set in the other. The crypto_kx_*() API can be used to do so.

When using AES-GCM, it is also recommended to switch to a new key before reaching ~ 350 GB encrypted with the same key. If frequent rekeying is not an option, use (X)ChaCha20-Poly1305 instead.