From f7223c30718aa87d5b8c8bc86c931ce1b2e436ec Mon Sep 17 00:00:00 2001 From: Joachim Bauernberger Date: Sun, 25 Oct 2026 18:02:33 +0100 Subject: [PATCH] ssl: Clear TLS 1.3 secrets from the exported client session mbedtls_ssl_get_session() copies the whole mbedtls_ssl_session struct, including the TLS 1.3 app_secrets field. That field holds the connection's client and server application traffic secrets, the exporter master secret and the resumption master secret, so an exported session carried enough material to decrypt the connection it came from. None of these secrets is needed to resume the session: the resumption key is derived and stored separately when the NewSessionTicket is processed, session serialization does not include app_secrets, and keying-material export reads the live session. Zeroize app_secrets in the exported copy, at the export point only, so that mbedtls_ssl_session_copy() (also used by mbedtls_ssl_set_session()) is left unchanged. Fixes #10916. Signed-off-by: Joachim Bauernberger --- .../ssl-get-session-tls13-app-secrets.txt | 6 +++ library/ssl_tls.c | 8 ++++ tests/suites/test_suite_ssl.data | 3 ++ tests/suites/test_suite_ssl.function | 37 +++++++++++++++++++ 4 files changed, 54 insertions(+) create mode 100644 ChangeLog.d/ssl-get-session-tls13-app-secrets.txt diff --git a/ChangeLog.d/ssl-get-session-tls13-app-secrets.txt b/ChangeLog.d/ssl-get-session-tls13-app-secrets.txt new file mode 100644 index 0000000000..28bafb6cf0 --- /dev/null +++ b/ChangeLog.d/ssl-get-session-tls13-app-secrets.txt @@ -0,0 +1,6 @@ +Security + * Clear the TLS 1.3 application traffic secrets, exporter master secret and + resumption master secret from the session exported by + mbedtls_ssl_get_session(). These secrets are not needed to resume the + session and previously remained in the exported session object. Fixes + #10916. diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 1f44fc71a7..2289cb782e 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -3053,6 +3053,14 @@ int mbedtls_ssl_get_session(const mbedtls_ssl_context *ssl, return ret; } +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + /* These belong to the connection, not the exported session, and are not + * needed to resume. Clear them. */ + if (dst->tls_version == MBEDTLS_SSL_VERSION_TLS1_3) { + mbedtls_platform_zeroize(&dst->app_secrets, sizeof(dst->app_secrets)); + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + /* Remember that we've exported the session. */ ssl->session->exported = 1; return 0; diff --git a/tests/suites/test_suite_ssl.data b/tests/suites/test_suite_ssl.data index 0a9784fcaa..49c2f93dcb 100644 --- a/tests/suites/test_suite_ssl.data +++ b/tests/suites/test_suite_ssl.data @@ -3417,6 +3417,9 @@ ssl_ecjpake_set_password:1 Test Elliptic curves' info parsing elliptic_curve_get_properties +TLS 1.3 export session clears application secrets +tls13_export_session_clears_app_secrets + TLS 1.3 resume session with ticket tls13_resume_session_with_ticket diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function index 622ecd1f73..bfdd033df3 100644 --- a/tests/suites/test_suite_ssl.function +++ b/tests/suites/test_suite_ssl.function @@ -4889,6 +4889,43 @@ exit: } /* END_CASE */ +/* BEGIN_CASE depends_on: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 */ +void tls13_export_session_clears_app_secrets() +{ + int ret = -1; + mbedtls_test_handshake_test_options client_options; + mbedtls_test_handshake_test_options server_options; + mbedtls_ssl_session exported_session; + mbedtls_ssl_tls13_application_secrets zero_secrets; + + mbedtls_test_init_handshake_options(&client_options); + mbedtls_test_init_handshake_options(&server_options); + mbedtls_ssl_session_init(&exported_session); + memset(&zero_secrets, 0, sizeof(zero_secrets)); + + PSA_INIT(); + + client_options.pk_alg = MBEDTLS_PK_ECDSA; + server_options.pk_alg = MBEDTLS_PK_ECDSA; + + /* Full TLS 1.3 handshake with a ticket, then export the session. */ + ret = mbedtls_test_get_tls13_ticket(&client_options, &server_options, + &exported_session); + TEST_EQUAL(ret, 0); + + /* The exported session must not carry the connection's secrets. */ + TEST_MEMORY_COMPARE(&exported_session.app_secrets, + sizeof(exported_session.app_secrets), + &zero_secrets, sizeof(zero_secrets)); + +exit: + mbedtls_ssl_session_free(&exported_session); + mbedtls_test_free_handshake_options(&client_options); + mbedtls_test_free_handshake_options(&server_options); + PSA_DONE(); +} +/* END_CASE */ + /* BEGIN_CASE depends_on: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 */ void tls13_resume_session_with_ticket() {