Commit f2a41c74ae for openssl.org

commit f2a41c74aeed0f0c1cfb04f4f07bd476c39f39c1
Author: Ryan Hooper <ryhooper@cisco.com>
Date:   Tue Sep 9 10:43:51 2025 -0400

    Updated SSL Trace to display the name for all MLKEM-based groups

    Make SSL Trace to display the name of the MLKEM512, MLKEM768,
    MLKEM1024 and SecP384r1MLKEM1024 groups.

    Fixes #28476

    Reviewed-by: Matt Caswell <matt@openssl.org>
    Reviewed-by: Tomas Mraz <tomas@openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/28499)

diff --git a/ssl/t1_trce.c b/ssl/t1_trce.c
index ed8112a94b..9abb5350bb 100644
--- a/ssl/t1_trce.c
+++ b/ssl/t1_trce.c
@@ -548,8 +548,12 @@ static const ssl_trace_tbl ssl_groups_tbl[] = {
     {258, "ffdhe4096"},
     {259, "ffdhe6144"},
     {260, "ffdhe8192"},
+    {512, "MLKEM512"},
+    {513, "MLKEM768"},
+    {514, "MLKEM1024"},
     {4587, "SecP256r1MLKEM768"},
     {4588, "X25519MLKEM768"},
+    {4589, "SecP384r1MLKEM1024"},
     {25497, "X25519Kyber768Draft00"},
     {25498, "SecP256r1Kyber768Draft00"},
     {0xFF01, "arbitrary_explicit_prime_curves"},
diff --git a/test/build.info b/test/build.info
index 4f70870515..57ee94071a 100644
--- a/test/build.info
+++ b/test/build.info
@@ -31,7 +31,8 @@ IF[{- !$disabled{tests} -}]
           testutil/format_output.c testutil/load.c testutil/fake_random.c \
           testutil/test_cleanup.c testutil/main.c testutil/testutil_init.c \
           testutil/options.c testutil/test_options.c testutil/provider.c \
-          testutil/apps_shims.c testutil/random.c testutil/helper.c $LIBAPPSSRC
+          testutil/apps_shims.c testutil/random.c testutil/helper.c \
+          testutil/compare.c $LIBAPPSSRC
   INCLUDE[libtestutil.a]=../include ../apps/include ..
   DEPEND[libtestutil.a]=../libcrypto

diff --git a/test/quicapitest.c b/test/quicapitest.c
index c98764b00f..fa0185c6b8 100644
--- a/test/quicapitest.c
+++ b/test/quicapitest.c
@@ -429,91 +429,6 @@ static int test_version(void)
 }

 #if defined(DO_SSL_TRACE_TEST)
