@@ -19505,6 +19505,114 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aes_eax_test(void)
1950519505 }
1950619506
1950719507 }
19508+
19509+ /* Regression test: wc_AesEaxDecryptAuth must reject authTagSz below
19510+ * WOLFSSL_MIN_AUTH_TAG_SZ (including zero), otherwise an attacker could
19511+ * bypass tag verification by supplying an empty tag. */
19512+ #if WOLFSSL_MIN_AUTH_TAG_SZ > 0
19513+ {
19514+ byte zero_ct[16];
19515+ byte zero_pt[16];
19516+ byte zero_tag[16];
19517+ XMEMSET(zero_ct, 0, sizeof(zero_ct));
19518+ XMEMSET(zero_tag, 0, sizeof(zero_tag));
19519+
19520+ ret = wc_AesEaxDecryptAuth(vectors[0].key,
19521+ (word32)vectors[0].key_length,
19522+ zero_pt,
19523+ zero_ct, (word32)sizeof(zero_ct),
19524+ vectors[0].iv,
19525+ (word32)vectors[0].iv_length,
19526+ zero_tag, 0,
19527+ vectors[0].aad,
19528+ (word32)vectors[0].aad_length);
19529+ if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
19530+ return WC_TEST_RET_ENC_EC(ret);
19531+ }
19532+
19533+ #if WOLFSSL_MIN_AUTH_TAG_SZ > 1
19534+ ret = wc_AesEaxDecryptAuth(vectors[0].key,
19535+ (word32)vectors[0].key_length,
19536+ zero_pt,
19537+ zero_ct, (word32)sizeof(zero_ct),
19538+ vectors[0].iv,
19539+ (word32)vectors[0].iv_length,
19540+ zero_tag, WOLFSSL_MIN_AUTH_TAG_SZ - 1,
19541+ vectors[0].aad,
19542+ (word32)vectors[0].aad_length);
19543+ if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
19544+ return WC_TEST_RET_ENC_EC(ret);
19545+ }
19546+ #endif
19547+
19548+ /* Upper bound: authTagSz > WC_AES_BLOCK_SIZE must be rejected.
19549+ * Pins the '>' operator in the validation against mutation to '>='
19550+ * and prevents an over-read of the caller-supplied tag buffer. */
19551+ ret = wc_AesEaxDecryptAuth(vectors[0].key,
19552+ (word32)vectors[0].key_length,
19553+ zero_pt,
19554+ zero_ct, (word32)sizeof(zero_ct),
19555+ vectors[0].iv,
19556+ (word32)vectors[0].iv_length,
19557+ zero_tag, WC_AES_BLOCK_SIZE + 1,
19558+ vectors[0].aad,
19559+ (word32)vectors[0].aad_length);
19560+ if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
19561+ return WC_TEST_RET_ENC_EC(ret);
19562+ }
19563+
19564+ /* Direct incremental-API coverage: wc_AesEaxDecryptFinal must also
19565+ * reject authInSz of zero and below WOLFSSL_MIN_AUTH_TAG_SZ. The
19566+ * one-shot API above is a separate code path. Heap-allocate the
19567+ * AesEax context to keep stack usage within Linux kernel limits. */
19568+ {
19569+ AesEax *eax = (AesEax *)XMALLOC(sizeof(*eax), HEAP_HINT,
19570+ DYNAMIC_TYPE_TMP_BUFFER);
19571+ if (eax == NULL) {
19572+ return WC_TEST_RET_ENC_NC;
19573+ }
19574+ XMEMSET(eax, 0, sizeof(*eax));
19575+ ret = wc_AesEaxInit(eax,
19576+ vectors[0].key, (word32)vectors[0].key_length,
19577+ vectors[0].iv, (word32)vectors[0].iv_length,
19578+ vectors[0].aad,
19579+ (word32)vectors[0].aad_length);
19580+ if (ret != 0) {
19581+ XFREE(eax, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
19582+ return WC_TEST_RET_ENC_EC(ret);
19583+ }
19584+
19585+ ret = wc_AesEaxDecryptFinal(eax, zero_tag, 0);
19586+ if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
19587+ wc_AesEaxFree(eax);
19588+ XFREE(eax, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
19589+ return WC_TEST_RET_ENC_EC(ret);
19590+ }
19591+
19592+ #if WOLFSSL_MIN_AUTH_TAG_SZ > 1
19593+ ret = wc_AesEaxDecryptFinal(eax, zero_tag,
19594+ WOLFSSL_MIN_AUTH_TAG_SZ - 1);
19595+ if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
19596+ wc_AesEaxFree(eax);
19597+ XFREE(eax, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
19598+ return WC_TEST_RET_ENC_EC(ret);
19599+ }
19600+ #endif
19601+
19602+ /* Upper bound: authInSz > WC_AES_BLOCK_SIZE must be rejected. */
19603+ ret = wc_AesEaxDecryptFinal(eax, zero_tag, WC_AES_BLOCK_SIZE + 1);
19604+ if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
19605+ wc_AesEaxFree(eax);
19606+ XFREE(eax, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
19607+ return WC_TEST_RET_ENC_EC(ret);
19608+ }
19609+
19610+ wc_AesEaxFree(eax);
19611+ XFREE(eax, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
19612+ }
19613+ }
19614+ #endif /* WOLFSSL_MIN_AUTH_TAG_SZ > 0 */
19615+
1950819616 return 0;
1950919617}
1951019618
0 commit comments