Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions jni/jni_aesccm.c
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,9 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_AesCcm_wc_1AesCcmEncrypt
authInSz = (*env)->GetArrayLength(env, authInArr);
}

/* authIn can be null */
if (in == NULL || inLen == 0 || nonce == NULL || nonceSz == 0 ||
authTag == NULL || authTagSz == 0) {
/* in can be NULL if inLen is 0 - case with only AAD to gen tag */
if ((inLen != 0 && in == NULL) || nonce == NULL || authTag == NULL ||
nonceSz < 7 || nonceSz > 13 || authTagSz > WC_AES_BLOCK_SIZE) {
ret = BAD_FUNC_ARG;
}

Expand Down Expand Up @@ -325,8 +325,9 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_AesCcm_wc_1AesCcmDecrypt
authInSz = (*env)->GetArrayLength(env, authInArr);
}

if (in == NULL || inLen == 0 || nonce == NULL || nonceSz == 0 ||
authTag == NULL || authTagSz == 0) {
/* in can be NULL if inLen is 0 - case with only AAD to verify tag */
if ((inLen != 0 && in == NULL) || nonce == NULL || authTag == NULL ||
nonceSz < 7 || nonceSz > 13 || authTagSz > WC_AES_BLOCK_SIZE) {
ret = BAD_FUNC_ARG;
}

Expand Down
31 changes: 29 additions & 2 deletions src/main/java/com/wolfssl/provider/jce/WolfCryptCipher.java
Original file line number Diff line number Diff line change
Expand Up @@ -379,10 +379,19 @@ protected int engineGetOutputSize(int inputLen)
if (isBlockCipher()) {
if (buffered != null && buffered.length > 0) {
totalSz = inputLen + buffered.length;
} else {
totalSz = inputLen;
}

/* For block ciphers that require block boundaries, round
* to next block size. GCM, CCM, CTR, and OFB do not require block
* boundaries. */
if (cipherMode != CipherMode.WC_GCM &&
cipherMode != CipherMode.WC_CCM &&
cipherMode != CipherMode.WC_CTR &&
cipherMode != CipherMode.WC_OFB) {
totalBlocks = totalSz / blockSize;
totalSz = totalBlocks * blockSize;
} else {
totalBlocks = inputLen / blockSize;
}
}

Expand Down Expand Up @@ -1061,6 +1070,15 @@ private byte[] wolfCryptFinal(byte[] input, int inputOffset, int len)
tmpOut = totalOut;
}
else {
/* Case where input is only the authentication tag,
* zero-length plaintext */
if (tmpIn.length < this.gcmTagLen) {
throw new AEADBadTagException(
"Input too short for GCM tag, got " +
tmpIn.length + " bytes, need at least " +
this.gcmTagLen);
}

/* Get auth tag from end of ciphertext */
byte[] tag = Arrays.copyOfRange(tmpIn,
tmpIn.length - this.gcmTagLen,
Expand Down Expand Up @@ -1099,6 +1117,15 @@ else if (cipherMode == CipherMode.WC_CCM) {
tmpOut = totalOut;
}
else {
/* Case where input is only the authentication tag,
* zero-length plaintext */
if (tmpIn.length < this.gcmTagLen) {
throw new AEADBadTagException(
"Input too short for CCM tag, got " +
tmpIn.length + " bytes, need at least " +
this.gcmTagLen);
}

/* Get auth tag from end of ciphertext */
byte[] tag = Arrays.copyOfRange(tmpIn,
tmpIn.length - this.gcmTagLen,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6918,5 +6918,102 @@ public void testPKCS5PaddingDecryptBehavior()
assertArrayEquals("Decrypted result should match original plaintext",
plaintext, fullResult);
}

/*
* Test AES-GCM and AES-CCM with zero-length plaintext.
*/
@Test
public void testAesGcmCcmZeroLengthPlaintext()
throws NoSuchAlgorithmException, NoSuchProviderException,
NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException,
IllegalBlockSizeException, BadPaddingException {

/* Test AES-GCM with zero-length plaintext */
if (enabledJCEAlgos.contains("AES/GCM/NoPadding")) {
byte[] key = new byte[16];
byte[] iv = new byte[12];
byte[] aad = new byte[100];

secureRandom.nextBytes(key);
secureRandom.nextBytes(iv);
for (int i = 0; i < aad.length; i++) {
aad[i] = (byte) (i % 256);
}

/* Test zero-length plaintext */
byte[] plaintext = new byte[0];

/* Encrypt */
Cipher encCipher =
Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv);
encCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"),
gcmSpec);
encCipher.updateAAD(aad);
byte[] ciphertext = encCipher.doFinal(plaintext);

/* Should have only the authentication tag (16 bytes) */
assertEquals("GCM ciphertext should be tag length only",
16, ciphertext.length);

/* Decrypt */
Cipher decCipher =
Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
decCipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"),
gcmSpec);
decCipher.updateAAD(aad);
byte[] decrypted = decCipher.doFinal(ciphertext);

/* Should decrypt to zero-length plaintext */
assertEquals("Decrypted plaintext should be zero length",
0, decrypted.length);
assertArrayEquals("Decrypted should match original plaintext",
plaintext, decrypted);
}

/* Test AES-CCM with zero-length plaintext */
if (enabledJCEAlgos.contains("AES/CCM/NoPadding")) {
byte[] key = new byte[16];
byte[] nonce = new byte[11]; /* 88-bit nonce for CCM */
byte[] aad = new byte[50];

secureRandom.nextBytes(key);
secureRandom.nextBytes(nonce);
for (int i = 0; i < aad.length; i++) {
aad[i] = (byte) (i % 256);
}

/* Test zero-length plaintext */
byte[] plaintext = new byte[0];

/* Encrypt */
Cipher encCipher =
Cipher.getInstance("AES/CCM/NoPadding", jceProvider);
GCMParameterSpec ccmSpec = new GCMParameterSpec(128, nonce);
encCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"),
ccmSpec);
encCipher.updateAAD(aad);
byte[] ciphertext = encCipher.doFinal(plaintext);

/* Should have only the authentication tag (16 bytes) */
assertEquals("CCM ciphertext should be tag length only",
16, ciphertext.length);

/* Decrypt */
Cipher decCipher =
Cipher.getInstance("AES/CCM/NoPadding", jceProvider);
decCipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"),
ccmSpec);
decCipher.updateAAD(aad);
byte[] decrypted = decCipher.doFinal(ciphertext);

