From 4a8d476568d1ce171d266d062ac8f6055c6da714 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 13 Jan 2026 15:42:11 +0100 Subject: [PATCH 01/22] ssl_tls.c: Allow client hello fragmentation Signed-off-by: Ronald Cron --- library/ssl_tls.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/library/ssl_tls.c b/library/ssl_tls.c index a8687277ea..d7773be3e0 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -3286,13 +3286,6 @@ size_t mbedtls_ssl_get_output_max_frag_len(const mbedtls_ssl_context *ssl) #if defined(MBEDTLS_SSL_PROTO_DTLS) size_t mbedtls_ssl_get_current_mtu(const mbedtls_ssl_context *ssl) { - /* Return unlimited mtu for client hello messages to avoid fragmentation. */ - if (ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT && - (ssl->state == MBEDTLS_SSL_CLIENT_HELLO || - ssl->state == MBEDTLS_SSL_SERVER_HELLO)) { - return 0; - } - if (ssl->handshake == NULL || ssl->handshake->mtu == 0) { return ssl->mtu; } From faa9d95638993ffdc89690622eb05224005c4b84 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Wed, 14 Jan 2026 16:32:48 +0100 Subject: [PATCH 02/22] ssl-opt.sh: Relax deps of handshake defrag tests Relax the dependencies of the tests about handshake message defragmentation/reassembly on server side. TLS 1.3 does not need to be enable anymore for this to work for TLS 1.2 handshake messages. Signed-off-by: Ronald Cron --- scripts/generate_tls_handshake_tests.py | 3 --- tests/ssl-opt.sh | 3 --- 2 files changed, 6 deletions(-) diff --git a/scripts/generate_tls_handshake_tests.py b/scripts/generate_tls_handshake_tests.py index 30f27b1b37..76c8e45d57 100755 --- a/scripts/generate_tls_handshake_tests.py +++ b/scripts/generate_tls_handshake_tests.py @@ -6,12 +6,9 @@ Generate miscellaneous TLS test cases relating to the handshake. # Copyright The Mbed TLS Contributors # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later -import sys - import framework_scripts_path # pylint: disable=unused-import from mbedtls_framework import tls_handshake_tests if __name__ == '__main__': - sys.argv[1:1] = ["--no-tls12-client-hello-defragmentation-support"] tls_handshake_tests.main() diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index c129db0946..ef84feb3b4 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -14853,7 +14853,6 @@ run_test "Handshake defragmentation on server: len=256, client-initiated rene -s "Consume: waiting for more handshake fragments 256/" \ requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 -requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION run_test "Handshake defragmentation on server: len=128, client-initiated renegotiation" \ "$P_SRV debug_level=4 exchanges=2 renegotiation=1 auth_mode=required" \ @@ -14870,7 +14869,6 @@ run_test "Handshake defragmentation on server: len=128, client-initiated rene -s "Consume: waiting for more handshake fragments 128/" \ requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 -requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION run_test "Handshake defragmentation on server: len=4, client-initiated renegotiation" \ "$P_SRV debug_level=4 exchanges=2 renegotiation=1 auth_mode=required" \ @@ -14887,7 +14885,6 @@ run_test "Handshake defragmentation on server: len=4, client-initiated renego -s "Consume: waiting for more handshake fragments 4/" \ requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 -requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_3 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION run_test "Handshake defragmentation on server: len=4, client-initiated server-rejected renegotiation" \ "$P_SRV debug_level=4 exchanges=2 renegotiation=0 auth_mode=required" \ From 0a8c35d273257d7102ef8af1e351fa9c7bb688d9 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Wed, 21 Jan 2026 11:33:35 +0100 Subject: [PATCH 03/22] ssl_server2.c: DTLS: Attempt to read the response to the close notification Signed-off-by: Ronald Cron --- programs/ssl/ssl_server2.c | 50 ++++++++++++++++++- tests/compat.sh | 1 + tests/scripts/components-configuration-tls.sh | 1 + 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c index 82c01e1168..b01fcfcb05 100644 --- a/programs/ssl/ssl_server2.c +++ b/programs/ssl/ssl_server2.c @@ -4235,7 +4235,55 @@ close_notify: } while (ret == MBEDTLS_ERR_SSL_WANT_WRITE); ret = 0; - mbedtls_printf(" done\n"); + /* + * In the DTLS case, attempt to read a possible response to the close + * notification. This avoids reconnecting to the same client when we + * reset and later receive its close-notification response during + * step 3 (waiting for a client to connect). + * + * Stop waiting for the response if the connection has already ended. + * + * The waiting loop below relies on mbedtls_ssl_read() returning regularly + * in order to keep the total waiting time approximately bounded to 1s. If + * no read timeout is configured (see the read_timeout option), or if the + * configured timeout is close to or larger than 1s, the total waiting time + * may exceed 1s by a significant margin. + */ +#if defined(MBEDTLS_SSL_PROTO_DTLS) && defined(MBEDTLS_HAVE_TIME) + if (opt.transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + mbedtls_ms_time_t start = mbedtls_ms_time(); + for (;;) { + ret = mbedtls_ssl_read(&ssl, buf, opt.buffer_size); + /* + * mbedtls_ssl_read() returned some data or timed out, loop if we + * have not spent already too much time, quite arbitrarily 1s. + */ + if ((ret > 0) || (ret == MBEDTLS_ERR_SSL_TIMEOUT)) { + if ((mbedtls_ms_time() - start) < 1000) { + continue; + } + } + + if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) { + mbedtls_printf(" done, received client close notification.\n"); + } else { + /* ret = 0, silent transport EOF or ret < 0 except + * MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY. Note that we do not + * handle specifically the non-fatal error codes like + * MBEDTLS_ERR_SSL_WANT_READ as we do not really expect them + * here. + */ + mbedtls_printf(" done\n"); + } + break; + } + ret = 0; + } else +#endif /* MBEDTLS_SSL_PROTO_DTLS && MBEDTLS_HAVE_TIME */ + { + mbedtls_printf(" done\n"); + } + fflush(stdout); #if defined(MBEDTLS_SSL_CACHE_C) if (opt.cache_remove > 0) { diff --git a/tests/compat.sh b/tests/compat.sh index 22da5ee4ed..1bbed59929 100755 --- a/tests/compat.sh +++ b/tests/compat.sh @@ -660,6 +660,7 @@ setup_arguments() # with OpenSSL 1.0.1h, -www, -WWW and -HTTP break DTLS handshakes if is_dtls "$MODE"; then O_SERVER_ARGS="$O_SERVER_ARGS" + M_SERVER_ARGS="$M_SERVER_ARGS read_timeout=1000" else O_SERVER_ARGS="$O_SERVER_ARGS -www" fi diff --git a/tests/scripts/components-configuration-tls.sh b/tests/scripts/components-configuration-tls.sh index 23c9d68b9f..d2a6049777 100644 --- a/tests/scripts/components-configuration-tls.sh +++ b/tests/scripts/components-configuration-tls.sh @@ -385,6 +385,7 @@ component_test_tls1_2_ccm_psk_psa () { component_test_tls1_2_ccm_psk_dtls_legacy () { msg "build: configs/config-ccm-psk-dtls1_2.h" cp configs/config-ccm-psk-dtls1_2.h "$CONFIG_H" + scripts/config.py set MBEDTLS_HAVE_TIME # test-ref-configs works by overwriting mbedtls_config.h; this makes cmake # want to re-generate generated files that depend on it, quite correctly. # However this doesn't work as the generation script expects a specific From 17acd871c13bb9dbfb8b0fbdcd12cb5cb1950813 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Mon, 12 Jan 2026 12:45:40 +0100 Subject: [PATCH 04/22] ssl_tls12_server.c: Document replay check and update in ssl_parse_client_hello() Signed-off-by: Ronald Cron --- library/ssl_tls12_server.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/ssl_tls12_server.c b/library/ssl_tls12_server.c index acf4bfb07b..b2703c8cd2 100644 --- a/library/ssl_tls12_server.c +++ b/library/ssl_tls12_server.c @@ -981,6 +981,9 @@ read_record_header: memcpy(&ssl->cur_out_ctr[2], ssl->in_ctr + 2, sizeof(ssl->cur_out_ctr) - 2); + /* Check for record replay and then update the window. This replicates what + * is done in `ssl_get_next_record()` when the record is not fetched through + * `mbedtls_ssl_read_record()`. */ #if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) if (mbedtls_ssl_dtls_replay_check(ssl) != 0) { MBEDTLS_SSL_DEBUG_MSG(1, ("replayed record, discarding")); From 06abef23072c174c8cbf1ef145ca6865116617b6 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Wed, 14 Jan 2026 13:48:52 +0100 Subject: [PATCH 05/22] ssl_tls12_server.c: Use mbedtls_ssl_read_record() only to read the ClientHello In ssl_tls12_server.c:ssl_parse_client_hello(), remove the code that directly reads the received data to read the record expected to contain the ClientHello message. The function already supported handling a ClientHello read via mbedtls_ssl_read_record() in the following cases: - when the ClientHello was read as a post-handshake message (renegotiation). - when the ClientHello was read by ssl_tls13_process_client_hello() during TLS 1.3 or TLS 1.2 version negotiation. Signed-off-by: Ronald Cron --- library/ssl_msg.c | 5 ++ library/ssl_tls12_server.c | 120 ++++++------------------------------- 2 files changed, 22 insertions(+), 103 deletions(-) diff --git a/library/ssl_msg.c b/library/ssl_msg.c index 38f81bd099..b353345a35 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -5830,6 +5830,11 @@ static int ssl_tls12_handle_hs_message_post_handshake(mbedtls_ssl_context *ssl) ssl->renego_status = MBEDTLS_SSL_RENEGOTIATION_PENDING; } #endif + + /* Keep the ClientHello message for ssl_parse_client_hello() */ + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER) { + ssl->keep_current_message = 1; + } ret = mbedtls_ssl_start_renegotiation(ssl); if (ret != MBEDTLS_ERR_SSL_WAITING_SERVER_HELLO_RENEGO && ret != 0) { diff --git a/library/ssl_tls12_server.c b/library/ssl_tls12_server.c index b2703c8cd2..d34d810f10 100644 --- a/library/ssl_tls12_server.c +++ b/library/ssl_tls12_server.c @@ -912,29 +912,22 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) MBEDTLS_SSL_DEBUG_MSG(2, ("=> parse client hello")); - int renegotiating; - -#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) -read_record_header: -#endif /* - * If renegotiating, then the input was read with mbedtls_ssl_read_record(), - * otherwise read it ourselves manually in order to support SSLv2 - * ClientHello, which doesn't use the same record layer format. - * Otherwise in a scenario of TLS 1.3/TLS 1.2 version negotiation, the - * ClientHello has been already fully fetched by the TLS 1.3 code and the - * flag ssl->keep_current_message is raised. + * Fetch the expected ClientHello handshake message. Do not ask + * mbedtls_ssl_read_record() to update the handshake digest, to align + * with cases where the ClientHello may already have been fetched in + * ssl_tls13_process_client_hello() or as a post-handshake message + * (renegotiation). */ - renegotiating = 0; -#if defined(MBEDTLS_SSL_RENEGOTIATION) - renegotiating = (ssl->renego_status != MBEDTLS_SSL_INITIAL_HANDSHAKE); -#endif - if (!renegotiating && !ssl->keep_current_message) { - if ((ret = mbedtls_ssl_fetch_input(ssl, 5)) != 0) { - /* No alert on a read error. */ - MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_fetch_input", ret); - return ret; - } + if ((ret = mbedtls_ssl_read_record(ssl, 0)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_read_record ", ret); + return ret; + } + + ret = mbedtls_ssl_update_handshake_status(ssl); + if (0 != ret) { + MBEDTLS_SSL_DEBUG_RET(1, ("mbedtls_ssl_update_handshake_status"), ret); + return ret; } buf = ssl->in_hdr; @@ -953,7 +946,8 @@ read_record_header: MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, message type: %d", buf[0])); - if (buf[0] != MBEDTLS_SSL_MSG_HANDSHAKE) { + if ((ssl->in_msgtype != MBEDTLS_SSL_MSG_HANDSHAKE) || + (buf[0] != MBEDTLS_SSL_MSG_HANDSHAKE)) { MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; } @@ -980,66 +974,11 @@ read_record_header: memcpy(&ssl->cur_out_ctr[2], ssl->in_ctr + 2, sizeof(ssl->cur_out_ctr) - 2); - - /* Check for record replay and then update the window. This replicates what - * is done in `ssl_get_next_record()` when the record is not fetched through - * `mbedtls_ssl_read_record()`. */ -#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) - if (mbedtls_ssl_dtls_replay_check(ssl) != 0) { - MBEDTLS_SSL_DEBUG_MSG(1, ("replayed record, discarding")); - ssl->next_record_offset = 0; - ssl->in_left = 0; - goto read_record_header; - } - - /* No MAC to check yet, so we can update right now */ - mbedtls_ssl_dtls_replay_update(ssl); -#endif } #endif /* MBEDTLS_SSL_PROTO_DTLS */ - msg_len = MBEDTLS_GET_UINT16_BE(ssl->in_len, 0); - -#if defined(MBEDTLS_SSL_RENEGOTIATION) - if (ssl->renego_status != MBEDTLS_SSL_INITIAL_HANDSHAKE) { - /* Set by mbedtls_ssl_read_record() */ - msg_len = ssl->in_hslen; - } else -#endif - { - if (ssl->keep_current_message) { - ssl->keep_current_message = 0; - } else { - if (msg_len > MBEDTLS_SSL_IN_CONTENT_LEN) { - MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); - return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; - } - - if ((ret = mbedtls_ssl_fetch_input(ssl, - mbedtls_ssl_in_hdr_len(ssl) + msg_len)) != 0) { - MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_fetch_input", ret); - return ret; - } - - /* Done reading this record, get ready for the next one */ -#if defined(MBEDTLS_SSL_PROTO_DTLS) - if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { - ssl->next_record_offset = msg_len + mbedtls_ssl_in_hdr_len(ssl); - } else -#endif - ssl->in_left = 0; - } - } - buf = ssl->in_msg; - - MBEDTLS_SSL_DEBUG_BUF(4, "record contents", buf, msg_len); - - ret = ssl->handshake->update_checksum(ssl, buf, msg_len); - if (0 != ret) { - MBEDTLS_SSL_DEBUG_RET(1, ("update_checksum"), ret); - return ret; - } + msg_len = ssl->in_hslen; /* * Handshake layer: @@ -1049,13 +988,6 @@ read_record_header: * 6 . 8 DTLS only: fragment offset * 9 . 11 DTLS only: fragment length */ - if (msg_len < mbedtls_ssl_hs_hdr_len(ssl)) { - MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); - return MBEDTLS_ERR_SSL_DECODE_ERROR; - } - - MBEDTLS_SSL_DEBUG_MSG(3, ("client hello v3, handshake type: %d", buf[0])); - if (buf[0] != MBEDTLS_SSL_HS_CLIENT_HELLO) { MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; @@ -1086,24 +1018,6 @@ read_record_header: ssl->handshake->out_msg_seq = cli_msg_seq; ssl->handshake->in_msg_seq = cli_msg_seq + 1; } - { - /* - * For now we don't support fragmentation, so make sure - * fragment_offset == 0 and fragment_length == length - */ - size_t fragment_offset, fragment_length, length; - fragment_offset = MBEDTLS_GET_UINT24_BE(ssl->in_msg, 6); - fragment_length = MBEDTLS_GET_UINT24_BE(ssl->in_msg, 9); - length = MBEDTLS_GET_UINT24_BE(ssl->in_msg, 1); - MBEDTLS_SSL_DEBUG_MSG( - 4, ("fragment_offset=%u fragment_length=%u length=%u", - (unsigned) fragment_offset, (unsigned) fragment_length, - (unsigned) length)); - if (fragment_offset != 0 || length != fragment_length) { - MBEDTLS_SSL_DEBUG_MSG(1, ("ClientHello fragmentation not supported")); - return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; - } - } } #endif /* MBEDTLS_SSL_PROTO_DTLS */ From 5a744e8d34c232e4b22a22f305372ded8fc1fb11 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Thu, 8 Jan 2026 09:15:40 +0100 Subject: [PATCH 06/22] ssl_tls12_server.c: Move ClientHello message_seq adjustment Move ClientHello message_seq adjustment to the record layer. Signed-off-by: Ronald Cron --- library/ssl_msg.c | 21 +++++++++++++++++++++ library/ssl_tls12_server.c | 28 ---------------------------- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/library/ssl_msg.c b/library/ssl_msg.c index b353345a35..6f2ac8826a 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -3261,6 +3261,27 @@ int mbedtls_ssl_prepare_handshake_record(mbedtls_ssl_context *ssl) return MBEDTLS_ERR_SSL_INVALID_RECORD; } + /* + * When establishing the connection, the client may go through a series + * of ClientHello and HelloVerifyRequest requests and responses. The + * server does not keep any trace of these initial round trips as + * intended: minimum allocated ressources as long as the reachability + * of the client has not been confirmed. When receiving the "first + * ClientHello" from server perspective, we may thus need to adapt + * the next expected `message_seq` for the incoming and outgoing + * handshake messages. + */ + if (ssl->in_msg[0] == MBEDTLS_SSL_HS_CLIENT_HELLO && + ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER && + ssl->state == MBEDTLS_SSL_CLIENT_HELLO +#if defined(MBEDTLS_SSL_RENEGOTIATION) + && ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE +#endif + ) { + ssl->handshake->in_msg_seq = recv_msg_seq; + ssl->handshake->out_msg_seq = recv_msg_seq; + } + if (ssl->handshake != NULL && ((mbedtls_ssl_is_handshake_over(ssl) == 0 && recv_msg_seq != ssl->handshake->in_msg_seq) || diff --git a/library/ssl_tls12_server.c b/library/ssl_tls12_server.c index d34d810f10..c5db5536b9 100644 --- a/library/ssl_tls12_server.c +++ b/library/ssl_tls12_server.c @@ -993,34 +993,6 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; } -#if defined(MBEDTLS_SSL_PROTO_DTLS) - if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { - /* - * Copy the client's handshake message_seq on initial handshakes, - * check sequence number on renego. - */ -#if defined(MBEDTLS_SSL_RENEGOTIATION) - if (ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS) { - /* This couldn't be done in ssl_prepare_handshake_record() */ - unsigned int cli_msg_seq = (unsigned int) MBEDTLS_GET_UINT16_BE(ssl->in_msg, 4); - if (cli_msg_seq != ssl->handshake->in_msg_seq) { - MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message_seq: " - "%u (expected %u)", cli_msg_seq, - ssl->handshake->in_msg_seq)); - return MBEDTLS_ERR_SSL_DECODE_ERROR; - } - - ssl->handshake->in_msg_seq++; - } else -#endif - { - unsigned int cli_msg_seq = (unsigned int) MBEDTLS_GET_UINT16_BE(ssl->in_msg, 4); - ssl->handshake->out_msg_seq = cli_msg_seq; - ssl->handshake->in_msg_seq = cli_msg_seq + 1; - } - } -#endif /* MBEDTLS_SSL_PROTO_DTLS */ - buf += mbedtls_ssl_hs_hdr_len(ssl); msg_len -= mbedtls_ssl_hs_hdr_len(ssl); From 6a9fc0ce77446b6053a5b31e57b49f3a3864af0a Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Thu, 22 Jan 2026 18:43:57 +0100 Subject: [PATCH 07/22] ssl_tls12_server.c: Move ClientHello record sequence_number init Signed-off-by: Ronald Cron --- library/ssl_msg.c | 9 +++++++++ library/ssl_tls12_server.c | 19 ------------------- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/library/ssl_msg.c b/library/ssl_msg.c index 6f2ac8826a..cd933f24f7 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -3280,6 +3280,15 @@ int mbedtls_ssl_prepare_handshake_record(mbedtls_ssl_context *ssl) ) { ssl->handshake->in_msg_seq = recv_msg_seq; ssl->handshake->out_msg_seq = recv_msg_seq; + + /* Epoch should be 0 for initial handshakes */ + if (ssl->in_ctr[0] != 0 || ssl->in_ctr[1] != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + memcpy(&ssl->cur_out_ctr[2], ssl->in_ctr + 2, + sizeof(ssl->cur_out_ctr) - 2); } if (ssl->handshake != NULL && diff --git a/library/ssl_tls12_server.c b/library/ssl_tls12_server.c index c5db5536b9..16bbf7eb57 100644 --- a/library/ssl_tls12_server.c +++ b/library/ssl_tls12_server.c @@ -958,25 +958,6 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, protocol version: [%d:%d]", buf[1], buf[2])); - /* For DTLS if this is the initial handshake, remember the client sequence - * number to use it in our next message (RFC 6347 4.2.1) */ -#if defined(MBEDTLS_SSL_PROTO_DTLS) - if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM -#if defined(MBEDTLS_SSL_RENEGOTIATION) - && ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE -#endif - ) { - /* Epoch should be 0 for initial handshakes */ - if (ssl->in_ctr[0] != 0 || ssl->in_ctr[1] != 0) { - MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); - return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; - } - - memcpy(&ssl->cur_out_ctr[2], ssl->in_ctr + 2, - sizeof(ssl->cur_out_ctr) - 2); - } -#endif /* MBEDTLS_SSL_PROTO_DTLS */ - buf = ssl->in_msg; msg_len = ssl->in_hslen; From 2086b20f28b7f9ad90c8c6fbca4461cb22ab34f4 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Thu, 22 Jan 2026 18:46:53 +0100 Subject: [PATCH 08/22] ssl_tls12_server.c: parse_client_hello: Remove remaining record level code Signed-off-by: Ronald Cron --- library/ssl_tls12_server.c | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/library/ssl_tls12_server.c b/library/ssl_tls12_server.c index 16bbf7eb57..47803dc85c 100644 --- a/library/ssl_tls12_server.c +++ b/library/ssl_tls12_server.c @@ -930,34 +930,6 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) return ret; } - buf = ssl->in_hdr; - - MBEDTLS_SSL_DEBUG_BUF(4, "record header", buf, mbedtls_ssl_in_hdr_len(ssl)); - - /* - * TLS Client Hello - * - * Record layer: - * 0 . 0 message type - * 1 . 2 protocol version - * 3 . 11 DTLS: epoch + record sequence number - * 3 . 4 message length - */ - MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, message type: %d", - buf[0])); - - if ((ssl->in_msgtype != MBEDTLS_SSL_MSG_HANDSHAKE) || - (buf[0] != MBEDTLS_SSL_MSG_HANDSHAKE)) { - MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); - return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; - } - - MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, message len.: %d", - MBEDTLS_GET_UINT16_BE(ssl->in_len, 0))); - - MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, protocol version: [%d:%d]", - buf[1], buf[2])); - buf = ssl->in_msg; msg_len = ssl->in_hslen; @@ -969,7 +941,8 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) * 6 . 8 DTLS only: fragment offset * 9 . 11 DTLS only: fragment length */ - if (buf[0] != MBEDTLS_SSL_HS_CLIENT_HELLO) { + if ((ssl->in_msgtype != MBEDTLS_SSL_MSG_HANDSHAKE) || + (buf[0] != MBEDTLS_SSL_HS_CLIENT_HELLO)) { MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; } From 09546ee1204388d94c0ae8f865657468132bd25c Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Fri, 23 Jan 2026 15:36:49 +0100 Subject: [PATCH 09/22] ssl_msg.c: Remove some now unnecessary code Signed-off-by: Ronald Cron --- library/ssl_msg.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/library/ssl_msg.c b/library/ssl_msg.c index cd933f24f7..b284e6dcda 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -5167,14 +5167,9 @@ static int ssl_get_next_record(mbedtls_ssl_context *ssl) /* The record content type may change during decryption, * so re-read it. */ ssl->in_msgtype = rec.type; - /* Also update the input buffer, because unfortunately - * the server-side ssl_parse_client_hello() reparses the - * record header when receiving a ClientHello initiating - * a renegotiation. */ - ssl->in_hdr[0] = rec.type; + ssl->in_msg = rec.buf + rec.data_offset; ssl->in_msglen = rec.data_len; - MBEDTLS_PUT_UINT16_BE(rec.data_len, ssl->in_len, 0); return 0; } From f20d48f71918b1a875703772b4de93ebc75597e5 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Wed, 14 Jan 2026 09:42:27 +0100 Subject: [PATCH 10/22] ssl-opt.sh: Add interop test of DTLS defragmentation on server side Signed-off-by: Ronald Cron --- tests/ssl-opt.sh | 151 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index ef84feb3b4..4641811d35 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -10982,6 +10982,51 @@ run_test "DTLS reassembly: fragmentation, nbio, renego (gnutls server)" \ -C "error" \ -s "Extra-header:" +requires_gnutls +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS reassembly: no fragmentation (gnutls client)" \ + "$P_SRV debug_level=2 dtls=1" \ + "$G_NEXT_CLI -u --mtu 2048 --insecure 127.0.0.1" \ + 0 \ + -S "found fragmented DTLS handshake message" \ + -S "error" + +requires_gnutls +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS reassembly: some fragmentation (gnutls client)" \ + "$P_SRV debug_level=2 dtls=1 auth_mode=required" \ + "$G_NEXT_CLI -u --mtu 256 --insecure 127.0.0.1 --x509certfile $DATA_FILES_PATH/server5.crt --x509keyfile $DATA_FILES_PATH/server5.key" \ + 0 \ + -s "found fragmented DTLS handshake message" \ + -s "Certificate handshake message has been buffered and reassembled" \ + -S "error" + +# Set the MTU to 128 bytes. The minimum size of a DTLS 1.2 record +# containing a ClientHello handshake message is 69 bytes, without any cookie, +# ciphersuite, or extension. With an MTU of 128 bytes, the ClientHello handshake +# message is therefore very likely to be fragmented in most library +# configurations. +requires_gnutls +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS reassembly: more fragmentation (gnutls client)" \ + "$P_SRV debug_level=2 dtls=1" \ + "$G_NEXT_CLI -u --mtu 128 --insecure 127.0.0.1" \ + 0 \ + -s "ClientHello handshake message has been buffered and reassembled" \ + -S "error" + +requires_gnutls +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS reassembly: more fragmentation, nbio (gnutls client)" \ + "$P_SRV debug_level=2 dtls=1 nbio=2" \ + "$G_NEXT_CLI -u --mtu 128 --insecure 127.0.0.1" \ + 0 \ + -s "ClientHello handshake message has been buffered and reassembled" \ + -S "error" + +# No fragmentation and renegotiation tests with GnuTLS client as the feature +# does not work properly. + requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS reassembly: no fragmentation (openssl server)" \ "$O_SRV -dtls -mtu 2048" \ @@ -11012,6 +11057,37 @@ run_test "DTLS reassembly: fragmentation, nbio (openssl server)" \ -c "Certificate handshake message has been buffered and reassembled" \ -C "error" +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS reassembly: no fragmentation (openssl client)" \ + "$P_SRV debug_level=2 dtls=1 auth_mode=required" \ + "$O_NEXT_CLI -dtls -mtu 2048 -cert $DATA_FILES_PATH/server5.crt -key $DATA_FILES_PATH/server5.key" \ + 0 \ + -S "found fragmented DTLS handshake message" \ + -S "error" + +# Minimum possible MTU for OpenSSL server: 256 bytes. +# We expect the server Certificate handshake to be fragmented and verify that +# this is the case. Depending on the configuration, other handshake messages may +# also be fragmented like the ClientHello, ClientKeyExchange or +# CertificateVerify messages. +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS reassembly: some fragmentation (openssl client)" \ + "$P_SRV debug_level=2 dtls=1 auth_mode=required" \ + "$O_NEXT_CLI -dtls -mtu 256 -cert $DATA_FILES_PATH/server5.crt -key $DATA_FILES_PATH/server5.key" \ + 0 \ + -s "found fragmented DTLS handshake message" \ + -s "Certificate handshake message has been buffered and reassembled" \ + -S "error" + +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS reassembly: fragmentation, nbio (openssl client)" \ + "$P_SRV debug_level=2 dtls=1 auth_mode=required nbio=2" \ + "$O_NEXT_CLI -dtls -mtu 256 -cert $DATA_FILES_PATH/server5.crt -key $DATA_FILES_PATH/server5.key" \ + 0 \ + -s "found fragmented DTLS handshake message" \ + -s "Certificate handshake message has been buffered and reassembled" \ + -S "error" + # Tests for sending fragmented handshake messages with DTLS # # Use client auth when we need the client to send large messages, @@ -12892,6 +12968,43 @@ run_test "DTLS proxy: 3d, openssl server, fragmentation, nbio" \ -c "HTTP/1.0 200 OK" \ -c "Certificate handshake message has been buffered and reassembled" +requires_openssl_next +client_needs_more_time 6 +not_with_valgrind # risk of non-mbedtls peer timing out +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS proxy: 3d, openssl client" \ + -p "$P_PXY drop=5 delay=5 duplicate=5" \ + "$P_SRV dgram_packing=0 dtls=1 hs_timeout=500-60000 tickets=0" \ + "$O_NEXT_CLI -dtls1_2 -mtu 2048" \ + 0 \ + -s "HTTP/1.0 200 OK" + +requires_openssl_next +client_needs_more_time 8 +not_with_valgrind # risk of non-mbedtls peer timing out +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS proxy: 3d, openssl client, fragmentation" \ + -p "$P_PXY drop=5 delay=5 duplicate=5" \ + "$P_SRV debug_level=2 dgram_packing=0 auth_mode=required dtls=1 hs_timeout=500-60000 tickets=0" \ + "$O_NEXT_CLI -dtls1_2 -mtu 256 -cert $DATA_FILES_PATH/server5.crt -key $DATA_FILES_PATH/server5.key" \ + 0 \ + -s "HTTP/1.0 200 OK" \ + -s "found fragmented DTLS handshake message" \ + -s "Certificate handshake message has been buffered and reassembled" + +requires_openssl_next +client_needs_more_time 8 +not_with_valgrind # risk of non-mbedtls peer timing out +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS proxy: 3d, openssl client, fragmentation, nbio" \ + -p "$P_PXY drop=5 delay=5 duplicate=5" \ + "$P_SRV debug_level=2 dgram_packing=0 auth_mode=required dtls=1 hs_timeout=500-60000 nbio=2 tickets=0" \ + "$O_NEXT_CLI -dtls1_2 -mtu 256 -cert $DATA_FILES_PATH/server5.crt -key $DATA_FILES_PATH/server5.key" \ + 0 \ + -s "HTTP/1.0 200 OK" \ + -s "found fragmented DTLS handshake message" \ + -s "Certificate handshake message has been buffered and reassembled" + requires_gnutls client_needs_more_time 6 not_with_valgrind # risk of non-mbedtls peer timing out @@ -12930,6 +13043,44 @@ run_test "DTLS proxy: 3d, gnutls server, fragmentation, nbio" \ -c "Extra-header:" \ -c "Certificate handshake message has been buffered and reassembled" +requires_gnutls +client_needs_more_time 6 +not_with_valgrind # risk of non-mbedtls peer timing out +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS proxy: 3d, gnutls client" \ + -p "$P_PXY drop=5 delay=5 duplicate=5" \ + "$P_SRV dgram_packing=0 dtls=1" \ + "$G_NEXT_CLI -u --mtu 2048 --insecure 127.0.0.1" \ + 0 \ + -s "HTTP/1.0 200 OK" + +# Set the MTU to 128 bytes. The ClientHello is not surely fragmented but very +# likely. Do not set it to 56 bytes where we would be sure that the ClientHello +# is fragmented as then experimentally the handshake fails too often. +requires_gnutls +client_needs_more_time 8 +not_with_valgrind # risk of non-mbedtls peer timing out +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS proxy: 3d, gnutls client, fragmentation" \ + -p "$P_PXY drop=5 delay=5 duplicate=5" \ + "$P_SRV dgram_packing=0 dtls=1 debug_level=2" \ + "$G_NEXT_CLI -u --mtu 128 --insecure 127.0.0.1" \ + 0 \ + -s "HTTP/1.0 200 OK" \ + -s "ClientHello handshake message has been buffered and reassembled" + +requires_gnutls +client_needs_more_time 8 +not_with_valgrind # risk of non-mbedtls peer timing out +requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 +run_test "DTLS proxy: 3d, gnutls client, fragmentation, nbio=2" \ + -p "$P_PXY drop=5 delay=5 duplicate=5" \ + "$P_SRV dgram_packing=0 dtls=1 debug_level=2 nbio=2" \ + "$G_NEXT_CLI -u --mtu 128 --insecure 127.0.0.1" \ + 0 \ + -s "HTTP/1.0 200 OK" \ + -s "ClientHello handshake message has been buffered and reassembled" + requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "export keys functionality" \ "$P_SRV eap_tls=1 debug_level=3" \ From f44b6545fef93cc2f758aedcdaea267c12f8ad9d Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Fri, 16 Jan 2026 16:50:39 +0100 Subject: [PATCH 11/22] ssl-opt.sh: Add tests with CH fragmented with DTLS in default config Signed-off-by: Ronald Cron --- tests/scripts/analyze_outcomes.py | 2 +- tests/ssl-opt.sh | 120 ++++++++++++++++++++++++++++-- 2 files changed, 113 insertions(+), 9 deletions(-) diff --git a/tests/scripts/analyze_outcomes.py b/tests/scripts/analyze_outcomes.py index 7b43145151..0266f1457c 100755 --- a/tests/scripts/analyze_outcomes.py +++ b/tests/scripts/analyze_outcomes.py @@ -43,7 +43,7 @@ class CoverageTask(outcome_analysis.CoverageTask): 'DTLS cookie: enabled, IPv6', # Disabled due to OpenSSL bug. # https://github.com/openssl/openssl/issues/18887 - 'DTLS fragmenting: 3d, openssl client, DTLS 1.2', + 'DTLS fragmenting: 3d, MTU=512, openssl client, DTLS 1.2', # We don't run ssl-opt.sh with Valgrind on the CI because # it's extremely slow. We don't intend to change this. 'DTLS fragmenting: proxy MTU: auto-reduction (with valgrind)', diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index 4641811d35..4423b1a285 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -11371,6 +11371,37 @@ run_test "DTLS fragmenting: both (MTU=512)" \ -c "found fragmented DTLS handshake message" \ -C "error" +# Depending on the ciphersuite selected to encrypt the application data, the +# maximum application data payload per record may be small with an MTU of 128. +# For example, with TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384, this maximum is +# 35 bytes. We therefore reduce the size of the client request and the server +# response in this test. +requires_config_enabled MBEDTLS_SSL_PROTO_DTLS +requires_max_content_len 2048 +run_test "DTLS fragmenting: both (MTU=128)" \ + -p "$P_PXY mtu=128" \ + "$P_SRV dtls=1 debug_level=5 auth_mode=required \ + crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ + key_file=$DATA_FILES_PATH/server7.key \ + response_size=8 \ + hs_timeout=2500-60000 \ + mtu=128" \ + "$P_CLI dtls=1 debug_level=2 \ + crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ + key_file=$DATA_FILES_PATH/server8.key \ + request_size=8 \ + hs_timeout=2500-60000 \ + mtu=128" \ + 0 \ + -s "found fragmented DTLS handshake message" \ + -s "fragmenting Certificate handshake message" \ + -s "fragmenting ServerKeyExchange handshake message" \ + -c "found fragmented DTLS handshake message" \ + -c "fragmenting ClientHello handshake message" \ + -c "fragmenting Certificate handshake message" \ + -c "fragmenting CertificateVerify handshake message" \ + -C "error" + # Test for automatic MTU reduction on repeated resend. # Forcing ciphersuite for this test to fit the MTU of 508 with full config. # The ratio of max/min timeout should ideally equal 4 to accept two @@ -11747,7 +11778,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C requires_gnutls requires_max_content_len 2048 -run_test "DTLS fragmenting: gnutls server, DTLS 1.2" \ +run_test "DTLS fragmenting: MTU=512, gnutls server, DTLS 1.2" \ "$G_SRV -u" \ "$P_CLI dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ @@ -11757,6 +11788,21 @@ run_test "DTLS fragmenting: gnutls server, DTLS 1.2" \ -c "fragmenting Certificate handshake message" \ -C "error" +requires_config_enabled MBEDTLS_SSL_PROTO_DTLS +requires_config_enabled MBEDTLS_RSA_C +requires_gnutls +requires_max_content_len 2048 +run_test "DTLS fragmenting: MTU=128, gnutls server, DTLS 1.2" \ + "$G_NEXT_SRV -u" \ + "$P_CLI dtls=1 debug_level=2 \ + crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ + key_file=$DATA_FILES_PATH/server8.key \ + mtu=128 force_version=dtls12" \ + 0 \ + -c "fragmenting ClientHello handshake message" \ + -c "fragmenting Certificate handshake message" \ + -C "error" + # We use --insecure for the GnuTLS client because it expects # the hostname / IP it connects to to be the name used in the # certificate obtained from the server. Here, however, it @@ -11769,7 +11815,7 @@ requires_config_enabled MBEDTLS_RSA_C requires_gnutls requires_not_i686 requires_max_content_len 2048 -run_test "DTLS fragmenting: gnutls client, DTLS 1.2" \ +run_test "DTLS fragmenting: MTU=512, gnutls client, DTLS 1.2" \ "$P_SRV dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ key_file=$DATA_FILES_PATH/server7.key \ @@ -11781,7 +11827,7 @@ run_test "DTLS fragmenting: gnutls client, DTLS 1.2" \ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C requires_max_content_len 2048 -run_test "DTLS fragmenting: openssl server, DTLS 1.2" \ +run_test "DTLS fragmenting: MTU=512, openssl server, DTLS 1.2" \ "$O_SRV -dtls1_2 -verify 10" \ "$P_CLI dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ @@ -11791,10 +11837,29 @@ run_test "DTLS fragmenting: openssl server, DTLS 1.2" \ -c "fragmenting Certificate handshake message" \ -C "error" +# Depending on the ciphersuite selected to encrypt the application data, the +# maximum application data payload per record may be small with an MTU of 128. +# For example, with TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384, this maximum is +# 35 bytes. We therefore reduce the size of the client request in this test. requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C requires_max_content_len 2048 -run_test "DTLS fragmenting: openssl client, DTLS 1.2" \ +run_test "DTLS fragmenting: MTU=128, openssl server, DTLS 1.2" \ + "$O_NEXT_SRV -dtls1_2 -verify 10" \ + "$P_CLI dtls=1 debug_level=2 \ + crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ + key_file=$DATA_FILES_PATH/server8.key \ + request_size=8 \ + mtu=128 force_version=dtls12" \ + 0 \ + -c "fragmenting ClientHello handshake message" \ + -c "fragmenting Certificate handshake message" \ + -C "error" + +requires_config_enabled MBEDTLS_SSL_PROTO_DTLS +requires_config_enabled MBEDTLS_RSA_C +requires_max_content_len 2048 +run_test "DTLS fragmenting: MTU=512, openssl client, DTLS 1.2" \ "$P_SRV dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ key_file=$DATA_FILES_PATH/server7.key \ @@ -11812,7 +11877,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C client_needs_more_time 4 requires_max_content_len 2048 -run_test "DTLS fragmenting: 3d, gnutls server, DTLS 1.2" \ +run_test "DTLS fragmenting: 3d, MTU=512, gnutls server, DTLS 1.2" \ -p "$P_PXY drop=8 delay=8 duplicate=8" \ "$G_NEXT_SRV -u" \ "$P_CLI dgram_packing=0 dtls=1 debug_level=2 \ @@ -11823,12 +11888,29 @@ run_test "DTLS fragmenting: 3d, gnutls server, DTLS 1.2" \ -c "fragmenting Certificate handshake message" \ -C "error" +requires_gnutls_next +requires_config_enabled MBEDTLS_SSL_PROTO_DTLS +requires_config_enabled MBEDTLS_RSA_C +client_needs_more_time 6 +requires_max_content_len 2048 +run_test "DTLS fragmenting: 3d, MTU=128, gnutls server, DTLS 1.2" \ + -p "$P_PXY drop=8 delay=8 duplicate=8" \ + "$G_NEXT_SRV -u" \ + "$P_CLI dgram_packing=0 dtls=1 debug_level=2 \ + crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ + key_file=$DATA_FILES_PATH/server8.key \ + hs_timeout=250-60000 mtu=128 force_version=dtls12" \ + 0 \ + -c "fragmenting ClientHello handshake message" \ + -c "fragmenting Certificate handshake message" \ + -C "error" + requires_gnutls_next requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C client_needs_more_time 4 requires_max_content_len 2048 -run_test "DTLS fragmenting: 3d, gnutls client, DTLS 1.2" \ +run_test "DTLS fragmenting: 3d, MTU=512, gnutls client, DTLS 1.2" \ -p "$P_PXY drop=8 delay=8 duplicate=8" \ "$P_SRV dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ @@ -11845,7 +11927,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C client_needs_more_time 4 requires_max_content_len 2048 -run_test "DTLS fragmenting: 3d, openssl server, DTLS 1.2" \ +run_test "DTLS fragmenting: 3d, MTU=512, openssl server, DTLS 1.2" \ -p "$P_PXY drop=8 delay=8 duplicate=8" \ "$O_NEXT_SRV -dtls1_2 -verify 10" \ "$P_CLI dtls=1 debug_level=2 \ @@ -11856,6 +11938,28 @@ run_test "DTLS fragmenting: 3d, openssl server, DTLS 1.2" \ -c "fragmenting Certificate handshake message" \ -C "error" +# Depending on the ciphersuite selected to encrypt the application data, the +# maximum application data payload per record may be small with an MTU of 128. +# For example, with TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384, this maximum is +# 35 bytes. We therefore reduce the size of the client request in this test. +requires_openssl_next +requires_config_enabled MBEDTLS_SSL_PROTO_DTLS +requires_config_enabled MBEDTLS_RSA_C +client_needs_more_time 4 +requires_max_content_len 2048 +run_test "DTLS fragmenting: 3d, MTU=128, openssl server, DTLS 1.2" \ + -p "$P_PXY drop=8 delay=8 duplicate=8" \ + "$O_NEXT_SRV -dtls1_2 -verify 10" \ + "$P_CLI dtls=1 debug_level=2 \ + crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ + key_file=$DATA_FILES_PATH/server8.key \ + request_size=8 \ + hs_timeout=250-60000 mtu=128 force_version=dtls12" \ + 0 \ + -c "fragmenting ClientHello handshake message" \ + -c "fragmenting Certificate handshake message" \ + -C "error" + ## the test below will time out with certain seed. ## The cause is an openssl bug (https://github.com/openssl/openssl/issues/18887) skip_next_test @@ -11863,7 +11967,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C client_needs_more_time 4 requires_max_content_len 2048 -run_test "DTLS fragmenting: 3d, openssl client, DTLS 1.2" \ +run_test "DTLS fragmenting: 3d, MTU=512, openssl client, DTLS 1.2" \ -p "$P_PXY drop=8 delay=8 duplicate=8" \ "$P_SRV dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ From 50d1a74ad8474918276b16b4e7fb304fd92c170a Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 10 Feb 2026 19:12:21 +0100 Subject: [PATCH 12/22] ssl-opt.sh: Fix/improve comments Signed-off-by: Ronald Cron --- tests/ssl-opt.sh | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index 4423b1a285..8dcb8ea3d6 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -11004,8 +11004,9 @@ run_test "DTLS reassembly: some fragmentation (gnutls client)" \ # Set the MTU to 128 bytes. The minimum size of a DTLS 1.2 record # containing a ClientHello handshake message is 69 bytes, without any cookie, # ciphersuite, or extension. With an MTU of 128 bytes, the ClientHello handshake -# message is therefore very likely to be fragmented in most library -# configurations. +# message is therefore very likely to be fragmented, regardless of the +# GnuTLS client version. For example, the ClientHello sent by the GnuTLS 3.7.2 +# client is 206 bytes in this test. requires_gnutls requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS reassembly: more fragmentation (gnutls client)" \ @@ -11066,10 +11067,11 @@ run_test "DTLS reassembly: no fragmentation (openssl client)" \ -S "error" # Minimum possible MTU for OpenSSL server: 256 bytes. -# We expect the server Certificate handshake to be fragmented and verify that -# this is the case. Depending on the configuration, other handshake messages may -# also be fragmented like the ClientHello, ClientKeyExchange or -# CertificateVerify messages. +# We expect the client Certificate handshake message to be fragmented and +# verify that this is the case. With OpenSSL 3.0.13, the ClientHello handshake +# message is 224 bytes and also fragmented. However, it may not hold across +# OpenSSL version updates. Therefore, we do not verify that the ClientHello is +# reassembled by the server. requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS reassembly: some fragmentation (openssl client)" \ "$P_SRV debug_level=2 dtls=1 auth_mode=required" \ @@ -13158,9 +13160,12 @@ run_test "DTLS proxy: 3d, gnutls client" \ 0 \ -s "HTTP/1.0 200 OK" -# Set the MTU to 128 bytes. The ClientHello is not surely fragmented but very -# likely. Do not set it to 56 bytes where we would be sure that the ClientHello -# is fragmented as then experimentally the handshake fails too often. +# Set the MTU to 128 bytes. The ClientHello is not guaranteed to be surely +# fragmented but it is very likely. For example, the ClientHello sent by the +# GnuTLS 3.7.2 client is 206 bytes in this test. We expect ClientHello +# fragmentation to remain the case across GnuTLS version updates. Avoid using a +# smaller MTU, as the smaller the MTU, the more likely the handshake is to fail +# in this very unreliable connection emulation. requires_gnutls client_needs_more_time 8 not_with_valgrind # risk of non-mbedtls peer timing out From 404daf5f6d311c4e02af0598dca022b4a945b324 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Wed, 11 Feb 2026 09:19:18 +0100 Subject: [PATCH 13/22] ssl-opt.sh: DTLS reassembly: Improve max_content_len requirements Signed-off-by: Ronald Cron --- tests/ssl-opt.sh | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index 8dcb8ea3d6..3ddecf6e09 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -11331,7 +11331,7 @@ run_test "DTLS fragmenting: server (MTU)" \ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C -requires_max_content_len 2048 +requires_max_content_len 1024 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS fragmenting: both (MTU=1024)" \ -p "$P_PXY mtu=1024" \ @@ -11354,7 +11354,7 @@ run_test "DTLS fragmenting: both (MTU=1024)" \ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C requires_hash_alg SHA_256 -requires_max_content_len 2048 +requires_max_content_len 512 run_test "DTLS fragmenting: both (MTU=512)" \ -p "$P_PXY mtu=512" \ "$P_SRV dtls=1 debug_level=2 auth_mode=required \ @@ -11379,7 +11379,7 @@ run_test "DTLS fragmenting: both (MTU=512)" \ # 35 bytes. We therefore reduce the size of the client request and the server # response in this test. requires_config_enabled MBEDTLS_SSL_PROTO_DTLS -requires_max_content_len 2048 +requires_max_content_len 128 run_test "DTLS fragmenting: both (MTU=128)" \ -p "$P_PXY mtu=128" \ "$P_SRV dtls=1 debug_level=5 auth_mode=required \ @@ -11457,7 +11457,7 @@ run_test "DTLS fragmenting: proxy MTU: auto-reduction (with valgrind)" \ not_with_valgrind # spurious autoreduction due to timeout requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C -requires_max_content_len 2048 +requires_max_content_len 1024 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS fragmenting: proxy MTU, simple handshake (MTU=1024)" \ -p "$P_PXY mtu=1024" \ @@ -11484,7 +11484,7 @@ run_test "DTLS fragmenting: proxy MTU, simple handshake (MTU=1024)" \ not_with_valgrind # spurious autoreduction due to timeout requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C -requires_max_content_len 2048 +requires_max_content_len 512 run_test "DTLS fragmenting: proxy MTU, simple handshake (MTU=512)" \ -p "$P_PXY mtu=512" \ "$P_SRV dtls=1 debug_level=2 auth_mode=required \ @@ -11507,7 +11507,7 @@ run_test "DTLS fragmenting: proxy MTU, simple handshake (MTU=512)" \ not_with_valgrind # spurious autoreduction due to timeout requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C -requires_max_content_len 2048 +requires_max_content_len 1024 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS fragmenting: proxy MTU, simple handshake, nbio (MTU=1024)" \ -p "$P_PXY mtu=1024" \ @@ -11531,7 +11531,7 @@ run_test "DTLS fragmenting: proxy MTU, simple handshake, nbio (MTU=1024)" \ not_with_valgrind # spurious autoreduction due to timeout requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C -requires_max_content_len 2048 +requires_max_content_len 512 run_test "DTLS fragmenting: proxy MTU, simple handshake, nbio (MTU=512)" \ -p "$P_PXY mtu=512" \ "$P_SRV dtls=1 debug_level=2 auth_mode=required \ @@ -11564,7 +11564,7 @@ run_test "DTLS fragmenting: proxy MTU, simple handshake, nbio (MTU=512)" \ not_with_valgrind # spurious autoreduction due to timeout requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C -requires_max_content_len 2048 +requires_max_content_len 1450 run_test "DTLS fragmenting: proxy MTU, resumed handshake" \ -p "$P_PXY mtu=1450" \ "$P_SRV dtls=1 debug_level=2 auth_mode=required \ @@ -11591,7 +11591,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C requires_hash_alg SHA_256 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION -requires_max_content_len 2048 +requires_max_content_len 512 run_test "DTLS fragmenting: proxy MTU, ChachaPoly renego" \ -p "$P_PXY mtu=512" \ "$P_SRV dtls=1 debug_level=2 auth_mode=required \ @@ -11620,7 +11620,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C requires_hash_alg SHA_256 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION -requires_max_content_len 2048 +requires_max_content_len 512 run_test "DTLS fragmenting: proxy MTU, AES-GCM renego" \ -p "$P_PXY mtu=512" \ "$P_SRV dtls=1 debug_level=2 auth_mode=required \ @@ -11649,7 +11649,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C requires_hash_alg SHA_256 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION -requires_max_content_len 2048 +requires_max_content_len 1024 run_test "DTLS fragmenting: proxy MTU, AES-CCM renego" \ -p "$P_PXY mtu=1024" \ "$P_SRV dtls=1 debug_level=2 auth_mode=required \ @@ -11679,7 +11679,7 @@ requires_config_enabled MBEDTLS_RSA_C requires_hash_alg SHA_256 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION requires_config_enabled MBEDTLS_SSL_ENCRYPT_THEN_MAC -requires_max_content_len 2048 +requires_max_content_len 1024 run_test "DTLS fragmenting: proxy MTU, AES-CBC EtM renego" \ -p "$P_PXY mtu=1024" \ "$P_SRV dtls=1 debug_level=2 auth_mode=required \ @@ -11708,7 +11708,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C requires_hash_alg SHA_256 requires_config_enabled MBEDTLS_SSL_RENEGOTIATION -requires_max_content_len 2048 +requires_max_content_len 1024 run_test "DTLS fragmenting: proxy MTU, AES-CBC non-EtM renego" \ -p "$P_PXY mtu=1024" \ "$P_SRV dtls=1 debug_level=2 auth_mode=required \ @@ -11734,7 +11734,7 @@ run_test "DTLS fragmenting: proxy MTU, AES-CBC non-EtM renego" \ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C client_needs_more_time 2 -requires_max_content_len 2048 +requires_max_content_len 512 run_test "DTLS fragmenting: proxy MTU + 3d" \ -p "$P_PXY mtu=512 drop=8 delay=8 duplicate=8" \ "$P_SRV dgram_packing=0 dtls=1 debug_level=2 auth_mode=required \ @@ -11755,7 +11755,7 @@ run_test "DTLS fragmenting: proxy MTU + 3d" \ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C client_needs_more_time 2 -requires_max_content_len 2048 +requires_max_content_len 512 run_test "DTLS fragmenting: proxy MTU + 3d, nbio" \ -p "$P_PXY mtu=512 drop=8 delay=8 duplicate=8" \ "$P_SRV dtls=1 debug_level=2 auth_mode=required \ From 82d549a297df54fd018f6966e49e8360e410cc09 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Wed, 11 Feb 2026 09:08:03 +0100 Subject: [PATCH 14/22] ssl-opt.sh: Use more diverse MTUs Do not use only power of 2 MTUs. Use diverse MTUs in DTLS reassembly/ fragmenting/proxy tests. Signed-off-by: Ronald Cron --- tests/ssl-opt.sh | 189 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 142 insertions(+), 47 deletions(-) diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index 3ddecf6e09..c9c9ec3929 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -10942,7 +10942,7 @@ run_test "DTLS reassembly: more fragmentation (gnutls server)" \ requires_gnutls requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS reassembly: more fragmentation, nbio (gnutls server)" \ - "$G_SRV -u --mtu 128" \ + "$G_SRV -u --mtu 109" \ "$P_CLI dtls=1 nbio=2 debug_level=2" \ 0 \ -c "found fragmented DTLS handshake message" \ @@ -10954,7 +10954,7 @@ requires_gnutls requires_config_enabled MBEDTLS_SSL_RENEGOTIATION requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS reassembly: fragmentation, renego (gnutls server)" \ - "$G_SRV -u --mtu 256" \ + "$G_SRV -u --mtu 241" \ "$P_CLI debug_level=3 dtls=1 renegotiation=1 renegotiate=1" \ 0 \ -c "found fragmented DTLS handshake message" \ @@ -10995,7 +10995,7 @@ requires_gnutls requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS reassembly: some fragmentation (gnutls client)" \ "$P_SRV debug_level=2 dtls=1 auth_mode=required" \ - "$G_NEXT_CLI -u --mtu 256 --insecure 127.0.0.1 --x509certfile $DATA_FILES_PATH/server5.crt --x509keyfile $DATA_FILES_PATH/server5.key" \ + "$G_NEXT_CLI -u --mtu 211 --insecure 127.0.0.1 --x509certfile $DATA_FILES_PATH/server5.crt --x509keyfile $DATA_FILES_PATH/server5.key" \ 0 \ -s "found fragmented DTLS handshake message" \ -s "Certificate handshake message has been buffered and reassembled" \ @@ -11011,7 +11011,7 @@ requires_gnutls requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS reassembly: more fragmentation (gnutls client)" \ "$P_SRV debug_level=2 dtls=1" \ - "$G_NEXT_CLI -u --mtu 128 --insecure 127.0.0.1" \ + "$G_NEXT_CLI -u --mtu 103 --insecure 127.0.0.1" \ 0 \ -s "ClientHello handshake message has been buffered and reassembled" \ -S "error" @@ -11020,7 +11020,7 @@ requires_gnutls requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS reassembly: more fragmentation, nbio (gnutls client)" \ "$P_SRV debug_level=2 dtls=1 nbio=2" \ - "$G_NEXT_CLI -u --mtu 128 --insecure 127.0.0.1" \ + "$G_NEXT_CLI -u --mtu 103 --insecure 127.0.0.1" \ 0 \ -s "ClientHello handshake message has been buffered and reassembled" \ -S "error" @@ -11051,7 +11051,7 @@ run_test "DTLS reassembly: fragmentation (openssl server)" \ requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS reassembly: fragmentation, nbio (openssl server)" \ - "$O_SRV -dtls -mtu 256" \ + "$O_SRV -dtls -mtu 273" \ "$P_CLI dtls=1 nbio=2 debug_level=2" \ 0 \ -c "found fragmented DTLS handshake message" \ @@ -11084,7 +11084,7 @@ run_test "DTLS reassembly: some fragmentation (openssl client)" \ requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS reassembly: fragmentation, nbio (openssl client)" \ "$P_SRV debug_level=2 dtls=1 auth_mode=required nbio=2" \ - "$O_NEXT_CLI -dtls -mtu 256 -cert $DATA_FILES_PATH/server5.crt -key $DATA_FILES_PATH/server5.key" \ + "$O_NEXT_CLI -dtls -mtu 269 -cert $DATA_FILES_PATH/server5.crt -key $DATA_FILES_PATH/server5.key" \ 0 \ -s "found fragmented DTLS handshake message" \ -s "Certificate handshake message has been buffered and reassembled" \ @@ -11331,20 +11331,20 @@ run_test "DTLS fragmenting: server (MTU)" \ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C -requires_max_content_len 1024 +requires_max_content_len 1038 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 -run_test "DTLS fragmenting: both (MTU=1024)" \ - -p "$P_PXY mtu=1024" \ +run_test "DTLS fragmenting: both (MTU=1038)" \ + -p "$P_PXY mtu=1038" \ "$P_SRV dtls=1 debug_level=2 auth_mode=required \ crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ key_file=$DATA_FILES_PATH/server7.key \ hs_timeout=2500-60000 \ - mtu=1024" \ + mtu=1038" \ "$P_CLI dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ key_file=$DATA_FILES_PATH/server8.key \ hs_timeout=2500-60000 \ - mtu=1024" \ + mtu=1038" \ 0 \ -s "found fragmented DTLS handshake message" \ -c "found fragmented DTLS handshake message" \ @@ -11354,20 +11354,20 @@ run_test "DTLS fragmenting: both (MTU=1024)" \ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C requires_hash_alg SHA_256 -requires_max_content_len 512 -run_test "DTLS fragmenting: both (MTU=512)" \ - -p "$P_PXY mtu=512" \ +requires_max_content_len 509 +run_test "DTLS fragmenting: both (MTU=509)" \ + -p "$P_PXY mtu=509" \ "$P_SRV dtls=1 debug_level=2 auth_mode=required \ crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ key_file=$DATA_FILES_PATH/server7.key \ hs_timeout=2500-60000 \ - mtu=512" \ + mtu=509" \ "$P_CLI dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ key_file=$DATA_FILES_PATH/server8.key \ force_ciphersuite=TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256 \ hs_timeout=2500-60000 \ - mtu=512" \ + mtu=509" \ 0 \ -s "found fragmented DTLS handshake message" \ -c "found fragmented DTLS handshake message" \ @@ -11377,7 +11377,7 @@ run_test "DTLS fragmenting: both (MTU=512)" \ # maximum application data payload per record may be small with an MTU of 128. # For example, with TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384, this maximum is # 35 bytes. We therefore reduce the size of the client request and the server -# response in this test. +# response in this test and the two following tests. requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_max_content_len 128 run_test "DTLS fragmenting: both (MTU=128)" \ @@ -11404,6 +11404,58 @@ run_test "DTLS fragmenting: both (MTU=128)" \ -c "fragmenting CertificateVerify handshake message" \ -C "error" +requires_config_enabled MBEDTLS_SSL_PROTO_DTLS +requires_max_content_len 2048 +run_test "DTLS fragmenting: both (MTU=107)" \ + -p "$P_PXY mtu=107" \ + "$P_SRV dtls=1 debug_level=5 auth_mode=required \ + crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ + key_file=$DATA_FILES_PATH/server7.key \ + response_size=8 \ + hs_timeout=2500-60000 \ + mtu=107" \ + "$P_CLI dtls=1 debug_level=2 \ + crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ + key_file=$DATA_FILES_PATH/server8.key \ + request_size=8 \ + hs_timeout=2500-60000 \ + mtu=107" \ + 0 \ + -s "found fragmented DTLS handshake message" \ + -s "fragmenting Certificate handshake message" \ + -s "fragmenting ServerKeyExchange handshake message" \ + -c "found fragmented DTLS handshake message" \ + -c "fragmenting ClientHello handshake message" \ + -c "fragmenting Certificate handshake message" \ + -c "fragmenting CertificateVerify handshake message" \ + -C "error" + +requires_config_enabled MBEDTLS_SSL_PROTO_DTLS +requires_max_content_len 2048 +run_test "DTLS fragmenting: both (MTU=133)" \ + -p "$P_PXY mtu=133" \ + "$P_SRV dtls=1 debug_level=5 auth_mode=required \ + crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ + key_file=$DATA_FILES_PATH/server7.key \ + response_size=8 \ + hs_timeout=2500-60000 \ + mtu=133" \ + "$P_CLI dtls=1 debug_level=2 \ + crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ + key_file=$DATA_FILES_PATH/server8.key \ + request_size=8 \ + hs_timeout=2500-60000 \ + mtu=133" \ + 0 \ + -s "found fragmented DTLS handshake message" \ + -s "fragmenting Certificate handshake message" \ + -s "fragmenting ServerKeyExchange handshake message" \ + -c "found fragmented DTLS handshake message" \ + -c "fragmenting ClientHello handshake message" \ + -c "fragmenting Certificate handshake message" \ + -c "fragmenting CertificateVerify handshake message" \ + -C "error" + # Test for automatic MTU reduction on repeated resend. # Forcing ciphersuite for this test to fit the MTU of 508 with full config. # The ratio of max/min timeout should ideally equal 4 to accept two @@ -11780,12 +11832,12 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C requires_gnutls requires_max_content_len 2048 -run_test "DTLS fragmenting: MTU=512, gnutls server, DTLS 1.2" \ +run_test "DTLS fragmenting: MTU=501, gnutls server, DTLS 1.2" \ "$G_SRV -u" \ "$P_CLI dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ key_file=$DATA_FILES_PATH/server8.key \ - mtu=512 force_version=dtls12" \ + mtu=501 force_version=dtls12" \ 0 \ -c "fragmenting Certificate handshake message" \ -C "error" @@ -11794,12 +11846,13 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C requires_gnutls requires_max_content_len 2048 -run_test "DTLS fragmenting: MTU=128, gnutls server, DTLS 1.2" \ +run_test "DTLS fragmenting: MTU=110, gnutls server, DTLS 1.2" \ "$G_NEXT_SRV -u" \ "$P_CLI dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ key_file=$DATA_FILES_PATH/server8.key \ - mtu=128 force_version=dtls12" \ + request_size=35 \ + mtu=110 force_version=dtls12" \ 0 \ -c "fragmenting ClientHello handshake message" \ -c "fragmenting Certificate handshake message" \ @@ -11817,11 +11870,25 @@ requires_config_enabled MBEDTLS_RSA_C requires_gnutls requires_not_i686 requires_max_content_len 2048 -run_test "DTLS fragmenting: MTU=512, gnutls client, DTLS 1.2" \ +run_test "DTLS fragmenting: MTU=536, gnutls client, DTLS 1.2" \ "$P_SRV dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ key_file=$DATA_FILES_PATH/server7.key \ - mtu=512 force_version=dtls12" \ + mtu=536 force_version=dtls12" \ + "$G_CLI -u --insecure 127.0.0.1" \ + 0 \ + -s "fragmenting Certificate handshake message" + +requires_config_enabled MBEDTLS_SSL_PROTO_DTLS +requires_config_enabled MBEDTLS_RSA_C +requires_gnutls +requires_not_i686 +requires_max_content_len 2048 +run_test "DTLS fragmenting: MTU=149, gnutls client, DTLS 1.2" \ + "$P_SRV dtls=1 debug_level=2 \ + crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ + key_file=$DATA_FILES_PATH/server7.key \ + mtu=149 force_version=dtls12" \ "$G_CLI -u --insecure 127.0.0.1" \ 0 \ -s "fragmenting Certificate handshake message" @@ -11829,12 +11896,12 @@ run_test "DTLS fragmenting: MTU=512, gnutls client, DTLS 1.2" \ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C requires_max_content_len 2048 -run_test "DTLS fragmenting: MTU=512, openssl server, DTLS 1.2" \ +run_test "DTLS fragmenting: MTU=525, openssl server, DTLS 1.2" \ "$O_SRV -dtls1_2 -verify 10" \ "$P_CLI dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ key_file=$DATA_FILES_PATH/server8.key \ - mtu=512 force_version=dtls12" \ + mtu=525 force_version=dtls12" \ 0 \ -c "fragmenting Certificate handshake message" \ -C "error" @@ -11846,13 +11913,13 @@ run_test "DTLS fragmenting: MTU=512, openssl server, DTLS 1.2" \ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C requires_max_content_len 2048 -run_test "DTLS fragmenting: MTU=128, openssl server, DTLS 1.2" \ +run_test "DTLS fragmenting: MTU=130, openssl server, DTLS 1.2" \ "$O_NEXT_SRV -dtls1_2 -verify 10" \ "$P_CLI dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ key_file=$DATA_FILES_PATH/server8.key \ request_size=8 \ - mtu=128 force_version=dtls12" \ + mtu=130 force_version=dtls12" \ 0 \ -c "fragmenting ClientHello handshake message" \ -c "fragmenting Certificate handshake message" \ @@ -11870,6 +11937,18 @@ run_test "DTLS fragmenting: MTU=512, openssl client, DTLS 1.2" \ 0 \ -s "fragmenting Certificate handshake message" +requires_config_enabled MBEDTLS_SSL_PROTO_DTLS +requires_config_enabled MBEDTLS_RSA_C +requires_max_content_len 2048 +run_test "DTLS fragmenting: MTU=131, openssl client, DTLS 1.2" \ + "$P_SRV dtls=1 debug_level=2 \ + crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ + key_file=$DATA_FILES_PATH/server7.key \ + mtu=131 force_version=dtls12" \ + "$O_CLI -dtls1_2" \ + 0 \ + -s "fragmenting Certificate handshake message" + # interop tests for DTLS fragmentating with unreliable connection # # again we just want to test that the we fragment in a way that @@ -11879,13 +11958,13 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C client_needs_more_time 4 requires_max_content_len 2048 -run_test "DTLS fragmenting: 3d, MTU=512, gnutls server, DTLS 1.2" \ +run_test "DTLS fragmenting: 3d, MTU=434, gnutls server, DTLS 1.2" \ -p "$P_PXY drop=8 delay=8 duplicate=8" \ "$G_NEXT_SRV -u" \ "$P_CLI dgram_packing=0 dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ key_file=$DATA_FILES_PATH/server8.key \ - hs_timeout=250-60000 mtu=512 force_version=dtls12" \ + hs_timeout=250-60000 mtu=434 force_version=dtls12" \ 0 \ -c "fragmenting Certificate handshake message" \ -C "error" @@ -11895,13 +11974,14 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C client_needs_more_time 6 requires_max_content_len 2048 -run_test "DTLS fragmenting: 3d, MTU=128, gnutls server, DTLS 1.2" \ +run_test "DTLS fragmenting: 3d, MTU=103, gnutls server, DTLS 1.2" \ -p "$P_PXY drop=8 delay=8 duplicate=8" \ "$G_NEXT_SRV -u" \ "$P_CLI dgram_packing=0 dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ key_file=$DATA_FILES_PATH/server8.key \ - hs_timeout=250-60000 mtu=128 force_version=dtls12" \ + request_size=35 \ + hs_timeout=250-60000 mtu=103 force_version=dtls12" \ 0 \ -c "fragmenting ClientHello handshake message" \ -c "fragmenting Certificate handshake message" \ @@ -11912,12 +11992,27 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C client_needs_more_time 4 requires_max_content_len 2048 -run_test "DTLS fragmenting: 3d, MTU=512, gnutls client, DTLS 1.2" \ +run_test "DTLS fragmenting: 3d, MTU=614, gnutls client, DTLS 1.2" \ -p "$P_PXY drop=8 delay=8 duplicate=8" \ "$P_SRV dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ key_file=$DATA_FILES_PATH/server7.key \ - hs_timeout=250-60000 mtu=512 force_version=dtls12" \ + hs_timeout=250-60000 mtu=614 force_version=dtls12" \ + "$G_NEXT_CLI -u --insecure 127.0.0.1" \ + 0 \ + -s "fragmenting Certificate handshake message" + +requires_gnutls_next +requires_config_enabled MBEDTLS_SSL_PROTO_DTLS +requires_config_enabled MBEDTLS_RSA_C +client_needs_more_time 4 +requires_max_content_len 2048 +run_test "DTLS fragmenting: 3d, MTU=116, gnutls client, DTLS 1.2" \ + -p "$P_PXY drop=8 delay=8 duplicate=8" \ + "$P_SRV dtls=1 debug_level=2 \ + crt_file=$DATA_FILES_PATH/server7_int-ca.crt \ + key_file=$DATA_FILES_PATH/server7.key \ + hs_timeout=250-60000 mtu=116 force_version=dtls12" \ "$G_NEXT_CLI -u --insecure 127.0.0.1" \ 0 \ -s "fragmenting Certificate handshake message" @@ -11929,13 +12024,13 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C client_needs_more_time 4 requires_max_content_len 2048 -run_test "DTLS fragmenting: 3d, MTU=512, openssl server, DTLS 1.2" \ +run_test "DTLS fragmenting: 3d, MTU=541, openssl server, DTLS 1.2" \ -p "$P_PXY drop=8 delay=8 duplicate=8" \ "$O_NEXT_SRV -dtls1_2 -verify 10" \ "$P_CLI dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ key_file=$DATA_FILES_PATH/server8.key \ - hs_timeout=250-60000 mtu=512 force_version=dtls12" \ + hs_timeout=250-60000 mtu=541 force_version=dtls12" \ 0 \ -c "fragmenting Certificate handshake message" \ -C "error" @@ -11949,14 +12044,14 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS requires_config_enabled MBEDTLS_RSA_C client_needs_more_time 4 requires_max_content_len 2048 -run_test "DTLS fragmenting: 3d, MTU=128, openssl server, DTLS 1.2" \ +run_test "DTLS fragmenting: 3d, MTU=108, openssl server, DTLS 1.2" \ -p "$P_PXY drop=8 delay=8 duplicate=8" \ "$O_NEXT_SRV -dtls1_2 -verify 10" \ "$P_CLI dtls=1 debug_level=2 \ crt_file=$DATA_FILES_PATH/server8_int-ca2.crt \ key_file=$DATA_FILES_PATH/server8.key \ request_size=8 \ - hs_timeout=250-60000 mtu=128 force_version=dtls12" \ + hs_timeout=250-60000 mtu=108 force_version=dtls12" \ 0 \ -c "fragmenting ClientHello handshake message" \ -c "fragmenting Certificate handshake message" \ @@ -13056,7 +13151,7 @@ not_with_valgrind # risk of non-mbedtls peer timing out requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS proxy: 3d, openssl server, fragmentation" \ -p "$P_PXY drop=5 delay=5 duplicate=5 protect_hvr=1" \ - "$O_NEXT_SRV -dtls1_2 -mtu 256" \ + "$O_NEXT_SRV -dtls1_2 -mtu 277" \ "$P_CLI dgram_packing=0 dtls=1 debug_level=2 hs_timeout=500-60000 tickets=0" \ 0 \ -c "HTTP/1.0 200 OK" \ @@ -13068,7 +13163,7 @@ not_with_valgrind # risk of non-mbedtls peer timing out requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS proxy: 3d, openssl server, fragmentation, nbio" \ -p "$P_PXY drop=5 delay=5 duplicate=5 protect_hvr=1" \ - "$O_NEXT_SRV -dtls1_2 -mtu 256" \ + "$O_NEXT_SRV -dtls1_2 -mtu 268" \ "$P_CLI dgram_packing=0 dtls=1 debug_level=2 hs_timeout=500-60000 nbio=2 tickets=0" \ 0 \ -c "HTTP/1.0 200 OK" \ @@ -13092,7 +13187,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS proxy: 3d, openssl client, fragmentation" \ -p "$P_PXY drop=5 delay=5 duplicate=5" \ "$P_SRV debug_level=2 dgram_packing=0 auth_mode=required dtls=1 hs_timeout=500-60000 tickets=0" \ - "$O_NEXT_CLI -dtls1_2 -mtu 256 -cert $DATA_FILES_PATH/server5.crt -key $DATA_FILES_PATH/server5.key" \ + "$O_NEXT_CLI -dtls1_2 -mtu 260 -cert $DATA_FILES_PATH/server5.crt -key $DATA_FILES_PATH/server5.key" \ 0 \ -s "HTTP/1.0 200 OK" \ -s "found fragmented DTLS handshake message" \ @@ -13105,7 +13200,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS proxy: 3d, openssl client, fragmentation, nbio" \ -p "$P_PXY drop=5 delay=5 duplicate=5" \ "$P_SRV debug_level=2 dgram_packing=0 auth_mode=required dtls=1 hs_timeout=500-60000 nbio=2 tickets=0" \ - "$O_NEXT_CLI -dtls1_2 -mtu 256 -cert $DATA_FILES_PATH/server5.crt -key $DATA_FILES_PATH/server5.key" \ + "$O_NEXT_CLI -dtls1_2 -mtu 259 -cert $DATA_FILES_PATH/server5.crt -key $DATA_FILES_PATH/server5.key" \ 0 \ -s "HTTP/1.0 200 OK" \ -s "found fragmented DTLS handshake message" \ @@ -13129,7 +13224,7 @@ not_with_valgrind # risk of non-mbedtls peer timing out requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS proxy: 3d, gnutls server, fragmentation" \ -p "$P_PXY drop=5 delay=5 duplicate=5" \ - "$G_NEXT_SRV -u --mtu 512" \ + "$G_NEXT_SRV -u --mtu 499" \ "$P_CLI dgram_packing=0 dtls=1 debug_level=2 hs_timeout=500-60000" \ 0 \ -s "Extra-header:" \ @@ -13142,7 +13237,7 @@ not_with_valgrind # risk of non-mbedtls peer timing out requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS proxy: 3d, gnutls server, fragmentation, nbio" \ -p "$P_PXY drop=5 delay=5 duplicate=5" \ - "$G_NEXT_SRV -u --mtu 512" \ + "$G_NEXT_SRV -u --mtu 528" \ "$P_CLI dgram_packing=0 dtls=1 debug_level=2 hs_timeout=500-60000 nbio=2" \ 0 \ -s "Extra-header:" \ @@ -13160,7 +13255,7 @@ run_test "DTLS proxy: 3d, gnutls client" \ 0 \ -s "HTTP/1.0 200 OK" -# Set the MTU to 128 bytes. The ClientHello is not guaranteed to be surely +# Set the MTU to 131 bytes. The ClientHello is not guaranteed to be surely # fragmented but it is very likely. For example, the ClientHello sent by the # GnuTLS 3.7.2 client is 206 bytes in this test. We expect ClientHello # fragmentation to remain the case across GnuTLS version updates. Avoid using a @@ -13173,7 +13268,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS proxy: 3d, gnutls client, fragmentation" \ -p "$P_PXY drop=5 delay=5 duplicate=5" \ "$P_SRV dgram_packing=0 dtls=1 debug_level=2" \ - "$G_NEXT_CLI -u --mtu 128 --insecure 127.0.0.1" \ + "$G_NEXT_CLI -u --mtu 131 --insecure 127.0.0.1" \ 0 \ -s "HTTP/1.0 200 OK" \ -s "ClientHello handshake message has been buffered and reassembled" @@ -13185,7 +13280,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2 run_test "DTLS proxy: 3d, gnutls client, fragmentation, nbio=2" \ -p "$P_PXY drop=5 delay=5 duplicate=5" \ "$P_SRV dgram_packing=0 dtls=1 debug_level=2 nbio=2" \ - "$G_NEXT_CLI -u --mtu 128 --insecure 127.0.0.1" \ + "$G_NEXT_CLI -u --mtu 135 --insecure 127.0.0.1" \ 0 \ -s "HTTP/1.0 200 OK" \ -s "ClientHello handshake message has been buffered and reassembled" From 1bdb0901c90dfbffc29e674f70219137860f1398 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Fri, 13 Feb 2026 10:52:28 +0100 Subject: [PATCH 15/22] Update mbedtls_ssl_handshake() documentation Signed-off-by: Ronald Cron --- include/mbedtls/ssl.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h index 3cdddf7d72..a81bb686e3 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -5084,13 +5084,6 @@ int mbedtls_ssl_get_session(const mbedtls_ssl_context *ssl, * supported with some limitations (those limitations do * not apply to DTLS, where defragmentation is fully * supported): - * - On an Mbed TLS server that only accepts TLS 1.2, - * the initial ClientHello message must not be fragmented. - * A TLS 1.2 ClientHello may be fragmented if the server - * also accepts TLS 1.3 connections (meaning - * that #MBEDTLS_SSL_PROTO_TLS1_3 enabled, and the - * accepted versions have not been restricted with - * mbedtls_ssl_conf_max_tls_version() or the like). * - The first fragment of a handshake message must be * at least 4 bytes long. * - Non-handshake records must not be interleaved between From 38213a785666d65cf787c3f2a6cd9c4943aa1fa9 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Fri, 13 Feb 2026 11:03:13 +0100 Subject: [PATCH 16/22] Add change log Signed-off-by: Ronald Cron --- ChangeLog.d/dtls-client-hello-defragmentation.txt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 ChangeLog.d/dtls-client-hello-defragmentation.txt diff --git a/ChangeLog.d/dtls-client-hello-defragmentation.txt b/ChangeLog.d/dtls-client-hello-defragmentation.txt new file mode 100644 index 0000000000..f5ff0b754c --- /dev/null +++ b/ChangeLog.d/dtls-client-hello-defragmentation.txt @@ -0,0 +1,5 @@ +Bugfix + * Support re-assembly of fragmented DTLS 1.2 ClientHello in Mbed TLS server. + * Support re-assembly of fragmented TLS 1.2 ClientHello in Mbed TLS server + even if TLS 1.3 support is disabled. This removes the main limitation on + support for re-assembly of fragmented handshake messages in TLS 1.2. From 97a25e4c68120dc3231362da745edc1373686e28 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Mon, 23 Feb 2026 14:09:13 +0100 Subject: [PATCH 17/22] ssl_tls12_server.c: Update hs status after some validations of the ClientHello Signed-off-by: Ronald Cron --- library/ssl_tls12_server.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/library/ssl_tls12_server.c b/library/ssl_tls12_server.c index 47803dc85c..eae4996fc8 100644 --- a/library/ssl_tls12_server.c +++ b/library/ssl_tls12_server.c @@ -924,12 +924,6 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) return ret; } - ret = mbedtls_ssl_update_handshake_status(ssl); - if (0 != ret) { - MBEDTLS_SSL_DEBUG_RET(1, ("mbedtls_ssl_update_handshake_status"), ret); - return ret; - } - buf = ssl->in_msg; msg_len = ssl->in_hslen; @@ -1135,6 +1129,21 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) ext_len = 0; } + /* + * Update the handshake checksum after performing preliminary + * validation of the ClientHello and before parsing its extensions. + * + * The checksum must be updated before parsing the extensions because + * ssl_parse_session_ticket_ext() may decrypt the ticket in place and + * therefore modify the ClientHello message. This occurs when using + * the Mbed TLS ssl_ticket.c implementation. + */ + ret = mbedtls_ssl_update_handshake_status(ssl); + if (0 != ret) { + MBEDTLS_SSL_DEBUG_RET(1, ("mbedtls_ssl_update_handshake_status"), ret); + return ret; + } + ext = buf + ext_offset + 2; MBEDTLS_SSL_DEBUG_BUF(3, "client hello extensions", ext, ext_len); @@ -1276,7 +1285,11 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) #if defined(MBEDTLS_SSL_SESSION_TICKETS) case MBEDTLS_TLS_EXT_SESSION_TICKET: MBEDTLS_SSL_DEBUG_MSG(3, ("found session ticket extension")); - + /* + * If the Mbed TLS ssl_ticket.c implementation is used, the + * ticket is decrypted in place. This modifies the ClientHello + * message in the input buffer. + */ ret = ssl_parse_session_ticket_ext(ssl, ext + 4, ext_size); if (ret != 0) { return ret; From 6b529ff50de49f3f7e770480093a4d08ab50460f Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Wed, 25 Feb 2026 17:49:35 +0100 Subject: [PATCH 18/22] component_test_tls1_2_ccm_psk_dtls_psa: Enable MBEDTLS_HAVE_TIME As done in component_test_tls1_2_ccm_psk_dtls_legacy enable MBEDTLS_HAVE_TIME in component_test_tls1_2_ccm_psk_dtls_psa. Signed-off-by: Ronald Cron --- tests/scripts/components-configuration-tls.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/scripts/components-configuration-tls.sh b/tests/scripts/components-configuration-tls.sh index d2a6049777..731f695bd5 100644 --- a/tests/scripts/components-configuration-tls.sh +++ b/tests/scripts/components-configuration-tls.sh @@ -420,6 +420,7 @@ component_test_tls1_2_ccm_psk_dtls_psa () { cp configs/config-ccm-psk-dtls1_2.h "$CONFIG_H" scripts/config.py set MBEDTLS_PSA_CRYPTO_C scripts/config.py set MBEDTLS_USE_PSA_CRYPTO + scripts/config.py set MBEDTLS_HAVE_TIME # test-ref-configs works by overwriting mbedtls_config.h; this makes cmake # want to re-generate generated files that depend on it, quite correctly. # However this doesn't work as the generation script expects a specific From 7d022d3275453b605ad258cefa58b951c7067020 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Wed, 25 Feb 2026 17:52:44 +0100 Subject: [PATCH 19/22] ssl-opt.sh: DTLS fragmenting: Do not check for ServerKeyExchange fragmentation In DTLS fragmenting tests, do not check for ServerKeyExchange message fragmentation as depending on the configuration and consequently the chosen ciphersuite the server may not send the message. Signed-off-by: Ronald Cron --- tests/ssl-opt.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh index c9c9ec3929..6e91581cc3 100755 --- a/tests/ssl-opt.sh +++ b/tests/ssl-opt.sh @@ -11397,7 +11397,6 @@ run_test "DTLS fragmenting: both (MTU=128)" \ 0 \ -s "found fragmented DTLS handshake message" \ -s "fragmenting Certificate handshake message" \ - -s "fragmenting ServerKeyExchange handshake message" \ -c "found fragmented DTLS handshake message" \ -c "fragmenting ClientHello handshake message" \ -c "fragmenting Certificate handshake message" \ @@ -11423,7 +11422,6 @@ run_test "DTLS fragmenting: both (MTU=107)" \ 0 \ -s "found fragmented DTLS handshake message" \ -s "fragmenting Certificate handshake message" \ - -s "fragmenting ServerKeyExchange handshake message" \ -c "found fragmented DTLS handshake message" \ -c "fragmenting ClientHello handshake message" \ -c "fragmenting Certificate handshake message" \ @@ -11449,7 +11447,6 @@ run_test "DTLS fragmenting: both (MTU=133)" \ 0 \ -s "found fragmented DTLS handshake message" \ -s "fragmenting Certificate handshake message" \ - -s "fragmenting ServerKeyExchange handshake message" \ -c "found fragmented DTLS handshake message" \ -c "fragmenting ClientHello handshake message" \ -c "fragmenting Certificate handshake message" \ From 1222d8cbf4862a89b7f5189debe4b2bb518a7ed5 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Mon, 9 Mar 2026 23:28:50 +0100 Subject: [PATCH 20/22] Improve comments Signed-off-by: Ronald Cron --- library/ssl_msg.c | 4 ++-- library/ssl_tls12_server.c | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/library/ssl_msg.c b/library/ssl_msg.c index b284e6dcda..1288dcca0e 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -3264,8 +3264,8 @@ int mbedtls_ssl_prepare_handshake_record(mbedtls_ssl_context *ssl) /* * When establishing the connection, the client may go through a series * of ClientHello and HelloVerifyRequest requests and responses. The - * server does not keep any trace of these initial round trips as - * intended: minimum allocated ressources as long as the reachability + * server intentionally does not keep trace of these initial round + * trips: minimum allocated ressources as long as the reachability * of the client has not been confirmed. When receiving the "first * ClientHello" from server perspective, we may thus need to adapt * the next expected `message_seq` for the incoming and outgoing diff --git a/library/ssl_tls12_server.c b/library/ssl_tls12_server.c index eae4996fc8..a3f1c2afe4 100644 --- a/library/ssl_tls12_server.c +++ b/library/ssl_tls12_server.c @@ -914,10 +914,11 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) /* * Fetch the expected ClientHello handshake message. Do not ask - * mbedtls_ssl_read_record() to update the handshake digest, to align - * with cases where the ClientHello may already have been fetched in - * ssl_tls13_process_client_hello() or as a post-handshake message - * (renegotiation). + * mbedtls_ssl_read_record() to update the handshake digest, because the + * ClientHello may already have been read in ssl_tls13_process_client_hello() + * or as a post-handshake message (renegotiation). In those cases we need + * to update the digest ourselves, and it is simpler to do so + * unconditionally than to track whether it is needed. */ if ((ret = mbedtls_ssl_read_record(ssl, 0)) != 0) { MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_read_record ", ret); From d6977afbd82f261fc7bb112104764daf2918d38c Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 10 Mar 2026 08:25:14 +0100 Subject: [PATCH 21/22] ssl_tls12_server.c: Move back the digest update Move back the digest update just after the call to mbedtls_ssl_read_record(). It fits well here as we explain in the comment associated to the call to mbedtls_ssl_read_record() that we update it manually. Signed-off-by: Ronald Cron --- library/ssl_tls12_server.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/library/ssl_tls12_server.c b/library/ssl_tls12_server.c index a3f1c2afe4..0fbe519ce1 100644 --- a/library/ssl_tls12_server.c +++ b/library/ssl_tls12_server.c @@ -925,6 +925,20 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) return ret; } + /* + * Update the handshake checksum. + * + * Note that the checksum must be updated before parsing the extensions + * because ssl_parse_session_ticket_ext() may decrypt the ticket in place + * and therefore modify the ClientHello message. This occurs when using + * the Mbed TLS ssl_ticket.c implementation. + */ + ret = mbedtls_ssl_update_handshake_status(ssl); + if (0 != ret) { + MBEDTLS_SSL_DEBUG_RET(1, ("mbedtls_ssl_update_handshake_status"), ret); + return ret; + } + buf = ssl->in_msg; msg_len = ssl->in_hslen; @@ -1130,21 +1144,6 @@ static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) ext_len = 0; } - /* - * Update the handshake checksum after performing preliminary - * validation of the ClientHello and before parsing its extensions. - * - * The checksum must be updated before parsing the extensions because - * ssl_parse_session_ticket_ext() may decrypt the ticket in place and - * therefore modify the ClientHello message. This occurs when using - * the Mbed TLS ssl_ticket.c implementation. - */ - ret = mbedtls_ssl_update_handshake_status(ssl); - if (0 != ret) { - MBEDTLS_SSL_DEBUG_RET(1, ("mbedtls_ssl_update_handshake_status"), ret); - return ret; - } - ext = buf + ext_offset + 2; MBEDTLS_SSL_DEBUG_BUF(3, "client hello extensions", ext, ext_len); From 7476e4067da1bd89ecab4b234cd2a81f44fc2f35 Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Tue, 10 Mar 2026 08:49:25 +0100 Subject: [PATCH 22/22] Restore seq number check of post-handshake ClientHello msg The check was wrongly removed by the commit "ssl_tls12_server.c: Move ClientHello message_seq adjustment". Signed-off-by: Ronald Cron --- library/ssl_msg.c | 59 +++++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/library/ssl_msg.c b/library/ssl_msg.c index 1288dcca0e..7a7182c2be 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -3261,34 +3261,47 @@ int mbedtls_ssl_prepare_handshake_record(mbedtls_ssl_context *ssl) return MBEDTLS_ERR_SSL_INVALID_RECORD; } - /* - * When establishing the connection, the client may go through a series - * of ClientHello and HelloVerifyRequest requests and responses. The - * server intentionally does not keep trace of these initial round - * trips: minimum allocated ressources as long as the reachability - * of the client has not been confirmed. When receiving the "first - * ClientHello" from server perspective, we may thus need to adapt - * the next expected `message_seq` for the incoming and outgoing - * handshake messages. - */ if (ssl->in_msg[0] == MBEDTLS_SSL_HS_CLIENT_HELLO && - ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER && - ssl->state == MBEDTLS_SSL_CLIENT_HELLO + ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER) { + if (ssl->state == MBEDTLS_SSL_CLIENT_HELLO #if defined(MBEDTLS_SSL_RENEGOTIATION) - && ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE + && ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE #endif - ) { - ssl->handshake->in_msg_seq = recv_msg_seq; - ssl->handshake->out_msg_seq = recv_msg_seq; + ) { + /* + * When establishing the connection, the client may go through + * a series of ClientHello and HelloVerifyRequest requests and + * responses. The server intentionally does not keep trace of + * these initial round trips: minimum allocated ressources as + * long as the reachability of the client has not been + * confirmed. When receiving the "first ClientHello" from + * server perspective, we may thus need to adapt the next + * expected `message_seq` for the incoming and outgoing + * handshake messages. + */ + ssl->handshake->in_msg_seq = recv_msg_seq; + ssl->handshake->out_msg_seq = recv_msg_seq; - /* Epoch should be 0 for initial handshakes */ - if (ssl->in_ctr[0] != 0 || ssl->in_ctr[1] != 0) { - MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); - return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + /* Epoch should be 0 for initial handshakes */ + if (ssl->in_ctr[0] != 0 || ssl->in_ctr[1] != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + memcpy(&ssl->cur_out_ctr[2], ssl->in_ctr + 2, + sizeof(ssl->cur_out_ctr) - 2); + } else if (mbedtls_ssl_is_handshake_over(ssl) == 1) { + /* In case of a post-handshake ClientHello that initiates a + * renegotiation check that the handshake message sequence + * number is zero. + */ + if (recv_msg_seq != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message_seq: " + "%u (expected 0)", + recv_msg_seq)); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } } - - memcpy(&ssl->cur_out_ctr[2], ssl->in_ctr + 2, - sizeof(ssl->cur_out_ctr) - 2); } if (ssl->handshake != NULL &&