From 028a407f1e868147995d7702cb8f01932c91832b Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Wed, 24 Jun 2026 19:31:42 +0200 Subject: [PATCH 1/2] dtls13: add check over handshake message length --- src/dtls13.c | 15 ++++++ tests/api/test_dtls.c | 105 ++++++++++++++++++++++++++++++++++++++++++ tests/api/test_dtls.h | 2 + wolfssl/internal.h | 3 +- 4 files changed, 124 insertions(+), 1 deletion(-) diff --git a/src/dtls13.c b/src/dtls13.c index 070fc2e9f2b..134bb4fd201 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -1898,6 +1898,21 @@ static int _Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, word32 size, return INCOMPLETE_DATA; } + /* Cap the handshake message size before it can be buffered for reassembly, + * matching the DTLSv1.2 path (DoDtlsHandShakeMsg()). RFC 9147 Sec 4.5.2 + * says invalid records SHOULD be silently discarded, so only error out once + * the record is authenticated (received in an encrypted epoch); a plaintext + * message is just dropped. */ + if (messageLength > MAX_HANDSHAKE_SZ) { + WOLFSSL_MSG("Handshake message too large"); + if (IsEncryptionOn(ssl, 0)) { + WOLFSSL_ERROR_VERBOSE(HANDSHAKE_SIZE_ERROR); + return HANDSHAKE_SIZE_ERROR; + } + *processedSize = idx + fragLength; + return 0; + } + if (fragOff + fragLength > messageLength) return BUFFER_ERROR; diff --git a/tests/api/test_dtls.c b/tests/api/test_dtls.c index aa0c9578be2..502c70e0dc7 100644 --- a/tests/api/test_dtls.c +++ b/tests/api/test_dtls.c @@ -1411,6 +1411,107 @@ int test_dtls13_short_read(void) } #endif /* WOLFSSL_DTLS13 && !defined(WOLFSSL_DTLS_RECORDS_CAN_SPAN_DATAGRAMS) */ +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) +/* A plaintext (epoch 0) handshake message whose handshake message_length claims + * more than MAX_HANDSHAKE_SZ must not be buffered for reassembly. We use the + * server's plaintext ServerHello: the client accepts a fragmented ServerHello + * (Dtls13AcceptFragmented()), so an incomplete one is normally stored for + * reassembly. Without the message_length cap in _Dtls13HandshakeRecv() the + * spoofed message would be added to the rx list; with it the unauthenticated + * message is silently dropped. */ +int test_dtls13_oversized_msg_length(void) +{ + EXPECT_DECLS; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + char sh[TEST_MEMIO_BUF_SZ]; + int shSz = (int)sizeof(sh); + int recLen = 0; + byte hsMsg[DTLS_HANDSHAKE_HEADER_SZ + 1]; + word32 idx = 0; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0); + + /* CH1 -> server, then server emits its first flight (plaintext ServerHello + * is the first record). */ + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntGT(test_ctx.c_msg_count, 0); + ExpectIntEQ(test_memio_copy_message(&test_ctx, 1, sh, &shSz, 0), 0); + ExpectIntGE(shSz, DTLS_RECORD_HEADER_SZ + DTLS_HANDSHAKE_HEADER_SZ); + /* First record is a plaintext handshake ServerHello. */ + ExpectIntEQ((byte)sh[0], handshake); + ExpectIntEQ((byte)sh[DTLS_RECORD_HEADER_SZ], server_hello); + if (EXPECT_SUCCESS()) + recLen = (((byte)sh[DTLS_RECORD_HEADER_SZ - 2]) << 8) | + (byte)sh[DTLS_RECORD_HEADER_SZ - 1]; + /* the ServerHello record must be fully contained in the copied message */ + ExpectIntLE(DTLS_RECORD_HEADER_SZ + recLen, shSz); + + /* Spoof only the handshake message_length of the ServerHello record, + * leaving the record length and fragment_length intact so it clears record + * parsing and looks like the first fragment of an oversized message. */ + c32to24((word32)MAX_HANDSHAKE_SZ + 1, + (byte*)sh + DTLS_RECORD_HEADER_SZ + 1); + test_memio_clear_buffer(&test_ctx, 1); + ExpectIntEQ(test_memio_inject_message(&test_ctx, 1, sh, + DTLS_RECORD_HEADER_SZ + recLen), 0); + + /* The client must reject the oversized ServerHello without buffering it. */ + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectNull(ssl_c->dtls_rx_msg_list); + ExpectIntEQ(ssl_c->dtls_rx_msg_list_sz, 0); + + wolfSSL_free(ssl_c); + ssl_c = NULL; + wolfSSL_CTX_free(ctx_c); + ctx_c = NULL; + wolfSSL_free(ssl_s); + ssl_s = NULL; + wolfSSL_CTX_free(ctx_s); + ctx_s = NULL; + + /* Stretching the test suite a bit, ideally we should not test using + * internal state, but we have no way to forge encrypted packet yet */ + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + if (EXPECT_SUCCESS()) { + ssl_c->keys.curEpoch64 = w64From32(0x0, DTLS13_EPOCH_TRAFFIC0); + ssl_c->keys.decryptedCur = 1; + ssl_c->curRL.pvMajor = ssl_c->version.major; + ssl_c->curRL.pvMinor = DTLSv1_2_MINOR; + } + XMEMSET(hsMsg, 0, sizeof(hsMsg)); + hsMsg[0] = key_update; + /* oversized message_length, single-byte first fragment of it */ + c32to24((word32)MAX_HANDSHAKE_SZ + 1, hsMsg + 1); + hsMsg[DTLS_HANDSHAKE_HEADER_SZ - 1] = 1; + ExpectIntEQ(Dtls13HandshakeRecv(ssl_c, hsMsg, &idx, (word32)sizeof(hsMsg)), + HANDSHAKE_SIZE_ERROR); + ExpectNull(ssl_c->dtls_rx_msg_list); + + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_s); + + return EXPECT_RESULT(); +} +#else +int test_dtls13_oversized_msg_length(void) +{ + return TEST_SKIPPED; +} +#endif /* HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES && WOLFSSL_DTLS13 */ + #if !defined(WOLFSSL_DTLS_RECORDS_CAN_SPAN_DATAGRAMS) int test_dtls12_short_read(void) { @@ -1658,6 +1759,10 @@ int test_dtls13_longer_length(void) { return TEST_SKIPPED; } +int test_dtls13_oversized_msg_length(void) +{ + return TEST_SKIPPED; +} int test_dtls_record_cross_boundaries(void) { return TEST_SKIPPED; diff --git a/tests/api/test_dtls.h b/tests/api/test_dtls.h index 28c62757213..28b84547b7a 100644 --- a/tests/api/test_dtls.h +++ b/tests/api/test_dtls.h @@ -37,6 +37,7 @@ int test_dtls12_record_length_mismatch(void); int test_dtls12_short_read(void); int test_dtls13_longer_length(void); int test_dtls13_short_read(void); +int test_dtls13_oversized_msg_length(void); int test_records_span_network_boundaries(void); int test_dtls_record_cross_boundaries(void); int test_dtls_rtx_across_epoch_change(void); @@ -124,6 +125,7 @@ int test_WOLFSSL_dtls_version_alert(void); TEST_DECL_GROUP("dtls", test_dtls12_short_read), \ TEST_DECL_GROUP("dtls", test_dtls13_longer_length), \ TEST_DECL_GROUP("dtls", test_dtls13_short_read), \ + TEST_DECL_GROUP("dtls", test_dtls13_oversized_msg_length), \ TEST_DECL_GROUP("dtls", test_records_span_network_boundaries), \ TEST_DECL_GROUP("dtls", test_dtls_record_cross_boundaries), \ TEST_DECL_GROUP("dtls", test_dtls_rtx_across_epoch_change), \ diff --git a/wolfssl/internal.h b/wolfssl/internal.h index d54496e0f9c..fdb48a9dc85 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -7451,6 +7451,7 @@ WOLFSSL_LOCAL void DtlsSetSeqNumForReply(WOLFSSL* ssl); #ifdef WOLFSSL_API_PREFIX_MAP #define Dtls13GetEpoch wolfSSL_Dtls13GetEpoch #define Dtls13CheckEpoch wolfSSL_Dtls13CheckEpoch + #define Dtls13HandshakeRecv wolfSSL_Dtls13HandshakeRecv #define Dtls13WriteAckMessage wolfSSL_Dtls13WriteAckMessage #define Dtls13RtxAddAck wolfSSL_Dtls13RtxAddAck #define Dtls13DoScheduledWork wolfSSL_Dtls13DoScheduledWork @@ -7495,7 +7496,7 @@ WOLFSSL_LOCAL int Dtls13HandshakeSend(WOLFSSL* ssl, byte* output, int hash_output); WOLFSSL_LOCAL int Dtls13RecordRecvd(WOLFSSL* ssl); WOLFSSL_TEST_VIS int Dtls13CheckEpoch(WOLFSSL* ssl, enum HandShakeType type); -WOLFSSL_LOCAL int Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, +WOLFSSL_TEST_VIS int Dtls13HandshakeRecv(WOLFSSL* ssl, byte* input, word32* inOutIdx, word32 totalSz); WOLFSSL_LOCAL int Dtls13HandshakeAddHeader(WOLFSSL* ssl, byte* output, enum HandShakeType msg_type, word32 length); From 6cc88fff85fb759ec4f4129750d27132b8a5d23e Mon Sep 17 00:00:00 2001 From: Marco Oliverio Date: Mon, 20 Jul 2026 13:17:08 +0200 Subject: [PATCH 2/2] add WOLFSSL_OCSP_SCREEN_RESPONDER to known macros --- .wolfssl_known_macro_extras | 1 + 1 file changed, 1 insertion(+) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 3f60ca60f04..bbd61efe04a 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -939,6 +939,7 @@ WOLFSSL_NRF51_AES WOLFSSL_NXP_CASPER_ECC_MUL2ADD WOLFSSL_NXP_CASPER_ECC_MULMOD WOLFSSL_NXP_LPC55S6X +WOLFSSL_OCSP_SCREEN_RESPONDER WOLFSSL_OLDTLS_AEAD_CIPHERSUITES WOLFSSL_OLD_SET_CURVES_LIST WOLFSSL_OLD_TIMINGPADVERIFY