From d71272700a144ac4c8b8e19349b34a8c090f7612 Mon Sep 17 00:00:00 2001 From: Naveed Date: Fri, 23 Oct 2026 20:38:13 +0530 Subject: [PATCH 1/2] Add test for TLS 1.2 resumption with a TLS 1.3 ticket Offer a ticket issued for a TLS 1.3 session in the session_ticket extension of a TLS 1.2 ClientHello and check that the server does a full handshake instead of resuming. Signed-off-by: Naveed --- tests/suites/test_suite_ssl.data | 3 ++ tests/suites/test_suite_ssl.function | 75 ++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/tests/suites/test_suite_ssl.data b/tests/suites/test_suite_ssl.data index 0a9784fcaa..dd1333d0c2 100644 --- a/tests/suites/test_suite_ssl.data +++ b/tests/suites/test_suite_ssl.data @@ -3420,6 +3420,9 @@ elliptic_curve_get_properties TLS 1.3 resume session with ticket tls13_resume_session_with_ticket +TLS 1.2 resume session with TLS 1.3 ticket +tls12_resume_session_with_tls13_ticket + TLS 1.3 server propagates RMS computation error depends_on:MBEDTLS_SSL_PROTO_TLS1_3:MBEDTLS_TEST_AT_LEAST_ONE_TLS1_3_CIPHERSUITE:MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED:MBEDTLS_X509_RSASSA_PSS_SUPPORT:PSA_WANT_ALG_RSA_PKCS1V15_SIGN tls_transcript_error_propagation:MBEDTLS_SSL_IS_SERVER:MBEDTLS_SSL_VERSION_TLS1_3:"<= parse finished message":MBEDTLS_SSL_CLIENT_FINISHED:MBEDTLS_SSL_FLUSH_BUFFERS:MBEDTLS_ERR_SSL_INTERNAL_ERROR diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function index 622ecd1f73..2a387e98bf 100644 --- a/tests/suites/test_suite_ssl.function +++ b/tests/suites/test_suite_ssl.function @@ -4966,6 +4966,81 @@ exit: } /* END_CASE */ +/* BEGIN_CASE depends_on:MBEDTLS_SSL_PROTO_TLS1_2:MBEDTLS_SSL_PROTO_TLS1_3:MBEDTLS_SSL_CLI_C:MBEDTLS_SSL_SRV_C:MBEDTLS_TEST_AT_LEAST_ONE_TLS1_3_CIPHERSUITE:MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED:MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED:PSA_WANT_ALG_SHA_256:PSA_WANT_ECC_SECP_R1_256:PSA_WANT_ECC_SECP_R1_384:PSA_HAVE_ALG_ECDSA_VERIFY:MBEDTLS_SSL_SESSION_TICKETS:MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED:PSA_WANT_KEY_TYPE_AES:PSA_WANT_ALG_GCM */ +void tls12_resume_session_with_tls13_ticket() +{ + int ret = -1; + mbedtls_test_ssl_endpoint client_ep, server_ep; + memset(&client_ep, 0, sizeof(client_ep)); + memset(&server_ep, 0, sizeof(server_ep)); + mbedtls_test_handshake_test_options client_options; + mbedtls_test_handshake_test_options server_options; + mbedtls_ssl_session saved_session; + + mbedtls_test_init_handshake_options(&client_options); + mbedtls_test_init_handshake_options(&server_options); + mbedtls_ssl_session_init(&saved_session); + + PSA_INIT(); + + /* + * Run first handshake to get a TLS 1.3 ticket from the server. + */ + client_options.pk_alg = MBEDTLS_PK_ECDSA; + server_options.pk_alg = MBEDTLS_PK_ECDSA; + + ret = mbedtls_test_get_tls13_ticket(&client_options, &server_options, + &saved_session); + TEST_EQUAL(ret, 0); + TEST_EQUAL(saved_session.tls_version, MBEDTLS_SSL_VERSION_TLS1_3); + + /* + * Offer the TLS 1.3 ticket in the session_ticket extension of a TLS 1.2 + * ClientHello. A TLS 1.3 session has no TLS 1.2 master secret, so the + * server must not resume from it. + */ + saved_session.tls_version = MBEDTLS_SSL_VERSION_TLS1_2; + saved_session.ciphersuite = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256; + client_options.client_max_version = MBEDTLS_SSL_VERSION_TLS1_2; + client_options.cipher = "TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256"; + + ret = mbedtls_test_ssl_endpoint_init(&client_ep, MBEDTLS_SSL_IS_CLIENT, + &client_options); + TEST_EQUAL(ret, 0); + + ret = mbedtls_test_ssl_endpoint_init(&server_ep, MBEDTLS_SSL_IS_SERVER, + &server_options); + TEST_EQUAL(ret, 0); + + mbedtls_ssl_conf_session_tickets_cb(&server_ep.conf, + mbedtls_test_ticket_write, + mbedtls_test_ticket_parse, + NULL); + + ret = mbedtls_test_mock_socket_connect(&(client_ep.socket), + &(server_ep.socket), 1024); + TEST_EQUAL(ret, 0); + + ret = mbedtls_ssl_set_session(&(client_ep.ssl), &saved_session); + TEST_EQUAL(ret, 0); + + TEST_EQUAL(mbedtls_test_move_handshake_to_state( + &(server_ep.ssl), &(client_ep.ssl), + MBEDTLS_SSL_HANDSHAKE_WRAPUP), 0); + + TEST_EQUAL(server_ep.ssl.tls_version, MBEDTLS_SSL_VERSION_TLS1_2); + TEST_EQUAL(server_ep.ssl.handshake->resume, 0); + +exit: + mbedtls_test_ssl_endpoint_free(&client_ep); + mbedtls_test_ssl_endpoint_free(&server_ep); + mbedtls_test_free_handshake_options(&client_options); + mbedtls_test_free_handshake_options(&server_options); + mbedtls_ssl_session_free(&saved_session); + PSA_DONE(); +} +/* END_CASE */ + /* * The !MBEDTLS_SSL_PROTO_TLS1_2 dependency of tls13_read_early_data() below is * a temporary workaround to not run the test in Windows-2013 where there is From 8d0f61723ee5f1424008fb8d980a0f6d848ac7b9 Mon Sep 17 00:00:00 2001 From: Naveed Date: Fri, 23 Oct 2026 20:38:13 +0530 Subject: [PATCH 2/2] Ignore TLS 1.3 tickets in the TLS 1.2 session_ticket extension ssl_parse_session_ticket_ext() accepted any ticket that the ticket callback could parse, including one issued for a TLS 1.3 session. Such a session has no TLS 1.2 master secret, so the server resumed with an all-zero master secret. Ignore the ticket unless it is for TLS 1.2, as the TLS 1.3 server already does for TLS 1.2 tickets. Signed-off-by: Naveed --- ChangeLog.d/tls12-reject-tls13-ticket.txt | 8 ++++++++ library/ssl_tls12_server.c | 10 ++++++++++ 2 files changed, 18 insertions(+) create mode 100644 ChangeLog.d/tls12-reject-tls13-ticket.txt diff --git a/ChangeLog.d/tls12-reject-tls13-ticket.txt b/ChangeLog.d/tls12-reject-tls13-ticket.txt new file mode 100644 index 0000000000..c31207c0ab --- /dev/null +++ b/ChangeLog.d/tls12-reject-tls13-ticket.txt @@ -0,0 +1,8 @@ +Security + * Fix a TLS 1.2 server accepting a session ticket that was issued for a + TLS 1.3 session. The session was resumed with an all-zero master secret, + so anyone holding the ticket could complete the abbreviated handshake and + derive the connection keys from public handshake values. TLS 1.3 tickets + appear in clear in the pre_shared_key extension of a TLS 1.3 resumption + ClientHello. This affects servers that enable both TLS 1.2 and TLS 1.3 + and session tickets. diff --git a/library/ssl_tls12_server.c b/library/ssl_tls12_server.c index 598f8b946b..cded600873 100644 --- a/library/ssl_tls12_server.c +++ b/library/ssl_tls12_server.c @@ -480,6 +480,16 @@ static int ssl_parse_session_ticket_ext(mbedtls_ssl_context *ssl, return 0; } + /* + * A ticket issued for a TLS 1.3 session has no TLS 1.2 master secret, + * so it must not be used to resume a TLS 1.2 session. + */ + if (session.tls_version != MBEDTLS_SSL_VERSION_TLS1_2) { + mbedtls_ssl_session_free(&session); + MBEDTLS_SSL_DEBUG_MSG(3, ("ticket TLS version is not 1.2")); + return 0; + } + /* * Keep the session ID sent by the client, since we MUST send it back to * inform them we're accepting the ticket (RFC 5077 section 3.4)