-static void strip_line_ends(char *str)
-{
-    size_t i;
-
-    for (i = strlen(str);
-         i > 0 && (str[i - 1] == '\n' || str[i - 1] == '\r');
-         i--);
-
-    str[i] = '\0';
-}
-
-static int compare_with_file(BIO *membio)
-{
-    BIO *file = NULL, *newfile = NULL;
-    char buf1[8192], buf2[8192];
-    char *reffile;
-    int ret = 0;
-    size_t i;
-
-#ifdef OPENSSL_NO_ZLIB
-    reffile = test_mk_file_path(datadir, "ssltraceref.txt");
-#else
-    reffile = test_mk_file_path(datadir, "ssltraceref-zlib.txt");
-#endif
-    if (!TEST_ptr(reffile))
-        goto err;
-
-    file = BIO_new_file(reffile, "rb");
-    if (!TEST_ptr(file))
-        goto err;
-
-    newfile = BIO_new_file("ssltraceref-new.txt", "wb");
-    if (!TEST_ptr(newfile))
-        goto err;
-
-    while (BIO_gets(membio, buf2, sizeof(buf2)) > 0)
-        if (BIO_puts(newfile, buf2) <= 0) {
-            TEST_error("Failed writing new file data");
-            goto err;
-        }
-
-    if (!TEST_int_ge(BIO_seek(membio, 0), 0))
-        goto err;
-
-    while (BIO_gets(file, buf1, sizeof(buf1)) > 0) {
-        size_t line_len;
-
-        if (BIO_gets(membio, buf2, sizeof(buf2)) <= 0) {
-            TEST_error("Failed reading mem data");
-            goto err;
-        }
-        strip_line_ends(buf1);
-        strip_line_ends(buf2);
-        line_len = strlen(buf1);
-        if (line_len > 0 && buf1[line_len - 1] == '?') {
-            /* Wildcard at the EOL means ignore anything after it */
-            if (strlen(buf2) > line_len)
-                buf2[line_len] = '\0';
-        }
-        if (line_len != strlen(buf2)) {
-            TEST_error("Actual and ref line data length mismatch");
-            TEST_info("%s", buf1);
-            TEST_info("%s", buf2);
-           goto err;
-        }
-        for (i = 0; i < line_len; i++) {
-            /* '?' is a wild card character in the reference text */
-            if (buf1[i] == '?')
-                buf2[i] = '?';
-        }
-        if (!TEST_str_eq(buf1, buf2))
-            goto err;
-    }
-    if (!TEST_true(BIO_eof(file))
-            || !TEST_true(BIO_eof(membio)))
-        goto err;
-
-    ret = 1;
- err:
-    OPENSSL_free(reffile);
-    BIO_free(file);
-    BIO_free(newfile);
-    return ret;
-}
-
 /*
  * Tests that the SSL_trace() msg_callback works as expected with a QUIC
  * connection. This also provides testing of the msg_callback at the same time.
@@ -525,6 +440,7 @@ static int test_ssl_trace(void)
     QUIC_TSERVER *qtserv = NULL;
     int testresult = 0;
     BIO *bio = NULL;
+    char *reffile = NULL;

     if (!TEST_ptr(cctx = SSL_CTX_new_ex(libctx, NULL, OSSL_QUIC_client_method()))
             || !TEST_ptr(bio = BIO_new(BIO_s_mem()))
@@ -548,7 +464,13 @@ static int test_ssl_trace(void)
         if (!TEST_int_gt(BIO_pending(bio), 0))
             goto err;
     } else {
-        if (!TEST_true(compare_with_file(bio)))
+
+# ifdef OPENSSL_NO_ZLIB
+        reffile = test_mk_file_path(datadir, "ssltraceref.txt");
+# else
+        reffile = test_mk_file_path(datadir, "ssltraceref-zlib.txt");
+# endif
+        if (!TEST_true(compare_with_reference_file(bio, reffile)))
             goto err;
     }

@@ -558,6 +480,7 @@ static int test_ssl_trace(void)
     SSL_free(clientquic);
     SSL_CTX_free(cctx);
     BIO_free(bio);
+    OPENSSL_free(reffile);

     return testresult;
 }
diff --git a/test/recipes/90-test_sslapi.t b/test/recipes/90-test_sslapi.t
index 650e0d1ffb..9bb5c50c47 100644
--- a/test/recipes/90-test_sslapi.t
+++ b/test/recipes/90-test_sslapi.t
@@ -45,7 +45,10 @@ ok(run(test(["sslapitest", srctop_dir("test", "certs"),
              srctop_file("test",
                          "recipes",
                          "90-test_sslapi_data",
-                         "dhparams.pem")])),
+                         "dhparams.pem"),
+             srctop_dir("test",
+                        "recipes",
+                        "90-test_sslapi_data")])),
              "running sslapitest");

 SKIP: {
@@ -62,7 +65,10 @@ SKIP: {
                  srctop_file("test",
                              "recipes",
                              "90-test_sslapi_data",
-                             "dhparams.pem")])),
+                             "dhparams.pem"),
+                 srctop_dir("test",
+                            "recipes",
+                            "90-test_sslapi_data")])),
                  "running sslapitest with default fips config");

     run(test(["fips_version_test", "-config", $provconf, ">=3.1.0"]),
@@ -140,7 +146,10 @@ SKIP: {
                     srctop_file("test",
                                 "recipes",
                                 "90-test_sslapi_data",
-                                "dhparams.pem")])),
+                                "dhparams.pem"),
+                    srctop_dir("test",
+                               "recipes",
+                               "90-test_sslapi_data")])),
        "running sslapitest with modified fips config");
 }

diff --git a/test/recipes/90-test_sslapi_data/ssltraceref-zlib.txt b/test/recipes/90-test_sslapi_data/ssltraceref-zlib.txt
new file mode 100644
index 0000000000..05aed8299b
--- /dev/null
+++ b/test/recipes/90-test_sslapi_data/ssltraceref-zlib.txt
@@ -0,0 +1,255 @@
+Sent TLS Record
+Header:
+  Version = TLS 1.0 (0x301)
+  Content Type = Handshake (22)
+  Length = ?
+    ClientHello, Length=?
+      client_version=0x303 (TLS 1.2)
+      Random:
+        gmt_unix_time=0x?
+        random_bytes (len=28): ?
+      session_id (len=?
+      cipher_suites (len=2)
+        {0x13, 0x01} TLS_AES_128_GCM_SHA256
+      compression_methods (len=1)
+        No Compression (0x00)
+      extensions, length = ?
+        extension_type=ec_point_formats(11), length=4
+          uncompressed (0)
+          ansiX962_compressed_prime (1)
+          ansiX962_compressed_char2 (2)
+        extension_type=supported_groups(10), length=20
+          MLKEM512 (512)
+          MLKEM768 (513)
+          MLKEM1024 (514)
+          X25519MLKEM768 (4588)
+          SecP256r1MLKEM768 (4587)
+          SecP384r1MLKEM1024 (4589)
+          secp521r1 (P-521) (25)
+          secp384r1 (P-384) (24)
+          secp256r1 (P-256) (23)
+        extension_type=session_ticket(35), length=0
+        extension_type=encrypt_then_mac(22), length=0
+        extension_type=extended_master_secret(23), length=0
+        extension_type=signature_algorithms(13), length=?
+          mldsa65 (0x0905)
+          mldsa87 (0x0906)
+          mldsa44 (0x0904)
+          ecdsa_secp256r1_sha256 (0x0403)
+          ecdsa_secp384r1_sha384 (0x0503)
+          ecdsa_secp521r1_sha512 (0x0603)
+          ed25519 (0x0807)
+          ed448 (0x0808)
+          ecdsa_brainpoolP256r1tls13_sha256 (0x081a)
+          ecdsa_brainpoolP384r1tls13_sha384 (0x081b)
+          ecdsa_brainpoolP512r1tls13_sha512 (0x081c)
+          rsa_pss_pss_sha256 (0x0809)
+          rsa_pss_pss_sha384 (0x080a)
+          rsa_pss_pss_sha512 (0x080b)
+          rsa_pss_rsae_sha256 (0x0804)
+          rsa_pss_rsae_sha384 (0x0805)
+          rsa_pss_rsae_sha512 (0x0806)
+          rsa_pkcs1_sha256 (0x0401)
+          rsa_pkcs1_sha384 (0x0501)
+          rsa_pkcs1_sha512 (0x0601)
+        extension_type=supported_versions(43), length=3
+          TLS 1.3 (772)
+        extension_type=psk_key_exchange_modes(45), length=2
+          psk_dhe_ke (1)
+        extension_type=key_share(51), length=806
+            NamedGroup: MLKEM512 (512)
+            key_exchange:  (len=800): ?
+        extension_type=compress_certificate(27), length=3
+          zlib (1)
+
+Received TLS Record
+Header:
+  Version = TLS 1.2 (0x303)
+  Content Type = Handshake (22)
+  Length = 858
+    ServerHello, Length=854
+      server_version=0x303 (TLS 1.2)
+      Random:
+        gmt_unix_time=0x?
+        random_bytes (len=28): ?
+      session_id (len=?
+      cipher_suite {0x13, 0x01} TLS_AES_128_GCM_SHA256
+      compression_method: No Compression (0x00)
+      extensions, length = ?
+        extension_type=supported_versions(43), length=2
+            TLS 1.3 (772)
+        extension_type=key_share(51), length=772
+            NamedGroup: MLKEM512 (512)
+            key_exchange:  (len=768): ?
+
+Received TLS Record
+Header:
+  Version = TLS 1.2 (0x303)
+  Content Type = ChangeCipherSpec (20)
+  Length = 1
+    change_cipher_spec (1)
+
+Received TLS Record
+Header:
+  Version = TLS 1.2 (0x303)
+  Content Type = ApplicationData (23)
+  Length = 23
+  Inner Content Type = Handshake (22)
+    EncryptedExtensions, Length=2
+      No extensions
+
+Received TLS Record
+Header:
+  Version = TLS 1.2 (0x303)
+  Content Type = ApplicationData (23)
+  Length = 839
+  Inner Content Type = Handshake (22)
+    Certificate, Length=818
+      context (len=0):
+      certificate_list, length=814
+        ASN.1Cert, length=809
+------details-----
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 2 (0x2)
+        Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN = Root CA
+        Validity
+            Not Before: Jan 14 22:29:46 2016 GMT
+            Not After : Jan 15 22:29:46 2116 GMT
+        Subject: CN = server.example
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:d5:5d:60:6a:df:fc:61:ee:48:aa:8c:11:48:43:
+                    a5:6d:b6:52:5d:aa:98:49:b1:61:92:35:b1:fc:3a:
+                    04:25:0c:6d:79:ff:b4:d5:c9:e9:5c:1c:3b:e0:ab:
+                    b3:b8:7d:a3:de:6d:bd:e0:dd:d7:5a:bf:14:47:11:
+                    42:5e:a6:82:d0:61:c1:7f:dd:13:46:e6:09:85:07:
+                    0e:f2:d4:fc:1a:64:d2:0a:ad:20:ab:20:6b:96:f0:
+                    ad:cc:c4:19:53:55:dc:01:1d:a4:b3:ef:8a:b4:49:
+                    53:5d:8a:05:1c:f1:dc:e1:44:bf:c5:d7:e2:77:19:
+                    57:5c:97:0b:75:ee:88:43:71:0f:ca:6c:c1:b4:b2:
+                    50:a7:77:46:6c:58:0f:11:bf:f1:76:24:5a:ae:39:
+                    42:b7:51:67:29:e1:d0:55:30:6f:17:e4:91:ea:ad:
+                    f8:28:c2:43:6f:a2:64:a9:fb:9d:98:92:62:48:3e:
+                    eb:0d:4f:82:4a:8a:ff:3f:72:ee:96:b5:ae:a1:c1:
+                    98:ba:ef:7d:90:75:6d:ff:5a:52:9e:ab:f5:c0:7e:
+                    d0:87:43:db:85:07:07:0f:7d:38:7a:fd:d1:d3:ee:
+                    65:1d:d3:ea:39:6a:87:37:ee:4a:d3:e0:0d:6e:f5:
+                    70:ac:c2:bd:f1:6e:f3:92:95:5e:a9:f0:a1:65:95:
+                    93:8d
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier:
+                C0:E7:84:BF:E8:59:27:33:10:B0:52:4F:51:52:2F:06:D6:C0:7A:CD
+            X509v3 Authority Key Identifier:
+                70:7F:2E:AE:83:68:59:98:04:23:2A:CD:EB:3E:17:CD:24:DD:01:49
+            X509v3 Basic Constraints:
+                CA:FALSE
+            X509v3 Extended Key Usage:
+                TLS Web Server Authentication
+            X509v3 Subject Alternative Name:
+                DNS:server.example
+    Signature Algorithm: sha256WithRSAEncryption
+    Signature Value:
+        7b:d3:04:43:75:8a:0f:11:ae:c4:fb:d7:a1:a2:9e:fe:20:18:
+        d5:f4:2f:31:88:46:b6:75:8c:ee:e5:9b:97:a6:b9:a3:cd:60:
+        9a:46:c3:48:97:e5:97:68:f7:5a:86:35:73:d9:69:9e:f9:5f:
+        74:b9:e6:94:13:01:cb:6a:dc:e3:c4:04:e9:65:da:9c:a4:8b:
+        28:f3:f9:9a:7f:bf:97:1f:45:92:e5:05:b1:56:e6:0b:f6:47:
+        de:1e:89:b6:2b:e1:4d:df:4a:7e:01:d3:23:dc:97:8c:47:fe:
+        5f:c7:cc:98:46:0e:c4:83:5b:ca:8a:f1:52:09:be:6b:ec:3f:
+        09:8b:d0:93:02:bf:e1:51:e7:d1:7e:34:56:19:74:d0:ff:28:
+        25:de:b7:9f:56:52:91:7d:20:29:85:0a:80:44:5f:71:32:25:
+        71:0f:c2:16:e2:5f:6b:1d:3f:32:5b:0a:3c:74:1c:b9:62:f1:
+        ed:07:50:a3:6d:b4:b4:31:0a:c0:53:44:6a:3a:88:84:8b:2d:
+        a9:b0:37:8e:e6:18:36:bd:9a:20:40:0f:01:92:8b:3d:aa:61:
+        e7:ae:2c:ed:36:cd:3a:07:86:74:3a:29:b3:d7:3a:b4:00:a9:
+        c2:f5:92:78:0e:e2:0f:a3:fe:bb:be:e0:06:53:84:59:1d:90:
+        69:e5:b6:f9
+-----BEGIN CERTIFICATE-----
+MIIDJTCCAg2gAwIBAgIBAjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdSb290
+IENBMCAXDTE2MDExNDIyMjk0NloYDzIxMTYwMTE1MjIyOTQ2WjAZMRcwFQYDVQQD
+DA5zZXJ2ZXIuZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ANVdYGrf/GHuSKqMEUhDpW22Ul2qmEmxYZI1sfw6BCUMbXn/tNXJ6VwcO+Crs7h9
+o95tveDd11q/FEcRQl6mgtBhwX/dE0bmCYUHDvLU/Bpk0gqtIKsga5bwrczEGVNV
+3AEdpLPvirRJU12KBRzx3OFEv8XX4ncZV1yXC3XuiENxD8pswbSyUKd3RmxYDxG/
+8XYkWq45QrdRZynh0FUwbxfkkeqt+CjCQ2+iZKn7nZiSYkg+6w1PgkqK/z9y7pa1
+rqHBmLrvfZB1bf9aUp6r9cB+0IdD24UHBw99OHr90dPuZR3T6jlqhzfuStPgDW71
+cKzCvfFu85KVXqnwoWWVk40CAwEAAaN9MHswHQYDVR0OBBYEFMDnhL/oWSczELBS
+T1FSLwbWwHrNMB8GA1UdIwQYMBaAFHB/Lq6DaFmYBCMqzes+F80k3QFJMAkGA1Ud
+EwQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwGQYDVR0RBBIwEIIOc2VydmVyLmV4
+YW1wbGUwDQYJKoZIhvcNAQELBQADggEBAHvTBEN1ig8RrsT716Ginv4gGNX0LzGI
+RrZ1jO7lm5emuaPNYJpGw0iX5Zdo91qGNXPZaZ75X3S55pQTActq3OPEBOll2pyk
+iyjz+Zp/v5cfRZLlBbFW5gv2R94eibYr4U3fSn4B0yPcl4xH/l/HzJhGDsSDW8qK
+8VIJvmvsPwmL0JMCv+FR59F+NFYZdND/KCXet59WUpF9ICmFCoBEX3EyJXEPwhbi
+X2sdPzJbCjx0HLli8e0HUKNttLQxCsBTRGo6iISLLamwN47mGDa9miBADwGSiz2q
+YeeuLO02zToHhnQ6KbPXOrQAqcL1kngO4g+j/ru+4AZThFkdkGnltvk=
+-----END CERTIFICATE-----
+------------------
+        No extensions
+
+Received TLS Record
+Header:
+  Version = TLS 1.2 (0x303)
+  Content Type = ApplicationData (23)
+  Length = 281
+  Inner Content Type = Handshake (22)
+    CertificateVerify, Length=260
+      Signature Algorithm: rsa_pss_rsae_sha256 (0x0804)
+      Signature (len=256): ?
+
+Received TLS Record
+Header:
+  Version = TLS 1.2 (0x303)
+  Content Type = ApplicationData (23)
+  Length = 53
+  Inner Content Type = Handshake (22)
+    Finished, Length=32
+      verify_data (len=32): ?
+
+Sent TLS Record
+Header:
+  Version = TLS 1.2 (0x303)
+  Content Type = ChangeCipherSpec (20)
+  Length = 1
+    change_cipher_spec (1)
+
+Sent TLS Record
+Header:
+  Version = TLS 1.2 (0x303)
+  Content Type = ApplicationData (23)
+  Length = 53
+  Inner Content Type = Handshake (22)
+    Finished, Length=32
+      verify_data (len=32): ?
+
+Received TLS Record
+Header:
+  Version = TLS 1.2 (0x303)
+  Content Type = ApplicationData (23)
+  Length = 234
+  Inner Content Type = Handshake (22)
+    NewSessionTicket, Length=213
+        ticket_lifetime_hint=7200
+        ticket_age_add=?
+        ticket_nonce (len=8): ?
+        ticket (len=192): ?
+        No extensions
+
+Received TLS Record
+Header:
+  Version = TLS 1.2 (0x303)
+  Content Type = ApplicationData (23)
+  Length = 234
+  Inner Content Type = Handshake (22)
+    NewSessionTicket, Length=213
+        ticket_lifetime_hint=7200
+        ticket_age_add=?
+        ticket_nonce (len=8): ?
+        ticket (len=192): ?
+        No extensions
+
diff --git a/test/recipes/90-test_sslapi_data/ssltraceref.txt b/test/recipes/90-test_sslapi_data/ssltraceref.txt
new file mode 100644
index 0000000000..5d332da235
--- /dev/null
+++ b/test/recipes/90-test_sslapi_data/ssltraceref.txt
@@ -0,0 +1,253 @@
+Sent TLS Record
+Header:
+  Version = TLS 1.0 (0x301)
+  Content Type = Handshake (22)
+  Length = ?
+    ClientHello, Length=?
+      client_version=0x303 (TLS 1.2)
+      Random:
+        gmt_unix_time=0x?
+        random_bytes (len=28): ?
+      session_id (len=?
+      cipher_suites (len=2)
+        {0x13, 0x01} TLS_AES_128_GCM_SHA256
+      compression_methods (len=1)
+        No Compression (0x00)
+      extensions, length = ?
+        extension_type=ec_point_formats(11), length=4
+          uncompressed (0)
+          ansiX962_compressed_prime (1)
+          ansiX962_compressed_char2 (2)
+        extension_type=supported_groups(10), length=20
+          MLKEM512 (512)
+          MLKEM768 (513)
+          MLKEM1024 (514)
+          X25519MLKEM768 (4588)
+          SecP256r1MLKEM768 (4587)
+          SecP384r1MLKEM1024 (4589)
+          secp521r1 (P-521) (25)
+          secp384r1 (P-384) (24)
+          secp256r1 (P-256) (23)
+        extension_type=session_ticket(35), length=0
+        extension_type=encrypt_then_mac(22), length=0
+        extension_type=extended_master_secret(23), length=0
+        extension_type=signature_algorithms(13), length=?
+          mldsa65 (0x0905)
+          mldsa87 (0x0906)
+          mldsa44 (0x0904)
+          ecdsa_secp256r1_sha256 (0x0403)
+          ecdsa_secp384r1_sha384 (0x0503)
+          ecdsa_secp521r1_sha512 (0x0603)
+          ed25519 (0x0807)
+          ed448 (0x0808)
+          ecdsa_brainpoolP256r1tls13_sha256 (0x081a)
+          ecdsa_brainpoolP384r1tls13_sha384 (0x081b)
+          ecdsa_brainpoolP512r1tls13_sha512 (0x081c)
+          rsa_pss_pss_sha256 (0x0809)
+          rsa_pss_pss_sha384 (0x080a)
+          rsa_pss_pss_sha512 (0x080b)
+          rsa_pss_rsae_sha256 (0x0804)
+          rsa_pss_rsae_sha384 (0x0805)
+          rsa_pss_rsae_sha512 (0x0806)
+          rsa_pkcs1_sha256 (0x0401)
+          rsa_pkcs1_sha384 (0x0501)
+          rsa_pkcs1_sha512 (0x0601)
+        extension_type=supported_versions(43), length=3
+          TLS 1.3 (772)
+        extension_type=psk_key_exchange_modes(45), length=2
+          psk_dhe_ke (1)
+        extension_type=key_share(51), length=806
+            NamedGroup: MLKEM512 (512)
+            key_exchange:  (len=800): ?
+
+Received TLS Record
+Header:
+  Version = TLS 1.2 (0x303)
+  Content Type = Handshake (22)
+  Length = 858
+    ServerHello, Length=854
+      server_version=0x303 (TLS 1.2)
+      Random:
+        gmt_unix_time=0x?
+        random_bytes (len=28): ?
+      session_id (len=?
+      cipher_suite {0x13, 0x01} TLS_AES_128_GCM_SHA256
+      compression_method: No Compression (0x00)
+      extensions, length = ?
+        extension_type=supported_versions(43), length=2
+            TLS 1.3 (772)
+        extension_type=key_share(51), length=772
+            NamedGroup: MLKEM512 (512)
+            key_exchange:  (len=768): ?
+
+Received TLS Record
+Header:
+  Version = TLS 1.2 (0x303)
+  Content Type = ChangeCipherSpec (20)
+  Length = 1
+    change_cipher_spec (1)
+
+Received TLS Record
+Header:
+  Version = TLS 1.2 (0x303)
+  Content Type = ApplicationData (23)
+  Length = 23
+  Inner Content Type = Handshake (22)
+    EncryptedExtensions, Length=2
+      No extensions
+
+Received TLS Record
+Header:
+  Version = TLS 1.2 (0x303)
+  Content Type = ApplicationData (23)
+  Length = 839
+  Inner Content Type = Handshake (22)
+    Certificate, Length=818
+      context (len=0):
+      certificate_list, length=814
+        ASN.1Cert, length=809
+------details-----
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 2 (0x2)
+        Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN = Root CA
+        Validity
+            Not Before: Jan 14 22:29:46 2016 GMT
+            Not After : Jan 15 22:29:46 2116 GMT
+        Subject: CN = server.example
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:d5:5d:60:6a:df:fc:61:ee:48:aa:8c:11:48:43:
+                    a5:6d:b6:52:5d:aa:98:49:b1:61:92:35:b1:fc:3a:
+                    04:25:0c:6d:79:ff:b4:d5:c9:e9:5c:1c:3b:e0:ab:
+                    b3:b8:7d:a3:de:6d:bd:e0:dd:d7:5a:bf:14:47:11:
+                    42:5e:a6:82:d0:61:c1:7f:dd:13:46:e6:09:85:07:
+                    0e:f2:d4:fc:1a:64:d2:0a:ad:20:ab:20:6b:96:f0:
+                    ad:cc:c4:19:53:55:dc:01:1d:a4:b3:ef:8a:b4:49:
+                    53:5d:8a:05:1c:f1:dc:e1:44:bf:c5:d7:e2:77:19:
+                    57:5c:97:0b:75:ee:88:43:71:0f:ca:6c:c1:b4:b2:
+                    50:a7:77:46:6c:58:0f:11:bf:f1:76:24:5a:ae:39:
+                    42:b7:51:67:29:e1:d0:55:30:6f:17:e4:91:ea:ad:
+                    f8:28:c2:43:6f:a2:64:a9:fb:9d:98:92:62:48:3e:
+                    eb:0d:4f:82:4a:8a:ff:3f:72:ee:96:b5:ae:a1:c1:
+                    98:ba:ef:7d:90:75:6d:ff:5a:52:9e:ab:f5:c0:7e:
+                    d0:87:43:db:85:07:07:0f:7d:38:7a:fd:d1:d3:ee:
+                    65:1d:d3:ea:39:6a:87:37:ee:4a:d3:e0:0d:6e:f5:
+                    70:ac:c2:bd:f1:6e:f3:92:95:5e:a9:f0:a1:65:95:
+                    93:8d
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier:
+                C0:E7:84:BF:E8:59:27:33:10:B0:52:4F:51:52:2F:06:D6:C0:7A:CD
+            X509v3 Authority Key Identifier:
+                70:7F:2E:AE:83:68:59:98:04:23:2A:CD:EB:3E:17:CD:24:DD:01:49
+            X509v3 Basic Constraints:
+                CA:FALSE
+            X509v3 Extended Key Usage:
+                TLS Web Server Authentication
+            X509v3 Subject Alternative Name:
+                DNS:server.example
+    Signature Algorithm: sha256WithRSAEncryption
+    Signature Value:
+        7b:d3:04:43:75:8a:0f:11:ae:c4:fb:d7:a1:a2:9e:fe:20:18:
+        d5:f4:2f:31:88:46:b6:75:8c:ee:e5:9b:97:a6:b9:a3:cd:60:
+        9a:46:c3:48:97:e5:97:68:f7:5a:86:35:73:d9:69:9e:f9:5f:
+        74:b9:e6:94:13:01:cb:6a:dc:e3:c4:04:e9:65:da:9c:a4:8b:
+        28:f3:f9:9a:7f:bf:97:1f:45:92:e5:05:b1:56:e6:0b:f6:47:
+        de:1e:89:b6:2b:e1:4d:df:4a:7e:01:d3:23:dc:97:8c:47:fe:
+        5f:c7:cc:98:46:0e:c4:83:5b:ca:8a:f1:52:09:be:6b:ec:3f:
+        09:8b:d0:93:02:bf:e1:51:e7:d1:7e:34:56:19:74:d0:ff:28:
+        25:de:b7:9f:56:52:91:7d:20:29:85:0a:80:44:5f:71:32:25:
+        71:0f:c2:16:e2:5f:6b:1d:3f:32:5b:0a:3c:74:1c:b9:62:f1:
+        ed:07:50:a3:6d:b4:b4:31:0a:c0:53:44:6a:3a:88:84:8b:2d:
+        a9:b0:37:8e:e6:18:36:bd:9a:20:40:0f:01:92:8b:3d:aa:61:
+        e7:ae:2c:ed:36:cd:3a:07:86:74:3a:29:b3:d7:3a:b4:00:a9:
+        c2:f5:92:78:0e:e2:0f:a3:fe:bb:be:e0:06:53:84:59:1d:90:
+        69:e5:b6:f9
+-----BEGIN CERTIFICATE-----
+MIIDJTCCAg2gAwIBAgIBAjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdSb290
+IENBMCAXDTE2MDExNDIyMjk0NloYDzIxMTYwMTE1MjIyOTQ2WjAZMRcwFQYDVQQD
+DA5zZXJ2ZXIuZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ANVdYGrf/GHuSKqMEUhDpW22Ul2qmEmxYZI1sfw6BCUMbXn/tNXJ6VwcO+Crs7h9
+o95tveDd11q/FEcRQl6mgtBhwX/dE0bmCYUHDvLU/Bpk0gqtIKsga5bwrczEGVNV
+3AEdpLPvirRJU12KBRzx3OFEv8XX4ncZV1yXC3XuiENxD8pswbSyUKd3RmxYDxG/
+8XYkWq45QrdRZynh0FUwbxfkkeqt+CjCQ2+iZKn7nZiSYkg+6w1PgkqK/z9y7pa1
+rqHBmLrvfZB1bf9aUp6r9cB+0IdD24UHBw99OHr90dPuZR3T6jlqhzfuStPgDW71
+cKzCvfFu85KVXqnwoWWVk40CAwEAAaN9MHswHQYDVR0OBBYEFMDnhL/oWSczELBS
+T1FSLwbWwHrNMB8GA1UdIwQYMBaAFHB/Lq6DaFmYBCMqzes+F80k3QFJMAkGA1Ud
+EwQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwGQYDVR0RBBIwEIIOc2VydmVyLmV4
+YW1wbGUwDQYJKoZIhvcNAQELBQADggEBAHvTBEN1ig8RrsT716Ginv4gGNX0LzGI
+RrZ1jO7lm5emuaPNYJpGw0iX5Zdo91qGNXPZaZ75X3S55pQTActq3OPEBOll2pyk
+iyjz+Zp/v5cfRZLlBbFW5gv2R94eibYr4U3fSn4B0yPcl4xH/l/HzJhGDsSDW8qK
+8VIJvmvsPwmL0JMCv+FR59F+NFYZdND/KCXet59WUpF9ICmFCoBEX3EyJXEPwhbi
+X2sdPzJbCjx0HLli8e0HUKNttLQxCsBTRGo6iISLLamwN47mGDa9miBADwGSiz2q
+YeeuLO02zToHhnQ6KbPXOrQAqcL1kngO4g+j/ru+4AZThFkdkGnltvk=
+-----END CERTIFICATE-----
+------------------
+        No extensions
+
+Received TLS Record
+Header:
+  Version = TLS 1.2 (0x303)
+  Content Type = ApplicationData (23)
+  Length = 281
+  Inner Content Type = Handshake (22)
+    CertificateVerify, Length=260
+      Signature Algorithm: rsa_pss_rsae_sha256 (0x0804)
+      Signature (len=256): ?
+
+Received TLS Record
+Header:
+  Version = TLS 1.2 (0x303)
+  Content Type = ApplicationData (23)
+  Length = 53
+  Inner Content Type = Handshake (22)
+    Finished, Length=32
+      verify_data (len=32): ?
+
+Sent TLS Record
+Header:
+  Version = TLS 1.2 (0x303)
+  Content Type = ChangeCipherSpec (20)
+  Length = 1
+    change_cipher_spec (1)
+
+Sent TLS Record
+Header:
+  Version = TLS 1.2 (0x303)
+  Content Type = ApplicationData (23)
+  Length = 53
+  Inner Content Type = Handshake (22)
+    Finished, Length=32
+      verify_data (len=32): ?
+
+Received TLS Record
+Header:
+  Version = TLS 1.2 (0x303)
+  Content Type = ApplicationData (23)
+  Length = 234
+  Inner Content Type = Handshake (22)
+    NewSessionTicket, Length=213
+        ticket_lifetime_hint=7200
+        ticket_age_add=?
+        ticket_nonce (len=8): ?
+        ticket (len=192): ?
+        No extensions
+
+Received TLS Record
+Header:
+  Version = TLS 1.2 (0x303)
+  Content Type = ApplicationData (23)
+  Length = 234
+  Inner Content Type = Handshake (22)
+    NewSessionTicket, Length=213
+        ticket_lifetime_hint=7200
+        ticket_age_add=?
+        ticket_nonce (len=8): ?
+        ticket (len=192): ?
+        No extensions
+
diff --git a/test/sslapitest.c b/test/sslapitest.c
index 673e7969aa..3fd051a37b 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -105,6 +105,7 @@ static char *privkey8192 = NULL;
 static char *srpvfile = NULL;
 static char *tmpfilename = NULL;
 static char *dhfile = NULL;
+static char *datadir = NULL;

 static int is_fips = 0;
 static int fips_ems_check = 0;
@@ -128,6 +129,15 @@ static X509 *ocspcert = NULL;

 #define CLIENT_VERSION_LEN      2

+/* The ssltrace test assumes some options are switched on/off */
+#if !defined(OPENSSL_NO_SSL_TRACE) \
+    && defined(OPENSSL_NO_BROTLI) && defined(OPENSSL_NO_ZSTD) \
+    && !defined(OPENSSL_NO_ECX) && !defined(OPENSSL_NO_DH) \
+    && !defined(OPENSSL_NO_ML_DSA) && !defined(OPENSSL_NO_ML_KEM) \
+    && !defined(OPENSSL_NO_TLS1_3)
+# define DO_SSL_TRACE_TEST
+#endif
+
 /*
  * This structure is used to validate that the correct number of log messages
  * of various types are emitted when emitting secret logs.
@@ -13730,6 +13740,74 @@ static int test_no_renegotiation(int idx)
     return testresult;
 }

+#if defined(DO_SSL_TRACE_TEST)
+/*
+ * Tests that the SSL_trace() msg_callback works as expected with a PQ Groups.
+ */
+static int test_ssl_trace(void)
+{
+    SSL_CTX *sctx = NULL, *cctx = NULL;
+    SSL *serverssl = NULL, *clientssl = NULL;
+    int testresult = 0;
+    BIO *bio = NULL;
+    char *reffile = NULL;
+    char *grouplist = "MLKEM512:MLKEM768:MLKEM1024:X25519MLKEM768:SecP256r1MLKEM768"
+        ":SecP384r1MLKEM1024:secp521r1:secp384r1:secp256r1";
+
+    if (!TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(),
+                                       TLS_client_method(),
+                                       TLS1_3_VERSION, TLS1_3_VERSION,
+                                       &sctx, &cctx, cert, privkey))
+            || !TEST_ptr(bio = BIO_new(BIO_s_mem()))
+            || !TEST_true(SSL_CTX_set1_groups_list(sctx, grouplist))
+            || !TEST_true(SSL_CTX_set1_groups_list(cctx, grouplist))
+            || !TEST_true(SSL_CTX_set_ciphersuites(cctx,
+                                                   "TLS_AES_128_GCM_SHA256"))
+            || !TEST_true(SSL_CTX_set_ciphersuites(sctx,
+                                                   "TLS_AES_128_GCM_SHA256"))
+# ifdef SSL_OP_LEGACY_EC_POINT_FORMATS
+            || !TEST_true(SSL_CTX_set_options(cctx, SSL_OP_LEGACY_EC_POINT_FORMATS))
+            || !TEST_true(SSL_CTX_set_options(sctx, SSL_OP_LEGACY_EC_POINT_FORMATS))
+# endif
+            || !TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl,
+                                             NULL, NULL)))
+        goto err;
+
+    SSL_set_msg_callback(clientssl, SSL_trace);
+    SSL_set_msg_callback_arg(clientssl, bio);
+
+    if (!TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)))
+        goto err;
+
+    /* Skip the comparison of the trace when the fips provider is used. */
+    if (is_fips) {
+        /* Check whether there was something written. */
+        if (!TEST_int_gt(BIO_pending(bio), 0))
+            goto err;
+    } else {
+
+# ifdef OPENSSL_NO_ZLIB
+        reffile = test_mk_file_path(datadir, "ssltraceref.txt");
+# else
+        reffile = test_mk_file_path(datadir, "ssltraceref-zlib.txt");
+# endif
+        if (!TEST_true(compare_with_reference_file(bio, reffile)))
+            goto err;
+    }
+
+    testresult = 1;
+ err:
+    BIO_free(bio);
+    SSL_free(serverssl);
+    SSL_free(clientssl);
+    SSL_CTX_free(sctx);
+    SSL_CTX_free(cctx);
+    OPENSSL_free(reffile);
+
+    return testresult;
+}
+#endif
+
 OPT_TEST_DECLARE_USAGE("certfile privkeyfile srpvfile tmpfile provider config dhfile\n")

 int setup_tests(void)
