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