Skip to content

Commit 72d960f

Browse files
committed
Fix ArrayIndexOutOfBoundsException in AES-GCM/CCM with zero-length input, correct CCM arg sanitization
1 parent da42515 commit 72d960f

File tree

4 files changed

+135
-25
lines changed

4 files changed

+135
-25
lines changed

jni/jni_aesccm.c

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -192,14 +192,14 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_AesCcm_wc_1AesCcmEncrypt
192192
authInSz = (*env)->GetArrayLength(env, authInArr);
193193
}
194194

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

201201
/* Allocate new buffer to hold ciphertext */
202-
if (ret == 0) {
202+
if (ret == 0 && inLen > 0) {
203203
out = (byte*)XMALLOC(inLen, NULL, DYNAMIC_TYPE_TMP_BUFFER);
204204
if (out == NULL) {
205205
ret = MEMORY_E;
@@ -325,12 +325,13 @@ JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_AesCcm_wc_1AesCcmDecrypt
325325
authInSz = (*env)->GetArrayLength(env, authInArr);
326326
}
327327

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

333-
if (ret == 0) {
334+
if (ret == 0 && inLen > 0) {
334335
out = (byte*)XMALLOC(inLen, NULL, DYNAMIC_TYPE_TMP_BUFFER);
335336
if (out == NULL) {
336337
ret = MEMORY_E;

src/main/java/com/wolfssl/provider/jce/WolfCryptCipher.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,6 +1061,15 @@ private byte[] wolfCryptFinal(byte[] input, int inputOffset, int len)
10611061
tmpOut = totalOut;
10621062
}
10631063
else {
1064+
/* Case where input is only the authentication tag,
1065+
* zero-length plaintext */
1066+
if (tmpIn.length < this.gcmTagLen) {
1067+
throw new AEADBadTagException(
1068+
"Input too short for GCM tag, got " +
1069+
tmpIn.length + " bytes, need at least " +
1070+
this.gcmTagLen);
1071+
}
1072+
10641073
/* Get auth tag from end of ciphertext */
10651074
byte[] tag = Arrays.copyOfRange(tmpIn,
10661075
tmpIn.length - this.gcmTagLen,
@@ -1099,6 +1108,15 @@ else if (cipherMode == CipherMode.WC_CCM) {
10991108
tmpOut = totalOut;
11001109
}
11011110
else {
1111+
/* Case where input is only the authentication tag,
1112+
* zero-length plaintext */
1113+
if (tmpIn.length < this.gcmTagLen) {
1114+
throw new AEADBadTagException(
1115+
"Input too short for CCM tag, got " +
1116+
tmpIn.length + " bytes, need at least " +
1117+
this.gcmTagLen);
1118+
}
1119+
11021120
/* Get auth tag from end of ciphertext */
11031121
byte[] tag = Arrays.copyOfRange(tmpIn,
11041122
tmpIn.length - this.gcmTagLen,

src/test/java/com/wolfssl/provider/jce/test/WolfCryptCipherTest.java

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6918,5 +6918,102 @@ public void testPKCS5PaddingDecryptBehavior()
69186918
assertArrayEquals("Decrypted result should match original plaintext",
69196919
plaintext, fullResult);
69206920
}
6921+
6922+
/*
6923+
* Test AES-GCM and AES-CCM with zero-length plaintext.
6924+
*/
6925+
@Test
6926+
public void testAesGcmCcmZeroLengthPlaintext()
6927+
throws NoSuchAlgorithmException, NoSuchProviderException,
6928+
NoSuchPaddingException, InvalidKeyException,
6929+
InvalidAlgorithmParameterException,
6930+
IllegalBlockSizeException, BadPaddingException {
6931+
6932+
/* Test AES-GCM with zero-length plaintext */
6933+
if (enabledJCEAlgos.contains("AES/GCM/NoPadding")) {
6934+
byte[] key = new byte[16];
6935+
byte[] iv = new byte[12];
6936+
byte[] aad = new byte[100];
6937+
6938+
secureRandom.nextBytes(key);
6939+
secureRandom.nextBytes(iv);
6940+
for (int i = 0; i < aad.length; i++) {
6941+
aad[i] = (byte) (i % 256);
6942+
}
6943+
6944+
/* Test zero-length plaintext */
6945+
byte[] plaintext = new byte[0];
6946+
6947+
/* Encrypt */
6948+
Cipher encCipher =
6949+
Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
6950+
GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv);
6951+
encCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"),
6952+
gcmSpec);
6953+
encCipher.updateAAD(aad);
6954+
byte[] ciphertext = encCipher.doFinal(plaintext);
6955+
6956+
/* Should have only the authentication tag (16 bytes) */
6957+
assertEquals("GCM ciphertext should be tag length only",
6958+
16, ciphertext.length);
6959+
6960+
/* Decrypt */
6961+
Cipher decCipher =
6962+
Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
6963+
decCipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"),
6964+
gcmSpec);
6965+
decCipher.updateAAD(aad);
6966+
byte[] decrypted = decCipher.doFinal(ciphertext);
6967+
6968+
/* Should decrypt to zero-length plaintext */
6969+
assertEquals("Decrypted plaintext should be zero length",
6970+
0, decrypted.length);
6971+
assertArrayEquals("Decrypted should match original plaintext",
6972+
plaintext, decrypted);
6973+
}
6974+
6975+
/* Test AES-CCM with zero-length plaintext */
6976+
if (enabledJCEAlgos.contains("AES/CCM/NoPadding")) {
6977+
byte[] key = new byte[16];
6978+
byte[] nonce = new byte[11]; /* 88-bit nonce for CCM */
6979+
byte[] aad = new byte[50];
6980+
6981+
secureRandom.nextBytes(key);
6982+
secureRandom.nextBytes(nonce);
6983+
for (int i = 0; i < aad.length; i++) {
6984+
aad[i] = (byte) (i % 256);
6985+
}
6986+
6987+
/* Test zero-length plaintext */
6988+
byte[] plaintext = new byte[0];
6989+
6990+
/* Encrypt */
6991+
Cipher encCipher =
6992+
Cipher.getInstance("AES/CCM/NoPadding", jceProvider);
6993+
GCMParameterSpec ccmSpec = new GCMParameterSpec(128, nonce);
6994+
encCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"),
6995+
ccmSpec);
6996+
encCipher.updateAAD(aad);
6997+
byte[] ciphertext = encCipher.doFinal(plaintext);
6998+
6999+
/* Should have only the authentication tag (16 bytes) */
7000+
assertEquals("CCM ciphertext should be tag length only",
7001+
16, ciphertext.length);
7002+
7003+
/* Decrypt */
7004+
Cipher decCipher =
7005+
Cipher.getInstance("AES/CCM/NoPadding", jceProvider);
7006+
decCipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"),
7007+
ccmSpec);
7008+
decCipher.updateAAD(aad);
7009+
byte[] decrypted = decCipher.doFinal(ciphertext);
7010+
7011+
/* Should decrypt to zero-length plaintext */
7012+
assertEquals("Decrypted plaintext should be zero length",
7013+
0, decrypted.length);
7014+
assertArrayEquals("Decrypted should match original plaintext",
7015+
plaintext, decrypted);
7016+
}
7017+
}
69217018
}
69227019

src/test/java/com/wolfssl/wolfcrypt/test/AesCcmTest.java

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -257,12 +257,11 @@ public void testAesCcm128() throws WolfCryptException {
257257
plain = dec.decrypt(cipher, iv3, tag, a3);
258258
assertArrayEquals(p3, plain);
259259

260-
/* bad encrypt arguments: null input */
260+
/* success case: null input with aad */
261261
try {
262262
enc.encrypt(null, iv3, tag, a3);
263-
fail("encrypt() with null input should fail");
264263
} catch (WolfCryptException e) {
265-
/* expected */
264+
fail("encrypt() with null input should pass");
266265
}
267266

268267
/* bad encrypt arguments: null iv */
@@ -281,12 +280,11 @@ public void testAesCcm128() throws WolfCryptException {
281280
/* expected */
282281
}
283282

284-
/* bad decrypt arguments: null input */
283+
/* success case: null input, valid AAD and tag */
285284
try {
286285
enc.decrypt(null, iv3, tag, a3);
287-
fail("decrypt() with null input should fail");
288286
} catch (WolfCryptException e) {
289-
/* expected */
287+
fail("decrypt() with null input should pass");
290288
}
291289

292290
/* bad decrypt arguments: null iv */
@@ -336,12 +334,11 @@ public void testAesCcm192() throws WolfCryptException {
336334
assertNotNull(plain);
337335
assertArrayEquals(p2, plain);
338336

339-
/* bad encrypt arguments: null input */
337+
/* success case: null input, but with AAD */
340338
try {
341339
enc.encrypt(null, iv2, tag, a2);
342-
fail("encrypt() with null input should fail");
343340
} catch (WolfCryptException e) {
344-
/* expected */
341+
fail("encrypt() with null input should pass");
345342
}
346343

347344
/* bad encrypt arguments: null iv */
@@ -360,12 +357,11 @@ public void testAesCcm192() throws WolfCryptException {
360357
/* expected */
361358
}
362359

363-
/* bad decrypt arguments: null input */
360+
/* success case: null input, valid AAD and tag */
364361
try {
365362
enc.decrypt(null, iv2, tag, a2);
366-
fail("decrypt() with null input should fail");
367363
} catch (WolfCryptException e) {
368-
/* expected */
364+
fail("decrypt() with null input should pass");
369365
}
370366

371367
/* bad decrypt arguments: null iv */
@@ -415,12 +411,11 @@ public void testAesCcm256() throws WolfCryptException {
415411
assertNotNull(plain);
416412
assertArrayEquals(p1, plain);
417413

418-
/* bad encrypt arguments: null input */
414+
/* success case: null input, but with AAD */
419415
try {
420416
enc.encrypt(null, iv1, tag, a1);
421-
fail("encrypt() with null input should fail");
422417
} catch (WolfCryptException e) {
423-
/* expected */
418+
fail("encrypt() with null input should pass");
424419
}
425420

426421
/* bad encrypt arguments: null iv */
@@ -439,12 +434,11 @@ public void testAesCcm256() throws WolfCryptException {
439434
/* expected */
440435
}
441436

442-
/* bad decrypt arguments: null input */
437+
/* success case: null input, valid AAD and tag */
443438
try {
444439
enc.decrypt(null, iv1, tag, a1);
445-
fail("decrypt() with null input should fail");
446440
} catch (WolfCryptException e) {
447-
/* expected */
441+
fail("decrypt() with null input should pass");
448442
}
449443

450444
/* bad decrypt arguments: null iv */

0 commit comments

Comments
 (0)