Insecure generation of random numbers - Static IV
Need
Generation of a unique random nonce for every AEAD encryption
Context
• Usage of Elixir 1.15+ for building application services
• Usage of the Erlang :crypto module for authenticated encryption
Description
1. Non compliant code
defmodule MyApp.Vault do
def encrypt(key, plaintext) do
nonce = "000000000000"
{ciphertext, tag} =
:crypto.crypto_one_time_aead(:aes_256_gcm, key, nonce, plaintext, "", true)
ciphertext <> tag...The `encrypt/2` function below encrypts data with AES-256-GCM through `:crypto.crypto_one_time_aead/6`, but it passes the same 12-byte nonce, `"000000000000"`, on every call. GCM is only secure when a nonce is never used twice with the same key. With a fixed nonce, every message is encrypted with the same keystream, so an attacker who captures two ciphertexts can XOR them and obtain the XOR of the two plaintexts, which often reveals both. Nonce reuse also exposes the internal authentication key of GCM, which lets the attacker forge ciphertexts with valid tags, so the integrity protection is lost as well. ChaCha20-Poly1305 and every other AEAD construction fail in the same way when a nonce is repeated, whether it comes from a literal, a module attribute or a counter that restarts with the node.
2. Steps
• Never pass literal, module attribute or zero-filled nonces to `:crypto.crypto_one_time_aead` or to any other AEAD encryption function.
• Generate a new 12-byte nonce for every message with `:crypto.strong_rand_bytes/1`.
• Store or transmit the nonce together with the ciphertext and the tag, since it is required for decryption and does not need to be secret.
• Treat an `:error` result from decryption as tampered data and never use partial output.
• Rotate the key before it encrypts about 2^32 messages with random nonces.
3. Secure code example
defmodule MyApp.Vault do
@nonce_bytes 12
@tag_bytes 16
def encrypt(key, plaintext) do
nonce = :crypto.strong_rand_bytes(@nonce_bytes)
{ciphertext, tag} =...The corrected function draws a new 12-byte nonce for every message with `:crypto.strong_rand_bytes/1`, which reads from the cryptographically secure generator of the operating system. The probability that two messages share a nonce under the same key is negligible, so the confidentiality and integrity guarantees of GCM hold. The nonce is not secret. The function returns it at the start of the output, followed by the ciphertext and the 16-byte tag, and `decrypt/2` splits the three parts to call `crypto_one_time_aead/7` with the same nonce. Decryption returns `:error` when the tag does not match, which happens whenever the data was modified. Random 96-bit nonces are safe for up to about 2^32 messages per key, so keys used at a larger scale should be rotated.