/* Should decrypt to zero-length plaintext */
assertEquals("Decrypted plaintext should be zero length",
0, decrypted.length);
assertArrayEquals("Decrypted should match original plaintext",
plaintext, decrypted);
}
}
}

84 changes: 48 additions & 36 deletions src/test/java/com/wolfssl/wolfcrypt/test/AesCcmTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -257,12 +257,14 @@ public void testAesCcm128() throws WolfCryptException {
plain = dec.decrypt(cipher, iv3, tag, a3);
assertArrayEquals(p3, plain);

/* bad encrypt arguments: null input */
try {
enc.encrypt(null, iv3, tag, a3);
fail("encrypt() with null input should fail");
} catch (WolfCryptException e) {
/* expected */
/* success case: null input with aad. FIPSv2 incorrectly returned
* BAD_FUNC_ARG when in buffer was null. Skip this test for v2 */
if (Fips.fipsVersion != 2) {
try {
enc.encrypt(null, iv3, tag, a3);
} catch (WolfCryptException e) {
fail("encrypt() with null input should pass");
}
}

/* bad encrypt arguments: null iv */
Expand All @@ -281,12 +283,14 @@ public void testAesCcm128() throws WolfCryptException {
/* expected */
}

/* bad decrypt arguments: null input */
try {
enc.decrypt(null, iv3, tag, a3);
fail("decrypt() with null input should fail");
} catch (WolfCryptException e) {
/* expected */
/* success case: null input with aad. FIPSv2 incorrectly returned
* BAD_FUNC_ARG when in buffer was null. Skip this test for v2 */
if (Fips.fipsVersion != 2) {
try {
enc.decrypt(null, iv3, tag, a3);
} catch (WolfCryptException e) {
fail("decrypt() with null input should pass");
}
}

/* bad decrypt arguments: null iv */
Expand Down Expand Up @@ -336,12 +340,14 @@ public void testAesCcm192() throws WolfCryptException {
assertNotNull(plain);
assertArrayEquals(p2, plain);

/* bad encrypt arguments: null input */
try {
enc.encrypt(null, iv2, tag, a2);
fail("encrypt() with null input should fail");
} catch (WolfCryptException e) {
/* expected */
/* success case: null input with aad. FIPSv2 incorrectly returned
* BAD_FUNC_ARG when in buffer was null. Skip this test for v2 */
if (Fips.fipsVersion != 2) {
try {
enc.encrypt(null, iv2, tag, a2);
} catch (WolfCryptException e) {
fail("encrypt() with null input should pass");
}
}

/* bad encrypt arguments: null iv */
Expand All @@ -360,12 +366,14 @@ public void testAesCcm192() throws WolfCryptException {
/* expected */
}

/* bad decrypt arguments: null input */
try {
enc.decrypt(null, iv2, tag, a2);
fail("decrypt() with null input should fail");
} catch (WolfCryptException e) {
/* expected */
/* success case: null input with aad. FIPSv2 incorrectly returned
* BAD_FUNC_ARG when in buffer was null. Skip this test for v2 */
if (Fips.fipsVersion != 2) {
try {
enc.decrypt(null, iv2, tag, a2);
} catch (WolfCryptException e) {
fail("decrypt() with null input should pass");
}
}

/* bad decrypt arguments: null iv */
Expand Down Expand Up @@ -415,12 +423,14 @@ public void testAesCcm256() throws WolfCryptException {
assertNotNull(plain);
assertArrayEquals(p1, plain);

/* bad encrypt arguments: null input */
try {
enc.encrypt(null, iv1, tag, a1);
fail("encrypt() with null input should fail");
} catch (WolfCryptException e) {
/* expected */
/* success case: null input with aad. FIPSv2 incorrectly returned
* BAD_FUNC_ARG when in buffer was null. Skip this test for v2 */
if (Fips.fipsVersion != 2) {
try {
enc.encrypt(null, iv1, tag, a1);
} catch (WolfCryptException e) {
fail("encrypt() with null input should pass");
}
}

/* bad encrypt arguments: null iv */
Expand All @@ -439,12 +449,14 @@ public void testAesCcm256() throws WolfCryptException {
/* expected */
}

/* bad decrypt arguments: null input */
try {
enc.decrypt(null, iv1, tag, a1);
fail("decrypt() with null input should fail");
} catch (WolfCryptException e) {
/* expected */
/* success case: null input with aad. FIPSv2 incorrectly returned
* BAD_FUNC_ARG when in buffer was null. Skip this test for v2 */
if (Fips.fipsVersion != 2) {
try {
enc.decrypt(null, iv1, tag, a1);
} catch (WolfCryptException e) {
fail("decrypt() with null input should pass");
}
}

/* bad decrypt arguments: null iv */
Expand Down
Loading