From 646ff6dde4f858b9c59617fbc92a14a508307163 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 15 Apr 2026 14:43:05 +0200 Subject: [PATCH 01/14] tls: fix TLSX_ALPN_GetSize word16 overflow (F-2128) Match the TLSX_SNI_GetSize pattern: use a word32 accumulator and return 0 if the aggregate size exceeds WOLFSSL_MAX_16BIT, so a large number of ALPN entries can no longer silently wrap the length computation. --- src/tls.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/tls.c b/src/tls.c index c6082612172..dde6a335a98 100644 --- a/src/tls.c +++ b/src/tls.c @@ -1822,16 +1822,20 @@ static void TLSX_ALPN_FreeAll(ALPN *list, void* heap) static word16 TLSX_ALPN_GetSize(ALPN *list) { ALPN* alpn; - word16 length = OPAQUE16_LEN; /* list length */ + word32 length = OPAQUE16_LEN; /* list length */ while ((alpn = list)) { list = alpn->next; length++; /* protocol name length is on one byte */ - length += (word16)XSTRLEN(alpn->protocol_name); + length += (word32)XSTRLEN(alpn->protocol_name); + + if (length > WOLFSSL_MAX_16BIT) { + return 0; + } } - return length; + return (word16)length; } /** Writes the ALPN objects of a list in a buffer. */ @@ -14979,9 +14983,16 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType, isRequest); break; - case TLSX_APPLICATION_LAYER_PROTOCOL: - length += ALPN_GET_SIZE((ALPN*)extension->data); + case TLSX_APPLICATION_LAYER_PROTOCOL: { + word16 alpnSz = ALPN_GET_SIZE((ALPN*)extension->data); + /* 0 on non-empty list means 16-bit overflow. */ + if (alpnSz == 0 && extension->data != NULL) { + ret = LENGTH_ERROR; + break; + } + length += alpnSz; break; + } #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_SIGALG) case TLSX_SIGNATURE_ALGORITHMS: length += SA_GET_SIZE(extension->data); From 535aaf2325356d517acb761cf81e7bd33ed6dbc4 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 15 Apr 2026 14:43:26 +0200 Subject: [PATCH 02/14] tls: fix TLSX_TCA_GetSize word16 overflow (F-2131) Mirror the TLSX_SNI_GetSize pattern: accumulate into a word32 and return 0 when the aggregate size exceeds WOLFSSL_MAX_16BIT so large idSz values or many TCA entries no longer silently wrap to a small value that undersizes the TLSX_TCA_Write output buffer. --- src/tls.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/tls.c b/src/tls.c index dde6a335a98..e94634f149e 100644 --- a/src/tls.c +++ b/src/tls.c @@ -2961,7 +2961,7 @@ static void TLSX_TCA_FreeAll(TCA* list, void* heap) static word16 TLSX_TCA_GetSize(TCA* list) { TCA* tca; - word16 length = OPAQUE16_LEN; /* list length */ + word32 length = OPAQUE16_LEN; /* list length */ while ((tca = list)) { list = tca->next; @@ -2979,9 +2979,13 @@ static word16 TLSX_TCA_GetSize(TCA* list) length += OPAQUE16_LEN + tca->idSz; break; } + + if (length > WOLFSSL_MAX_16BIT) { + return 0; + } } - return length; + return (word16)length; } /** Writes the TCA objects of a list in a buffer. */ @@ -14941,8 +14945,15 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType, case TLSX_TRUSTED_CA_KEYS: /* TCA only sends the list on the request. */ - if (isRequest) - length += TCA_GET_SIZE((TCA*)extension->data); + if (isRequest) { + word16 tcaSz = TCA_GET_SIZE((TCA*)extension->data); + /* 0 on non-empty list means 16-bit overflow. */ + if (tcaSz == 0 && extension->data != NULL) { + ret = LENGTH_ERROR; + break; + } + length += tcaSz; + } break; case TLSX_MAX_FRAGMENT_LENGTH: From b8da926ae8f9d7eb20d94c8e3f4cc276d75f162b Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 15 Apr 2026 14:44:04 +0200 Subject: [PATCH 03/14] tls: fix TLSX_PreSharedKey_GetSize word16 overflow (F-2925) Both TLSX_PreSharedKey_GetSize and TLSX_PreSharedKey_GetSizeBinders accumulate per-identity bytes into a word16. With enough PSK entries (or large binderLen/identityLen values) the accumulator wraps silently and the caller allocates an undersized extension buffer, which TLSX_PreSharedKey_Write then overflows. Switch both accumulators to word32 and return LENGTH_ERROR when the total would exceed the 16-bit wire length field. --- src/tls.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/tls.c b/src/tls.c index e94634f149e..eed415b2128 100644 --- a/src/tls.c +++ b/src/tls.c @@ -11941,14 +11941,22 @@ static int TLSX_PreSharedKey_GetSize(PreSharedKey* list, byte msgType, { if (msgType == client_hello) { /* Length of identities + Length of binders. */ - word16 len = OPAQUE16_LEN + OPAQUE16_LEN; + word32 len = OPAQUE16_LEN + OPAQUE16_LEN; while (list != NULL) { /* Each entry has: identity, ticket age and binder. */ len += OPAQUE16_LEN + list->identityLen + OPAQUE32_LEN + - OPAQUE8_LEN + (word16)list->binderLen; + OPAQUE8_LEN + (word32)list->binderLen; + if (len > WOLFSSL_MAX_16BIT) { + WOLFSSL_ERROR_VERBOSE(LENGTH_ERROR); + return LENGTH_ERROR; + } list = list->next; } - *pSz += len; + if ((word32)*pSz + len > WOLFSSL_MAX_16BIT) { + WOLFSSL_ERROR_VERBOSE(LENGTH_ERROR); + return LENGTH_ERROR; + } + *pSz += (word16)len; return 0; } @@ -11971,7 +11979,7 @@ static int TLSX_PreSharedKey_GetSize(PreSharedKey* list, byte msgType, int TLSX_PreSharedKey_GetSizeBinders(PreSharedKey* list, byte msgType, word16* pSz) { - word16 len; + word32 len; if (msgType != client_hello) { WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E); @@ -11981,11 +11989,15 @@ int TLSX_PreSharedKey_GetSizeBinders(PreSharedKey* list, byte msgType, /* Length of all binders. */ len = OPAQUE16_LEN; while (list != NULL) { - len += OPAQUE8_LEN + (word16)list->binderLen; + len += OPAQUE8_LEN + (word32)list->binderLen; + if (len > WOLFSSL_MAX_16BIT) { + WOLFSSL_ERROR_VERBOSE(LENGTH_ERROR); + return LENGTH_ERROR; + } list = list->next; } - *pSz = len; + *pSz = (word16)len; return 0; } From 0f9fb2fb1db0bb969489ec902eba2fc820911d36 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 15 Apr 2026 14:44:24 +0200 Subject: [PATCH 04/14] tls: fix TLSX_CA_Names_GetSize word16 overflow (F-2927) The CA Names extension size accumulator was a word16. With enough CA entries (or large DER-encoded names) the running total can wrap silently, leaving TLSX_CA_Names_Write to overflow an undersized extension buffer. Match TLSX_SNI_GetSize: use a word32 accumulator and return 0 when the total exceeds WOLFSSL_MAX_16BIT. --- src/tls.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/tls.c b/src/tls.c index eed415b2128..9407cd42cee 100644 --- a/src/tls.c +++ b/src/tls.c @@ -7600,7 +7600,7 @@ static word16 TLSX_CA_Names_GetSize(void* data) { WOLFSSL* ssl = (WOLFSSL*)data; WOLF_STACK_OF(WOLFSSL_X509_NAME)* names; - word16 size = 0; + word32 size = 0; /* Length of names */ size += OPAQUE16_LEN; @@ -7610,11 +7610,14 @@ static word16 TLSX_CA_Names_GetSize(void* data) if (name != NULL) { /* 16-bit length | SEQ | Len | DER of name */ - size += (word16)(OPAQUE16_LEN + SetSequence(name->rawLen, seq) + + size += (word32)(OPAQUE16_LEN + SetSequence(name->rawLen, seq) + name->rawLen); + if (size > WOLFSSL_MAX_16BIT) { + return 0; + } } } - return size; + return (word16)size; } static word16 TLSX_CA_Names_Write(void* data, byte* output) @@ -15093,9 +15096,16 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType, #endif #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CA_NAMES) - case TLSX_CERTIFICATE_AUTHORITIES: - length += CAN_GET_SIZE(extension->data); + case TLSX_CERTIFICATE_AUTHORITIES: { + word16 canSz = CAN_GET_SIZE(extension->data); + /* 0 on non-empty list means 16-bit overflow. */ + if (canSz == 0 && extension->data != NULL) { + ret = LENGTH_ERROR; + break; + } + length += canSz; break; + } #endif #endif #ifdef WOLFSSL_SRTP From ef73b3b2337fdde653600fa9b6f35f998d456c32 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 15 Apr 2026 14:48:54 +0200 Subject: [PATCH 05/14] tests: add EMS resumption downgrade negative test (F-2915) Covers the HandleResumeHistory check that RFC 7627 Section 5.3 requires: if the original session used Extended Master Secret, the server MUST abort when a resumption ClientHello is received without EMS. The new memio test performs a TLS 1.2 handshake with EMS, saves the session, disables EMS on a fresh client, resumes with the saved session, and asserts the server returns EXT_MASTER_SECRET_NEEDED_E. --- tests/api.c | 1 + tests/api/test_tls_ext.c | 50 ++++++++++++++++++++++++++++++++++++++++ tests/api/test_tls_ext.h | 1 + 3 files changed, 52 insertions(+) diff --git a/tests/api.c b/tests/api.c index 05a7688d7ff..b9f25415700 100644 --- a/tests/api.c +++ b/tests/api.c @@ -37503,6 +37503,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_select_next_proto), #endif TEST_DECL(test_tls_ems_downgrade), + TEST_DECL(test_tls_ems_resumption_downgrade), TEST_DECL(test_wolfSSL_DisableExtendedMasterSecret), TEST_DECL(test_certificate_authorities_certificate_request), TEST_DECL(test_certificate_authorities_client_hello), diff --git a/tests/api/test_tls_ext.c b/tests/api/test_tls_ext.c index ad6053f7275..4dd736f5eb1 100644 --- a/tests/api/test_tls_ext.c +++ b/tests/api/test_tls_ext.c @@ -103,6 +103,56 @@ int test_tls_ems_downgrade(void) } +/* F-2915: resumption of an EMS session without EMS must abort with + * EXT_MASTER_SECRET_NEEDED_E (RFC 7627 Section 5.3). */ +int test_tls_ems_resumption_downgrade(void) +{ + EXPECT_DECLS; +#if !defined(WOLFSSL_NO_TLS12) && defined(HAVE_EXTENDED_MASTER) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(NO_SESSION_CACHE) + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + WOLFSSL_SESSION *session = NULL; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + ExpectNotNull(session = wolfSSL_get1_session(ssl_c)); + + wolfSSL_free(ssl_c); + ssl_c = NULL; + wolfSSL_free(ssl_s); + ssl_s = NULL; + test_memio_clear_buffer(&test_ctx, 0); + test_memio_clear_buffer(&test_ctx, 1); + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_set_session(ssl_c, session), WOLFSSL_SUCCESS); + /* Drop EMS from the resumption ClientHello to simulate a downgrade. */ + ExpectIntEQ(wolfSSL_DisableExtendedMasterSecret(ssl_c), WOLFSSL_SUCCESS); + + ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(wolfSSL_get_error(ssl_s, 0), + WC_NO_ERR_TRACE(EXT_MASTER_SECRET_NEEDED_E)); + + wolfSSL_SESSION_free(session); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + + int test_wolfSSL_DisableExtendedMasterSecret(void) { EXPECT_DECLS; diff --git a/tests/api/test_tls_ext.h b/tests/api/test_tls_ext.h index ec7a5223bef..8d9d8fcb9fd 100644 --- a/tests/api/test_tls_ext.h +++ b/tests/api/test_tls_ext.h @@ -23,6 +23,7 @@ #define TESTS_API_TEST_TLS_EXT_H int test_tls_ems_downgrade(void); +int test_tls_ems_resumption_downgrade(void); int test_wolfSSL_DisableExtendedMasterSecret(void); int test_certificate_authorities_certificate_request(void); int test_certificate_authorities_client_hello(void); From 01cc5b165549832f13bd903e50ba88542a09dc11 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 15 Apr 2026 14:52:02 +0200 Subject: [PATCH 06/14] tests: add ChaCha20-Poly1305 AEAD tag negative test (F-2921) Cover the Poly1305 ConstantCompare tag check in ChachaAEADDecrypt that no existing test was hitting (VERIFY_MAC_ERROR never expected in the suite). A memio-based TLS 1.2 handshake over ECDHE-RSA-CHACHA20-POLY1305 completes, the server's IORecv is then replaced with a wrapper that flips the final byte of the next record body so the forged Poly1305 tag no longer matches. The server's wolfSSL_read must surface VERIFY_MAC_ERROR. --- tests/api.c | 1 + tests/api/test_tls_ext.c | 66 ++++++++++++++++++++++++++++++++++++++++ tests/api/test_tls_ext.h | 1 + 3 files changed, 68 insertions(+) diff --git a/tests/api.c b/tests/api.c index b9f25415700..8590ad18d46 100644 --- a/tests/api.c +++ b/tests/api.c @@ -37504,6 +37504,7 @@ TEST_CASE testCases[] = { #endif TEST_DECL(test_tls_ems_downgrade), TEST_DECL(test_tls_ems_resumption_downgrade), + TEST_DECL(test_tls12_chacha20_poly1305_bad_tag), TEST_DECL(test_wolfSSL_DisableExtendedMasterSecret), TEST_DECL(test_certificate_authorities_certificate_request), TEST_DECL(test_certificate_authorities_client_hello), diff --git a/tests/api/test_tls_ext.c b/tests/api/test_tls_ext.c index 4dd736f5eb1..6643b385652 100644 --- a/tests/api/test_tls_ext.c +++ b/tests/api/test_tls_ext.c @@ -153,6 +153,72 @@ int test_tls_ems_resumption_downgrade(void) } +#if !defined(WOLFSSL_NO_TLS12) && \ + defined(BUILD_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) +static int test_chacha_bad_tag_trigger = 0; + +static int test_chacha_bad_tag_io_recv(WOLFSSL* ssl, char* buf, int sz, + void* ctx) +{ + int ret = test_memio_read_cb(ssl, buf, sz, ctx); + /* Tamper with a byte from the encrypted record payload on the first + * read that spans past the 5-byte TLS record header, so the Poly1305 + * authentication check no longer matches. */ + if (test_chacha_bad_tag_trigger && ret > 5) { + buf[ret - 1] ^= 0xFF; + test_chacha_bad_tag_trigger = 0; + } + return ret; +} +#endif + +/* F-2921: TLS 1.2 ChaCha20-Poly1305 must surface VERIFY_MAC_ERROR when + * the Poly1305 tag is corrupted. */ +int test_tls12_chacha20_poly1305_bad_tag(void) +{ + EXPECT_DECLS; +#if !defined(WOLFSSL_NO_TLS12) && \ + defined(BUILD_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + const char msg[] = "tamper me"; + char recvBuf[32]; + int ret; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + test_ctx.c_ciphers = test_ctx.s_ciphers = + "ECDHE-RSA-CHACHA20-POLY1305"; + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + wolfSSL_SSLSetIORecv(ssl_s, test_chacha_bad_tag_io_recv); + + ExpectIntEQ(wolfSSL_write(ssl_c, msg, (int)XSTRLEN(msg)), + (int)XSTRLEN(msg)); + + test_chacha_bad_tag_trigger = 1; + ret = wolfSSL_read(ssl_s, recvBuf, sizeof(recvBuf)); + ExpectIntLE(ret, 0); + ExpectIntEQ(wolfSSL_get_error(ssl_s, ret), + WC_NO_ERR_TRACE(VERIFY_MAC_ERROR)); + + test_chacha_bad_tag_trigger = 0; + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + + int test_wolfSSL_DisableExtendedMasterSecret(void) { EXPECT_DECLS; diff --git a/tests/api/test_tls_ext.h b/tests/api/test_tls_ext.h index 8d9d8fcb9fd..fb8a23d1a2a 100644 --- a/tests/api/test_tls_ext.h +++ b/tests/api/test_tls_ext.h @@ -24,6 +24,7 @@ int test_tls_ems_downgrade(void); int test_tls_ems_resumption_downgrade(void); +int test_tls12_chacha20_poly1305_bad_tag(void); int test_wolfSSL_DisableExtendedMasterSecret(void); int test_certificate_authorities_certificate_request(void); int test_certificate_authorities_client_hello(void); From d97d0370d142de87fc5cff9b8de91f0fa7d2e064 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 15 Apr 2026 14:53:38 +0200 Subject: [PATCH 07/14] tests: add TLS 1.3 null cipher HMAC negative test (F-2916) Tls13IntegrityOnly_Decrypt was completely untouched by existing tests, so any mutation of its ConstantCompare would pass CI. Add a memio TLS 1.3 handshake over TLS13-SHA256-SHA256 (integrity-only NULL cipher), then corrupt the final byte of the next record body via an IORecv wrapper and assert the server surfaces DECRYPT_ERROR. --- tests/api.c | 1 + tests/api/test_tls_ext.c | 65 ++++++++++++++++++++++++++++++++++++++++ tests/api/test_tls_ext.h | 1 + 3 files changed, 67 insertions(+) diff --git a/tests/api.c b/tests/api.c index 8590ad18d46..8e4b76d029b 100644 --- a/tests/api.c +++ b/tests/api.c @@ -37505,6 +37505,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_tls_ems_downgrade), TEST_DECL(test_tls_ems_resumption_downgrade), TEST_DECL(test_tls12_chacha20_poly1305_bad_tag), + TEST_DECL(test_tls13_null_cipher_bad_hmac), TEST_DECL(test_wolfSSL_DisableExtendedMasterSecret), TEST_DECL(test_certificate_authorities_certificate_request), TEST_DECL(test_certificate_authorities_client_hello), diff --git a/tests/api/test_tls_ext.c b/tests/api/test_tls_ext.c index 6643b385652..976a3d94900 100644 --- a/tests/api/test_tls_ext.c +++ b/tests/api/test_tls_ext.c @@ -219,6 +219,71 @@ int test_tls12_chacha20_poly1305_bad_tag(void) } +#if defined(WOLFSSL_TLS13) && defined(HAVE_NULL_CIPHER) && \ + defined(BUILD_TLS_SHA256_SHA256) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) +static int test_tls13_null_bad_hmac_trigger = 0; + +static int test_tls13_null_bad_hmac_io_recv(WOLFSSL* ssl, char* buf, int sz, + void* ctx) +{ + int ret = test_memio_read_cb(ssl, buf, sz, ctx); + /* Tamper with a byte from the encrypted record payload on the first + * read that spans past the 5-byte TLS record header, so the HMAC tag + * check in Tls13IntegrityOnly_Decrypt no longer matches. */ + if (test_tls13_null_bad_hmac_trigger && ret > 5) { + buf[ret - 1] ^= 0xFF; + test_tls13_null_bad_hmac_trigger = 0; + } + return ret; +} +#endif + +/* F-2916: TLS 1.3 integrity-only decryption must surface DECRYPT_ERROR + * when the HMAC tag is corrupted. */ +int test_tls13_null_cipher_bad_hmac(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_TLS13) && defined(HAVE_NULL_CIPHER) && \ + defined(BUILD_TLS_SHA256_SHA256) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + const char msg[] = "integrity only"; + char recvBuf[32]; + int ret; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + test_ctx.c_ciphers = test_ctx.s_ciphers = "TLS13-SHA256-SHA256"; + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + wolfSSL_SSLSetIORecv(ssl_s, test_tls13_null_bad_hmac_io_recv); + + ExpectIntEQ(wolfSSL_write(ssl_c, msg, (int)XSTRLEN(msg)), + (int)XSTRLEN(msg)); + + test_tls13_null_bad_hmac_trigger = 1; + ret = wolfSSL_read(ssl_s, recvBuf, sizeof(recvBuf)); + ExpectIntLE(ret, 0); + ExpectIntEQ(wolfSSL_get_error(ssl_s, ret), + WC_NO_ERR_TRACE(DECRYPT_ERROR)); + + test_tls13_null_bad_hmac_trigger = 0; + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + + int test_wolfSSL_DisableExtendedMasterSecret(void) { EXPECT_DECLS; diff --git a/tests/api/test_tls_ext.h b/tests/api/test_tls_ext.h index fb8a23d1a2a..275888adf01 100644 --- a/tests/api/test_tls_ext.h +++ b/tests/api/test_tls_ext.h @@ -25,6 +25,7 @@ int test_tls_ems_downgrade(void); int test_tls_ems_resumption_downgrade(void); int test_tls12_chacha20_poly1305_bad_tag(void); +int test_tls13_null_cipher_bad_hmac(void); int test_wolfSSL_DisableExtendedMasterSecret(void); int test_certificate_authorities_certificate_request(void); int test_certificate_authorities_client_hello(void); From 920e175dd62bb1c6304ce6546bfee628a4d5c897 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 15 Apr 2026 14:58:21 +0200 Subject: [PATCH 08/14] tests: add SCR verify_data mismatch test (F-2913, F-2914) Cover both branches of TLSX_SecureRenegotiation_Parse's ConstantCompare against the cached Finished verify_data: a single memio test loops over client-side and server-side corruption, renegotiates, and asserts the offending peer surfaces SECURE_RENEGOTIATION_E. --- tests/api.c | 1 + tests/api/test_tls_ext.c | 66 ++++++++++++++++++++++++++++++++++++++++ tests/api/test_tls_ext.h | 1 + 3 files changed, 68 insertions(+) diff --git a/tests/api.c b/tests/api.c index 8e4b76d029b..3792778f525 100644 --- a/tests/api.c +++ b/tests/api.c @@ -37506,6 +37506,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_tls_ems_resumption_downgrade), TEST_DECL(test_tls12_chacha20_poly1305_bad_tag), TEST_DECL(test_tls13_null_cipher_bad_hmac), + TEST_DECL(test_scr_verify_data_mismatch), TEST_DECL(test_wolfSSL_DisableExtendedMasterSecret), TEST_DECL(test_certificate_authorities_certificate_request), TEST_DECL(test_certificate_authorities_client_hello), diff --git a/tests/api/test_tls_ext.c b/tests/api/test_tls_ext.c index 976a3d94900..9c2368ef2e8 100644 --- a/tests/api/test_tls_ext.c +++ b/tests/api/test_tls_ext.c @@ -284,6 +284,72 @@ int test_tls13_null_cipher_bad_hmac(void) } +/* F-2913 and F-2914: the TLSX_SecureRenegotiation_Parse + * ConstantCompare against the cached Finished verify_data must reject + * a mismatch on both the client and server sides. */ +int test_scr_verify_data_mismatch(void) +{ + EXPECT_DECLS; +#if defined(HAVE_SECURE_RENEGOTIATION) && !defined(WOLFSSL_NO_TLS12) && \ + defined(BUILD_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) + int side; + + for (side = 0; side < 2; side++) { + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + WOLFSSL *failing; + byte data; + int ret; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + test_ctx.c_ciphers = test_ctx.s_ciphers = + "ECDHE-RSA-AES128-GCM-SHA256"; + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, + &ssl_s, wolfTLSv1_2_client_method, + wolfTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_CTX_UseSecureRenegotiation(ctx_c), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_UseSecureRenegotiation(ctx_s), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl_c), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl_s), WOLFSSL_SUCCESS); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* side 0: corrupt the client's copy; side 1: corrupt the + * server's copy. */ + if (side == 0) { + if (ssl_c != NULL && ssl_c->secure_renegotiation != NULL) + ssl_c->secure_renegotiation->server_verify_data[0] ^= 0xFF; + failing = ssl_c; + } + else { + if (ssl_s != NULL && ssl_s->secure_renegotiation != NULL) + ssl_s->secure_renegotiation->client_verify_data[0] ^= 0xFF; + failing = ssl_s; + } + + ret = wolfSSL_Rehandshake(ssl_c); + (void)ret; + (void)wolfSSL_read(ssl_s, &data, 1); + (void)wolfSSL_read(ssl_c, &data, 1); + ExpectIntEQ(wolfSSL_get_error(failing, 0), + WC_NO_ERR_TRACE(SECURE_RENEGOTIATION_E)); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + } +#endif + return EXPECT_RESULT(); +} + int test_wolfSSL_DisableExtendedMasterSecret(void) { EXPECT_DECLS; diff --git a/tests/api/test_tls_ext.h b/tests/api/test_tls_ext.h index 275888adf01..bbf3250969f 100644 --- a/tests/api/test_tls_ext.h +++ b/tests/api/test_tls_ext.h @@ -26,6 +26,7 @@ int test_tls_ems_downgrade(void); int test_tls_ems_resumption_downgrade(void); int test_tls12_chacha20_poly1305_bad_tag(void); int test_tls13_null_cipher_bad_hmac(void); +int test_scr_verify_data_mismatch(void); int test_wolfSSL_DisableExtendedMasterSecret(void); int test_certificate_authorities_certificate_request(void); int test_certificate_authorities_client_hello(void); From 9aa69f49961988130e6853a2ccabf90a401e9ee4 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 15 Apr 2026 15:04:56 +0200 Subject: [PATCH 09/14] tests: add default ticket key callback HMAC negative test (F-2922) wolfSSL_TicketKeyCb is the built-in ticket callback registered by the OpenSSL-compat wolfSSL_CTX_set_tlsext_ticket_key_cb API. Its ConstantCompare of the ticket HMAC was never reached in any test, so a deletion of the check would silently accept forged tickets. New test sets up the compat callback, establishes a TLS 1.2 session, saves it, flips a byte of the encrypted ticket, and asserts the resumption attempt does not complete. --- tests/api.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/tests/api.c b/tests/api.c index 3792778f525..1c8e2064bdd 100644 --- a/tests/api.c +++ b/tests/api.c @@ -10473,6 +10473,78 @@ static int test_wolfSSL_SCR_check_enabled(void) return EXPECT_RESULT(); } +/* F-2922: wolfSSL_TicketKeyCb must reject a session ticket whose HMAC + * does not match its encrypted contents. */ +static int test_wolfSSL_ticket_keycb_bad_hmac(void) +{ + EXPECT_DECLS; +#if defined(HAVE_SESSION_TICKET) && !defined(WOLFSSL_NO_TLS12) && \ + defined(OPENSSL_EXTRA) && defined(HAVE_AES_CBC) && \ + defined(WOLFSSL_AES_256) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + WOLFSSL_SESSION *session = NULL; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + + ExpectIntEQ(OpenSSLTicketInit(), 0); + ExpectIntEQ(wolfSSL_CTX_set_tlsext_ticket_key_cb(ctx_s, + myTicketEncCbOpenSSL), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c), WOLFSSL_SUCCESS); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectNotNull(session = wolfSSL_get1_session(ssl_c)); + ExpectIntGT(session->ticketLen, 0); + + /* Corrupt a byte of the ticket HMAC so the server's HMAC + * verification rejects it. */ + if (session != NULL && session->ticket != NULL && session->ticketLen > 0) + session->ticket[session->ticketLen - 1] ^= 0xFF; + + wolfSSL_free(ssl_c); + ssl_c = NULL; + wolfSSL_free(ssl_s); + ssl_s = NULL; + test_memio_clear_buffer(&test_ctx, 0); + test_memio_clear_buffer(&test_ctx, 1); + + ExpectNotNull(ssl_c = wolfSSL_new(ctx_c)); + ExpectNotNull(ssl_s = wolfSSL_new(ctx_s)); + wolfSSL_SetIOReadCtx(ssl_c, &test_ctx); + wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx); + wolfSSL_SetIOReadCtx(ssl_s, &test_ctx); + wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx); + ExpectIntEQ(wolfSSL_set_session(ssl_c, session), WOLFSSL_SUCCESS); + /* Disable the process-global session cache lookup on the server so that + * the ticket is the only resumption path - with WOLFSSL_TICKET_HAVE_ID + * the server could otherwise resume by session ID. */ + if (ssl_s != NULL) + ssl_s->options.sessionCacheOff = 1; + + /* Corrupted ticket bytes fail the HMAC check in + * wolfSSL_TicketKeyCb; the session must not resume. */ + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(wolfSSL_session_reused(ssl_c), 0); + + wolfSSL_SESSION_free(session); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + OpenSSLTicketCleanup(); +#endif + return EXPECT_RESULT(); +} + + #if !defined(NO_WOLFSSL_SERVER) && !defined(NO_TLS) && \ !defined(NO_FILESYSTEM) && (!defined(NO_RSA) || defined(HAVE_ECC)) /* Called when writing. */ @@ -37516,6 +37588,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_clear_secure_renegotiation), TEST_DECL(test_wolfSSL_SCR_Reconnect), TEST_DECL(test_wolfSSL_SCR_check_enabled), + TEST_DECL(test_wolfSSL_ticket_keycb_bad_hmac), TEST_DECL(test_tls_ext_duplicate), TEST_DECL(test_tls_ext_word16_overflow), TEST_DECL(test_tls_bad_legacy_version), From 2df493609298a8747d5a32ba5ee64a4328411d7c Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 15 Apr 2026 15:06:43 +0200 Subject: [PATCH 10/14] tests: add HRR cipher-suite mismatch negative test (F-2126) DoTls13ClientHello enforces RFC 8446 Section 4.1.4 by comparing the cipher suite in the second ClientHello to the hrrCipherSuite cached on the server from the HelloRetryRequest. No existing test covers the mismatch branch, so a deletion of the check would silently allow a client to switch cipher suite between CH1 and CH2. Drive a partial handshake until the server has emitted the HRR, then flip the cached hrrCipherSuite on the server; processing CH2 must surface INVALID_PARAMETER. --- tests/api.c | 1 + tests/api/test_tls_ext.c | 60 ++++++++++++++++++++++++++++++++++++++++ tests/api/test_tls_ext.h | 1 + 3 files changed, 62 insertions(+) diff --git a/tests/api.c b/tests/api.c index 1c8e2064bdd..c51c4c264a9 100644 --- a/tests/api.c +++ b/tests/api.c @@ -37579,6 +37579,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_tls12_chacha20_poly1305_bad_tag), TEST_DECL(test_tls13_null_cipher_bad_hmac), TEST_DECL(test_scr_verify_data_mismatch), + TEST_DECL(test_tls13_hrr_cipher_suite_mismatch), TEST_DECL(test_wolfSSL_DisableExtendedMasterSecret), TEST_DECL(test_certificate_authorities_certificate_request), TEST_DECL(test_certificate_authorities_client_hello), diff --git a/tests/api/test_tls_ext.c b/tests/api/test_tls_ext.c index 9c2368ef2e8..87143fc6420 100644 --- a/tests/api/test_tls_ext.c +++ b/tests/api/test_tls_ext.c @@ -350,6 +350,66 @@ int test_scr_verify_data_mismatch(void) return EXPECT_RESULT(); } +/* F-2126: DoTls13ClientHello must reject a second ClientHello whose + * cipher suite does not match the server's HelloRetryRequest. The + * client offers two suites in CH1 and only a different one in CH2. */ +int test_tls13_hrr_cipher_suite_mismatch(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_TLS13) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \ + defined(BUILD_TLS_AES_128_GCM_SHA256) && \ + defined(BUILD_TLS_AES_256_GCM_SHA384) + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + int ret; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + /* Both suites supported on both ends; server prefers the first + * offered suite, which will be the one committed in the HRR. */ + test_ctx.c_ciphers = test_ctx.s_ciphers = + "TLS13-AES128-GCM-SHA256:TLS13-AES256-GCM-SHA384"; + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + /* Force HRR by withholding key_share entries in CH1. */ + ExpectIntEQ(wolfSSL_NoKeyShares(ssl_c), WOLFSSL_SUCCESS); + + /* CH1 / HRR */ + ExpectIntEQ(wolfSSL_connect(ssl_c), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + ExpectIntEQ(wolfSSL_get_error(ssl_c, 0), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(wolfSSL_accept(ssl_s), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)); + ExpectIntEQ(wolfSSL_get_error(ssl_s, 0), WOLFSSL_ERROR_WANT_READ); + + /* Restrict the client to a different suite than the one the + * server committed to in the HRR, so CH2 offers only that. */ + ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, "TLS13-AES256-GCM-SHA384"), + WOLFSSL_SUCCESS); + + /* CH2 */ + (void)wolfSSL_connect(ssl_c); + (void)wolfSSL_accept(ssl_s); + (void)wolfSSL_connect(ssl_c); + /* The cipher-suite mismatch is caught server-side; the server's + * alert reaches the client, so either peer can surface it. */ + ret = wolfSSL_get_error(ssl_s, 0); + if (ret != WC_NO_ERR_TRACE(INVALID_PARAMETER)) + ret = wolfSSL_get_error(ssl_c, 0); + ExpectIntEQ(ret, WC_NO_ERR_TRACE(INVALID_PARAMETER)); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + + int test_wolfSSL_DisableExtendedMasterSecret(void) { EXPECT_DECLS; diff --git a/tests/api/test_tls_ext.h b/tests/api/test_tls_ext.h index bbf3250969f..ad87b3365fb 100644 --- a/tests/api/test_tls_ext.h +++ b/tests/api/test_tls_ext.h @@ -27,6 +27,7 @@ int test_tls_ems_resumption_downgrade(void); int test_tls12_chacha20_poly1305_bad_tag(void); int test_tls13_null_cipher_bad_hmac(void); int test_scr_verify_data_mismatch(void); +int test_tls13_hrr_cipher_suite_mismatch(void); int test_wolfSSL_DisableExtendedMasterSecret(void); int test_certificate_authorities_certificate_request(void); int test_certificate_authorities_client_hello(void); From ff60134ff013855472de5b01767e9de79005eb61 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 15 Apr 2026 15:08:44 +0200 Subject: [PATCH 11/14] tests: add TLS 1.3 ticket age out-of-window test (F-1824) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DoClientTicketCheck's ticket-age bounds (-1000 ms low bound and MAX_TICKET_AGE_DIFF*1000+1000 ms high bound) were never exercised by any integration test, so mutations of the constants went undetected. Establish a TLS 1.3 session, read the NewSessionTicket, then shift the client's cached ageAdd by well over 1 second so the server's unobfuscated diff falls outside the valid window on resumption. The server must reject the PSK — session_reused stays 0. --- tests/api.c | 1 + tests/api/test_tls_ext.c | 63 ++++++++++++++++++++++++++++++++++++++++ tests/api/test_tls_ext.h | 1 + 3 files changed, 65 insertions(+) diff --git a/tests/api.c b/tests/api.c index c51c4c264a9..148a9942bb8 100644 --- a/tests/api.c +++ b/tests/api.c @@ -37580,6 +37580,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_tls13_null_cipher_bad_hmac), TEST_DECL(test_scr_verify_data_mismatch), TEST_DECL(test_tls13_hrr_cipher_suite_mismatch), + TEST_DECL(test_tls13_ticket_age_out_of_window), TEST_DECL(test_wolfSSL_DisableExtendedMasterSecret), TEST_DECL(test_certificate_authorities_certificate_request), TEST_DECL(test_certificate_authorities_client_hello), diff --git a/tests/api/test_tls_ext.c b/tests/api/test_tls_ext.c index 87143fc6420..1fcd7e7de7b 100644 --- a/tests/api/test_tls_ext.c +++ b/tests/api/test_tls_ext.c @@ -410,6 +410,69 @@ int test_tls13_hrr_cipher_suite_mismatch(void) } +/* F-1824: DoClientTicketCheck must reject a PSK whose obfuscated age + * falls outside the [-1000, MAX_TICKET_AGE_DIFF*1000+1000] ms window. */ +int test_tls13_ticket_age_out_of_window(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL; + WOLFSSL_CTX *ctx_s = NULL; + WOLFSSL *ssl_c = NULL; + WOLFSSL *ssl_s = NULL; + WOLFSSL_SESSION *session = NULL; + byte tmp; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + /* Pump post-handshake reads so the NewSessionTicket reaches the + * client. */ + (void)wolfSSL_read(ssl_c, &tmp, sizeof(tmp)); + (void)wolfSSL_read(ssl_s, &tmp, sizeof(tmp)); + (void)wolfSSL_read(ssl_c, &tmp, sizeof(tmp)); + + ExpectNotNull(session = wolfSSL_get1_session(ssl_c)); + + /* Flip the high bit to push the unobfuscated age out of window. */ + if (session != NULL) + session->ticketAdd ^= 0x80000000U; + + wolfSSL_free(ssl_c); + ssl_c = NULL; + wolfSSL_free(ssl_s); + ssl_s = NULL; + test_memio_clear_buffer(&test_ctx, 0); + test_memio_clear_buffer(&test_ctx, 1); + + ExpectNotNull(ssl_c = wolfSSL_new(ctx_c)); + ExpectNotNull(ssl_s = wolfSSL_new(ctx_s)); + wolfSSL_SetIOReadCtx(ssl_c, &test_ctx); + wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx); + wolfSSL_SetIOReadCtx(ssl_s, &test_ctx); + wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx); + ExpectIntEQ(wolfSSL_set_session(ssl_c, session), WOLFSSL_SUCCESS); + + /* PSK rejected, full handshake must still succeed. */ + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0); + + wolfSSL_SESSION_free(session); + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + + int test_wolfSSL_DisableExtendedMasterSecret(void) { EXPECT_DECLS; diff --git a/tests/api/test_tls_ext.h b/tests/api/test_tls_ext.h index ad87b3365fb..7d0921c9ad2 100644 --- a/tests/api/test_tls_ext.h +++ b/tests/api/test_tls_ext.h @@ -28,6 +28,7 @@ int test_tls12_chacha20_poly1305_bad_tag(void); int test_tls13_null_cipher_bad_hmac(void); int test_scr_verify_data_mismatch(void); int test_tls13_hrr_cipher_suite_mismatch(void); +int test_tls13_ticket_age_out_of_window(void); int test_wolfSSL_DisableExtendedMasterSecret(void); int test_certificate_authorities_certificate_request(void); int test_certificate_authorities_client_hello(void); From f3e183a338a4607d1d5c098be675958fadf81403 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Mon, 20 Apr 2026 17:45:55 +0000 Subject: [PATCH 12/14] fenrir: address review feedback on PR 10230 - tls.c: TLSX_CertWithExternPsk_GetSize takes word16*, but length was widened to word32 in TLSX_GetSize. Use the hsz staging variable like the other cases so WOLFSSL_CERT_WITH_EXTERN_PSK builds compile. - tls.c: silence -Wunused-variable for hsz in builds where every case that consumes it (TLS 1.3, PSK, ETM, early data, PHA, cookie, cert with extern PSK) is compiled out, e.g. user_settings_tls12.h. - test_tls_ext.c: assert session->ticketLen > 0 before mutating ticketAdd in the ticket-age out-of-window test so it fails loudly if no NewSessionTicket was received. --- src/tls.c | 2 ++ tests/api/test_tls_ext.c | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/tls.c b/src/tls.c index 9407cd42cee..905cd9b88bb 100644 --- a/src/tls.c +++ b/src/tls.c @@ -14930,6 +14930,8 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType, msgType == certificate_request); (void)cbShim; + (void)hsz; + while ((extension = list)) { list = extension->next; diff --git a/tests/api/test_tls_ext.c b/tests/api/test_tls_ext.c index 1fcd7e7de7b..d183bfe6db9 100644 --- a/tests/api/test_tls_ext.c +++ b/tests/api/test_tls_ext.c @@ -439,6 +439,9 @@ int test_tls13_ticket_age_out_of_window(void) (void)wolfSSL_read(ssl_c, &tmp, sizeof(tmp)); ExpectNotNull(session = wolfSSL_get1_session(ssl_c)); + /* The test only exercises the age window check if the client actually + * received a NewSessionTicket and the session carries ticket material. */ + ExpectIntGT(session->ticketLen, 0); /* Flip the high bit to push the unobfuscated age out of window. */ if (session != NULL) From 558c329571bf6ac911e5ec6051c50e8eba45a396 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Tue, 28 Apr 2026 13:22:53 +0200 Subject: [PATCH 13/14] fixup! fenrir: address review feedback on PR 10230 --- src/tls.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/tls.c b/src/tls.c index 905cd9b88bb..9407cd42cee 100644 --- a/src/tls.c +++ b/src/tls.c @@ -14930,8 +14930,6 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType, msgType == certificate_request); (void)cbShim; - (void)hsz; - while ((extension = list)) { list = extension->next; From 9d49f7f26139f41bdf2666f4284661491cf7dccd Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Tue, 28 Apr 2026 14:54:20 +0200 Subject: [PATCH 14/14] TLSX_GetSize: return error as soon as it happens --- src/tls.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tls.c b/src/tls.c index 9407cd42cee..f7dc51de0be 100644 --- a/src/tls.c +++ b/src/tls.c @@ -15145,6 +15145,9 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType, break; } + if (ret != 0) + return ret; + /* Early exit: stop accumulating as soon as the running total * cannot possibly fit the 2-byte wire length. Check *before* * marking the extension as processed so the semaphore is not