-
Bug
-
Resolution: Not an Issue
-
P4
-
None
-
8u25
-
x86
-
os_x
FULL PRODUCT VERSION :
java version "1.8.0_25"
Java(TM) SE Runtime Environment (build 1.8.0_25-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Mac OS X 10.10 (14A389)
A DESCRIPTION OF THE PROBLEM :
JDK 8 documentation explains that we should call Cipher.updateAAD for AES-GCM: http://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.html
Doing this seems to guarantee AEADBadTagException-s in decryption, while never calling updateAAD works fine and validates the tag as expected. It seems like updateAAD causes some internal state corruption or something similar.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
- Set AEAD tag (AAD) through updateAAD when decrypting AES-GCM, as documented.
OR
- Call updateAAD when encrypting (as test cases and other sites suggest)
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Decryption will succeed, unless tag is actually invalid.
ACTUAL -
AEADBadTagException will always be thrown when calling doFinal.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
https://gist.github.com/praseodym/f2499b3e14d872fe5b4a
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import java.nio.ByteBuffer;
import java.security.SecureRandom;
/**
* JDK 8 documentation explains that we should call Cipher.updateAAD:
* http://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.html
*
* However, doing this seems to guarantee AEADBadTagException-s,
* while not doing this works fine and validates the tag as expected.
*/
public class AESGCMUpdateAAD {
// AES-GCM parameters
public static final int AES_KEY_SIZE = 128; // in bits
public static final int GCM_NONCE_LENGTH = 12; // in bytes
public static final int GCM_TAG_LENGTH = 16; // in bytes
public static void main(String[] args) throws Exception {
byte[] input = "Hello AES-GCM World!".getBytes();
// Initialise random and generate key
SecureRandom random = SecureRandom.getInstanceStrong();
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(AES_KEY_SIZE, random);
SecretKey key = keyGen.generateKey();
// Encrypt
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
final byte[] nonce = new byte[GCM_NONCE_LENGTH];
random.nextBytes(nonce);
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, nonce);
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
final byte[] tag = new byte[GCM_TAG_LENGTH];
random.nextBytes(tag);
// UNEXPECTED: Uncommenting this will cause an AEADBadTagException when decrypting
// cipher.updateAAD(tag);
byte[] cipherText = cipher.doFinal(input);
// Decrypt; nonce is shared implicitly
cipher.init(Cipher.DECRYPT_MODE, key, spec);
ByteBuffer cipherBuffer = ByteBuffer.wrap(cipherText);
cipherBuffer.position(cipherText.length - GCM_TAG_LENGTH); // Tag placed at end
// UNEXPECTED: Uncommenting this will cause an AEADBadTagException when decrypting
// cipher.updateAAD(cipherBuffer);
cipherBuffer.rewind();
// EXPECTED: Uncommenting this will cause an AEADBadTagException when decrypting
// cipherText[10] = 54; // Mangle cipher text
byte[] plainText = cipher.doFinal(cipherText);
// Output result (should equal input)
System.out.println(new String(plainText));
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Never call updateAAD.
java version "1.8.0_25"
Java(TM) SE Runtime Environment (build 1.8.0_25-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Mac OS X 10.10 (14A389)
A DESCRIPTION OF THE PROBLEM :
JDK 8 documentation explains that we should call Cipher.updateAAD for AES-GCM: http://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.html
Doing this seems to guarantee AEADBadTagException-s in decryption, while never calling updateAAD works fine and validates the tag as expected. It seems like updateAAD causes some internal state corruption or something similar.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
- Set AEAD tag (AAD) through updateAAD when decrypting AES-GCM, as documented.
OR
- Call updateAAD when encrypting (as test cases and other sites suggest)
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Decryption will succeed, unless tag is actually invalid.
ACTUAL -
AEADBadTagException will always be thrown when calling doFinal.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
https://gist.github.com/praseodym/f2499b3e14d872fe5b4a
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import java.nio.ByteBuffer;
import java.security.SecureRandom;
/**
* JDK 8 documentation explains that we should call Cipher.updateAAD:
* http://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.html
*
* However, doing this seems to guarantee AEADBadTagException-s,
* while not doing this works fine and validates the tag as expected.
*/
public class AESGCMUpdateAAD {
// AES-GCM parameters
public static final int AES_KEY_SIZE = 128; // in bits
public static final int GCM_NONCE_LENGTH = 12; // in bytes
public static final int GCM_TAG_LENGTH = 16; // in bytes
public static void main(String[] args) throws Exception {
byte[] input = "Hello AES-GCM World!".getBytes();
// Initialise random and generate key
SecureRandom random = SecureRandom.getInstanceStrong();
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(AES_KEY_SIZE, random);
SecretKey key = keyGen.generateKey();
// Encrypt
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
final byte[] nonce = new byte[GCM_NONCE_LENGTH];
random.nextBytes(nonce);
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, nonce);
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
final byte[] tag = new byte[GCM_TAG_LENGTH];
random.nextBytes(tag);
// UNEXPECTED: Uncommenting this will cause an AEADBadTagException when decrypting
// cipher.updateAAD(tag);
byte[] cipherText = cipher.doFinal(input);
// Decrypt; nonce is shared implicitly
cipher.init(Cipher.DECRYPT_MODE, key, spec);
ByteBuffer cipherBuffer = ByteBuffer.wrap(cipherText);
cipherBuffer.position(cipherText.length - GCM_TAG_LENGTH); // Tag placed at end
// UNEXPECTED: Uncommenting this will cause an AEADBadTagException when decrypting
// cipher.updateAAD(cipherBuffer);
cipherBuffer.rewind();
// EXPECTED: Uncommenting this will cause an AEADBadTagException when decrypting
// cipherText[10] = 54; // Mangle cipher text
byte[] plainText = cipher.doFinal(cipherText);
// Output result (should equal input)
System.out.println(new String(plainText));
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Never call updateAAD.