From 5def276e07e23b7b3b5842afbe526b4d3703ce13 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Tue, 16 Jun 2026 18:23:37 -0500 Subject: [PATCH 1/4] wolfcrypt/src/aes.c: catch and error on total length overflow in wc_AesGcmEncryptUpdate(), wc_AesGcmDecryptUpdate(), wc_AesCcmEncrypt(), and wc_AesCcmEncrypt(). --- wolfcrypt/src/aes.c | 49 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 8223bcc8fb2..84936c5462a 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -12808,6 +12808,18 @@ int wc_AesGcmEncryptUpdate(Aes* aes, byte* out, const byte* in, word32 sz, ret = MISSING_IV; } + /* Prevent overflow of aes->cSz and ->aSz. Per NIST SP 800-38D section + * 5.2.1.1, the maximum allowed ciphertext limit is 2^32 - 2 blocks, but we + * currently pass around the cunulative sizes in bytes as word32s, so we + * can't currently support the maximum allowed. + */ + if ((ret == 0) && + (((aes->cSz > 0xffffffff - sz)) || + ((aes->aSz > 0xffffffff - authInSz)))) + { + ret = AES_GCM_OVERFLOW_E; + } + if ((ret == 0) && aes->ctrSet && (aes->aSz == 0) && (aes->cSz == 0)) { aes->invokeCtr[0]++; if (aes->invokeCtr[0] == 0) { @@ -12950,6 +12962,18 @@ int wc_AesGcmDecryptUpdate(Aes* aes, byte* out, const byte* in, word32 sz, ret = MISSING_IV; } + /* Prevent overflow of aes->cSz and ->aSz. Per NIST SP 800-38D section + * 5.2.1.1, the maximum allowed ciphertext limit is 2^32 - 2 blocks, but we + * currently pass around the cunulative sizes in bytes as word32s, so we + * can't currently support the maximum allowed. + */ + if ((ret == 0) && + (((aes->cSz > 0xffffffff - sz)) || + ((aes->aSz > 0xffffffff - authInSz)))) + { + ret = AES_GCM_OVERFLOW_E; + } + if (ret == 0) { /* Decrypt with AAD and/or cipher text. */ #ifdef WOLFSSL_AESNI @@ -13586,6 +13610,17 @@ int wc_AesCcmEncrypt(Aes* aes, byte* out, const byte* in, word32 inSz, return BAD_FUNC_ARG; } + lenSz = (byte)(WC_AES_BLOCK_SIZE - 1U - nonceSz); + + /* With a large nonce, B[] runs out of room to represent inSz, and beyond + * that, the counter itself can wrap. + */ + if ((lenSz < sizeof(inSz)) && + (inSz >= ((word32)1 << (lenSz * 8)))) + { + return AES_CCM_OVERFLOW_E; + } + #ifdef WOLF_CRYPTO_CB #ifndef WOLF_CRYPTO_CB_FIND if (aes->devId != INVALID_DEVID) @@ -13602,7 +13637,7 @@ int wc_AesCcmEncrypt(Aes* aes, byte* out, const byte* in, word32 inSz, XMEMSET(A, 0, sizeof(A)); XMEMCPY(B+1, nonce, nonceSz); - lenSz = (byte)(WC_AES_BLOCK_SIZE - 1U - nonceSz); + B[0] = (byte)((authInSz > 0 ? 64 : 0) + (8 * (((byte)authTagSz - 2) / 2)) + (lenSz - 1)); @@ -13739,6 +13774,17 @@ int wc_AesCcmDecrypt(Aes* aes, byte* out, const byte* in, word32 inSz, return BAD_FUNC_ARG; } + lenSz = (byte)(WC_AES_BLOCK_SIZE - 1U - nonceSz); + + /* With a large nonce, B[] runs out of room to represent inSz, and beyond + * that, the counter itself can wrap. + */ + if ((lenSz < sizeof(inSz)) && + (inSz >= ((word32)1 << (lenSz * 8)))) + { + return AES_CCM_OVERFLOW_E; + } + #ifdef WOLF_CRYPTO_CB #ifndef WOLF_CRYPTO_CB_FIND if (aes->devId != INVALID_DEVID) @@ -13757,7 +13803,6 @@ int wc_AesCcmDecrypt(Aes* aes, byte* out, const byte* in, word32 inSz, oSz = inSz; XMEMSET(A, 0, sizeof A); XMEMCPY(B+1, nonce, nonceSz); - lenSz = (byte)(WC_AES_BLOCK_SIZE - 1U - nonceSz); B[0] = (byte)(lenSz - 1U); for (i = 0; i < lenSz; i++) From 10703840343515fdac1e57a276b09eea31686600 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 17 Jun 2026 11:59:54 -0500 Subject: [PATCH 2/4] wolfcrypt/src/aes.c and wolfcrypt/test/test.c: fixes from review, re "catch and error on total length overflow". --- wolfcrypt/src/aes.c | 12 ++--- wolfcrypt/test/test.c | 118 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 6 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 84936c5462a..58d567a612a 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -12810,12 +12810,12 @@ int wc_AesGcmEncryptUpdate(Aes* aes, byte* out, const byte* in, word32 sz, /* Prevent overflow of aes->cSz and ->aSz. Per NIST SP 800-38D section * 5.2.1.1, the maximum allowed ciphertext limit is 2^32 - 2 blocks, but we - * currently pass around the cunulative sizes in bytes as word32s, so we + * currently pass around the cumulative sizes in bytes as word32s, so we * can't currently support the maximum allowed. */ if ((ret == 0) && - (((aes->cSz > 0xffffffff - sz)) || - ((aes->aSz > 0xffffffff - authInSz)))) + ((aes->cSz > 0xffffffff - sz) || + (aes->aSz > 0xffffffff - authInSz))) { ret = AES_GCM_OVERFLOW_E; } @@ -12964,12 +12964,12 @@ int wc_AesGcmDecryptUpdate(Aes* aes, byte* out, const byte* in, word32 sz, /* Prevent overflow of aes->cSz and ->aSz. Per NIST SP 800-38D section * 5.2.1.1, the maximum allowed ciphertext limit is 2^32 - 2 blocks, but we - * currently pass around the cunulative sizes in bytes as word32s, so we + * currently pass around the cumulative sizes in bytes as word32s, so we * can't currently support the maximum allowed. */ if ((ret == 0) && - (((aes->cSz > 0xffffffff - sz)) || - ((aes->aSz > 0xffffffff - authInSz)))) + ((aes->cSz > 0xffffffff - sz) || + (aes->aSz > 0xffffffff - authInSz))) { ret = AES_GCM_OVERFLOW_E; } diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 204c30f5078..66e9c0193a6 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -19148,6 +19148,18 @@ static wc_test_ret_t aesgcm_stream_test(Aes* enc) ret = wc_AesGcmEncryptUpdate(enc, resultC, p, sizeof(p), a, sizeof(a)); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + +#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7, 0, 0) + ret = wc_AesGcmEncryptUpdate(enc, resultC, p, 0xffffffff, + NULL /* authIn */, 0 /* authInSz */); + if (ret != WC_NO_ERR_TRACE(AES_GCM_OVERFLOW_E)) { + if (ret == 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + else + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + } +#endif /* !HAVE_FIPS || FIPS_VERSION3_GE(7, 0, 0) */ + ret = wc_AesGcmEncryptFinal(enc, resultT, sizeof(t1)); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -19163,6 +19175,18 @@ static wc_test_ret_t aesgcm_stream_test(Aes* enc) ret = wc_AesGcmDecryptUpdate(enc, resultP, c1, sizeof(c1), a, sizeof(a)); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + +#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7, 0, 0) + ret = wc_AesGcmDecryptUpdate(enc, resultP, c1, 0xffffffff, + NULL /* authIn */, 0 /* authInSz */); + if (ret != WC_NO_ERR_TRACE(AES_GCM_OVERFLOW_E)) { + if (ret == 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + else + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + } +#endif /* !HAVE_FIPS || FIPS_VERSION3_GE(7, 0, 0) */ + ret = wc_AesGcmDecryptFinal(enc, t1, sizeof(t1)); if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); @@ -21095,6 +21119,100 @@ static wc_test_ret_t aesccm_128_badarg_test(Aes* enc) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); #endif +#if !defined(BENCH_EMBEDDED) && !defined(WOLFSSL_NO_MALLOC) + /* Test 64k - 1 with full nonce -- must work. */ + { + wc_static_assert(sizeof(iv) >= 13); + word32 clear_size = ((word32)1 << 16); + word32 cipher_size = ((word32)1 << 16); + byte *large_alloc = (byte *)XMALLOC(clear_size + cipher_size, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + byte *clear = large_alloc; + byte *cipher = large_alloc + clear_size; + + if (large_alloc == NULL) + ERROR_OUT(WC_TEST_RET_ENC_EC(MEMORY_E), out); + + XMEMSET(clear, 0, clear_size); + + ret = wc_AesCcmEncrypt(enc, cipher, clear, clear_size - 1, iv, + 13, t_empty2, sizeof(t_empty2), a, + sizeof(a)); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + +#ifdef HAVE_AES_DECRYPT + if (ret == 0) { + word32 i; + + XMEMSET(clear, 0xff, clear_size); + ret = wc_AesCcmDecrypt(enc, clear, cipher, cipher_size - 1, + iv, 13, t_empty2, sizeof(t_empty2), a, + sizeof(a)); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + else { + for (i = 0; i < clear_size - 1; ++i) { + if (clear[i] != 0) { + ret = WC_TEST_RET_ENC_I(i); + break; + } + } + } + } +#endif + + XFREE(large_alloc, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + if (ret != 0) + goto out; + } +#endif /* !BENCH_EMBEDDED && !WOLFSSL_NO_MALLOC */ + +#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7, 0, 0) + + ret = wc_AesCcmEncrypt(enc, buf, (const byte *)"", (word32)1 << 16, + iv, 13, t_empty2, sizeof(t_empty2), + a, sizeof(a)); + if (ret != WC_NO_ERR_TRACE(AES_CCM_OVERFLOW_E)) { + if (ret == 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + else + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + } +#ifdef HAVE_AES_DECRYPT + ret = wc_AesCcmDecrypt(enc, buf, (const byte *)"", + (word32)1 << 16, iv, 13, t_empty2, + sizeof(t_empty2), a, sizeof(a)); + if (ret != WC_NO_ERR_TRACE(AES_CCM_OVERFLOW_E)) { + if (ret == 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + else + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + } +#endif + + ret = wc_AesCcmEncrypt(enc, buf, (const byte *)"", (word32)1 << 24, + iv, 12, t_empty2, sizeof(t_empty2), + a, sizeof(a)); + if (ret != WC_NO_ERR_TRACE(AES_CCM_OVERFLOW_E)) { + if (ret == 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + else + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + } +#ifdef HAVE_AES_DECRYPT + ret = wc_AesCcmDecrypt(enc, buf, (const byte *)"", + (word32)1 << 24, iv, 12, t_empty2, + sizeof(t_empty2), a, sizeof(a)); + if (ret != WC_NO_ERR_TRACE(AES_CCM_OVERFLOW_E)) { + if (ret == 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + else + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + } +#endif + +#endif /* !HAVE_FIPS || FIPS_VERSION3_GE(7, 0, 0) */ + ret = 0; out: return ret; From 9d15bc707cd4fff30c1469f787db77c82f5bba07 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 17 Jun 2026 13:18:46 -0500 Subject: [PATCH 3/4] wolfcrypt/src/aes.c, wolfcrypt/src/port/caam/caam_aes.c, wolfcrypt/src/port/riscv/riscv-64-aes.c, wolfcrypt/src/port/silabs/silabs_aes.c, wolfcrypt/src/port/ti/ti-aes.c: implement AES-CCM counter overflow checks for ports; wolfcrypt/test/test.c: add missing !HAVE_SELFTEST gate around AES-CCM counter overflow test in aesccm_128_badarg_test(); wolfcrypt/src/error.c and wolfssl/wolfcrypt/error-crypt.h: update messages for AES_{GCM,CCM}_OVERFLOW_E. --- wolfcrypt/src/aes.c | 24 ++++++++++++++++++ wolfcrypt/src/error.c | 4 +-- wolfcrypt/src/port/caam/caam_aes.c | 28 ++++++++++++++++++--- wolfcrypt/src/port/riscv/riscv-64-aes.c | 33 ++++++++++++++++++++++--- wolfcrypt/src/port/silabs/silabs_aes.c | 26 +++++++++++++++++++ wolfcrypt/src/port/ti/ti-aes.c | 23 +++++++++++++---- wolfcrypt/test/test.c | 4 +-- wolfssl/wolfcrypt/error-crypt.h | 4 +-- 8 files changed, 127 insertions(+), 19 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 58d567a612a..5d5c54893f1 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -13384,6 +13384,18 @@ int wc_AesCcmEncrypt(Aes* aes, byte* out, const byte* in, word32 inSz, return status; } + { + word32 lenSz = (word32)WC_AES_BLOCK_SIZE - 1U - nonceSz; + /* With a large nonce, B[] runs out of room to represent inSz, and beyond + * that, the counter itself can wrap. + */ + if ((lenSz < sizeof(inSz)) && + (inSz >= ((word32)1 << (lenSz * 8)))) + { + return AES_CCM_OVERFLOW_E; + } + } + status = wolfSSL_CryptHwMutexLock(); if (status != 0) return status; @@ -13418,6 +13430,18 @@ int wc_AesCcmDecrypt(Aes* aes, byte* out, const byte* in, word32 inSz, return status; } + { + word32 lenSz = (word32)WC_AES_BLOCK_SIZE - 1U - nonceSz; + /* With a large nonce, B[] runs out of room to represent inSz, and beyond + * that, the counter itself can wrap. + */ + if ((lenSz < sizeof(inSz)) && + (inSz >= ((word32)1 << (lenSz * 8)))) + { + return AES_CCM_OVERFLOW_E; + } + } + status = wolfSSL_CryptHwMutexLock(); if (status != 0) return status; diff --git a/wolfcrypt/src/error.c b/wolfcrypt/src/error.c index 8826d9b1988..0f70a84cc8b 100644 --- a/wolfcrypt/src/error.c +++ b/wolfcrypt/src/error.c @@ -498,10 +498,10 @@ const char* wc_GetErrorString(int error) return "wolfcrypt FIPS ECDHE Known Answer Test Failure"; case AES_GCM_OVERFLOW_E: - return "AES-GCM invocation counter overflow"; + return "AES-GCM internal overflow averted"; case AES_CCM_OVERFLOW_E: - return "AES-CCM invocation counter overflow"; + return "AES-CCM internal overflow averted"; case RSA_KEY_PAIR_E: return "RSA Key Pair-Wise Consistency check fail"; diff --git a/wolfcrypt/src/port/caam/caam_aes.c b/wolfcrypt/src/port/caam/caam_aes.c index 123c198e004..4ab3ee7f944 100644 --- a/wolfcrypt/src/port/caam/caam_aes.c +++ b/wolfcrypt/src/port/caam/caam_aes.c @@ -494,7 +494,7 @@ int wc_AesCcmEncrypt(Aes* aes, byte* out, word32 keySz; word32 i; byte B0Ctr0[WC_AES_BLOCK_SIZE + WC_AES_BLOCK_SIZE]; - int lenSz; + word32 lenSz; byte mask = 0xFF; const word32 wordSz = (word32)sizeof(word32); int ret; @@ -513,10 +513,20 @@ int wc_AesCcmEncrypt(Aes* aes, byte* out, return ret; } + lenSz = WC_AES_BLOCK_SIZE - 1 - (byte)nonceSz; + /* With a large nonce, B[] runs out of room to represent inSz, and beyond + * that, the counter itself can wrap. + */ + if ((lenSz < sizeof(inSz)) && + (inSz >= ((word32)1 << (lenSz * 8)))) + { + return AES_CCM_OVERFLOW_E; + } + /* set up B0 and CTR0 similar to how wolfcrypt/src/aes.c does */ XMEMCPY(B0Ctr0+1, nonce, nonceSz); XMEMCPY(B0Ctr0+WC_AES_BLOCK_SIZE+1, nonce, nonceSz); - lenSz = WC_AES_BLOCK_SIZE - 1 - (byte)nonceSz; + B0Ctr0[0] = (authInSz > 0 ? 64 : 0) + (8 * (((byte)authTagSz - 2) / 2)) + (lenSz - 1); @@ -577,7 +587,7 @@ int wc_AesCcmDecrypt(Aes* aes, byte* out, word32 i; byte B0Ctr0[WC_AES_BLOCK_SIZE + WC_AES_BLOCK_SIZE]; byte tag[WC_AES_BLOCK_SIZE]; - int lenSz; + word32 lenSz; byte mask = 0xFF; const word32 wordSz = (word32)sizeof(word32); int ret; @@ -596,10 +606,20 @@ int wc_AesCcmDecrypt(Aes* aes, byte* out, return ret; } + lenSz = WC_AES_BLOCK_SIZE - 1 - (byte)nonceSz; + /* With a large nonce, B[] runs out of room to represent inSz, and beyond + * that, the counter itself can wrap. + */ + if ((lenSz < sizeof(inSz)) && + (inSz >= ((word32)1 << (lenSz * 8)))) + { + return AES_CCM_OVERFLOW_E; + } + /* set up B0 and CTR0 similar to how wolfcrypt/src/aes.c does */ XMEMCPY(B0Ctr0+1, nonce, nonceSz); XMEMCPY(B0Ctr0+WC_AES_BLOCK_SIZE+1, nonce, nonceSz); - lenSz = WC_AES_BLOCK_SIZE - 1 - (byte)nonceSz; + B0Ctr0[0] = (authInSz > 0 ? 64 : 0) + (8 * (((byte)authTagSz - 2) / 2)) + (lenSz - 1); diff --git a/wolfcrypt/src/port/riscv/riscv-64-aes.c b/wolfcrypt/src/port/riscv/riscv-64-aes.c index 64d77a36304..8dee9a87d51 100644 --- a/wolfcrypt/src/port/riscv/riscv-64-aes.c +++ b/wolfcrypt/src/port/riscv/riscv-64-aes.c @@ -9102,6 +9102,7 @@ int wc_AesCcmEncrypt(Aes* aes, byte* out, const byte* in, word32 inSz, const byte* authIn, word32 authInSz) { int ret = 0; + byte lenSz = 0; /* sanity check on arguments */ if ((aes == NULL) || ((inSz != 0) && ((in == NULL) || (out == NULL))) || @@ -9114,14 +9115,26 @@ int wc_AesCcmEncrypt(Aes* aes, byte* out, const byte* in, word32 inSz, ret = BAD_FUNC_ARG; } + if (ret == 0) { + lenSz = WC_AES_BLOCK_SIZE - 1 - (byte)nonceSz; + + /* With a large nonce, B[] runs out of room to represent inSz, and beyond + * that, the counter itself can wrap. + */ + if ((lenSz < sizeof(inSz)) && + (inSz >= ((word32)1 << (lenSz * 8)))) + { + ret = AES_CCM_OVERFLOW_E; + } + } + if (ret == 0) { byte A[WC_AES_BLOCK_SIZE]; byte B[WC_AES_BLOCK_SIZE]; - byte lenSz; byte i; XMEMCPY(B+1, nonce, nonceSz); - lenSz = WC_AES_BLOCK_SIZE - 1 - (byte)nonceSz; + B[0] = (authInSz > 0 ? 64 : 0) + (8 * (((byte)authTagSz - 2) / 2)) + (lenSz - 1); @@ -9180,6 +9193,7 @@ int wc_AesCcmDecrypt(Aes* aes, byte* out, const byte* in, word32 inSz, const byte* authIn, word32 authInSz) { int ret = 0; + byte lenSz = 0; /* sanity check on arguments */ if ((aes == NULL) || ((inSz != 0) && ((in == NULL) || (out == NULL))) || @@ -9192,16 +9206,27 @@ int wc_AesCcmDecrypt(Aes* aes, byte* out, const byte* in, word32 inSz, ret = BAD_FUNC_ARG; } + if (ret == 0) { + lenSz = WC_AES_BLOCK_SIZE - 1 - (byte)nonceSz; + + /* With a large nonce, B[] runs out of room to represent inSz, and beyond + * that, the counter itself can wrap. + */ + if ((lenSz < sizeof(inSz)) && + (inSz >= ((word32)1 << (lenSz * 8)))) + { + ret = AES_CCM_OVERFLOW_E; + } + } + if (ret == 0) { byte A[WC_AES_BLOCK_SIZE]; byte B[WC_AES_BLOCK_SIZE]; - byte lenSz; byte i; byte* o = out; word32 oSz = inSz; XMEMCPY(B+1, nonce, nonceSz); - lenSz = WC_AES_BLOCK_SIZE - 1 - (byte)nonceSz; B[0] = lenSz - 1; for (i = 0; i < lenSz; i++) { diff --git a/wolfcrypt/src/port/silabs/silabs_aes.c b/wolfcrypt/src/port/silabs/silabs_aes.c index 04cceb554ce..498acea21c5 100644 --- a/wolfcrypt/src/port/silabs/silabs_aes.c +++ b/wolfcrypt/src/port/silabs/silabs_aes.c @@ -273,10 +273,23 @@ int wc_AesCcmEncrypt_silabs (Aes* aes, byte* out, const byte* in, word32 sz, { sl_status_t status; if ((in == NULL) || (out == NULL) || (iv == NULL) || (authTag == NULL) || + (ivSz < CCM_NONCE_MIN_SZ) || (ivSz > CCM_NONCE_MAX_SZ) || (authIn == NULL && authInSz != 0) || (aes == NULL)) { return BAD_FUNC_ARG; } + { + word32 lenSz = (word32)WC_AES_BLOCK_SIZE - 1U - ivSz; + /* With a large nonce, B[] runs out of room to represent inSz, and beyond + * that, the counter itself can wrap. + */ + if ((lenSz < sizeof(sz)) && + (sz >= ((word32)1 << (lenSz * 8)))) + { + return AES_CCM_OVERFLOW_E; + } + } + status = sl_se_ccm_encrypt_and_tag( &(aes->ctx.cmd_ctx), &(aes->ctx.key), @@ -301,10 +314,23 @@ int wc_AesCcmDecrypt_silabs (Aes* aes, byte* out, const byte* in, word32 sz, { sl_status_t status; if ((in == NULL) || (out == NULL) || (iv == NULL) || (authTag == NULL) || + (ivSz < CCM_NONCE_MIN_SZ) || (ivSz > CCM_NONCE_MAX_SZ) || (authIn == NULL && authInSz != 0) || (aes == NULL)) { return BAD_FUNC_ARG; } + { + word32 lenSz = (word32)WC_AES_BLOCK_SIZE - 1U - ivSz; + /* With a large nonce, B[] runs out of room to represent inSz, and beyond + * that, the counter itself can wrap. + */ + if ((lenSz < sizeof(sz)) && + (sz >= ((word32)1 << (lenSz * 8)))) + { + return AES_CCM_OVERFLOW_E; + } + } + status = sl_se_ccm_auth_decrypt( &(aes->ctx.cmd_ctx), &(aes->ctx.key), diff --git a/wolfcrypt/src/port/ti/ti-aes.c b/wolfcrypt/src/port/ti/ti-aes.c index 2dfe3580872..3d8897156c3 100644 --- a/wolfcrypt/src/port/ti/ti-aes.c +++ b/wolfcrypt/src/port/ti/ti-aes.c @@ -330,7 +330,7 @@ static int AesAuthSetKey(Aes* aes, const byte* key, word32 keySz) static int AesAuthArgCheck(Aes* aes, byte* out, const byte* in, word32 inSz, const byte* nonce, word32 nonceSz, const byte* authTag, word32 authTagSz, - word32 *M, word32 *L) + word32 *M, word32 *L, int mode) { if (aes == NULL || nonce == NULL || authTag == NULL) return BAD_FUNC_ARG; @@ -376,6 +376,19 @@ static int AesAuthArgCheck(Aes* aes, byte* out, const byte* in, word32 inSz, default: return BAD_FUNC_ARG; } + + if (mode == AES_CFG_MODE_CCM) { + word32 lenSz = (word32)WC_AES_BLOCK_SIZE - 1U - nonceSz; + /* With a large nonce, B[] runs out of room to represent inSz, and beyond + * that, the counter itself can wrap. + */ + if ((lenSz < sizeof(inSz)) && + (inSz >= ((word32)1 << (lenSz * 8)))) + { + return AES_CCM_OVERFLOW_E; + } + } + return 0; } @@ -468,8 +481,8 @@ static int AesAuthEncrypt(Aes* aes, byte* out, const byte* in, word32 inSz, word32 tmpTag[WC_AES_BLOCK_SIZE/sizeof(word32)]; ret = AesAuthArgCheck(aes, out, in, inSz, nonce, nonceSz, authTag, - authTagSz, &M, &L); - if (ret == WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { + authTagSz, &M, &L, mode); + if (ret != 0) { return ret; } if ((authIn == NULL) && (authInSz > 0)) { @@ -571,8 +584,8 @@ static int AesAuthDecrypt(Aes* aes, byte* out, const byte* in, word32 inSz, word32 tmpTag[WC_AES_BLOCK_SIZE/sizeof(word32)]; ret = AesAuthArgCheck(aes, out, in, inSz, nonce, nonceSz, authTag, - authTagSz, &M, &L); - if (ret == WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { + authTagSz, &M, &L, mode); + if (ret != 0) { return ret; } if ((authIn == NULL) && (authInSz > 0)) { diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 66e9c0193a6..10be9bca2c8 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -21167,7 +21167,7 @@ static wc_test_ret_t aesccm_128_badarg_test(Aes* enc) } #endif /* !BENCH_EMBEDDED && !WOLFSSL_NO_MALLOC */ -#if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7, 0, 0) +#if !defined(HAVE_SELFTEST) && (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7, 0, 0)) ret = wc_AesCcmEncrypt(enc, buf, (const byte *)"", (word32)1 << 16, iv, 13, t_empty2, sizeof(t_empty2), @@ -21211,7 +21211,7 @@ static wc_test_ret_t aesccm_128_badarg_test(Aes* enc) } #endif -#endif /* !HAVE_FIPS || FIPS_VERSION3_GE(7, 0, 0) */ +#endif /* !HAVE_SELFTEST && (!HAVE_FIPS || FIPS_VERSION3_GE(7, 0, 0)) */ ret = 0; out: diff --git a/wolfssl/wolfcrypt/error-crypt.h b/wolfssl/wolfcrypt/error-crypt.h index 736954a0a76..5b089f118b4 100644 --- a/wolfssl/wolfcrypt/error-crypt.h +++ b/wolfssl/wolfcrypt/error-crypt.h @@ -251,8 +251,8 @@ enum wolfCrypt_ErrorCodes { AESCCM_KAT_FIPS_E = -257, /* AESCCM KAT failure */ SHA3_KAT_FIPS_E = -258, /* SHA-3 KAT failure */ ECDHE_KAT_FIPS_E = -259, /* ECDHE KAT failure */ - AES_GCM_OVERFLOW_E = -260, /* AES-GCM invocation counter overflow. */ - AES_CCM_OVERFLOW_E = -261, /* AES-CCM invocation counter overflow. */ + AES_GCM_OVERFLOW_E = -260, /* AES-GCM internal overflow averted */ + AES_CCM_OVERFLOW_E = -261, /* AES-CCM internal overflow averted */ RSA_KEY_PAIR_E = -262, /* RSA Key Pair-Wise Consistency check fail. */ DH_CHECK_PRIV_E = -263, /* DH Check Priv Key error */ From 881fe769a3b176335b50f966369e92c368bf5608 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Wed, 17 Jun 2026 13:55:10 -0500 Subject: [PATCH 4/4] wolfcrypt/src/aes.c, wolfcrypt/test/test.c: use WOLFSSL_MAX_32BIT rather than magic 0xffffffff; wolfcrypt/test/test.c: in aesgcm_stream_test(), implement tests for sSz overflow, and in aesccm_128_badarg_test(), fix line length. --- wolfcrypt/src/aes.c | 8 ++++---- wolfcrypt/test/test.c | 23 ++++++++++++++++++++--- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 5d5c54893f1..6806acbc965 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -12814,8 +12814,8 @@ int wc_AesGcmEncryptUpdate(Aes* aes, byte* out, const byte* in, word32 sz, * can't currently support the maximum allowed. */ if ((ret == 0) && - ((aes->cSz > 0xffffffff - sz) || - (aes->aSz > 0xffffffff - authInSz))) + ((aes->cSz > WOLFSSL_MAX_32BIT - sz) || + (aes->aSz > WOLFSSL_MAX_32BIT - authInSz))) { ret = AES_GCM_OVERFLOW_E; } @@ -12968,8 +12968,8 @@ int wc_AesGcmDecryptUpdate(Aes* aes, byte* out, const byte* in, word32 sz, * can't currently support the maximum allowed. */ if ((ret == 0) && - ((aes->cSz > 0xffffffff - sz) || - (aes->aSz > 0xffffffff - authInSz))) + ((aes->cSz > WOLFSSL_MAX_32BIT - sz) || + (aes->aSz > WOLFSSL_MAX_32BIT - authInSz))) { ret = AES_GCM_OVERFLOW_E; } diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 10be9bca2c8..e95bba5ae5e 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -19150,7 +19150,7 @@ static wc_test_ret_t aesgcm_stream_test(Aes* enc) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); #if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7, 0, 0) - ret = wc_AesGcmEncryptUpdate(enc, resultC, p, 0xffffffff, + ret = wc_AesGcmEncryptUpdate(enc, resultC, p, WOLFSSL_MAX_32BIT, NULL /* authIn */, 0 /* authInSz */); if (ret != WC_NO_ERR_TRACE(AES_GCM_OVERFLOW_E)) { if (ret == 0) @@ -19158,6 +19158,14 @@ static wc_test_ret_t aesgcm_stream_test(Aes* enc) else ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); } + ret = wc_AesGcmEncryptUpdate(enc, resultC, (const byte *)"", 0, + (const byte *)"", WOLFSSL_MAX_32BIT); + if (ret != WC_NO_ERR_TRACE(AES_GCM_OVERFLOW_E)) { + if (ret == 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + else + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + } #endif /* !HAVE_FIPS || FIPS_VERSION3_GE(7, 0, 0) */ ret = wc_AesGcmEncryptFinal(enc, resultT, sizeof(t1)); @@ -19177,7 +19185,7 @@ static wc_test_ret_t aesgcm_stream_test(Aes* enc) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); #if !defined(HAVE_FIPS) || FIPS_VERSION3_GE(7, 0, 0) - ret = wc_AesGcmDecryptUpdate(enc, resultP, c1, 0xffffffff, + ret = wc_AesGcmDecryptUpdate(enc, resultP, c1, WOLFSSL_MAX_32BIT, NULL /* authIn */, 0 /* authInSz */); if (ret != WC_NO_ERR_TRACE(AES_GCM_OVERFLOW_E)) { if (ret == 0) @@ -19185,6 +19193,14 @@ static wc_test_ret_t aesgcm_stream_test(Aes* enc) else ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); } + ret = wc_AesGcmDecryptUpdate(enc, resultP, (const byte *)"", 0, + (const byte *)"", WOLFSSL_MAX_32BIT); + if (ret != WC_NO_ERR_TRACE(AES_GCM_OVERFLOW_E)) { + if (ret == 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + else + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + } #endif /* !HAVE_FIPS || FIPS_VERSION3_GE(7, 0, 0) */ ret = wc_AesGcmDecryptFinal(enc, t1, sizeof(t1)); @@ -21125,7 +21141,8 @@ static wc_test_ret_t aesccm_128_badarg_test(Aes* enc) wc_static_assert(sizeof(iv) >= 13); word32 clear_size = ((word32)1 << 16); word32 cipher_size = ((word32)1 << 16); - byte *large_alloc = (byte *)XMALLOC(clear_size + cipher_size, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + byte *large_alloc = (byte *)XMALLOC(clear_size + cipher_size, + HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); byte *clear = large_alloc; byte *cipher = large_alloc + clear_size;