Commit 623ee23f04 for openssl.org

commit 623ee23f04fc7fefdcd86d395b33a8995d3a55b1
Author: Isaac Lewis <isaacsamual994@gmail.com>
Date:   Thu Aug 27 01:07:13 2026 +1000

    Cleanse TLS 1.3 connection secrets on reset and free

    Erase persistent TLS 1.3 connection secrets when an SSL connection is reset or released, and add regression coverage for both paths.

    Assisted-by: ChatGPT:gpt-5.6-sol

    Fixes: https://github.com/openssl/openssl/issues/32288
    Reviewed-by: Frederik Wedel-Heinen <fwh.openssl@gmail.com>
    Reviewed-by: Daniel Kubec <kubec@openssl.foundation>
    Merge-date: Wed Oct 28 06:06:15 2026
    Merged-from: https://github.com/openssl/openssl/pull/32518

diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index e855867181..8056df4670 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -572,6 +572,27 @@ int SSL_clear(SSL *s)
     return s->method->ssl_reset(s);
 }

+static void ssl_connection_clear_tls13_secrets(SSL_CONNECTION *sc)
+{
+    OPENSSL_cleanse(sc->early_secret, sizeof(sc->early_secret));
+    OPENSSL_cleanse(sc->handshake_secret, sizeof(sc->handshake_secret));
+    OPENSSL_cleanse(sc->master_secret, sizeof(sc->master_secret));
+    OPENSSL_cleanse(sc->resumption_master_secret,
+                    sizeof(sc->resumption_master_secret));
+    OPENSSL_cleanse(sc->client_finished_secret,
+                    sizeof(sc->client_finished_secret));
+    OPENSSL_cleanse(sc->server_finished_secret,
+                    sizeof(sc->server_finished_secret));
+    OPENSSL_cleanse(sc->client_app_traffic_secret,
+                    sizeof(sc->client_app_traffic_secret));
+    OPENSSL_cleanse(sc->server_app_traffic_secret,
+                    sizeof(sc->server_app_traffic_secret));
+    OPENSSL_cleanse(sc->exporter_master_secret,
+                    sizeof(sc->exporter_master_secret));
+    OPENSSL_cleanse(sc->early_exporter_master_secret,
+                    sizeof(sc->early_exporter_master_secret));
+}
+
 int ossl_ssl_connection_reset(SSL *s)
 {
     SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(s);
@@ -579,6 +600,8 @@ int ossl_ssl_connection_reset(SSL *s)
     if (sc == NULL)
         return 0;

+    ssl_connection_clear_tls13_secrets(sc);
+
     if (ssl_clear_bad_session(sc)) {
         SSL_SESSION_free(sc->session);
         sc->session = NULL;
@@ -1690,6 +1713,8 @@ void ossl_ssl_connection_free(SSL *ssl)
 #ifndef OPENSSL_NO_ECH
     ossl_ech_conn_clear(&s->ext.ech);
 #endif
+
+    ssl_connection_clear_tls13_secrets(s);
 }

 void SSL_set0_rbio(SSL *s, BIO *rbio)
diff --git a/test/tls13secretstest.c b/test/tls13secretstest.c
index 3ac98cc14c..78b793abc0 100644
--- a/test/tls13secretstest.c
+++ b/test/tls13secretstest.c
@@ -536,9 +536,117 @@ err:
     return ret;
 }