@@ -13764,6 +13842,8 @@ int setup_tests(void)
             || !TEST_ptr(dhfile = test_get_argument(5)))
         return 0;

+    datadir = test_get_argument(6);
+
     if (!TEST_true(OSSL_LIB_CTX_load_config(libctx, configfile)))
         return 0;

@@ -14065,6 +14145,10 @@ int setup_tests(void)
     ADD_TEST(test_quic_tls_early_data);
 #endif
     ADD_ALL_TESTS(test_no_renegotiation, 2);
+#if defined(DO_SSL_TRACE_TEST)
+    if (datadir != NULL)
+        ADD_TEST(test_ssl_trace);
+#endif
     return 1;

  err:
diff --git a/test/testutil.h b/test/testutil.h
index f02dcdfba6..a262d93719 100644
--- a/test/testutil.h
+++ b/test/testutil.h
@@ -652,4 +652,6 @@ X509 *load_cert_der(const unsigned char *bytes, int len);
 STACK_OF(X509) *load_certs_pem(const char *file);
 X509_REQ *load_csr_der(const char *file, OSSL_LIB_CTX *libctx);
 time_t test_asn1_string_to_time_t(const char *asn1_string);
+
+int compare_with_reference_file(BIO *membio, const char *reffile);
 #endif                          /* OSSL_TESTUTIL_H */
