When encrypting data using AES-GCM or AES-CCM, it is essential not to reuse the same initialization vector (IV, also called nonce) with a given key. To prevent this, it is recommended to either randomize the IV for each encryption or increment the IV after each encryption.

Why is this an issue?

When encrypting data using a counter (CTR) derived block cipher mode of operation, it is essential not to reuse the same initialization vector (IV) for a given key. An IV that complies with this requirement is called a "nonce" (number used once). Galois/Counter (GCM) and Counter with Cipher Block Chaining-Message Authentication Code (CCM) are both derived from counter mode.

When using AES-GCM or AES-CCM, a given key and IV pair will create a "keystream" that is used to encrypt a plaintext (original content) into a ciphertext (encrypted content.) For any key and IV pair, this keystream is always deterministic. Because of this property, encrypting several plaintexts with one key and IV pair can be catastrophic. If an attacker has access to one plaintext and its associated ciphertext, they are able to decrypt everything that was created using the same pair.

Additionally, IV reuse also drastically decreases the key recovery computational complexity by downgrading it to a simpler polynomial root-finding problem. This means that even without access to a plaintext/ciphertext pair, an attacker may still be able to decrypt all the sensitive data.

What is the potential impact?

If the encryption that is being used is flawed, attackers might be able to exploit it in several ways. They might be able to decrypt existing sensitive data or bypass key protections.

Below are some real-world scenarios that illustrate some impacts of an attacker exploiting the vulnerability.

Theft of sensitive data

The encrypted message might contain data that is considered sensitive and should not be known to third parties.

By not using the encryption algorithm correctly, the likelihood that an attacker might be able to recover the original sensitive data drastically increases.

Additional attack surface

Encrypted values are often considered trusted, since under normal circumstances it would not be possible for a third party to modify them. If an attacker is able to modify the cleartext of the encrypted message, it might be possible to trigger other vulnerabilities in the code.

How to fix it in Java Cryptography Extension

Code examples

The example uses a hardcoded IV as a nonce, which causes AES-CCM to be insecure. To fix it, a nonce is randomly generated instead.

Noncompliant code example

public void encrypt(byte[] key, byte[] ptxt) {
    byte[] nonce = "7cVgr5cbdCZV".getBytes("UTF-8");

    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
    GCMParameterSpec gcmSpec = new GCMParameterSpec(128, nonce);

    cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec); // Noncompliant
}

Compliant solution

public void encrypt(byte[] key, byte[] ptxt) {
    SecureRandom random = new SecureRandom();
    byte[] nonce = new byte[12];
    random.nextBytes(nonce);

    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
    GCMParameterSpec gcmSpec = new GCMParameterSpec(128, nonce);

    cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec);
}

How does this work?

For AES-GCM and AES-CCM, NIST recommends generating a nonce using either a deterministic approach or using a 'Random Bit Generator (RBG)'.

Generating nonces using random number generation

When using a randomized approach, NIST recommends a nonce of at least 96 bits using a cryptographically secure pseudorandom number generator (CSPRNG.) Such a generator can create output with a sufficiently low probability of the same number being output twice (also called a collision) for a long time. However, after 232 generated numbers for the same key, NIST recommends rotating this key for a new one. After that amount of generated numbers, the probability of a collision is high enough to be considered insecure.

The code example above demonstrates how CSPRNGs can be used to generate nonces.

Be careful to use a random number generator that is sufficiently secure. Default (non-cryptographically secure) RNGs might be more prone to collisions in their output, which is catastrophic for counter-based encryption modes.

Deterministically generating nonces

One method to prevent the same IV from being used multiple times for the same key is to update the IV in a deterministic way after each encryption. The most straightforward deterministic method for this is a counter.

The way this works is simple: for any key, the first IV is the number zero. After this IV is used to encrypt something with a key, it is incremented for that key (and is now equal to 1). Although this requires additional bookkeeping, it should guarantee that for each encryption key, an IV is never repeated.

For a secure implementation, NIST suggests generating these nonces in two parts: a fixed field and an invocation field. The fixed field should be used to identify the device executing the encryption (for example, it could contain a device ID), such that for one key, no two devices can generate the same nonce. The invocation field contains the counter as described above. For a 96-bit nonce, NIST recommends (but does not require) using a 32-bit fixed field and a 64-bit invocation field. Additional details can be found in the NIST Special Publication 800-38D.

How to fix it in BouncyCastle

Code examples

The example uses a hardcoded IV as a nonce, which causes AES-CCM to be insecure. To fix it, a nonce is randomly generated instead.

Noncompliant code example

public void encrypt(byte[] key, byte[] ptxt) {
    byte[] nonce = "7cVgr5cbdCZV".getBytes(StandardCharsets.UTF_8);

    BlockCipher engine = new AESEngine();
    AEADParameters params = new AEADParameters(new KeyParameter(key), 128, nonce);
    CCMBlockCipher cipher = new CCMBlockCipher(engine);

    cipher.init(true, params); // Noncompliant
}

Compliant solution

public void encrypt(byte[] key, byte[] ptxt) {
    SecureRandom random = new SecureRandom();
    byte[] nonce = new byte[12];
    random.nextBytes(nonce);

    BlockCipher engine = new AESEngine();
    AEADParameters params = new AEADParameters(new KeyParameter(key), 128, nonce);
    CCMBlockCipher cipher = new CCMBlockCipher(engine);

    cipher.init(true, params);
}

How does this work?

For AES-GCM and AES-CCM, NIST recommends generating a nonce using either a deterministic approach or using a 'Random Bit Generator (RBG)'.

Generating nonces using random number generation

When using a randomized approach, NIST recommends a nonce of at least 96 bits using a cryptographically secure pseudorandom number generator (CSPRNG.) Such a generator can create output with a sufficiently low probability of the same number being output twice (also called a collision) for a long time. However, after 232 generated numbers for the same key, NIST recommends rotating this key for a new one. After that amount of generated numbers, the probability of a collision is high enough to be considered insecure.

The code example above demonstrates how CSPRNGs can be used to generate nonces.

Be careful to use a random number generator that is sufficiently secure. Default (non-cryptographically secure) RNGs might be more prone to collisions in their output, which is catastrophic for counter-based encryption modes.

Deterministically generating nonces

One method to prevent the same IV from being used multiple times for the same key is to update the IV in a deterministic way after each encryption. The most straightforward deterministic method for this is a counter.

The way this works is simple: for any key, the first IV is the number zero. After this IV is used to encrypt something with a key, it is incremented for that key (and is now equal to 1). Although this requires additional bookkeeping, it should guarantee that for each encryption key, an IV is never repeated.

For a secure implementation, NIST suggests generating these nonces in two parts: a fixed field and an invocation field. The fixed field should be used to identify the device executing the encryption (for example, it could contain a device ID), such that for one key, no two devices can generate the same nonce. The invocation field contains the counter as described above. For a 96-bit nonce, NIST recommends (but does not require) using a 32-bit fixed field and a 64-bit invocation field. Additional details can be found in the NIST Special Publication 800-38D.

Resources

Standards