@@ -1689,6 +1689,8 @@ const CertificateKeyTestParams kCertificateKeyTests[] = {
16891689 " TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:" , SSL_SIGN_ECDSA_SECP256R1_SHA256},
16901690 {GetED25519TestCertificate, GetED25519TestKey, SSL_PKEY_ED25519,
16911691 " TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:" , SSL_SIGN_ED25519},
1692+ {GetMLDSA65TestCertificate, GetMLDSA65TestKey, SSL_PKEY_PQDSA, " " ,
1693+ SSL_SIGN_MLDSA65},
16921694};
16931695
16941696class MultipleCertificateSlotTest
@@ -1760,6 +1762,10 @@ TEST_P(MultipleCertificateSlotTest, CertificateSlotIndex) {
17601762 // ED25519 is not supported in versions prior to TLS1.2.
17611763 GTEST_SKIP ();
17621764 }
1765+ if (version < TLS1_3_VERSION && slot_index == SSL_PKEY_PQDSA) {
1766+ // ML-DSA is only defined for TLS 1.3.
1767+ GTEST_SKIP ();
1768+ }
17631769 bssl::UniquePtr<SSL_CTX> client_ctx (SSL_CTX_new (TLS_method ()));
17641770 bssl::UniquePtr<SSL_CTX> server_ctx (CreateContextWithCertificate (
17651771 TLS_method (), certificate_key_param ().certificate (),
@@ -1768,7 +1774,7 @@ TEST_P(MultipleCertificateSlotTest, CertificateSlotIndex) {
17681774 StandardCertificateSlotIndexTests (
17691775 client_ctx.get (), server_ctx.get (),
17701776 {SSL_SIGN_ED25519, SSL_SIGN_ECDSA_SECP256R1_SHA256,
1771- SSL_SIGN_RSA_PSS_RSAE_SHA256},
1777+ SSL_SIGN_RSA_PSS_RSAE_SHA256, SSL_SIGN_MLDSA65 },
17721778 slot_index, true );
17731779}
17741780
@@ -1778,6 +1784,10 @@ TEST_P(MultipleCertificateSlotTest, SetChainAndKeyIndex) {
17781784 // ED25519 is not supported in versions prior to TLS1.2.
17791785 GTEST_SKIP ();
17801786 }
1787+ if (version < TLS1_3_VERSION && slot_index == SSL_PKEY_PQDSA) {
1788+ // ML-DSA is only defined for TLS 1.3.
1789+ GTEST_SKIP ();
1790+ }
17811791 bssl::UniquePtr<SSL_CTX> client_ctx (SSL_CTX_new (TLS_method ()));
17821792 bssl::UniquePtr<SSL_CTX> server_ctx (SSL_CTX_new (TLS_method ()));
17831793
@@ -1797,7 +1807,7 @@ TEST_P(MultipleCertificateSlotTest, SetChainAndKeyIndex) {
17971807 StandardCertificateSlotIndexTests (
17981808 client_ctx.get (), server_ctx.get (),
17991809 {SSL_SIGN_ED25519, SSL_SIGN_ECDSA_SECP256R1_SHA256,
1800- SSL_SIGN_RSA_PSS_RSAE_SHA256},
1810+ SSL_SIGN_RSA_PSS_RSAE_SHA256, SSL_SIGN_MLDSA65 },
18011811 slot_index, true );
18021812}
18031813
@@ -1806,6 +1816,11 @@ TEST_P(MultipleCertificateSlotTest, AutomaticSelectionSigAlgs) {
18061816 // ED25519 is not supported in versions prior to TLS1.2.
18071817 GTEST_SKIP ();
18081818 }
1819+ if (slot_index == SSL_PKEY_PQDSA) {
1820+ // The server in this test only configures RSA, ECDSA and ED25519
1821+ // credentials, so ML-DSA cannot be selected here.
1822+ GTEST_SKIP ();
1823+ }
18091824
18101825 bssl::UniquePtr<SSL_CTX> client_ctx (SSL_CTX_new (TLS_method ()));
18111826 bssl::UniquePtr<SSL_CTX> server_ctx (SSL_CTX_new (TLS_method ()));
@@ -1837,9 +1852,10 @@ TEST_P(MultipleCertificateSlotTest, AutomaticSelectionSigAlgs) {
18371852
18381853TEST_P (MultipleCertificateSlotTest, AutomaticSelectionCipherAuth) {
18391854 if ((version < TLS1_2_VERSION && slot_index == SSL_PKEY_ED25519) ||
1840- version >= TLS1_3_VERSION) {
1855+ version >= TLS1_3_VERSION || slot_index == SSL_PKEY_PQDSA ) {
18411856 // ED25519 is not supported in versions prior to TLS1.2.
18421857 // TLS 1.3 not have cipher-based authentication configuration.
1858+ // ML-DSA is TLS 1.3 only and is not selectable via cipher-auth.
18431859 GTEST_SKIP ();
18441860 }
18451861
@@ -1884,6 +1900,10 @@ TEST_P(MultipleCertificateSlotTest, MissingCertificate) {
18841900 // ED25519 is not supported in versions prior to TLS1.2.
18851901 GTEST_SKIP ();
18861902 }
1903+ if (slot_index == SSL_PKEY_PQDSA) {
1904+ // The server in this test only configures RSA, ECDSA and ED25519 keys.
1905+ GTEST_SKIP ();
1906+ }
18871907
18881908 bssl::UniquePtr<SSL_CTX> client_ctx (SSL_CTX_new (TLS_method ()));
18891909 bssl::UniquePtr<SSL_CTX> server_ctx (SSL_CTX_new (TLS_method ()));
@@ -1911,6 +1931,10 @@ TEST_P(MultipleCertificateSlotTest, MissingPrivateKey) {
19111931 // ED25519 is not supported in versions prior to TLS1.2.
19121932 GTEST_SKIP ();
19131933 }
1934+ if (slot_index == SSL_PKEY_PQDSA) {
1935+ // The server in this test only configures RSA, ECDSA and ED25519 certs.
1936+ GTEST_SKIP ();
1937+ }
19141938
19151939 bssl::UniquePtr<SSL_CTX> client_ctx (SSL_CTX_new (TLS_method ()));
19161940 bssl::UniquePtr<SSL_CTX> server_ctx (SSL_CTX_new (TLS_method ()));
@@ -1988,8 +2012,9 @@ TEST_P(MLDSAHandshakeTest, HandshakeSucceeds) {
19882012}
19892013
19902014TEST_P (MLDSAHandshakeTest, RejectedInTLS12) {
1991- // ML-DSA is only defined for TLS 1.3. Forcing a TLS 1.2 handshake with an
1992- // ML-DSA sigalg preference must not succeed.
2015+ // When both sides are pinned to TLS 1.2 and ML-DSA is the only sigalg in
2016+ // play, the handshake must not succeed: the server has nothing to sign
2017+ // with after the version filter excludes ML-DSA.
19932018 bssl::UniquePtr<SSL_CTX> client_ctx (SSL_CTX_new (TLS_method ()));
19942019 bssl::UniquePtr<SSL_CTX> server_ctx (
19952020 CreateContextWithCertificate (TLS_method (), GetParam ().certificate (),
@@ -2039,6 +2064,157 @@ TEST(MLDSAHandshakeTest, CrossVariantMismatchFails) {
20392064 server_ctx.get ()));
20402065}
20412066
2067+ TEST_P (MLDSAHandshakeTest, ClientAuth) {
2068+ // Test ML-DSA for client certificate authentication (mutual TLS).
2069+ bssl::UniquePtr<SSL_CTX> client_ctx (CreateContextWithCertificate (
2070+ TLS_method (), GetParam ().certificate (), GetParam ().key ()));
2071+ bssl::UniquePtr<SSL_CTX> server_ctx (CreateContextWithCertificate (
2072+ TLS_method (), GetParam ().certificate (), GetParam ().key ()));
2073+ ASSERT_TRUE (client_ctx);
2074+ ASSERT_TRUE (server_ctx);
2075+ ASSERT_TRUE (SSL_CTX_set_min_proto_version (client_ctx.get (), TLS1_3_VERSION));
2076+ ASSERT_TRUE (SSL_CTX_set_max_proto_version (client_ctx.get (), TLS1_3_VERSION));
2077+ ASSERT_TRUE (SSL_CTX_set_min_proto_version (server_ctx.get (), TLS1_3_VERSION));
2078+ ASSERT_TRUE (SSL_CTX_set_max_proto_version (server_ctx.get (), TLS1_3_VERSION));
2079+
2080+ const uint16_t sigalgs[] = {GetParam ().sigalg };
2081+ ASSERT_TRUE (SSL_CTX_set_signing_algorithm_prefs (server_ctx.get (), sigalgs,
2082+ OPENSSL_ARRAY_SIZE (sigalgs)));
2083+ ASSERT_TRUE (SSL_CTX_set_signing_algorithm_prefs (client_ctx.get (), sigalgs,
2084+ OPENSSL_ARRAY_SIZE (sigalgs)));
2085+ ASSERT_TRUE (SSL_CTX_set_verify_algorithm_prefs (client_ctx.get (), sigalgs,
2086+ OPENSSL_ARRAY_SIZE (sigalgs)));
2087+ ASSERT_TRUE (SSL_CTX_set_verify_algorithm_prefs (server_ctx.get (), sigalgs,
2088+ OPENSSL_ARRAY_SIZE (sigalgs)));
2089+
2090+ // Server requests client certificate.
2091+ SSL_CTX_set_custom_verify (
2092+ server_ctx.get (), SSL_VERIFY_PEER,
2093+ [](SSL *ssl, uint8_t *out_alert) -> ssl_verify_result_t {
2094+ return ssl_verify_ok;
2095+ });
2096+
2097+ bssl::UniquePtr<SSL> client, server;
2098+ ASSERT_TRUE (ConnectClientAndServer (&client, &server, client_ctx.get (),
2099+ server_ctx.get ()));
2100+ EXPECT_EQ (SSL_get_peer_signature_algorithm (server.get ()), GetParam ().sigalg );
2101+ }
2102+
2103+ TEST_P (MLDSAHandshakeTest, FilteredFromTLS12) {
2104+ // ML-DSA is only defined for TLS 1.3 (draft-ietf-tls-mldsa §3.3). Confirm
2105+ // that even when the local sigalg prefs include ML-DSA, the negotiation
2106+ // does not pick it once the version is forced to TLS 1.2 -- the peer must
2107+ // fall back to a different sigalg, not fail.
2108+ bssl::UniquePtr<SSL_CTX> client_ctx (SSL_CTX_new (TLS_method ()));
2109+ bssl::UniquePtr<SSL_CTX> server_ctx (CreateContextWithCertificate (
2110+ TLS_method (), GetParam ().certificate (), GetParam ().key ()));
2111+ ASSERT_TRUE (client_ctx);
2112+ ASSERT_TRUE (server_ctx);
2113+
2114+ // Give the server an RSA cert/key as a fallback usable in TLS 1.2.
2115+ ASSERT_TRUE (
2116+ SSL_CTX_use_certificate (server_ctx.get (), GetTestCertificate ().get ()));
2117+ ASSERT_TRUE (SSL_CTX_use_PrivateKey (server_ctx.get (), GetTestKey ().get ()));
2118+
2119+ // Force TLS 1.2 from the client side; server allows 1.2..1.3.
2120+ ASSERT_TRUE (SSL_CTX_set_max_proto_version (client_ctx.get (), TLS1_2_VERSION));
2121+
2122+ const uint16_t sigalgs[] = {GetParam ().sigalg , SSL_SIGN_RSA_PSS_RSAE_SHA256};
2123+ ASSERT_TRUE (SSL_CTX_set_signing_algorithm_prefs (server_ctx.get (), sigalgs,
2124+ OPENSSL_ARRAY_SIZE (sigalgs)));
2125+ ASSERT_TRUE (SSL_CTX_set_verify_algorithm_prefs (client_ctx.get (), sigalgs,
2126+ OPENSSL_ARRAY_SIZE (sigalgs)));
2127+
2128+ bssl::UniquePtr<SSL> client, server;
2129+ ASSERT_TRUE (ConnectClientAndServer (&client, &server, client_ctx.get (),
2130+ server_ctx.get ()));
2131+ EXPECT_EQ (SSL_version (client.get ()), TLS1_2_VERSION);
2132+ EXPECT_NE (SSL_get_peer_signature_algorithm (client.get ()), GetParam ().sigalg );
2133+ }
2134+
2135+ TEST (MLDSAHandshakeTest, RejectedByPeerSigalgCheckInTLS12) {
2136+ // Even if a peer's local verify list contains ML-DSA (we advertise the
2137+ // codepoints in TLS 1.2 ClientHellos so a higher-version negotiation can
2138+ // pick them up), |tls12_check_peer_sigalg| must reject ML-DSA arriving in
2139+ // a TLS 1.2 ServerKeyExchange / CertificateVerify with illegal_parameter,
2140+ // per draft-ietf-tls-mldsa §3.3. Without this check, the handshake would
2141+ // only fail later with a non-spec WRONG_SIGNATURE_TYPE.
2142+ bssl::UniquePtr<SSL_CTX> client_ctx (SSL_CTX_new (TLS_method ()));
2143+ bssl::UniquePtr<SSL_CTX> server_ctx (CreateContextWithCertificate (
2144+ TLS_method (), GetTestCertificate (), GetTestKey ()));
2145+ ASSERT_TRUE (client_ctx);
2146+ ASSERT_TRUE (server_ctx);
2147+ ASSERT_TRUE (SSL_CTX_set_max_proto_version (client_ctx.get (), TLS1_2_VERSION));
2148+ ASSERT_TRUE (SSL_CTX_set_max_proto_version (server_ctx.get (), TLS1_2_VERSION));
2149+
2150+ bssl::UniquePtr<SSL> client, server;
2151+ ASSERT_TRUE (ConnectClientAndServer (&client, &server, client_ctx.get (),
2152+ server_ctx.get (), ClientConfig (),
2153+ /* shed_handshake_config=*/ false ));
2154+ ASSERT_EQ (SSL_version (client.get ()), TLS1_2_VERSION);
2155+
2156+ // Synthesize a fresh handshake state and seed its verify list with ML-DSA.
2157+ // |tls12_check_peer_sigalg| must still reject because the negotiated
2158+ // version is TLS 1.2.
2159+ bssl::UniquePtr<SSL_HANDSHAKE> hs = ssl_handshake_new (client.get ());
2160+ ASSERT_TRUE (hs);
2161+ static const uint16_t kSeedSigalgs [] = {
2162+ SSL_SIGN_MLDSA44, SSL_SIGN_MLDSA65, SSL_SIGN_MLDSA87,
2163+ SSL_SIGN_RSA_PSS_RSAE_SHA256};
2164+ ASSERT_TRUE (hs->config ->verify_sigalgs .CopyFrom (kSeedSigalgs ));
2165+
2166+ for (uint16_t sigalg :
2167+ {SSL_SIGN_MLDSA44, SSL_SIGN_MLDSA65, SSL_SIGN_MLDSA87}) {
2168+ uint8_t alert = 0 ;
2169+ EXPECT_FALSE (tls12_check_peer_sigalg (hs.get (), &alert, sigalg));
2170+ EXPECT_EQ (alert, SSL_AD_ILLEGAL_PARAMETER);
2171+ ERR_clear_error ();
2172+ }
2173+
2174+ // Sanity check: a valid TLS 1.2 sigalg from the same list still passes.
2175+ uint8_t alert = 0 ;
2176+ EXPECT_TRUE (
2177+ tls12_check_peer_sigalg (hs.get (), &alert, SSL_SIGN_RSA_PSS_RSAE_SHA256));
2178+ }
2179+
2180+ TEST (MLDSAHandshakeTest, ParamNidFilterSelectsMatchingVariant) {
2181+ // Server has an MLDSA-44 cert/key but advertises [MLDSA-65, MLDSA-87,
2182+ // MLDSA-44] in its signing prefs (so MLDSA-44 is *not* the first
2183+ // preference). |pkey_supports_algorithm| should reject MLDSA-65 and
2184+ // MLDSA-87 against the MLDSA-44 key (param_nid mismatch) and fall
2185+ // through to MLDSA-44, which both peers offer.
2186+ //
2187+ // This positively exercises the
2188+ // EVP_PKEY_pqdsa_get_type(pkey) != alg->param_nid
2189+ // filter that this PR adds.
2190+ bssl::UniquePtr<SSL_CTX> client_ctx (SSL_CTX_new (TLS_method ()));
2191+ bssl::UniquePtr<SSL_CTX> server_ctx (CreateContextWithCertificate (
2192+ TLS_method (), GetMLDSA44TestCertificate (), GetMLDSA44TestKey ()));
2193+ ASSERT_TRUE (client_ctx);
2194+ ASSERT_TRUE (server_ctx);
2195+ ASSERT_TRUE (SSL_CTX_set_min_proto_version (client_ctx.get (), TLS1_3_VERSION));
2196+ ASSERT_TRUE (SSL_CTX_set_max_proto_version (client_ctx.get (), TLS1_3_VERSION));
2197+ ASSERT_TRUE (SSL_CTX_set_min_proto_version (server_ctx.get (), TLS1_3_VERSION));
2198+ ASSERT_TRUE (SSL_CTX_set_max_proto_version (server_ctx.get (), TLS1_3_VERSION));
2199+
2200+ const uint16_t server_sigalgs[] = {SSL_SIGN_MLDSA65, SSL_SIGN_MLDSA87,
2201+ SSL_SIGN_MLDSA44};
2202+ ASSERT_TRUE (SSL_CTX_set_signing_algorithm_prefs (
2203+ server_ctx.get (), server_sigalgs, OPENSSL_ARRAY_SIZE (server_sigalgs)));
2204+
2205+ const uint16_t client_sigalgs[] = {SSL_SIGN_MLDSA44, SSL_SIGN_MLDSA65,
2206+ SSL_SIGN_MLDSA87};
2207+ ASSERT_TRUE (SSL_CTX_set_verify_algorithm_prefs (
2208+ client_ctx.get (), client_sigalgs, OPENSSL_ARRAY_SIZE (client_sigalgs)));
2209+
2210+ bssl::UniquePtr<SSL> client, server;
2211+ ASSERT_TRUE (ConnectClientAndServer (&client, &server, client_ctx.get (),
2212+ server_ctx.get ()));
2213+ // Despite MLDSA-65 being the server's first signing preference, the param
2214+ // filter must have skipped it because the cert is MLDSA-44.
2215+ EXPECT_EQ (SSL_get_peer_signature_algorithm (client.get ()), SSL_SIGN_MLDSA44);
2216+ }
2217+
20422218
20432219struct MultiTransferReadWriteTestParams {
20442220 const char suite[50 ];
0 commit comments