diff --git a/test/testutil/compare.c b/test/testutil/compare.c
new file mode 100644
index 0000000000..067fb878b5
--- /dev/null
+++ b/test/testutil/compare.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2017-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "../testutil.h"
+
+static void strip_line_ends(char *str)
+{
+    size_t i;
+
+    for (i = strlen(str);
+         i > 0 && (str[i - 1] == '\n' || str[i - 1] == '\r');
+         i--);
+
+    str[i] = '\0';
+}
+
+int compare_with_reference_file(BIO *membio, const char *reffile)
+{
+    BIO *file = NULL, *newfile = NULL;
+    char buf1[8192], buf2[8192];
+    int ret = 0;
+    size_t i;
+
+    if (!TEST_ptr(reffile))
+        goto err;
+
+    file = BIO_new_file(reffile, "rb");
+    if (!TEST_ptr(file))
+        goto err;
+
+    newfile = BIO_new_file("ssltraceref-new.txt", "wb");
+    if (!TEST_ptr(newfile))
+        goto err;
+
+    while (BIO_gets(membio, buf2, sizeof(buf2)) > 0)
+        if (BIO_puts(newfile, buf2) <= 0) {
+            TEST_error("Failed writing new file data");
+            goto err;
+        }
+
+    if (!TEST_int_ge(BIO_seek(membio, 0), 0))
+        goto err;
+
+    while (BIO_gets(file, buf1, sizeof(buf1)) > 0) {
+        size_t line_len;
+
+        if (BIO_gets(membio, buf2, sizeof(buf2)) <= 0) {
+            TEST_error("Failed reading mem data");
+            goto err;
+        }
+        strip_line_ends(buf1);
+        strip_line_ends(buf2);
+        line_len = strlen(buf1);
+        if (line_len > 0 && buf1[line_len - 1] == '?') {
+            /* Wildcard at the EOL means ignore anything after it */
+            if (strlen(buf2) > line_len)
+                buf2[line_len] = '\0';
+        }
+        if (line_len != strlen(buf2)) {
+            TEST_error("Actual and ref line data length mismatch");
+            TEST_info("%s", buf1);
+            TEST_info("%s", buf2);
+            goto err;
+        }
+        for (i = 0; i < line_len; i++) {
+            /* '?' is a wild card character in the reference text */
+            if (buf1[i] == '?')
+                buf2[i] = '?';
+        }
+        if (!TEST_str_eq(buf1, buf2))
+            goto err;
+    }
+    if (!TEST_true(BIO_eof(file))
+            || !TEST_true(BIO_eof(membio)))
+        goto err;
+
+    ret = 1;
+ err:
+    BIO_free(file);
+    BIO_free(newfile);
+    return ret;
+}