From a7cc526747ac0d21d6462ae4cf939ccaa9d4f59b Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Tue, 24 Mar 2026 05:53:04 -0700 Subject: [PATCH] Fix AES-EAX to allow empty plaintext The one-shot wc_AesEaxEncryptAuth/wc_AesEaxDecryptAuth and the incremental wc_AesEaxEncryptUpdate/wc_AesEaxDecryptUpdate functions unconditionally required non-NULL out/in/authIn pointers, returning BAD_FUNC_ARG even when the corresponding length was 0. EAX mode with empty plaintext is a valid authentication-only operation that produces just an authentication tag (OMAC over nonce and AAD). This is permitted by the EAX specification (Bellare, Rogaway, Wagner 2004) and exercised by Wycheproof test vectors. Fix: gate NULL pointer checks on the corresponding length being > 0. In the incremental Update functions, skip the AES-CTR and CMAC ciphertext update when inSz is 0 to avoid passing NULL to wc_AesCtrEncrypt (which also rejects NULL unconditionally). --- wolfcrypt/src/aes.c | 72 +++++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index b7d43c33d1..406cf23b77 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -16779,8 +16779,9 @@ int wc_AesEaxEncryptAuth(const byte* key, word32 keySz, byte* out, int ret; int eaxInited = 0; - if (key == NULL || out == NULL || in == NULL || nonce == NULL - || authTag == NULL || authIn == NULL) { + if (key == NULL || nonce == NULL || authTag == NULL + || (inSz > 0 && (out == NULL || in == NULL)) + || (authInSz > 0 && authIn == NULL)) { return BAD_FUNC_ARG; } @@ -16842,8 +16843,9 @@ int wc_AesEaxDecryptAuth(const byte* key, word32 keySz, byte* out, int ret; int eaxInited = 0; - if (key == NULL || out == NULL || in == NULL || nonce == NULL - || authTag == NULL || authIn == NULL) { + if (key == NULL || nonce == NULL || authTag == NULL + || (inSz > 0 && (out == NULL || in == NULL)) + || (authInSz > 0 && authIn == NULL)) { return BAD_FUNC_ARG; } @@ -17031,24 +17033,27 @@ int wc_AesEaxEncryptUpdate(AesEax* eax, byte* out, { int ret; - if (eax == NULL || out == NULL || in == NULL) { + if (eax == NULL || (inSz > 0 && (out == NULL || in == NULL)) + || (authInSz > 0 && authIn == NULL)) { return BAD_FUNC_ARG; } - /* - * Encrypt the plaintext using AES CTR - * C = CTR(M) - */ - if ((ret = wc_AesCtrEncrypt(&eax->aes, out, in, inSz)) != 0) { - return ret; - } + if (inSz > 0) { + /* + * Encrypt the plaintext using AES CTR + * C = CTR(M) + */ + if ((ret = wc_AesCtrEncrypt(&eax->aes, out, in, inSz)) != 0) { + return ret; + } - /* - * update OMAC with new ciphertext - * C' = OMAC^2_K(C) - */ - if ((ret = wc_CmacUpdate(&eax->ciphertextCmac, out, inSz)) != 0) { - return ret; + /* + * update OMAC with new ciphertext + * C' = OMAC^2_K(C) + */ + if ((ret = wc_CmacUpdate(&eax->ciphertextCmac, out, inSz)) != 0) { + return ret; + } } /* If there exists new auth data, update the OMAC for that as well */ @@ -17076,24 +17081,27 @@ int wc_AesEaxDecryptUpdate(AesEax* eax, byte* out, { int ret; - if (eax == NULL || out == NULL || in == NULL) { + if (eax == NULL || (inSz > 0 && (out == NULL || in == NULL)) + || (authInSz > 0 && authIn == NULL)) { return BAD_FUNC_ARG; } - /* - * Decrypt the plaintext using AES CTR - * C = CTR(M) - */ - if ((ret = wc_AesCtrEncrypt(&eax->aes, out, in, inSz)) != 0) { - return ret; - } + if (inSz > 0) { + /* + * Decrypt the plaintext using AES CTR + * C = CTR(M) + */ + if ((ret = wc_AesCtrEncrypt(&eax->aes, out, in, inSz)) != 0) { + return ret; + } - /* - * update OMAC with new ciphertext - * C' = OMAC^2_K(C) - */ - if ((ret = wc_CmacUpdate(&eax->ciphertextCmac, in, inSz)) != 0) { - return ret; + /* + * update OMAC with new ciphertext + * C' = OMAC^2_K(C) + */ + if ((ret = wc_CmacUpdate(&eax->ciphertextCmac, in, inSz)) != 0) { + return ret; + } } /* If there exists new auth data, update the OMAC for that as well */