From 0a88dd4628b6246935d26ff41d1f8c16f2fa329e Mon Sep 17 00:00:00 2001 From: Hideki Miyazaki Date: Sun, 30 Aug 2026 08:53:54 -0400 Subject: [PATCH 1/2] Reject repeated wc_AsconAEAD128_SetAD calls --- ChangeLog.md | 10 ++++++++++ doc/dox_comments/header_files/ascon.h | 4 +++- tests/api/test_ascon.c | 16 ++++++++++++---- wolfcrypt/src/ascon.c | 2 ++ 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 5301b6aa985..a0b05dbb20d 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -52,6 +52,16 @@ * Fixed `wc_PKCS7_DecodeEnvelopedData()` and `wc_PKCS7_DecodeAuthEnvelopedData()` failing on a message addressed to more than one recipient; AuthEnvelopedData never supported it at all. A message carrying no recipient for the reader now reports `PKCS7_RECIP_E` rather than a parse error. Streaming an AuthEnvelopedData now buffers the whole RecipientInfo set, as the EnvelopedData decoder already did, so peak memory rises by the size of that set. by @Frauschi (PR 11350) +* Fixed `wc_AsconAEAD128_SetAD` called more than once: the function + absorbs the key, nonce and associated data into the sponge state and then + applies domain separation, but unlike `wc_AsconAEAD128_SetKey()` and + `wc_AsconAEAD128_SetNonce()` it did not reject a repeat call. A caller that + passed associated data in more than one call re-ran the absorption on the + already-advanced state, and every call still returned 0, so the resulting + tag did not authenticate under any conforming Ascon implementation with no + error raised. `wc_AsconAEAD128_SetAD()` now returns `BAD_STATE_E` when the + associated data has already been set; it must be supplied in a single call. + ## Fixes * ML-KEM and ML-DSA now keep the key's device id on their internal SHAKE hashing, so a registered crypto callback sees the SHAKE-256 Update and Final calls. by @Frauschi (PR 11544) diff --git a/doc/dox_comments/header_files/ascon.h b/doc/dox_comments/header_files/ascon.h index c25c68fea7c..657a1038c1d 100644 --- a/doc/dox_comments/header_files/ascon.h +++ b/doc/dox_comments/header_files/ascon.h @@ -238,7 +238,9 @@ int wc_AsconAEAD128_SetNonce(wc_AsconAEAD128* a, const byte* nonce); \return 0 on success. \return BAD_FUNC_ARG if the context is NULL, or if the associated data pointer is NULL while the associated data size is greater than 0. - \return BAD_STATE_E if the key or nonce has not been set. + \return BAD_STATE_E if the key or nonce has not been set, or if the + associated data has already been set. The associated data must be + provided in a single call. \param a pointer to the initialized Ascon AEAD context. \param ad pointer to the associated data buffer. diff --git a/tests/api/test_ascon.c b/tests/api/test_ascon.c index 04b439d6784..c88e3237fe2 100644 --- a/tests/api/test_ascon.c +++ b/tests/api/test_ascon.c @@ -387,6 +387,7 @@ int test_ascon_aead128_edge_cases(void) * - wc_AsconAEAD128_SetNonce: a == NULL || nonce == NULL * - wc_AsconAEAD128_SetAD: a == NULL || (ad == NULL && adSz > 0) * !keySet || !nonceSet + * adSet (AD must be set in a single call) * - wc_AsconAEAD128_EncryptUpdate: a == NULL || (in == NULL && inSz > 0) * !keySet || !nonceSet || !adSet * - wc_AsconAEAD128_EncryptFinal: a == NULL || tag == NULL @@ -476,8 +477,12 @@ int test_ascon_decision_coverage(void) ExpectIntEQ(wc_AsconAEAD128_SetAD(asconAEAD, NULL, sizeof(ad)), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); /* a != NULL, ad == NULL, adSz == 0 -> inner && false, proceeds past - * arg check to the state check (both keySet/nonceSet true here) */ + * arg check to the state check (both keySet/nonceSet true here); key + * and nonce are set above so this succeeds and marks adSet. */ ExpectIntEQ(wc_AsconAEAD128_SetAD(asconAEAD, NULL, 0), 0); + /* adSet now true -> a repeat call is rejected: AD is one-shot */ + ExpectIntEQ(wc_AsconAEAD128_SetAD(asconAEAD, ad, sizeof(ad)), + WC_NO_ERR_TRACE(BAD_STATE_E)); /* State guard on SetAD: !keySet || !nonceSet, using freshly Init'd * contexts to isolate each operand. */ @@ -502,12 +507,15 @@ int test_ascon_decision_coverage(void) ExpectIntEQ(wc_AsconAEAD128_SetKey(&stateCtx, key), 0); ExpectIntEQ(wc_AsconAEAD128_SetNonce(&stateCtx, nonce), 0); ExpectIntEQ(wc_AsconAEAD128_SetAD(&stateCtx, ad, sizeof(ad)), 0); + /* adSet now true -> a repeat call is rejected: AD is one-shot */ + ExpectIntEQ(wc_AsconAEAD128_SetAD(&stateCtx, ad, sizeof(ad)), + WC_NO_ERR_TRACE(BAD_STATE_E)); wc_AsconAEAD128_Clear(&stateCtx); } - /* Finish setting up asconAEAD (key+nonce already set above) with AD - * so it is fully configured for the Encrypt/Decrypt tests below. */ - ExpectIntEQ(wc_AsconAEAD128_SetAD(asconAEAD, ad, sizeof(ad)), 0); + /* asconAEAD is already fully configured for the Encrypt/Decrypt tests + * below: key and nonce set above, and adSet marked by the + * SetAD(asconAEAD, NULL, 0) call above. */ /* ---------------------------------------------------------------- */ /* wc_AsconAEAD128_EncryptUpdate: */ diff --git a/wolfcrypt/src/ascon.c b/wolfcrypt/src/ascon.c index 203d7174b67..6bf398ad404 100644 --- a/wolfcrypt/src/ascon.c +++ b/wolfcrypt/src/ascon.c @@ -347,6 +347,8 @@ int wc_AsconAEAD128_SetAD(wc_AsconAEAD128* a, const byte* ad, return BAD_FUNC_ARG; if (!a->keySet || !a->nonceSet) /* key and nonce must be set before */ return BAD_STATE_E; + if (a->adSet) /* associated data is processed in a single call */ + return BAD_STATE_E; permutation(&a->state, ASCON_AEAD128_ROUNDS_PA); a->state.s64[3] ^= a->key[0]; From 4046cfe914cf1bf48c564c495dd578396ec59d1f Mon Sep 17 00:00:00 2001 From: Hideki Miyazaki Date: Sun, 30 Aug 2026 17:06:43 -0400 Subject: [PATCH 2/2] Addressed review comments --- doc/dox_comments/header_files/ascon.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/dox_comments/header_files/ascon.h b/doc/dox_comments/header_files/ascon.h index 657a1038c1d..70895f56430 100644 --- a/doc/dox_comments/header_files/ascon.h +++ b/doc/dox_comments/header_files/ascon.h @@ -237,7 +237,8 @@ int wc_AsconAEAD128_SetNonce(wc_AsconAEAD128* a, const byte* nonce); \return 0 on success. \return BAD_FUNC_ARG if the context is NULL, or if the associated data - pointer is NULL while the associated data size is greater than 0. + pointer is NULL while adSz is greater than 0. A NULL associated + data pointer is permitted when adSz is 0. \return BAD_STATE_E if the key or nonce has not been set, or if the associated data has already been set. The associated data must be provided in a single call.