From 9f323ae83a38b4be09d227be2f7c2af59eba5b0b 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 d3ea74641e96b8a3b6014fbef12922f4105cdf6c 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 23f576781b7..4524fcb4b10 100644 --- a/wolfcrypt/src/evp.c +++ b/wolfcrypt/src/evp.c @@ -8345,8 +8345,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 8bec57469840788c19d825156f6bde5657d3d06c 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 4524fcb4b10..ce405371437 100644 --- a/wolfcrypt/src/evp.c +++ b/wolfcrypt/src/evp.c @@ -13056,7 +13056,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; @@ -13092,17 +13095,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 */ @@ -13112,7 +13115,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); @@ -13126,7 +13129,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) { @@ -13142,7 +13145,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 ++; @@ -13152,7 +13155,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 93320f2fa89843f6b7cd5963b88fd0dee7735786 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 0ea1b04f2b7..bed3cf2e901 100644 --- a/src/internal.c +++ b/src/internal.c @@ -36925,6 +36925,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;