From 754f5cfe4161f886f83d4fae4358fd0c412219da Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Mon, 1 Jun 2026 18:31:02 +0200 Subject: [PATCH 01/11] F-5606: enforce DTLS 1.3 KeyUpdate 2^48-1 sending epoch ceiling RFC 9147 Section 4.2.1 limits the DTLS 1.3 epoch to 2^48-1. SendTls13KeyUpdate now refuses to send a KeyUpdate when the sending epoch is already at the maximum, and Dtls13KeyUpdateAckReceived rejects an epoch that would exceed the limit after incrementing (previously only a full 64-bit wrap to zero was checked). --- src/dtls13.c | 14 ++++++++++++-- src/tls13.c | 23 +++++++++++++++++++++-- wolfssl/internal.h | 5 +++++ 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/dtls13.c b/src/dtls13.c index 995d144119f..299dafca920 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -2720,17 +2720,27 @@ static int Dtls13RtxIsTrackedByRn(const Dtls13RtxRecord* r, w64wrapper epoch, static int Dtls13KeyUpdateAckReceived(WOLFSSL* ssl) { int ret; + /* Validate on a local copy so ssl->dtls13Epoch is left untouched when a + * check fails. */ + w64wrapper newEpoch = ssl->dtls13Epoch; ret = DeriveTls13Keys(ssl, update_traffic_key, ENCRYPT_SIDE_ONLY, 1); if (ret != 0) return ret; - w64Increment(&ssl->dtls13Epoch); + w64Increment(&newEpoch); /* Epoch wrapped up */ - if (w64IsZero(ssl->dtls13Epoch)) + if (w64IsZero(newEpoch)) return BAD_STATE_E; + /* RFC 9147 Section 4.2.1: the epoch must not exceed 2^48-1. */ + if (w64GT(newEpoch, + w64From32(DTLS13_EPOCH_MAX_HI32, DTLS13_EPOCH_MAX_LO32))) + return BAD_STATE_E; + + ssl->dtls13Epoch = newEpoch; + return Dtls13SetEpochKeys(ssl, ssl->dtls13Epoch, ENCRYPT_SIDE_ONLY); } diff --git a/src/tls13.c b/src/tls13.c index 8d7efb9df40..23fa8e25d26 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -12172,8 +12172,16 @@ int SendTls13KeyUpdate(WOLFSSL* ssl) WOLFSSL_ENTER("SendTls13KeyUpdate"); #ifdef WOLFSSL_DTLS13 - if (ssl->options.dtls) + if (ssl->options.dtls) { + /* RFC 9147 Section 4.2.1: do not send a KeyUpdate that would advance + * the sending epoch beyond 2^48-1. */ + if (w64GTE(ssl->dtls13Epoch, + w64From32(DTLS13_EPOCH_MAX_HI32, DTLS13_EPOCH_MAX_LO32))) { + WOLFSSL_MSG("DTLS 1.3 sending epoch at maximum; refusing KeyUpdate"); + return BAD_STATE_E; + } i = Dtls13GetRlHeaderLength(ssl, 1) + DTLS_HANDSHAKE_HEADER_SZ; + } #endif /* WOLFSSL_DTLS13 */ outputSz = OPAQUE8_LEN + MAX_MSG_EXTRA; @@ -12307,7 +12315,18 @@ static int DoTls13KeyUpdate(WOLFSSL* ssl, const byte* input, word32* inOutIdx, #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls) { - w64Increment(&ssl->dtls13PeerEpoch); + w64wrapper newEpoch = ssl->dtls13PeerEpoch; + w64Increment(&newEpoch); + + /* RFC 9147 Section 4.2.1: the epoch must not exceed 2^48-1. Reject a + * peer KeyUpdate that would advance the receiving epoch past the + * limit. Validate on a local copy so ssl->dtls13PeerEpoch is left + * untouched when the check fails. */ + if (w64GT(newEpoch, + w64From32(DTLS13_EPOCH_MAX_HI32, DTLS13_EPOCH_MAX_LO32))) + return BAD_STATE_E; + + ssl->dtls13PeerEpoch = newEpoch; ret = Dtls13SetEpochKeys(ssl, ssl->dtls13PeerEpoch, DECRYPT_SIDE_ONLY); if (ret != 0) diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 794808f38b4..fb5f8a3233f 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -5948,6 +5948,11 @@ enum { DTLS13_EPOCH_TRAFFIC0 = 3 }; +/* RFC 9147 Section 4.2.1: the DTLS 1.3 epoch is a 48-bit value and must not + * exceed 2^48-1. Expressed as the high/low 32-bit halves of a w64wrapper. */ +#define DTLS13_EPOCH_MAX_HI32 0x0000FFFFU +#define DTLS13_EPOCH_MAX_LO32 0xFFFFFFFFU + /* 64-bit epoch + 64-bit sequence number */ #define DTLS13_RN_SIZE (OPAQUE64_LEN + OPAQUE64_LEN) /* Maximum number of ACK records allowed in an ACK message */ From 7047d4e01ad8546023167660c754a393f0e59298 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Mon, 1 Jun 2026 18:31:20 +0200 Subject: [PATCH 02/11] F-5590: reject oversized length in wolfSSL_CRYPTO_memcmp The size_t length was cast directly to the int taken by ConstantCompare. On 64-bit builds a size with a negative low 32-bit value (e.g. 0x80000000) became a negative length, so ConstantCompare ran zero iterations and returned 0 (equal) without comparing. Reject size > INT_MAX with a non-zero (mismatch) result before narrowing. --- src/ssl.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/ssl.c b/src/ssl.c index efbbf3074e9..1dc5717476e 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -16199,9 +16199,24 @@ int wolfSSL_CTX_set_servername_arg(WOLFSSL_CTX* ctx, void* arg) int wolfSSL_CRYPTO_memcmp(const void *a, const void *b, size_t size) { + int ret = 0; + int chunk; + const byte* pa = (const byte*)a; + const byte* pb = (const byte*)b; + if (!a || !b) return -1; - return ConstantCompare((const byte*)a, (const byte*)b, (int)size); + /* ConstantCompare takes an int length. Compare in chunks of at most + * INT_MAX so a size that does not fit in an int is not narrowed into a + * negative or truncated length, which could wrongly report equality. */ + while (size > 0) { + chunk = (size > (size_t)INT_MAX) ? INT_MAX : (int)size; + ret |= ConstantCompare(pa, pb, chunk); + pa += chunk; + pb += chunk; + size -= (size_t)chunk; + } + return ret; } unsigned long wolfSSL_ERR_peek_last_error(void) From abb59434662c5b04851eb774261f52bdc1ab8d8b Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Mon, 1 Jun 2026 18:31:31 +0200 Subject: [PATCH 03/11] F-4594: return non-zero from wolfSSL_get_verify_result on NULL ssl WOLFSSL_FAILURE is 0, which equals X509_V_OK, so a NULL ssl was indistinguishable from successful verification under the standard "SSL_get_verify_result(ssl) \!= X509_V_OK" idiom. Return WOLFSSL_X509_V_ERR_APPLICATION_VERIFICATION (50, matching the OpenSSL compat value) instead, and add it to the X509 verify-error enum. --- src/ssl.c | 5 ++++- tests/api.c | 5 ++++- wolfssl/ssl.h | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/ssl.c b/src/ssl.c index 1dc5717476e..bd192f78af2 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -13025,7 +13025,10 @@ size_t wolfSSL_get_peer_finished(const WOLFSSL *ssl, void *buf, size_t count) long wolfSSL_get_verify_result(const WOLFSSL *ssl) { if (ssl == NULL) { - return WOLFSSL_FAILURE; + /* Return a non-zero error so the OpenSSL-idiomatic + * "!= X509_V_OK" check does not mistake a NULL ssl for a + * successful verification (X509_V_OK is 0). */ + return WOLFSSL_X509_V_ERR_APPLICATION_VERIFICATION; } return (long)ssl->peerVerifyRet; diff --git a/tests/api.c b/tests/api.c index a2873fd747d..183649f15c3 100644 --- a/tests/api.c +++ b/tests/api.c @@ -19069,7 +19069,10 @@ static int test_wolfSSL_verify_result(void) WOLFSSL_CTX* ctx = NULL; long result = 0xDEADBEEF; - ExpectIntEQ(WC_NO_ERR_TRACE(WOLFSSL_FAILURE), wolfSSL_get_verify_result(ssl)); + /* A NULL ssl returns a non-zero verify error (not X509_V_OK) so the + * OpenSSL-idiomatic "!= X509_V_OK" check is not fooled. See F-4594. */ + ExpectIntEQ(WOLFSSL_X509_V_ERR_APPLICATION_VERIFICATION, + wolfSSL_get_verify_result(ssl)); ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method())); ExpectNotNull(ssl = SSL_new(ctx)); diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 2281bb2f264..f46d0009afa 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -2732,6 +2732,7 @@ enum { WOLFSSL_X509_V_ERR_PATH_LENGTH_EXCEEDED = 25, WOLFSSL_X509_V_ERR_CERT_REJECTED = 28, WOLFSSL_X509_V_ERR_SUBJECT_ISSUER_MISMATCH = 29, + WOLFSSL_X509_V_ERR_APPLICATION_VERIFICATION = 50, WOLFSSL_X509_V_ERR_HOSTNAME_MISMATCH = 62, WOLFSSL_X509_V_ERR_IP_ADDRESS_MISMATCH = 64, WOLFSSL_X509_V_ERR_INVALID_CA = 79, From 7a0eca841f4cf77bd4f76ff05d31d8ac958f66e1 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Mon, 1 Jun 2026 18:31:46 +0200 Subject: [PATCH 04/11] F-5628: reject max_fragment_length response that differs from request TLSX_MFL_Parse only checked that the extension was requested and that the value was recognized, not that the server echoed the requested value. Per RFC 6066 Section 4, compare the ServerHello value against the locally stored request and abort with a fatal illegal_parameter alert on mismatch. --- src/tls.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/tls.c b/src/tls.c index 62118d0678b..bf58e2ee2da 100644 --- a/src/tls.c +++ b/src/tls.c @@ -3251,9 +3251,27 @@ static int TLSX_MFL_Parse(WOLFSSL* ssl, const byte* input, word16 length, #ifdef WOLFSSL_OLD_UNSUPPORTED_EXTENSION (void) isRequest; #else - if (!isRequest) + if (!isRequest) { + TLSX* extension; + if (TLSX_CheckUnsupportedExtension(ssl, TLSX_MAX_FRAGMENT_LENGTH)) return TLSX_HandleUnsupportedExtension(ssl); + + /* RFC 6066 Section 4: the server's response value must match the + * value the client requested. The request may have been configured on + * the WOLFSSL object or inherited from the WOLFSSL_CTX. */ + extension = TLSX_Find(ssl->extensions, TLSX_MAX_FRAGMENT_LENGTH); + if (extension == NULL) { + extension = TLSX_Find(ssl->ctx->extensions, + TLSX_MAX_FRAGMENT_LENGTH); + } + if (extension == NULL || extension->data == NULL || + ((byte*)extension->data)[0] != *input) { + SendAlert(ssl, alert_fatal, illegal_parameter); + WOLFSSL_ERROR_VERBOSE(UNKNOWN_MAX_FRAG_LEN_E); + return UNKNOWN_MAX_FRAG_LEN_E; + } + } #endif switch (*input) { From 6e7bf5fa66be4abcc8ca39445c65d04688f9ae8c Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Mon, 1 Jun 2026 18:31:58 +0200 Subject: [PATCH 05/11] F-5629: reject ServerHello ALPN response with multiple protocols A response ProtocolNameList was passed whole to ALPN_find_match, which accepts the first configured match, so a server could return more than one protocol. Per RFC 7301 Section 3.1 the ServerHello list must contain exactly one name; enforce that the single name spans the whole list and reject otherwise with a fatal illegal_parameter alert. --- src/tls.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/tls.c b/src/tls.c index bf58e2ee2da..c23f3f7a497 100644 --- a/src/tls.c +++ b/src/tls.c @@ -2065,6 +2065,15 @@ static int TLSX_ALPN_ParseAndSet(WOLFSSL *ssl, const byte *input, word16 length, byte sel_len = 0; TLSX *extension = NULL; + /* RFC 7301 Section 3.1: a ServerHello ALPN extension MUST contain + * exactly one protocol name. The first name's length byte plus its + * payload must therefore span the whole list. */ + if ((word16)(input[offset] + OPAQUE8_LEN) != size) { + SendAlert(ssl, alert_fatal, illegal_parameter); + WOLFSSL_ERROR_VERBOSE(BUFFER_ERROR); + return BUFFER_ERROR; + } + r = ALPN_find_match(ssl, &extension, &sel, &sel_len, input + offset, size); if (r != 0) return r; From 18b86922d9119e6657e32c345ad4327999de59cb Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Mon, 1 Jun 2026 18:32:10 +0200 Subject: [PATCH 06/11] F-5630: require signature_algorithms in TLS 1.3 CertificateRequest DoTls13CertificateRequest parsed the extensions but never verified the mandatory signature_algorithms extension was present. A request with none left peerSuites.hashSigAlgoSz at zero and was accepted. Per RFC 8446 Section 4.3.2, reject such a request with a fatal missing_extension alert before selecting a certificate response. --- src/tls13.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/tls13.c b/src/tls13.c index 23fa8e25d26..775a76be40a 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -6134,6 +6134,14 @@ static int DoTls13CertificateRequest(WOLFSSL* ssl, const byte* input, } *inOutIdx += len; + /* RFC 8446 Section 4.3.2: the signature_algorithms extension MUST be + * present in a CertificateRequest. */ + if (peerSuites.hashSigAlgoSz == 0) { + SendAlert(ssl, alert_fatal, missing_extension); + WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); + return INVALID_PARAMETER; + } + #ifdef WOLFSSL_CERT_SETUP_CB if ((ret = CertSetupCbWrapper(ssl)) != 0) return ret; From 0089fa852ed5a9f85c167d628b12bdf5822addeb Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Mon, 1 Jun 2026 18:32:25 +0200 Subject: [PATCH 07/11] F-5631: reject zero-length/duplicate post-handshake CertReq context DoTls13CertificateRequest rejected a non-empty context during the handshake but did the complementary check for post-handshake auth. Per RFC 8446 Section 4.3.2 a post-handshake certificate_request_context must be non-empty and unique; reject a zero-length context and one that duplicates a still-pending request context with a fatal illegal_parameter alert. --- src/tls13.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/tls13.c b/src/tls13.c index 775a76be40a..063b59b65eb 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -6100,11 +6100,24 @@ static int DoTls13CertificateRequest(WOLFSSL* ssl, const byte* input, return BUFFER_ERROR; /* INVALID_PARAMETER does not map to illegal_parameter in the central * alert path, so emit the alert explicitly before returning. */ - if (ssl->options.connectState < FINISHED_DONE && len > 0) { + if (ssl->options.connectState < FINISHED_DONE) { + /* RFC 8446 Section 4.3.2: in the handshake the context is zero + * length. */ + if (len > 0) { + SendAlert(ssl, alert_fatal, illegal_parameter); + WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); + return INVALID_PARAMETER; + } + } +#ifdef WOLFSSL_POST_HANDSHAKE_AUTH + else if (len == 0) { + /* RFC 8446 Section 4.3.2: a post-handshake CertificateRequest context + * MUST be non-empty and unique for the connection. */ SendAlert(ssl, alert_fatal, illegal_parameter); WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); return INVALID_PARAMETER; } +#endif #ifdef WOLFSSL_POST_HANDSHAKE_AUTH /* Remember the request context bytes; the CertReqCtx allocation and @@ -6113,6 +6126,18 @@ static int DoTls13CertificateRequest(WOLFSSL* ssl, const byte* input, */ reqCtxLen = len; reqCtxData = input + *inOutIdx; + /* Reject a context that duplicates one still pending on the connection. */ + if (ssl->options.connectState >= FINISHED_DONE) { + CertReqCtx* dup; + for (dup = ssl->certReqCtx; dup != NULL; dup = dup->next) { + if (dup->len == reqCtxLen && + XMEMCMP(&dup->ctx, reqCtxData, reqCtxLen) == 0) { + SendAlert(ssl, alert_fatal, illegal_parameter); + WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER); + return INVALID_PARAMETER; + } + } + } #endif *inOutIdx += len; From 289a2670f40f5dc5adbd120113fbfef1ff550467 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Mon, 1 Jun 2026 18:32:36 +0200 Subject: [PATCH 08/11] F-5591: reject negative group count in group setters wolfSSL_CTX_set_groups/wolfSSL_set_groups only rejected counts above WOLFSSL_MAX_GROUP_COUNT; a negative count skipped the copy loop and was cast to byte (e.g. 255) into numGroups, which InitSSL later trusts for a fixed-size copy. Reject count <= 0 in both, and in the set1_groups OpenSSL-compat wrappers. --- src/ssl.c | 8 ++++---- src/tls.c | 16 ++++++++++------ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/ssl.c b/src/ssl.c index bd192f78af2..395352835c4 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -3319,8 +3319,8 @@ int wolfSSL_CTX_set1_groups(WOLFSSL_CTX* ctx, int* groups, int i; int _groups[WOLFSSL_MAX_GROUP_COUNT]; WOLFSSL_ENTER("wolfSSL_CTX_set1_groups"); - if (count == 0) { - WOLFSSL_MSG("Group count is zero"); + if (count <= 0) { + WOLFSSL_MSG("Group count is not positive"); return WOLFSSL_FAILURE; } if (count > WOLFSSL_MAX_GROUP_COUNT) { @@ -3358,8 +3358,8 @@ int wolfSSL_set1_groups(WOLFSSL* ssl, int* groups, int count) int i; int _groups[WOLFSSL_MAX_GROUP_COUNT]; WOLFSSL_ENTER("wolfSSL_CTX_set1_groups"); - if (count == 0) { - WOLFSSL_MSG("Group count is zero"); + if (count <= 0) { + WOLFSSL_MSG("Group count is not positive"); return WOLFSSL_FAILURE; } if (count > WOLFSSL_MAX_GROUP_COUNT) { diff --git a/src/tls.c b/src/tls.c index c23f3f7a497..2defa82509c 100644 --- a/src/tls.c +++ b/src/tls.c @@ -391,15 +391,17 @@ ProtocolVersion MakeTLSv1_3(void) * ctx SSL/TLS context object. * groups Array of groups. * count Number of groups in array. - * returns BAD_FUNC_ARG when ctx or groups is NULL, not using TLS v1.3 or - * count is greater than WOLFSSL_MAX_GROUP_COUNT and WOLFSSL_SUCCESS on success. + * returns BAD_FUNC_ARG when ctx or groups is NULL, not using TLS v1.3, count is + * not positive or count is greater than WOLFSSL_MAX_GROUP_COUNT and + * WOLFSSL_SUCCESS on success. */ int wolfSSL_CTX_set_groups(WOLFSSL_CTX* ctx, int* groups, int count) { int ret, i; WOLFSSL_ENTER("wolfSSL_CTX_set_groups"); - if (ctx == NULL || groups == NULL || count > WOLFSSL_MAX_GROUP_COUNT) + if (ctx == NULL || groups == NULL || count <= 0 || + count > WOLFSSL_MAX_GROUP_COUNT) return BAD_FUNC_ARG; if (!IsTLS_ex(ctx->method->version)) return BAD_FUNC_ARG; @@ -436,15 +438,17 @@ int wolfSSL_CTX_set_groups(WOLFSSL_CTX* ctx, int* groups, int count) * ssl SSL/TLS object. * groups Array of groups. * count Number of groups in array. - * returns BAD_FUNC_ARG when ssl or groups is NULL, not using TLS v1.3 or - * count is greater than WOLFSSL_MAX_GROUP_COUNT and WOLFSSL_SUCCESS on success. + * returns BAD_FUNC_ARG when ssl or groups is NULL, not using TLS v1.3, count is + * not positive or count is greater than WOLFSSL_MAX_GROUP_COUNT and + * WOLFSSL_SUCCESS on success. */ int wolfSSL_set_groups(WOLFSSL* ssl, int* groups, int count) { int ret, i; WOLFSSL_ENTER("wolfSSL_set_groups"); - if (ssl == NULL || groups == NULL || count > WOLFSSL_MAX_GROUP_COUNT) + if (ssl == NULL || groups == NULL || count <= 0 || + count > WOLFSSL_MAX_GROUP_COUNT) return BAD_FUNC_ARG; if (!IsTLS_ex(ssl->version)) return BAD_FUNC_ARG; From 118d3a8226a105532257e47e7eadba2918d93bca Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Mon, 1 Jun 2026 18:32:46 +0200 Subject: [PATCH 09/11] F-5583: evict oldest DTLS 1.3 epoch slot, not the last eligible one Dtls13NewEpochSlot never updated oldestNumber after picking a candidate, so every eligible epoch compared "older" than the sentinel and the last eligible slot was returned instead of the lowest epoch number. Update oldestNumber alongside oldest so the true minimum is evicted. --- src/dtls13.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dtls13.c b/src/dtls13.c index 299dafca920..0de419def8e 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -2369,7 +2369,6 @@ static Dtls13Epoch* Dtls13NewEpochSlot(WOLFSSL* ssl) w64wrapper oldestNumber; int i; - /* FIXME: add max function */ oldestNumber = w64From32((word32)-1, (word32)-1); oldest = NULL; @@ -2380,8 +2379,10 @@ static Dtls13Epoch* Dtls13NewEpochSlot(WOLFSSL* ssl) if (!w64Equal(e->epochNumber, ssl->dtls13Epoch) && !w64Equal(e->epochNumber, ssl->dtls13PeerEpoch) && - w64LT(e->epochNumber, oldestNumber)) + w64LT(e->epochNumber, oldestNumber)) { oldest = e; + oldestNumber = e->epochNumber; + } } if (oldest == NULL) From 108b120d7fd3271af2d086ce46b81907660d7f5f Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Mon, 1 Jun 2026 18:32:54 +0200 Subject: [PATCH 10/11] F-5632: zeroize Camellia key schedule on cipher free FreeCiphersSide freed cipher->cam with XFREE only, leaving the expanded key schedule and IV in freed heap memory. Call wc_CamelliaFree (which ForceZeros the context) before XFREE, matching the ARIA cleanup above. --- src/internal.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/internal.c b/src/internal.c index dcd8fa8ab69..ccea4c02714 100644 --- a/src/internal.c +++ b/src/internal.c @@ -3282,6 +3282,7 @@ static void FreeCiphersSide(Ciphers *cipher, void* heap) cipher->aria = NULL; #endif #ifdef HAVE_CAMELLIA + wc_CamelliaFree(cipher->cam); XFREE(cipher->cam, heap, DYNAMIC_TYPE_CIPHER); cipher->cam = NULL; #endif From da719da30c6724c5873e0ffeb59ed7fbfd262552 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Mon, 1 Jun 2026 18:33:04 +0200 Subject: [PATCH 11/11] F-4591: fix right-justification of short DH shared secret The constant-time path of _DH_compute_key (DH_compute_key_padded) had the XMEMMOVE source/dest swapped and used (padded_keySz - keySz) as the length instead of keySz, overwriting the secret with junk when keySz < padded_keySz. Move key[0..keySz-1] to the high end, matching the idiom used in tls.c/sniffer.c. --- src/pk.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pk.c b/src/pk.c index 131e1367e0b..bfc039e5d0f 100644 --- a/src/pk.c +++ b/src/pk.c @@ -4977,8 +4977,7 @@ static int _DH_compute_key(unsigned char* key, const WOLFSSL_BIGNUM* otherPub, * correctly. */ if (keySz < padded_keySz) { - XMEMMOVE(key, key + (padded_keySz - keySz), - padded_keySz - keySz); + XMEMMOVE(key + (padded_keySz - keySz), key, keySz); XMEMSET(key, 0, padded_keySz - keySz); keySz = padded_keySz; }