From fb8efa4ec8164e4cd70cc6eb54240f35cc38d23a Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 16 Jul 2026 08:47:02 +0000 Subject: [PATCH 1/4] Validate NULL parameters in wc_AesCbcEncryptWithKey (F-1376) wc_AesCbcEncryptWithKey did not check out/in/key/iv for NULL before calling wc_AesSetKey/wc_AesCbcEncrypt, unlike its counterpart wc_AesCbcDecryptWithKey. A NULL key can reach wc_AesSetKey implementations that XMEMCPY userKey without a NULL guard (e.g. the STM32 path), causing a crash at the API boundary. Add the same NULL guard wc_AesCbcDecryptWithKey uses. --- wolfcrypt/src/wc_encrypt.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wolfcrypt/src/wc_encrypt.c b/wolfcrypt/src/wc_encrypt.c index 240011ac047..d91ffed1e6b 100644 --- a/wolfcrypt/src/wc_encrypt.c +++ b/wolfcrypt/src/wc_encrypt.c @@ -74,6 +74,10 @@ int wc_AesCbcEncryptWithKey(byte* out, const byte* in, word32 inSz, int ret = 0; WC_DECLARE_VAR(aes, Aes, 1, 0); + if (out == NULL || in == NULL || key == NULL || iv == NULL) { + return BAD_FUNC_ARG; + } + WC_ALLOC_VAR_EX(aes, Aes, 1, NULL, DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E); From 94e84b0e7a8def5f7d02a7299535ecfe6bb3348c Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 16 Jul 2026 08:47:47 +0000 Subject: [PATCH 2/4] Fix ChaCha20 EVP counter reconstruction shift UB (F-2653) wolfSSL_EVP_CipherInit reconstructed the 32-bit ChaCha20 counter with (word32)(iv[1] << 8) etc., which promotes the unsigned char to int and shifts before the cast. On 16-bit int platforms iv[1] << 8 is signed-overflow UB and iv[2] << 16 / iv[3] << 24 are UB unconditionally (shift >= int width). Even on 32-bit int, iv[3] << 24 with the high bit set is signed-overflow UB. Cast each byte to word32 before shifting so the computation is well-defined. --- wolfcrypt/src/evp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wolfcrypt/src/evp.c b/wolfcrypt/src/evp.c index ced8033430f..dff3856aa02 100644 --- a/wolfcrypt/src/evp.c +++ b/wolfcrypt/src/evp.c @@ -8347,8 +8347,8 @@ void wolfSSL_EVP_init(void) * combines them to a new iv. EVP is given exactly *one* iv, * so to pass it into chacha, we have to revert that first. * The counter comes first in little-endian */ - word32 counter = (word32)iv[0] + (word32)(iv[1] << 8) + - (word32)(iv[2] << 16) + (word32)(iv[3] << 24); + word32 counter = (word32)iv[0] | ((word32)iv[1] << 8) | + ((word32)iv[2] << 16) | ((word32)iv[3] << 24); if (wc_Chacha_SetIV(&ctx->cipher.chacha, iv + sizeof(counter), counter) != 0) { From 48a7d1f217323f21e0639760d092e726446c9afd Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 16 Jul 2026 08:47:47 +0000 Subject: [PATCH 3/4] Fix strict-aliasing violation in PrintPubKeyDH length (F-1972) PrintPubKeyDH declared length as word32 but passed (int*)&length to GetSequence/GetLength, which write through an int*. On platforms where sizeof(int) != sizeof(word32) this updates only part of the variable, leaving stale bytes. Declare length as int (matching the callee out-parameter type) and drop the (int*) casts; the values are non-negative ASN.1 lengths already used via (int) casts and pointer arithmetic. --- wolfcrypt/src/evp.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/wolfcrypt/src/evp.c b/wolfcrypt/src/evp.c index dff3856aa02..bfdfa99bb64 100644 --- a/wolfcrypt/src/evp.c +++ b/wolfcrypt/src/evp.c @@ -13058,7 +13058,10 @@ static int PrintPubKeyDH(WOLFSSL_BIO* out, const byte* pkey, int pkeySz, byte buff[WOLFSSL_EVP_EXPONENT_PRINT_MAX] = { 0 }; int res = WC_NO_ERR_TRACE(WOLFSSL_FAILURE); - word32 length; + /* int (not word32) to match the int* out-parameter of GetSequence/GetLength; + * casting &length aliased a word32 and corrupted it on platforms where + * sizeof(int) != sizeof(word32). Values are non-negative ASN.1 lengths. */ + int length; word32 inOutIdx; word32 oid; byte tagFound; @@ -13094,17 +13097,17 @@ static int PrintPubKeyDH(WOLFSSL_BIO* out, const byte* pkey, int pkeySz, int idx; int wsz; - if (GetSequence(pkey, &inOutIdx, (int*)&length, (word32)pkeySz) < 0) { + if (GetSequence(pkey, &inOutIdx, &length, (word32)pkeySz) < 0) { break; } - if (GetSequence(pkey, &inOutIdx, (int*)&length, (word32)pkeySz) < 0) { + if (GetSequence(pkey, &inOutIdx, &length, (word32)pkeySz) < 0) { break; } if (GetObjectId(pkey, &inOutIdx, &oid, oidIgnoreType, (word32)pkeySz) < 0) { break; } - if (GetSequence(pkey, &inOutIdx, (int*)&length, (word32)pkeySz) < 0) { + if (GetSequence(pkey, &inOutIdx, &length, (word32)pkeySz) < 0) { break; } /* get prime element */ @@ -13114,7 +13117,7 @@ static int PrintPubKeyDH(WOLFSSL_BIO* out, const byte* pkey, int pkeySz, if (tagFound != ASN_INTEGER) { break; } - if (GetLength(pkey, &inOutIdx, (int*)&length, (word32)pkeySz) <= 0) { + if (GetLength(pkey, &inOutIdx, &length, (word32)pkeySz) <= 0) { break; } prime = (byte*)(pkey + inOutIdx); @@ -13128,7 +13131,7 @@ static int PrintPubKeyDH(WOLFSSL_BIO* out, const byte* pkey, int pkeySz, if (tagFound != ASN_INTEGER) { break; } - if (GetLength(pkey, &inOutIdx, (int*)&length, (word32)pkeySz) <= 0) { + if (GetLength(pkey, &inOutIdx, &length, (word32)pkeySz) <= 0) { break; } if (length != 1) { @@ -13144,7 +13147,7 @@ static int PrintPubKeyDH(WOLFSSL_BIO* out, const byte* pkey, int pkeySz, if (tagFound != ASN_BIT_STRING) { break; } - if (GetLength(pkey, &inOutIdx, (int*)&length, (word32)pkeySz) <= 0) { + if (GetLength(pkey, &inOutIdx, &length, (word32)pkeySz) <= 0) { break; } inOutIdx ++; @@ -13154,7 +13157,7 @@ static int PrintPubKeyDH(WOLFSSL_BIO* out, const byte* pkey, int pkeySz, if (tagFound != ASN_INTEGER) { break; } - if (GetLength(pkey, &inOutIdx, (int*)&length, (word32)pkeySz) <= 0) { + if (GetLength(pkey, &inOutIdx, &length, (word32)pkeySz) <= 0) { break; } publicKeySz = (int)length; From 1ce3514698cad4eeb71c02fb4df8d5ca406d739e Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 16 Jul 2026 08:57:04 +0000 Subject: [PATCH 4/4] Send unexpected_message alert on duplicate TLS 1.3 handshake msg (F-4593) When a TLS 1.3 client received a second HelloRetryRequest, DoTls13ServerHello returned DUPLICATE_MSG_E without sending an alert. TranslateErrorToAlert had no mapping for DUPLICATE_MSG_E, so it returned invalid_alert and the dispatch tail skipped SendAlert - the connection aborted silently instead of with the RFC 8446-mandated unexpected_message alert. Map DUPLICATE_MSG_E to unexpected_message in TranslateErrorToAlert, alongside OUT_OF_ORDER_E. This mirrors the sanity-check path and covers any TLS 1.3 handler that returns DUPLICATE_MSG_E to the dispatch loop. --- src/internal.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/internal.c b/src/internal.c index 5914aa42033..7f6a1461c43 100644 --- a/src/internal.c +++ b/src/internal.c @@ -36950,6 +36950,7 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, case WC_NO_ERR_TRACE(BAD_CERTIFICATE_STATUS_ERROR): return bad_certificate_status_response; case WC_NO_ERR_TRACE(OUT_OF_ORDER_E): + case WC_NO_ERR_TRACE(DUPLICATE_MSG_E): return unexpected_message; default: return invalid_alert;