+static int connection_tls13_secrets_are_zero(const SSL_CONNECTION *sc)
+{
+    unsigned char zeros[EVP_MAX_MD_SIZE] = { 0 };
+
+#define SECRET_IS_ZERO(name) \
+    (memcmp(sc->name, zeros, sizeof(sc->name)) == 0)
+    return SECRET_IS_ZERO(early_secret)
+        && SECRET_IS_ZERO(handshake_secret)
+        && SECRET_IS_ZERO(master_secret)
+        && SECRET_IS_ZERO(resumption_master_secret)
+        && SECRET_IS_ZERO(client_finished_secret)
+        && SECRET_IS_ZERO(server_finished_secret)
+        && SECRET_IS_ZERO(client_app_traffic_secret)
+        && SECRET_IS_ZERO(server_app_traffic_secret)
+        && SECRET_IS_ZERO(exporter_master_secret)
+        && SECRET_IS_ZERO(early_exporter_master_secret);
+#undef SECRET_IS_ZERO
+}
+
+static void fill_connection_tls13_secrets(SSL_CONNECTION *sc)
+{
+#define FILL_SECRET(name) memset(sc->name, 0xa5, sizeof(sc->name))
+    FILL_SECRET(early_secret);
+    FILL_SECRET(handshake_secret);
+    FILL_SECRET(master_secret);
+    FILL_SECRET(resumption_master_secret);
+    FILL_SECRET(client_finished_secret);
+    FILL_SECRET(server_finished_secret);
+    FILL_SECRET(client_app_traffic_secret);
+    FILL_SECRET(server_app_traffic_secret);
+    FILL_SECRET(exporter_master_secret);
+    FILL_SECRET(early_exporter_master_secret);
+#undef FILL_SECRET
+}
+
+static int test_connection_secret_clear(void)
+{
+    SSL_CTX *ctx = NULL;
+    SSL *ssl = NULL;
+    SSL_CONNECTION *sc = NULL;
+    int ret = 0;
+
+    ctx = SSL_CTX_new(TLS_method());
+    if (!TEST_ptr(ctx)
+        || !TEST_ptr(ssl = SSL_new(ctx))
+        || !TEST_ptr(sc = SSL_CONNECTION_FROM_SSL_ONLY(ssl)))
+        goto err;
+
+    fill_connection_tls13_secrets(sc);
+
+    if (!TEST_true(SSL_clear(ssl))
+        || !TEST_true(connection_tls13_secrets_are_zero(sc)))
+        goto err;
+
+    ret = 1;
+err:
+    SSL_free(ssl);
+    SSL_CTX_free(ctx);
+    return ret;
+}
+
+static int free_secret_marker;
+static int free_secrets_were_cleared;
+
+static void check_connection_secrets_on_free(void *parent, void *ptr,
+                                             CRYPTO_EX_DATA *ad, int idx,
+                                             long argl, void *argp)
+{
+    SSL_CONNECTION *sc;
+
+    if (ptr != &free_secret_marker)
+        return;
+
+    sc = SSL_CONNECTION_FROM_SSL_ONLY((SSL *)parent);
+    free_secrets_were_cleared = sc != NULL
+        && connection_tls13_secrets_are_zero(sc);
+}
+
+static int test_connection_secret_free(void)
+{
+    SSL_CTX *ctx = NULL;
+    SSL *ssl = NULL;
+    SSL_CONNECTION *sc = NULL;
+    int idx = -1, ret = 0;
+
+    ctx = SSL_CTX_new(TLS_method());
+    if (!TEST_ptr(ctx)
+        || !TEST_ptr(ssl = SSL_new(ctx))
+        || !TEST_ptr(sc = SSL_CONNECTION_FROM_SSL_ONLY(ssl))
+        || !TEST_int_ge(idx = SSL_get_ex_new_index(0, NULL, NULL, NULL,
+                                                    check_connection_secrets_on_free), 0)
+        || !TEST_true(SSL_set_ex_data(ssl, idx, &free_secret_marker)))
+        goto err;
+
+    fill_connection_tls13_secrets(sc);
+    free_secrets_were_cleared = 0;
+    SSL_free(ssl);
+    ssl = NULL;
+
+    ret = TEST_true(free_secrets_were_cleared);
+err:
+    SSL_free(ssl);
+    SSL_CTX_free(ctx);
+    return ret;
+}
+
 int setup_tests(void)
 {
     ADD_TEST(test_handshake_secrets);
+    ADD_TEST(test_connection_secret_clear);
+    ADD_TEST(test_connection_secret_free);
     ADD_TEST(test_dtls13_transcript_hash);
     return 1;
 }