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