diff -up ./cmd/lib/secutil.c.mlkem_p256 ./cmd/lib/secutil.c --- ./cmd/lib/secutil.c.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./cmd/lib/secutil.c 2025-06-25 16:47:19.374362915 -0700 @@ -4198,43 +4198,42 @@ SECU_ParseSSLVersionRangeString(const ch return SECSuccess; } +#define NAME_AND_LEN(s) sizeof(s)-1,s +static const struct SSLNamedGroupString { + int len; + char *name; + SSLNamedGroup grp; +} sslNamedGroupStringArray[] = { + { NAME_AND_LEN("P256"), ssl_grp_ec_secp256r1 }, + { NAME_AND_LEN("P384"), ssl_grp_ec_secp384r1 }, + { NAME_AND_LEN("P521"), ssl_grp_ec_secp521r1 }, + { NAME_AND_LEN("x25519"), ssl_grp_ec_curve25519 }, + { NAME_AND_LEN("FF2048"), ssl_grp_ffdhe_2048 }, + { NAME_AND_LEN("FF3072"), ssl_grp_ffdhe_3072 }, + { NAME_AND_LEN("FF4096"), ssl_grp_ffdhe_4096 }, + { NAME_AND_LEN("FF6144"), ssl_grp_ffdhe_6144 }, + { NAME_AND_LEN("FF8192"), ssl_grp_ffdhe_8192 }, +#ifndef NSS_DISABLE_KYBER + { NAME_AND_LEN("xyber76800"), ssl_grp_kem_xyber768d00 }, +#endif + { NAME_AND_LEN("mlkem768x25519"), ssl_grp_kem_mlkem768x25519 }, + { NAME_AND_LEN("mlkem768secp256r1"), ssl_grp_kem_secp256r1mlkem768 }, +}; + +static const size_t sslNamedGroupStringLen=PR_ARRAY_SIZE(sslNamedGroupStringArray); + static SSLNamedGroup groupNameToNamedGroup(char *name) { - if (PL_strlen(name) == 4) { - if (!strncmp(name, "P256", 4)) { - return ssl_grp_ec_secp256r1; - } - if (!strncmp(name, "P384", 4)) { - return ssl_grp_ec_secp384r1; - } - if (!strncmp(name, "P521", 4)) { - return ssl_grp_ec_secp521r1; - } - } - if (PL_strlen(name) == 6) { - if (!strncmp(name, "x25519", 6)) { - return ssl_grp_ec_curve25519; - } - if (!strncmp(name, "FF2048", 6)) { - return ssl_grp_ffdhe_2048; - } - if (!strncmp(name, "FF3072", 6)) { - return ssl_grp_ffdhe_3072; - } - if (!strncmp(name, "FF4096", 6)) { - return ssl_grp_ffdhe_4096; - } - if (!strncmp(name, "FF6144", 6)) { - return ssl_grp_ffdhe_6144; - } - if (!strncmp(name, "FF8192", 6)) { - return ssl_grp_ffdhe_8192; - } - } - if (PL_strlen(name) == 11) { - if (!strncmp(name, "xyber768d00", 11)) { - return ssl_grp_kem_xyber768d00; + int len = PL_strlen(name); + int i; + + for (i=0; i < sslNamedGroupStringLen; i++) { + const struct SSLNamedGroupString *ngs = &sslNamedGroupStringArray[i]; + if (len == ngs->len) { + if (!strncmp(name, ngs->name, len)) { + return ngs->grp; + } } } if (PL_strlen(name) == 14) { @@ -4309,6 +4308,26 @@ done: return SECSuccess; } +const char * +SECU_NamedGroupToGroupName(SSLNamedGroup grp) { + int i; + static char unknownBuf[32]; + + if (grp == ssl_grp_none) { + return "None"; + } + + for (i=0; i < sslNamedGroupStringLen; i++) { + const struct SSLNamedGroupString *ngs = &sslNamedGroupStringArray[i]; + if (grp == ngs->grp) { + return ngs->name; + } + } + snprintf(unknownBuf, sizeof(unknownBuf), "Unknown %d\n", grp); + + return unknownBuf; +} + SSLSignatureScheme schemeNameToScheme(const char *name) { diff -up ./cmd/lib/secutil.h.mlkem_p256 ./cmd/lib/secutil.h --- ./cmd/lib/secutil.h.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./cmd/lib/secutil.h 2025-06-25 16:47:19.374362915 -0700 @@ -425,6 +425,8 @@ SECStatus parseGroupList(const char *arg SECStatus parseSigSchemeList(const char *arg, const SSLSignatureScheme **enabledSigSchemes, unsigned int *enabledSigSchemeCount); +const char *SECU_NamedGroupToGroupName(SSLNamedGroup grp); + typedef struct { SECItem label; PRBool hasContext; diff -up ./cmd/selfserv/selfserv.c.mlkem_p256 ./cmd/selfserv/selfserv.c --- ./cmd/selfserv/selfserv.c.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./cmd/selfserv/selfserv.c 2025-06-25 16:47:19.374362915 -0700 @@ -227,7 +227,11 @@ PrintParameterUsage() "-I comma separated list of enabled groups for TLS key exchange.\n" " The following values are valid:\n" " P256, P384, P521, x25519, FF2048, FF3072, FF4096, FF6144, FF8192,\n" - " xyber768d00, mlkem768x25519\n" + " " +#ifndef NSS_DISABLE_KYBER + "xyber768d00, " +#endif + "mlkem768x25519, mlkem768secp256r1\n" "-J comma separated list of enabled signature schemes in preference order.\n" " The following values are valid:\n" " rsa_pkcs1_sha1, rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pkcs1_sha512,\n" @@ -410,9 +414,11 @@ printSecurityInfo(PRFileDesc *fd) channel.isFIPS ? " FIPS" : ""); FPRINTF(stderr, "selfserv: Server Auth: %d-bit %s, Key Exchange: %d-bit %s\n" + " Key Exchange Group:%s\n" " Compression: %s, Extended Master Secret: %s\n", channel.authKeyBits, suite.authAlgorithmName, channel.keaKeyBits, suite.keaTypeName, + SECU_NamedGroupToGroupName(channel.keaGroup), channel.compressionMethodName, channel.extendedMasterSecretUsed ? "Yes" : "No"); } diff -up ./cmd/strsclnt/strsclnt.c.mlkem_p256 ./cmd/strsclnt/strsclnt.c --- ./cmd/strsclnt/strsclnt.c.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./cmd/strsclnt/strsclnt.c 2025-06-25 16:47:19.374362915 -0700 @@ -298,9 +298,11 @@ printSecurityInfo(PRFileDesc *fd) channel.isFIPS ? " FIPS" : ""); FPRINTF(stderr, "strsclnt: Server Auth: %d-bit %s, Key Exchange: %d-bit %s\n" + " Key Exchange Group:%s\n" " Compression: %s\n", channel.authKeyBits, suite.authAlgorithmName, channel.keaKeyBits, suite.keaTypeName, + SECU_NamedGroupToGroupName(channel.keaGroup), channel.compressionMethodName); } } diff -up ./cmd/tstclnt/tstclnt.c.mlkem_p256 ./cmd/tstclnt/tstclnt.c --- ./cmd/tstclnt/tstclnt.c.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./cmd/tstclnt/tstclnt.c 2025-06-25 16:47:19.374362915 -0700 @@ -176,10 +176,12 @@ printSecurityInfo(PRFileDesc *fd) channel.isFIPS ? " FIPS" : ""); FPRINTF(stderr, "tstclnt: Server Auth: %d-bit %s, Key Exchange: %d-bit %s\n" + " Key Exchange Group:%s\n" " Compression: %s, Extended Master Secret: %s\n" " Signature Scheme: %s\n", channel.authKeyBits, suite.authAlgorithmName, channel.keaKeyBits, suite.keaTypeName, + SECU_NamedGroupToGroupName(channel.keaGroup), channel.compressionMethodName, channel.extendedMasterSecretUsed ? "Yes" : "No", signatureSchemeName(channel.signatureScheme)); @@ -308,7 +310,11 @@ PrintParameterUsage() fprintf(stderr, "%-20s Comma separated list of enabled groups for TLS key exchange.\n" "%-20s The following values are valid:\n" "%-20s P256, P384, P521, x25519, FF2048, FF3072, FF4096, FF6144, FF8192\n" - "%-20s xyber768d00, mlkem768x25519\n", + "%-20s " +#ifndef NSS_DISABLE_KYBER + "xyber768d00, " +#endif + "mlkem768x25519, mlkem768secp256r1\n", "-I", "", "", ""); fprintf(stderr, "%-20s Comma separated list of signature schemes in preference order.\n" "%-20s The following values are valid:\n" diff -up ./coreconf/config.mk.mlkem_p256 ./coreconf/config.mk --- ./coreconf/config.mk.mlkem_p256 2025-06-25 16:47:19.375362928 -0700 +++ ./coreconf/config.mk 2025-06-25 16:51:23.790507978 -0700 @@ -179,6 +179,10 @@ ifdef NSS_DISABLE_LIBPKIX DEFINES += -DNSS_DISABLE_LIBPKIX endif +ifdef NSS_DISABLE_KYBER +DEFINES += -DNSS_DISABLE_KYBER +endif + ifdef NSS_DISABLE_DBM DEFINES += -DNSS_DISABLE_DBM endif diff -up ./gtests/nss_bogo_shim/nss_bogo_shim.cc.mlkem_p256 ./gtests/nss_bogo_shim/nss_bogo_shim.cc --- ./gtests/nss_bogo_shim/nss_bogo_shim.cc.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./gtests/nss_bogo_shim/nss_bogo_shim.cc 2025-06-25 16:47:19.375362928 -0700 @@ -435,6 +435,7 @@ class TestAgent { if (rv != SECSuccess) { return false; } +#ifndef NSS_DISABLE_KYBER // Xyber768 is disabled by policy by default, so if it's requested // we need to update the policy flags as well. for (auto group : groups) { @@ -442,6 +443,7 @@ class TestAgent { NSS_SetAlgorithmPolicy(SEC_OID_XYBER768D00, NSS_USE_ALG_IN_SSL_KX, 0); } } +#endif } return true; diff -up ./gtests/pk11_gtest/pk11_kem_unittest.cc.mlkem_p256 ./gtests/pk11_gtest/pk11_kem_unittest.cc --- ./gtests/pk11_gtest/pk11_kem_unittest.cc.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./gtests/pk11_gtest/pk11_kem_unittest.cc 2025-06-25 16:47:19.375362928 -0700 @@ -140,7 +140,10 @@ TEST_P(Pkcs11KEMTest, KemConsistencyTest } INSTANTIATE_TEST_SUITE_P(Pkcs11KEMTest, Pkcs11KEMTest, - ::testing::Values(CKP_NSS_KYBER_768_ROUND3, - CKP_NSS_ML_KEM_768)); + ::testing::Values( +#ifndef NSS_DISABLE_KYBER + CKP_NSS_KYBER_768_ROUND3, +#endif + CKP_NSS_ML_KEM_768)); } // namespace nss_test diff -up ./gtests/ssl_gtest/Makefile.mlkem_p256 ./gtests/ssl_gtest/Makefile --- ./gtests/ssl_gtest/Makefile.mlkem_p256 2025-06-26 08:11:04.554368549 -0700 +++ ./gtests/ssl_gtest/Makefile 2025-06-26 08:12:08.993988976 -0700 @@ -42,6 +42,13 @@ else SSLKEYLOGFILE_FILES = $(NULL) endif +ifndef NSS_DISABLE_KYBER +XYBER_FILES = tls_xyber_unittest.cc +else +XYBER_FILES = $(NULL) +endif + + ####################################################################### # (5) Execute "global" rules. (OPTIONAL) # ####################################################################### diff -up ./gtests/ssl_gtest/manifest.mn.mlkem_p256 ./gtests/ssl_gtest/manifest.mn --- ./gtests/ssl_gtest/manifest.mn.mlkem_p256 2025-06-25 16:47:19.358362709 -0700 +++ ./gtests/ssl_gtest/manifest.mn 2025-06-25 16:53:27.033093823 -0700 @@ -62,6 +62,6 @@ CPPSRCS = \ tls_psk_unittest.cc \ tls_subcerts_unittest.cc \ - tls_xyber_unittest.cc \ + $(XYBER_FILES) \ $(SSLKEYLOGFILE_FILES) \ $(NULL) diff -up ./gtests/ssl_gtest/ssl_0rtt_unittest.cc.mlkem_p256 ./gtests/ssl_gtest/ssl_0rtt_unittest.cc --- ./gtests/ssl_gtest/ssl_0rtt_unittest.cc.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./gtests/ssl_gtest/ssl_0rtt_unittest.cc 2025-06-25 16:47:19.375362928 -0700 @@ -197,8 +197,9 @@ TEST_P(TlsZeroRttReplayTest, ZeroRttRepl TEST_P(TlsConnectTls13, ZeroRttOptionsSetLate) { ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); Connect(); + client_->ConfigNamedGroups(kNonPQDHEGroups); SendReceive(); // Need to read so that we absorb the session ticket. - CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign); + CheckKeys(); Reset(); StartConnect(); // Now turn on 0-RTT but too late for the ticket. diff -up ./gtests/ssl_gtest/ssl_auth_unittest.cc.mlkem_p256 ./gtests/ssl_gtest/ssl_auth_unittest.cc --- ./gtests/ssl_gtest/ssl_auth_unittest.cc.mlkem_p256 2025-06-25 16:47:19.368362838 -0700 +++ ./gtests/ssl_gtest/ssl_auth_unittest.cc 2025-06-25 16:47:19.375362928 -0700 @@ -54,6 +54,7 @@ TEST_P(TlsConnectTls12Plus, ServerAuthRs PR_ARRAY_SIZE(kSignatureSchemePss)); server_->SetSignatureSchemes(kSignatureSchemePss, PR_ARRAY_SIZE(kSignatureSchemePss)); + client_->ConfigNamedGroups(kNonPQDHEGroups); Connect(); CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_pss, ssl_sig_rsa_pss_pss_sha256); @@ -84,6 +85,7 @@ TEST_P(TlsConnectTls12Plus, ServerAuthRs PR_ARRAY_SIZE(kSignatureSchemePss)); server_->SetSignatureSchemes(kSignatureSchemePss, PR_ARRAY_SIZE(kSignatureSchemePss)); + client_->ConfigNamedGroups(kNonPQDHEGroups); Connect(); CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_pss, ssl_sig_rsa_pss_pss_sha256); @@ -947,7 +949,7 @@ TEST_P(TlsConnectClientAuth, ClientAuthE client_->SetupClientAuth(std::get<2>(GetParam()), true); server_->RequestClientAuth(true); Connect(); - CheckKeys(ssl_kea_ecdh, ssl_auth_ecdsa); + CheckKeys(ssl_auth_ecdsa); } TEST_P(TlsConnectClientAuth, ClientAuthWithEch) { @@ -960,7 +962,7 @@ TEST_P(TlsConnectClientAuth, ClientAuthW client_->SetupClientAuth(std::get<2>(GetParam()), true); server_->RequestClientAuth(true); Connect(); - CheckKeys(ssl_kea_ecdh, ssl_auth_ecdsa); + CheckKeys(ssl_auth_ecdsa); } TEST_P(TlsConnectClientAuth, ClientAuthBigRsa) { @@ -1304,14 +1306,14 @@ static const SSLSignatureScheme kSignatu static const SSLSignatureScheme kSignatureSchemeRsaSha256[] = { ssl_sig_rsa_pkcs1_sha256}; -static SSLNamedGroup NamedGroupForEcdsa384(uint16_t version) { +static SSLNamedGroup NamedGroupForEcdsa384(const TlsConnectTestBase *ctbase) { // NSS tries to match the group size to the symmetric cipher. In TLS 1.1 and // 1.0, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA is the highest priority suite, so // we use P-384. With TLS 1.2 on we pick AES-128 GCM so use x25519. - if (version <= SSL_LIBRARY_VERSION_TLS_1_1) { + if (ctbase->GetVersion() <= SSL_LIBRARY_VERSION_TLS_1_1) { return ssl_grp_ec_secp384r1; } - return ssl_grp_ec_curve25519; + return ctbase->GetDefaultGroupFromKEA(ctbase->GetDefaultKEA()); } // When signature algorithms match up, this should connect successfully; even @@ -1323,7 +1325,7 @@ TEST_P(TlsConnectGeneric, SignatureAlgor server_->SetSignatureSchemes(kSignatureSchemeEcdsaSha384, PR_ARRAY_SIZE(kSignatureSchemeEcdsaSha384)); Connect(); - CheckKeys(ssl_kea_ecdh, NamedGroupForEcdsa384(version_), ssl_auth_ecdsa, + CheckKeys(GetDefaultKEA(), NamedGroupForEcdsa384(this), ssl_auth_ecdsa, ssl_sig_ecdsa_secp384r1_sha384); } @@ -1342,7 +1344,7 @@ TEST_P(TlsConnectGeneric, SignatureAlgor SSL_SignaturePrefSet(client_->ssl_fd(), clientAlgorithms, PR_ARRAY_SIZE(clientAlgorithms))); Connect(); - CheckKeys(ssl_kea_ecdh, NamedGroupForEcdsa384(version_), ssl_auth_ecdsa, + CheckKeys(GetDefaultKEA(), NamedGroupForEcdsa384(this), ssl_auth_ecdsa, ssl_sig_ecdsa_secp384r1_sha384); } @@ -1353,7 +1355,7 @@ TEST_P(TlsConnectGeneric, SignatureAlgor server_->SetSignatureSchemes(kSignatureSchemeEcdsaSha384, PR_ARRAY_SIZE(kSignatureSchemeEcdsaSha384)); Connect(); - CheckKeys(ssl_kea_ecdh, NamedGroupForEcdsa384(version_), ssl_auth_ecdsa, + CheckKeys(GetDefaultKEA(), NamedGroupForEcdsa384(this), ssl_auth_ecdsa, ssl_sig_ecdsa_secp384r1_sha384); } @@ -1445,6 +1447,7 @@ TEST_P(TlsConnectTls12, SignatureAlgorit TEST_P(TlsConnectTls13, UnsupportedSignatureSchemeAlert) { EnsureTlsSetup(); + client_->ConfigNamedGroups(kNonPQDHEGroups); auto filter = MakeTlsFilter(server_, ssl_sig_none); filter->EnableDecryption(); @@ -1456,6 +1459,8 @@ TEST_P(TlsConnectTls13, UnsupportedSigna TEST_P(TlsConnectTls13, InconsistentSignatureSchemeAlert) { EnsureTlsSetup(); + client_->ConfigNamedGroups(kNonPQDHEGroups); + server_->ConfigNamedGroups(kNonPQDHEGroups); // This won't work because we use an RSA cert by default. auto filter = MakeTlsFilter( @@ -1603,6 +1608,7 @@ static SECStatus AuthCompleteBlock(TlsAg // processed by the client, SSL_AuthCertificateComplete() is called. TEST_F(TlsConnectDatagram13, AuthCompleteBeforeFinished) { client_->SetAuthCertificateCallback(AuthCompleteBlock); + client_->ConfigNamedGroups(kNonPQDHEGroups); MakeTlsFilter(server_, client_, [this]() { EXPECT_EQ(SECSuccess, SSL_AuthCertificateComplete(client_->ssl_fd(), 0)); }); @@ -2036,8 +2042,7 @@ class TlsSignatureSchemeConfiguration EnsureTlsSetup(); configPeer->SetSignatureSchemes(&signature_scheme_, 1); Connect(); - CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, auth_type_, - signature_scheme_); + CheckKeys(auth_type_, signature_scheme_); } std::string certificate_; @@ -2071,7 +2076,7 @@ TEST_P(TlsSignatureSchemeConfiguration, client_->SetSignatureSchemes(&signature_scheme_, 1); server_->SetSignatureSchemes(&signature_scheme_, 1); Connect(); - CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, auth_type_, signature_scheme_); + CheckKeys(auth_type_, signature_scheme_); } class Tls12CertificateRequestReplacer : public TlsHandshakeFilter { diff -up ./gtests/ssl_gtest/ssl_ciphersuite_unittest.cc.mlkem_p256 ./gtests/ssl_gtest/ssl_ciphersuite_unittest.cc --- ./gtests/ssl_gtest/ssl_ciphersuite_unittest.cc.mlkem_p256 2025-06-25 16:47:19.368362838 -0700 +++ ./gtests/ssl_gtest/ssl_ciphersuite_unittest.cc 2025-06-25 16:47:19.375362928 -0700 @@ -42,6 +42,7 @@ class TlsCipherSuiteTestBase : public Tl EXPECT_EQ(SECSuccess, rv); if (rv == SECSuccess) { std::cerr << "Cipher suite: " << csinfo_.cipherSuiteName << std::endl; + std::cerr << "KEA: " << csinfo_.keaType << std::endl; } auth_type_ = csinfo_.authType; kea_type_ = csinfo_.keaType; diff -up ./gtests/ssl_gtest/ssl_damage_unittest.cc.mlkem_p256 ./gtests/ssl_gtest/ssl_damage_unittest.cc --- ./gtests/ssl_gtest/ssl_damage_unittest.cc.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./gtests/ssl_gtest/ssl_damage_unittest.cc 2025-06-25 16:47:19.375362928 -0700 @@ -60,6 +60,8 @@ TEST_F(TlsConnectTest, DamageSecretHandl TEST_P(TlsConnectGenericPre13, DamageServerSignature) { EnsureTlsSetup(); + server_->ConfigNamedGroups(kNonPQDHEGroups); + client_->ConfigNamedGroups(kNonPQDHEGroups); auto filter = MakeTlsFilter( server_, kTlsHandshakeServerKeyExchange); ExpectAlert(client_, kTlsAlertDecryptError); @@ -70,6 +72,8 @@ TEST_P(TlsConnectGenericPre13, DamageSer TEST_P(TlsConnectTls13, DamageServerSignature) { EnsureTlsSetup(); + server_->ConfigNamedGroups(kNonPQDHEGroups); + client_->ConfigNamedGroups(kNonPQDHEGroups); auto filter = MakeTlsFilter( server_, kTlsHandshakeCertificateVerify); filter->EnableDecryption(); diff -up ./gtests/ssl_gtest/ssl_dhe_unittest.cc.mlkem_p256 ./gtests/ssl_gtest/ssl_dhe_unittest.cc --- ./gtests/ssl_gtest/ssl_dhe_unittest.cc.mlkem_p256 2025-06-25 16:47:19.368362838 -0700 +++ ./gtests/ssl_gtest/ssl_dhe_unittest.cc 2025-06-25 16:47:19.375362928 -0700 @@ -29,7 +29,7 @@ TEST_P(TlsConnectGeneric, ConnectDhe) { TEST_P(TlsConnectTls13, SharesForBothEcdheAndDhe) { EnsureTlsSetup(); - client_->ConfigNamedGroups(kAllDHEGroups); + client_->ConfigNamedGroups(kNonPQDHEGroups); auto groups_capture = std::make_shared(client_, ssl_supported_groups_xtn); @@ -41,7 +41,7 @@ TEST_P(TlsConnectTls13, SharesForBothEcd Connect(); - CheckKeys(); + CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign); bool ec, dh; auto track_group_type = [&ec, &dh](SSLNamedGroup group) { diff -up ./gtests/ssl_gtest/ssl_drop_unittest.cc.mlkem_p256 ./gtests/ssl_gtest/ssl_drop_unittest.cc --- ./gtests/ssl_gtest/ssl_drop_unittest.cc.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./gtests/ssl_gtest/ssl_drop_unittest.cc 2025-06-25 16:47:19.375362928 -0700 @@ -201,6 +201,8 @@ class TlsDropDatagram13 : public TlsConn // ACKs TEST_P(TlsDropDatagram13, DropClientFirstFlightOnce) { client_filters_.drop_->Reset({0}); + client_->ConfigNamedGroups(kNonPQDHEGroups); + server_->ConfigNamedGroups(kNonPQDHEGroups); StartConnect(); client_->Handshake(); server_->Handshake(); @@ -210,6 +212,8 @@ TEST_P(TlsDropDatagram13, DropClientFirs TEST_P(TlsDropDatagram13, DropServerFirstFlightOnce) { server_filters_.drop_->Reset(0xff); + client_->ConfigNamedGroups(kNonPQDHEGroups); + server_->ConfigNamedGroups(kNonPQDHEGroups); StartConnect(); client_->Handshake(); // Send the first flight, all dropped. @@ -224,6 +228,8 @@ TEST_P(TlsDropDatagram13, DropServerFirs // TODO(ekr@rtfm.com): We should generate an empty ACK. TEST_P(TlsDropDatagram13, DropServerFirstRecordOnce) { server_filters_.drop_->Reset({0}); + client_->ConfigNamedGroups(kNonPQDHEGroups); + server_->ConfigNamedGroups(kNonPQDHEGroups); StartConnect(); client_->Handshake(); server_->Handshake(); @@ -236,6 +242,8 @@ TEST_P(TlsDropDatagram13, DropServerFirs // produce an ACK. TEST_P(TlsDropDatagram13, DropServerSecondRecordOnce) { server_filters_.drop_->Reset({1}); + client_->ConfigNamedGroups(kNonPQDHEGroups); + server_->ConfigNamedGroups(kNonPQDHEGroups); StartConnect(); client_->Handshake(); server_->Handshake(); @@ -299,6 +307,8 @@ TEST_P(TlsDropDatagram13, DropClientCert // Shrink the MTU down so that certs get split and drop the first piece. TEST_P(TlsDropDatagram13, DropFirstHalfOfServerCertificate) { server_filters_.drop_->Reset({2}); + client_->ConfigNamedGroups(kNonPQDHEGroups); + server_->ConfigNamedGroups(kNonPQDHEGroups); StartConnect(); ShrinkPostServerHelloMtu(); client_->Handshake(); @@ -326,6 +336,8 @@ TEST_P(TlsDropDatagram13, DropFirstHalfO // Shrink the MTU down so that certs get split and drop the second piece. TEST_P(TlsDropDatagram13, DropSecondHalfOfServerCertificate) { server_filters_.drop_->Reset({3}); + client_->ConfigNamedGroups(kNonPQDHEGroups); + server_->ConfigNamedGroups(kNonPQDHEGroups); StartConnect(); ShrinkPostServerHelloMtu(); client_->Handshake(); @@ -414,6 +426,8 @@ class TlsFragmentationAndRecoveryTest : private: void FirstFlightDropCertificate() { + client_->ConfigNamedGroups(kNonPQDHEGroups); + server_->ConfigNamedGroups(kNonPQDHEGroups); StartConnect(); client_->Handshake(); @@ -561,6 +575,8 @@ TEST_P(TlsDropDatagram13, NoDropsDuringZ TEST_P(TlsDropDatagram13, DropEEDuringZeroRtt) { SetupForZeroRtt(); + client_->ConfigNamedGroups(kNonPQDHEGroups); + server_->ConfigNamedGroups(kNonPQDHEGroups); SetFilters(); std::cerr << "Starting second handshake" << std::endl; client_->Set0RttEnabled(true); @@ -606,6 +622,8 @@ class TlsReorderDatagram13 : public TlsD // of the flight and will still produce an ACK. TEST_P(TlsDropDatagram13, ReorderServerEE) { server_filters_.drop_->Reset({1}); + client_->ConfigNamedGroups(kNonPQDHEGroups); + server_->ConfigNamedGroups(kNonPQDHEGroups); StartConnect(); client_->Handshake(); server_->Handshake(); @@ -684,6 +702,8 @@ TEST_F(TlsConnectDatagram13, SendOutOfOr // Shrink the MTU down so that certs get split and then swap the first and // second pieces of the server certificate. TEST_P(TlsReorderDatagram13, ReorderServerCertificate) { + client_->ConfigNamedGroups(kNonPQDHEGroups); + server_->ConfigNamedGroups(kNonPQDHEGroups); StartConnect(); ShrinkPostServerHelloMtu(); client_->Handshake(); diff -up ./gtests/ssl_gtest/ssl_ecdh_unittest.cc.mlkem_p256 ./gtests/ssl_gtest/ssl_ecdh_unittest.cc --- ./gtests/ssl_gtest/ssl_ecdh_unittest.cc.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./gtests/ssl_gtest/ssl_ecdh_unittest.cc 2025-06-25 16:47:19.376362941 -0700 @@ -490,7 +490,7 @@ TEST_P(TlsKeyExchangeTest13, EqualPriori Connect(); - CheckKeys(); + CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign); const std::vector shares = {ssl_grp_ec_curve25519}; CheckKEXDetails(client_groups, shares); } diff -up ./gtests/ssl_gtest/ssl_exporter_unittest.cc.mlkem_p256 ./gtests/ssl_gtest/ssl_exporter_unittest.cc --- ./gtests/ssl_gtest/ssl_exporter_unittest.cc.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./gtests/ssl_gtest/ssl_exporter_unittest.cc 2025-06-25 16:47:19.376362941 -0700 @@ -82,8 +82,11 @@ TEST_P(TlsConnectGenericPre13, ExporterC 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xFF}; EnsureTlsSetup(); + client_->ConfigNamedGroups(kNonPQDHEGroups); + server_->ConfigNamedGroups(kNonPQDHEGroups); Connect(); - CheckKeys(); + CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign); + static const size_t exporter_len = 10; uint8_t client_value[exporter_len] = {0}; diff -up ./gtests/ssl_gtest/ssl_extension_unittest.cc.mlkem_p256 ./gtests/ssl_gtest/ssl_extension_unittest.cc --- ./gtests/ssl_gtest/ssl_extension_unittest.cc.mlkem_p256 2025-06-25 16:47:19.368362838 -0700 +++ ./gtests/ssl_gtest/ssl_extension_unittest.cc 2025-06-25 16:47:19.376362941 -0700 @@ -1314,6 +1314,9 @@ TEST_P(TlsDisallowedUnadvertisedExtensio TEST_P(TlsConnectStream, IncludePadding) { EnsureTlsSetup(); + client_->ConfigNamedGroups(kNonPQDHEGroups); + server_->ConfigNamedGroups(kNonPQDHEGroups); + SSL_EnableTls13GreaseEch(client_->ssl_fd(), PR_FALSE); // Don't GREASE // This needs to be long enough to push a TLS 1.0 ClientHello over 255, but @@ -1372,7 +1375,7 @@ TEST_F(TlsConnectStreamTls13, ClientHell PR_TRUE) == SECSuccess); Connect(); SendReceive(); - CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none); + CheckKeys(ssl_kea_ecdh_hybrid, ssl_grp_kem_mlkem768x25519, ssl_auth_psk, ssl_sig_none); } /* This test checks that the ClientHello extension order is actually permuted diff -up ./gtests/ssl_gtest/ssl_gtest.gyp.mlkem_p256 ./gtests/ssl_gtest/ssl_gtest.gyp --- ./gtests/ssl_gtest/ssl_gtest.gyp.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./gtests/ssl_gtest/ssl_gtest.gyp 2025-06-25 16:47:19.376362941 -0700 @@ -63,7 +63,6 @@ 'tls_protect.cc', 'tls_psk_unittest.cc', 'tls_subcerts_unittest.cc', - 'tls_xyber_unittest.cc', ], 'dependencies': [ '<(DEPTH)/exports.gyp:nss_exports', diff -up ./gtests/ssl_gtest/ssl_hrr_unittest.cc.mlkem_p256 ./gtests/ssl_gtest/ssl_hrr_unittest.cc --- ./gtests/ssl_gtest/ssl_hrr_unittest.cc.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./gtests/ssl_gtest/ssl_hrr_unittest.cc 2025-06-25 16:47:19.376362941 -0700 @@ -22,6 +22,7 @@ TEST_P(TlsConnectTls13, HelloRetryReques const PRInt32 k0RttDataLen = static_cast(strlen(k0RttData)); SetupForZeroRtt(); // initial handshake as normal + client_->ConfigNamedGroups(kNonPQDHEGroups); static const std::vector groups = {ssl_grp_ec_secp384r1, ssl_grp_ec_secp521r1}; @@ -107,6 +108,7 @@ TEST_P(TlsConnectTls13, SecondClientHell auto orig_client = std::make_shared(client_->name(), TlsAgent::CLIENT, variant_); client_.swap(orig_client); + client_->ConfigNamedGroups(kNonPQDHEGroups); client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_1, SSL_LIBRARY_VERSION_TLS_1_3); client_->ConfigureSessionCache(RESUME_BOTH); @@ -370,6 +372,7 @@ TEST_P(TlsConnectTls13, RetryCallbackRet size_t cb_called = 0; EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(), RetryHello, &cb_called)); + client_->ConfigNamedGroups(kNonPQDHEGroups); // Do the first message exchange. StartConnect(); @@ -417,6 +420,7 @@ TEST_P(TlsConnectTls13, RetryCallbackRet size_t cb_called = 0; EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(), RetryHello, &cb_called)); + client_->ConfigNamedGroups(kNonPQDHEGroups); // Do the first message exchange. StartConnect(); @@ -950,7 +954,7 @@ TEST_P(TlsKeyExchange13, ConnectEcdhePre client_->ConfigNamedGroups(client_groups); server_->ConfigNamedGroups(server_groups); Connect(); - CheckKeys(); + CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519); static const std::vector expectedShares = { ssl_grp_ec_secp384r1}; CheckKEXDetails(client_groups, expectedShares, ssl_grp_ec_curve25519); @@ -997,7 +1001,7 @@ TEST_P(TlsKeyExchange13, ConnectEcdhePre EXPECT_EQ(SECSuccess, SSL_SendAdditionalKeyShares(client_->ssl_fd(), 1)); Connect(); - CheckKeys(); + CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519); CheckKEXDetails(client_groups, client_groups); } @@ -1043,7 +1047,7 @@ TEST_P(TlsKeyExchange13, EXPECT_EQ(2U, cb_called); EXPECT_TRUE(shares_capture2_->captured()) << "client should send shares"; - CheckKeys(); + CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519); static const std::vector client_shares( client_groups.begin(), client_groups.begin() + 2); CheckKEXDetails(client_groups, client_shares, server_groups[0]); diff -up ./gtests/ssl_gtest/ssl_keyupdate_unittest.cc.mlkem_p256 ./gtests/ssl_gtest/ssl_keyupdate_unittest.cc --- ./gtests/ssl_gtest/ssl_keyupdate_unittest.cc.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./gtests/ssl_gtest/ssl_keyupdate_unittest.cc 2025-06-25 16:47:19.376362941 -0700 @@ -1180,6 +1180,8 @@ TEST_F(TlsConnectDatagram13, DTLSKU_Wron TEST_F(TlsConnectDatagram13, DTLSKU_DamagedLength) { EnsureTlsSetup(); + client_->ConfigNamedGroups(kNonPQDHEGroups); + server_->ConfigNamedGroups(kNonPQDHEGroups); // Filter replacing the length value with 0. auto filter = MakeTlsFilter(client_, 3, 0); filter->EnableDecryption(); @@ -1217,6 +1219,8 @@ TEST_F(TlsConnectDatagram13, DTLSKU_Dama TEST_F(TlsConnectDatagram13, DTLSKU_DamagedFragmentLength) { EnsureTlsSetup(); + client_->ConfigNamedGroups(kNonPQDHEGroups); + server_->ConfigNamedGroups(kNonPQDHEGroups); // Filter replacing the fragment length with 1. auto filter = MakeTlsFilter(client_, 10, 1); filter->EnableDecryption(); @@ -1498,4 +1502,4 @@ TEST_F(TlsConnectDatagram13, DTLSKU_TooE client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT); } -} // namespace nss_test \ No newline at end of file +} // namespace nss_test diff -up ./gtests/ssl_gtest/ssl_loopback_unittest.cc.mlkem_p256 ./gtests/ssl_gtest/ssl_loopback_unittest.cc --- ./gtests/ssl_gtest/ssl_loopback_unittest.cc.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./gtests/ssl_gtest/ssl_loopback_unittest.cc 2025-06-25 16:47:19.376362941 -0700 @@ -37,7 +37,7 @@ TEST_P(TlsConnectGeneric, ConnectEcdsa) SetExpectedVersion(std::get<1>(GetParam())); Reset(TlsAgent::kServerEcdsa256); Connect(); - CheckKeys(ssl_kea_ecdh, ssl_auth_ecdsa); + CheckKeys(ssl_auth_ecdsa); } TEST_P(TlsConnectGeneric, CipherSuiteMismatch) { diff -up ./gtests/ssl_gtest/ssl_recordsep_unittest.cc.mlkem_p256 ./gtests/ssl_gtest/ssl_recordsep_unittest.cc --- ./gtests/ssl_gtest/ssl_recordsep_unittest.cc.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./gtests/ssl_gtest/ssl_recordsep_unittest.cc 2025-06-25 16:47:19.376362941 -0700 @@ -344,6 +344,8 @@ static void SendForwardReceive(std::shar } TEST_P(TlsConnectStream, ReplaceRecordLayer) { + client_->ConfigNamedGroups(kNonPQDHEGroups); + server_->ConfigNamedGroups(kNonPQDHEGroups); StartConnect(); client_->SetServerKeyBits(server_->server_key_bits()); @@ -387,7 +389,7 @@ TEST_P(TlsConnectStream, ReplaceRecordLa client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTED); server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTED); } - CheckKeys(); + CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign); // why? // Reading and writing application data should work. SendForwardReceive(client_, client_stage, server_); @@ -445,6 +447,8 @@ static SECStatus AuthCompleteBlock(TlsAg } TEST_P(TlsConnectStream, ReplaceRecordLayerAsyncLateAuth) { + client_->ConfigNamedGroups(kNonPQDHEGroups); + server_->ConfigNamedGroups(kNonPQDHEGroups); StartConnect(); client_->SetServerKeyBits(server_->server_key_bits()); @@ -494,7 +498,7 @@ TEST_P(TlsConnectStream, ReplaceRecordLa client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTED); server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTED); } - CheckKeys(); + CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign); // why? // Reading and writing application data should work. SendForwardReceive(client_, client_stage, server_); diff -up ./gtests/ssl_gtest/ssl_recordsize_unittest.cc.mlkem_p256 ./gtests/ssl_gtest/ssl_recordsize_unittest.cc --- ./gtests/ssl_gtest/ssl_recordsize_unittest.cc.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./gtests/ssl_gtest/ssl_recordsize_unittest.cc 2025-06-25 16:47:19.376362941 -0700 @@ -275,6 +275,7 @@ TEST_F(TlsConnectStreamTls13, ClientHell auto filter = MakeTlsFilter(client_, kTlsHandshakeClientHello); + client_->ConfigNamedGroups(kNonPQDHEGroups); // Add PSK with label long enough to push CH length into [256, 511]. std::vector label(100); EXPECT_EQ(SECSuccess, @@ -723,4 +724,4 @@ TEST_P(TlsConnectGeneric, RecordSizeLimi } } -} // namespace nss_test \ No newline at end of file +} // namespace nss_test diff -up ./gtests/ssl_gtest/ssl_resumption_unittest.cc.mlkem_p256 ./gtests/ssl_gtest/ssl_resumption_unittest.cc --- ./gtests/ssl_gtest/ssl_resumption_unittest.cc.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./gtests/ssl_gtest/ssl_resumption_unittest.cc 2025-06-25 16:47:19.376362941 -0700 @@ -175,11 +175,15 @@ TEST_P(TlsConnectGenericResumption, Conn TEST_P(TlsConnectGenericPre13, ResumeWithHigherVersionTls13) { uint16_t lower_version = version_; ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); + client_->ConfigNamedGroups(kNonPQDHEGroups); + server_->ConfigNamedGroups(kNonPQDHEGroups); Connect(); SendReceive(); CheckKeys(); Reset(); + client_->ConfigNamedGroups(kNonPQDHEGroups); + server_->ConfigNamedGroups(kNonPQDHEGroups); ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); EnsureTlsSetup(); auto psk_ext = std::make_shared( @@ -244,6 +248,8 @@ TEST_P(TlsConnectGenericPre13, ResumeWit // connection. This looks like compatibility mode, we just want to ensure // that we get TLS 1.3 rather than 1.2 (and no resumption). Reset(); + client_->ConfigNamedGroups(kNonPQDHEGroups); + server_->ConfigNamedGroups(kNonPQDHEGroups); auto client_sid = MakeTlsFilter(client_); auto server_sid = MakeTlsFilter(server_); ConfigureSessionCache(RESUME_SESSIONID, RESUME_SESSIONID); @@ -359,12 +365,14 @@ TEST_P(TlsConnectGenericResumption, Conn TEST_P(TlsConnectGeneric, ConnectWithExpiredTicketAtServer) { // This causes a ticket resumption. ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); + client_->ConfigNamedGroups(kNonPQDHEGroups); Connect(); SendReceive(); Reset(); ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); ExpectResumption(RESUME_NONE); + client_->ConfigNamedGroups(kNonPQDHEGroups); SSLExtensionType xtn = (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) ? ssl_tls13_pre_shared_key_xtn @@ -453,7 +461,7 @@ TEST_P(TlsConnectGeneric, ServerSNICertT Connect(); ScopedCERTCertificate cert2(SSL_PeerCertificate(client_->ssl_fd())); ASSERT_NE(nullptr, cert2.get()); - CheckKeys(ssl_kea_ecdh, ssl_auth_ecdsa); + CheckKeys(ssl_auth_ecdsa); EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert)); } @@ -613,7 +621,7 @@ TEST_P(TlsConnectGenericResumption, Resu client_->EnableSingleCipher(ChooseOneCipher(version_)); Connect(); SendReceive(); - CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign); + CheckKeys(ssl_auth_rsa_sign); Reset(); ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); @@ -628,7 +636,7 @@ TEST_P(TlsConnectGenericResumption, Resu auto ticket_capture = MakeTlsFilter(client_, ticket_extension); Connect(); - CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign); + CheckKeys(ssl_auth_rsa_sign); EXPECT_EQ(0U, ticket_capture->extension().len()); } @@ -655,7 +663,7 @@ TEST_P(TlsConnectStream, ResumptionOverr server_->EnableSingleCipher(ChooseOneCipher(version_)); Connect(); SendReceive(); - CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign); + CheckKeys(); Reset(); ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET); @@ -793,8 +801,7 @@ TEST_F(TlsConnectTest, TestTls13Resumpti MakeTlsFilter(client_, ssl_tls13_pre_shared_key_xtn); Connect(); SendReceive(); - CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_sign, - ssl_sig_rsa_pss_rsae_sha256); + CheckKeys(ssl_auth_rsa_sign, ssl_sig_rsa_pss_rsae_sha256); // The filter will go away when we reset, so save the captured extension. DataBuffer initialTicket(c1->extension()); ASSERT_LT(0U, initialTicket.len()); @@ -811,8 +818,7 @@ TEST_F(TlsConnectTest, TestTls13Resumpti ExpectResumption(RESUME_TICKET); Connect(); SendReceive(); - CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_sign, - ssl_sig_rsa_pss_rsae_sha256); + CheckKeys(ssl_auth_rsa_sign, ssl_sig_rsa_pss_rsae_sha256); ASSERT_LT(0U, c2->extension().len()); ScopedCERTCertificate cert2(SSL_PeerCertificate(client_->ssl_fd())); @@ -1089,7 +1095,7 @@ TEST_F(TlsConnectTest, TestTls13Resumpti Handshake(); SendReceive(); - CheckKeys(); + CheckKeys(ssl_kea_ecdh); } TEST_F(TlsConnectTest, TestTls13ResumptionForcedDowngrade) { @@ -1144,15 +1150,15 @@ TEST_P(TlsConnectGenericResumption, ReCo server_->EnableSingleCipher(ChooseOneCipher(version_)); Connect(); SendReceive(); - CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_sign, - ssl_sig_rsa_pss_rsae_sha256); + CheckKeys(ssl_auth_rsa_sign, ssl_sig_rsa_pss_rsae_sha256); // Resume Reset(); ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); ExpectResumption(RESUME_TICKET); Connect(); // Only the client knows this. - CheckKeysResumption(ssl_kea_ecdh, ssl_grp_none, ssl_grp_ec_curve25519, + CheckKeysResumption(GetDefaultKEA(), ssl_grp_none, + GetDefaultGroupFromKEA(GetDefaultKEA()), ssl_auth_rsa_sign, ssl_sig_rsa_pss_rsae_sha256); } @@ -1161,13 +1167,13 @@ TEST_P(TlsConnectGenericPre13, ReConnect server_->EnableSingleCipher(ChooseOneCipher(version_)); Connect(); SendReceive(); - CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_sign, - ssl_sig_rsa_pss_rsae_sha256); + CheckKeys(ssl_auth_rsa_sign, ssl_sig_rsa_pss_rsae_sha256); // Resume Reset(); ExpectResumption(RESUME_SESSIONID); Connect(); - CheckKeysResumption(ssl_kea_ecdh, ssl_grp_none, ssl_grp_ec_curve25519, + CheckKeysResumption(GetDefaultKEA(), ssl_grp_none, + GetDefaultGroupFromKEA(GetDefaultKEA()), ssl_auth_rsa_sign, ssl_sig_rsa_pss_rsae_sha256); } @@ -1176,15 +1182,15 @@ TEST_P(TlsConnectGenericResumption, ReCo server_->EnableSingleCipher(ChooseOneCipher(version_)); Connect(); SendReceive(); - CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_rsa_sign, - ssl_sig_rsa_pss_rsae_sha256); + CheckKeys(ssl_auth_rsa_sign, ssl_sig_rsa_pss_rsae_sha256); // Resume Reset(); ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); ExpectResumption(RESUME_TICKET); Connect(); // Only the client knows this. - CheckKeysResumption(ssl_kea_ecdh, ssl_grp_none, ssl_grp_ec_curve25519, + CheckKeysResumption(GetDefaultKEA(), ssl_grp_none, + GetDefaultGroupFromKEA(GetDefaultKEA()), ssl_auth_rsa_sign, ssl_sig_rsa_pss_rsae_sha256); // Resume connection again Reset(); @@ -1192,7 +1198,8 @@ TEST_P(TlsConnectGenericResumption, ReCo ExpectResumption(RESUME_TICKET, 2); Connect(); // Only the client knows this. - CheckKeysResumption(ssl_kea_ecdh, ssl_grp_none, ssl_grp_ec_curve25519, + CheckKeysResumption(GetDefaultKEA(), ssl_grp_none, + GetDefaultGroupFromKEA(GetDefaultKEA()), ssl_auth_rsa_sign, ssl_sig_rsa_pss_rsae_sha256); } @@ -1219,11 +1226,15 @@ void CheckGetInfoResult(PRTime now, uint // when resuming using an external token. TEST_P(TlsConnectGenericResumptionToken, CheckSessionId) { ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); + client_->ConfigNamedGroups(kNonPQDHEGroups); + server_->ConfigNamedGroups(kNonPQDHEGroups); auto original_sid = MakeTlsFilter(client_); Connect(); SendReceive(); Reset(); + client_->ConfigNamedGroups(kNonPQDHEGroups); + server_->ConfigNamedGroups(kNonPQDHEGroups); ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH); ExpectResumption(RESUME_TICKET); diff -up ./gtests/ssl_gtest/ssl_skip_unittest.cc.mlkem_p256 ./gtests/ssl_gtest/ssl_skip_unittest.cc --- ./gtests/ssl_gtest/ssl_skip_unittest.cc.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./gtests/ssl_gtest/ssl_skip_unittest.cc 2025-06-25 16:47:19.376362941 -0700 @@ -114,6 +114,9 @@ class Tls13SkipTest : public TlsConnectT void SetUp() override { TlsConnectTestBase::SetUp(); EnsureTlsSetup(); + // until we can fix filters to work with MLKEM + client_->ConfigNamedGroups(kNonPQDHEGroups); + server_->ConfigNamedGroups(kNonPQDHEGroups); } void ServerSkipTest(std::shared_ptr filter, int32_t error) { diff -up ./gtests/ssl_gtest/ssl_tls13compat_unittest.cc.mlkem_p256 ./gtests/ssl_gtest/ssl_tls13compat_unittest.cc --- ./gtests/ssl_gtest/ssl_tls13compat_unittest.cc.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./gtests/ssl_gtest/ssl_tls13compat_unittest.cc 2025-06-25 16:47:19.377362953 -0700 @@ -482,6 +482,7 @@ TEST_F(TlsConnectDatagram13, CompatModeD client_->SetOption(SSL_ENABLE_TLS13_COMPAT_MODE, PR_TRUE); auto client_records = MakeTlsFilter(client_); auto server_records = MakeTlsFilter(server_); + client_->ConfigNamedGroups(kNonPQDHEGroups); Connect(); ASSERT_EQ(2U, client_records->count()); // CH, Fin @@ -522,6 +523,7 @@ class AddSessionIdFilter : public TlsHan // mode. It should be ignored instead. TEST_F(TlsConnectDatagram13, CompatModeDtlsServer) { EnsureTlsSetup(); + client_->ConfigNamedGroups(kNonPQDHEGroups); auto client_records = std::make_shared(client_); client_->SetFilter( std::make_shared(ChainedPacketFilterInit( diff -up ./gtests/ssl_gtest/ssl_version_unittest.cc.mlkem_p256 ./gtests/ssl_gtest/ssl_version_unittest.cc --- ./gtests/ssl_gtest/ssl_version_unittest.cc.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./gtests/ssl_gtest/ssl_version_unittest.cc 2025-06-25 16:47:19.377362953 -0700 @@ -370,6 +370,7 @@ TEST_F(DtlsConnectTest, DtlsSupportedVer SSL_LIBRARY_VERSION_TLS_1_3); auto capture = MakeTlsFilter( client_, ssl_tls13_supported_versions_xtn); + client_->ConfigNamedGroups(kNonPQDHEGroups); Connect(); ASSERT_EQ(7U, capture->extension().len()); @@ -393,6 +394,7 @@ TEST_F(DtlsConnectTest, Dtls13VersionWor // Toggle the workaround, then verify both encodings are present. EnsureTlsSetup(); + client_->ConfigNamedGroups(kNonPQDHEGroups); SSL_SetDtls13VersionWorkaround(client_->ssl_fd(), PR_TRUE); SSL_SetDtls13VersionWorkaround(client_->ssl_fd(), PR_FALSE); SSL_SetDtls13VersionWorkaround(client_->ssl_fd(), PR_TRUE); diff -up ./gtests/ssl_gtest/tls_agent.cc.mlkem_p256 ./gtests/ssl_gtest/tls_agent.cc --- ./gtests/ssl_gtest/tls_agent.cc.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./gtests/ssl_gtest/tls_agent.cc 2025-06-25 16:47:19.377362953 -0700 @@ -522,12 +522,25 @@ const std::vector kAllDHE ssl_grp_ec_curve25519, ssl_grp_ec_secp256r1, ssl_grp_ec_secp384r1, ssl_grp_ec_secp521r1, ssl_grp_ffdhe_2048, ssl_grp_ffdhe_3072, ssl_grp_ffdhe_4096, ssl_grp_ffdhe_6144, ssl_grp_ffdhe_8192, - ssl_grp_kem_xyber768d00, ssl_grp_kem_mlkem768x25519, +#ifndef NSS_DISABLE_KYBER + ssl_grp_kem_xyber768d00, +#endif + ssl_grp_kem_mlkem768x25519, ssl_grp_kem_secp256r1mlkem768, +}; + +const std::vector kNonPQDHEGroups = { + ssl_grp_ec_curve25519, ssl_grp_ec_secp256r1, ssl_grp_ec_secp384r1, + ssl_grp_ec_secp521r1, ssl_grp_ffdhe_2048, ssl_grp_ffdhe_3072, + ssl_grp_ffdhe_4096, ssl_grp_ffdhe_6144, ssl_grp_ffdhe_8192, }; const std::vector kECDHEGroups = { ssl_grp_ec_curve25519, ssl_grp_ec_secp256r1, ssl_grp_ec_secp384r1, - ssl_grp_ec_secp521r1, ssl_grp_kem_xyber768d00, ssl_grp_kem_mlkem768x25519, + ssl_grp_ec_secp521r1, +#ifndef NSS_DISABLE_KYBER + ssl_grp_kem_xyber768d00, +#endif + ssl_grp_kem_mlkem768x25519, ssl_grp_kem_secp256r1mlkem768 }; const std::vector kFFDHEGroups = { @@ -537,13 +550,19 @@ const std::vector kFFDHEG // Defined because the big DHE groups are ridiculously slow. const std::vector kFasterDHEGroups = { ssl_grp_ec_curve25519, ssl_grp_ec_secp256r1, ssl_grp_ec_secp384r1, - ssl_grp_ffdhe_2048, ssl_grp_ffdhe_3072, ssl_grp_kem_xyber768d00, - ssl_grp_kem_mlkem768x25519, + ssl_grp_ffdhe_2048, ssl_grp_ffdhe_3072, +#ifndef NSS_DISABLE_KYBER + ssl_grp_kem_xyber768d00, +#endif + ssl_grp_kem_mlkem768x25519, ssl_grp_kem_secp256r1mlkem768, }; const std::vector kEcdhHybridGroups = { +#ifndef NSS_DISABLE_KYBER ssl_grp_kem_xyber768d00, +#endif ssl_grp_kem_mlkem768x25519, + ssl_grp_kem_secp256r1mlkem768, }; void TlsAgent::EnableCiphersByKeyExchange(SSLKEAType kea) { @@ -711,11 +730,14 @@ void TlsAgent::CheckKEA(SSLKEAType kea, if (kea_size == 0) { switch (kea_group) { case ssl_grp_ec_curve25519: +#ifndef NSS_DISABLE_KYBER case ssl_grp_kem_xyber768d00: +#endif case ssl_grp_kem_mlkem768x25519: kea_size = 255; break; case ssl_grp_ec_secp256r1: + case ssl_grp_kem_secp256r1mlkem768: kea_size = 256; break; case ssl_grp_ec_secp384r1: @@ -1344,6 +1366,10 @@ void TlsAgentTestBase::Reset(const std:: if (version_) { agent_->SetVersionRange(version_, version_); } + const std::vector groups = { + ssl_grp_ec_curve25519, ssl_grp_ec_secp256r1, ssl_grp_ec_secp384r1, + ssl_grp_ffdhe_2048}; + agent_->ConfigNamedGroups(groups); agent_->adapter()->SetPeer(sink_adapter_); agent_->StartConnect(); } diff -up ./gtests/ssl_gtest/tls_agent.h.mlkem_p256 ./gtests/ssl_gtest/tls_agent.h --- ./gtests/ssl_gtest/tls_agent.h.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./gtests/ssl_gtest/tls_agent.h 2025-06-25 16:47:19.377362953 -0700 @@ -52,6 +52,7 @@ class TlsCipherSpec; struct TlsRecord; const extern std::vector kAllDHEGroups; +const extern std::vector kNonPQDHEGroups; const extern std::vector kECDHEGroups; const extern std::vector kFFDHEGroups; const extern std::vector kFasterDHEGroups; diff -up ./gtests/ssl_gtest/tls_connect.cc.mlkem_p256 ./gtests/ssl_gtest/tls_connect.cc --- ./gtests/ssl_gtest/tls_connect.cc.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./gtests/ssl_gtest/tls_connect.cc 2025-06-25 16:47:19.377362953 -0700 @@ -505,21 +505,23 @@ void TlsConnectTestBase::CheckEarlyDataL EXPECT_EQ(expected_size, static_cast(preinfo.maxEarlyDataSize)); } -void TlsConnectTestBase::CheckKeys(SSLKEAType kea_type, SSLNamedGroup kea_group, - SSLAuthType auth_type, - SSLSignatureScheme sig_scheme) const { - if (kea_group != ssl_grp_none) { - client_->CheckKEA(kea_type, kea_group); - server_->CheckKEA(kea_type, kea_group); +SSLKEAType TlsConnectTestBase::GetDefaultKEA(void) const { + if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) { + return ssl_kea_ecdh_hybrid; } - server_->CheckAuthType(auth_type, sig_scheme); - client_->CheckAuthType(auth_type, sig_scheme); + return ssl_kea_ecdh; } -void TlsConnectTestBase::CheckKeys(SSLKEAType kea_type, - SSLAuthType auth_type) const { +SSLAuthType TlsConnectTestBase::GetDefaultAuth(void) const { + return ssl_auth_rsa_sign; +} + +SSLNamedGroup TlsConnectTestBase::GetDefaultGroupFromKEA(SSLKEAType kea_type) const { SSLNamedGroup group; switch (kea_type) { + case ssl_kea_ecdh_hybrid: + group = ssl_grp_kem_mlkem768x25519; + break; case ssl_kea_ecdh: group = ssl_grp_ec_curve25519; break; @@ -534,7 +536,10 @@ void TlsConnectTestBase::CheckKeys(SSLKE group = ssl_grp_none; break; } + return group; +} +SSLSignatureScheme TlsConnectTestBase::GetDefaultSchemeFromAuth(SSLAuthType auth_type) const { SSLSignatureScheme scheme; switch (auth_type) { case ssl_auth_rsa_decrypt: @@ -561,11 +566,54 @@ void TlsConnectTestBase::CheckKeys(SSLKE scheme = static_cast(0x0100); break; } + return scheme; +} +void TlsConnectTestBase::CheckKeys(SSLKEAType kea_type, SSLNamedGroup kea_group, + SSLAuthType auth_type, + SSLSignatureScheme sig_scheme) const { + if (kea_group != ssl_grp_none) { + client_->CheckKEA(kea_type, kea_group); + server_->CheckKEA(kea_type, kea_group); + } + server_->CheckAuthType(auth_type, sig_scheme); + client_->CheckAuthType(auth_type, sig_scheme); +} + +void TlsConnectTestBase::CheckKeys(SSLKEAType kea_type, + SSLNamedGroup kea_group) const { + SSLAuthType auth_type = GetDefaultAuth(); + SSLSignatureScheme scheme = GetDefaultSchemeFromAuth(auth_type); + CheckKeys(kea_type, kea_group, auth_type, scheme); +} + +void TlsConnectTestBase::CheckKeys( SSLAuthType auth_type, + SSLSignatureScheme sig_scheme) const { + SSLKEAType kea_type = GetDefaultKEA(); + SSLNamedGroup group = GetDefaultGroupFromKEA(kea_type); + CheckKeys(kea_type, group, auth_type, sig_scheme); +} + +void TlsConnectTestBase::CheckKeys(SSLKEAType kea_type, + SSLAuthType auth_type) const { + SSLNamedGroup group = GetDefaultGroupFromKEA(kea_type); + SSLSignatureScheme scheme = GetDefaultSchemeFromAuth(auth_type); CheckKeys(kea_type, group, auth_type, scheme); } +void TlsConnectTestBase::CheckKeys(SSLKEAType kea_type) const { + SSLAuthType auth_type = GetDefaultAuth(); + CheckKeys(kea_type, auth_type); +} + +void TlsConnectTestBase::CheckKeys(SSLAuthType auth_type) const { + SSLKEAType kea_type = GetDefaultKEA(); + CheckKeys(kea_type, auth_type); +} + void TlsConnectTestBase::CheckKeys() const { - CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign); + SSLKEAType kea_type = GetDefaultKEA(); + SSLAuthType auth_type = GetDefaultAuth(); + CheckKeys(kea_type, auth_type); } void TlsConnectTestBase::CheckKeysResumption(SSLKEAType kea_type, diff -up ./gtests/ssl_gtest/tls_connect.h.mlkem_p256 ./gtests/ssl_gtest/tls_connect.h --- ./gtests/ssl_gtest/tls_connect.h.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./gtests/ssl_gtest/tls_connect.h 2025-06-25 16:47:19.377362953 -0700 @@ -83,11 +83,23 @@ class TlsConnectTestBase : public ::test void ConnectWithCipherSuite(uint16_t cipher_suite); void CheckEarlyDataLimit(const std::shared_ptr& agent, size_t expected_size); + // Get the default KEA for our tls version + SSLKEAType GetDefaultKEA(void) const; + // Get the default auth for our tls version + SSLAuthType GetDefaultAuth(void) const; + // Find the default group for a given KEA + SSLNamedGroup GetDefaultGroupFromKEA(SSLKEAType kea_type) const; + // Find the default scheam for a given auth + SSLSignatureScheme GetDefaultSchemeFromAuth(SSLAuthType auth_type) const; // Check that the keys used in the handshake match expectations. void CheckKeys(SSLKEAType kea_type, SSLNamedGroup kea_group, SSLAuthType auth_type, SSLSignatureScheme sig_scheme) const; - // This version guesses some of the values. + // These version guesses some of the values. + void CheckKeys(SSLKEAType kea_type, SSLNamedGroup kea_group) const; + void CheckKeys(SSLAuthType auth_type, SSLSignatureScheme sig_scheme) const; void CheckKeys(SSLKEAType kea_type, SSLAuthType auth_type) const; + void CheckKeys(SSLKEAType kea_type) const; + void CheckKeys(SSLAuthType auth_type) const; // This version assumes defaults. void CheckKeys() const; // Check that keys on resumed sessions. @@ -103,6 +115,7 @@ class TlsConnectTestBase : public ::test void ConfigureVersion(uint16_t version); void SetExpectedVersion(uint16_t version); + uint16_t GetVersion(void) const { return version_; }; // Expect resumption of a particular type. void ExpectResumption(SessionResumptionMode expected, uint8_t num_resumed = 1); diff -up ./gtests/ssl_gtest/tls_ech_unittest.cc.mlkem_p256 ./gtests/ssl_gtest/tls_ech_unittest.cc --- ./gtests/ssl_gtest/tls_ech_unittest.cc.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./gtests/ssl_gtest/tls_ech_unittest.cc 2025-06-25 16:47:19.377362953 -0700 @@ -1106,7 +1106,7 @@ TEST_F(TlsConnectStreamTls13, EchAcceptW Handshake(); CheckConnected(); SendReceive(); - CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none); + CheckKeys(ssl_auth_psk, ssl_sig_none); // The PSK extension is present in CHOuter. ASSERT_TRUE(filter->captured()); diff -up ./gtests/ssl_gtest/tls_mlkem_unittest.cc.mlkem_p256 ./gtests/ssl_gtest/tls_mlkem_unittest.cc --- ./gtests/ssl_gtest/tls_mlkem_unittest.cc.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./gtests/ssl_gtest/tls_mlkem_unittest.cc 2025-06-25 16:47:19.377362953 -0700 @@ -7,6 +7,7 @@ #include "ssl.h" #include "sslerr.h" #include "sslproto.h" +#include "secmodti.h" /* for SEC_OID_SECP256R1MLKEM768, remove when shipped upstream */ extern "C" { // This is not something that should make you happy. @@ -30,7 +31,16 @@ TEST_P(TlsKeyExchangeTest13, Mlkem768x25 ssl_sig_rsa_pss_rsae_sha256); } -TEST_P(TlsKeyExchangeTest, Tls12ClientMlkem768x25519NotSupported) { +TEST_P(TlsKeyExchangeTest13, Mlkem768Secp256r1Supported) { + EnsureKeyShareSetup(); + ConfigNamedGroups({ssl_grp_kem_secp256r1mlkem768}); + + Connect(); + CheckKeys(ssl_kea_ecdh_hybrid, ssl_grp_kem_secp256r1mlkem768, + ssl_auth_rsa_sign, ssl_sig_rsa_pss_rsae_sha256); +} + +TEST_P(TlsKeyExchangeTest, Tls12ClientMlkem768StartNotSupported) { EnsureKeyShareSetup(); client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2, SSL_LIBRARY_VERSION_TLS_1_2); @@ -47,6 +57,7 @@ TEST_P(TlsKeyExchangeTest, Tls12ClientMl std::vector groups = GetGroupDetails(groups_capture_); for (auto group : groups) { EXPECT_NE(group, ssl_grp_kem_mlkem768x25519); + EXPECT_NE(group, ssl_grp_kem_secp256r1mlkem768); } } @@ -75,6 +86,31 @@ TEST_P(TlsKeyExchangeTest13, Tls12Server ssl_sig_rsa_pss_rsae_sha256); } +TEST_P(TlsKeyExchangeTest13, Tls12ServerMlkem768Secp256r1NotSupported) { + EnsureKeyShareSetup(); + + client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2, + SSL_LIBRARY_VERSION_TLS_1_3); + server_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2, + SSL_LIBRARY_VERSION_TLS_1_2); + + client_->DisableAllCiphers(); + client_->EnableCiphersByKeyExchange(ssl_kea_ecdh); + client_->EnableCiphersByKeyExchange(ssl_kea_ecdh_hybrid); + client_->ConfigNamedGroups( + {ssl_grp_kem_secp256r1mlkem768, ssl_grp_ec_secp256r1}); + EXPECT_EQ(SECSuccess, SSL_SendAdditionalKeyShares(client_->ssl_fd(), 1)); + + server_->EnableCiphersByKeyExchange(ssl_kea_ecdh); + server_->EnableCiphersByKeyExchange(ssl_kea_ecdh_hybrid); + server_->ConfigNamedGroups( + {ssl_grp_kem_secp256r1mlkem768, ssl_grp_ec_secp256r1}); + + Connect(); + CheckKeys(ssl_kea_ecdh, ssl_grp_ec_secp256r1, ssl_auth_rsa_sign, + ssl_sig_rsa_pss_rsae_sha256); +} + TEST_P(TlsKeyExchangeTest13, Mlkem768x25519ClientDisabledByPolicy) { EnsureKeyShareSetup(); client_->SetPolicy(SEC_OID_MLKEM768X25519, 0, NSS_USE_ALG_IN_SSL_KX); @@ -94,12 +130,37 @@ TEST_P(TlsKeyExchangeTest13, Mlkem768x25 {ssl_grp_kem_mlkem768x25519}, ssl_grp_ec_secp256r1); } +TEST_P(TlsKeyExchangeTest13, Mlkem768Secp256r1ClientDisabledByPolicy) { + EnsureKeyShareSetup(); + client_->SetPolicy(SEC_OID_SECP256R1MLKEM768, 0, NSS_USE_ALG_IN_SSL_KX); + ConfigNamedGroups({ssl_grp_kem_secp256r1mlkem768, ssl_grp_ec_secp256r1}); + + Connect(); + CheckKEXDetails({ssl_grp_ec_secp256r1}, {ssl_grp_ec_secp256r1}); +} + +TEST_P(TlsKeyExchangeTest13, Mlkem768Secp256r1ServerDisabledByPolicy) { + EnsureKeyShareSetup(); + server_->SetPolicy(SEC_OID_SECP256R1MLKEM768, 0, NSS_USE_ALG_IN_SSL_KX); + ConfigNamedGroups({ssl_grp_kem_secp256r1mlkem768, ssl_grp_ec_secp256r1}); + + Connect(); + CheckKEXDetails({ssl_grp_kem_secp256r1mlkem768, ssl_grp_ec_secp256r1}, + {ssl_grp_kem_secp256r1mlkem768}, ssl_grp_ec_secp256r1); +} + static void CheckECDHShareReuse( const std::shared_ptr& capture) { EXPECT_TRUE(capture->captured()); const DataBuffer& ext = capture->extension(); - DataBuffer hybrid_share; - DataBuffer x25519_share; + DataBuffer hybrid_share[4]; + DataBuffer ecdh_share[4]; + int hybrid_offset[4]; + SSLNamedGroup hybrid_ec_type[4]; + SSLNamedGroup ec_type[4]; + int ecdh_index[4]; + int nextHybrid = 0; + int nextECDH = 0; size_t offset = 0; uint32_t ext_len; @@ -112,11 +173,23 @@ static void CheckECDHShareReuse( ext.Read(offset, 2, &named_group); ext.Read(offset + 2, 2, &named_group_len); while (offset < ext.len()) { - if (named_group == ssl_grp_kem_mlkem768x25519) { - hybrid_share = DataBuffer(ext.data() + offset + 2 + 2, named_group_len); - } - if (named_group == ssl_grp_ec_curve25519) { - x25519_share = DataBuffer(ext.data() + offset + 2 + 2, named_group_len); + switch (named_group) { + case ssl_grp_kem_mlkem768x25519: + hybrid_share[nextHybrid] = DataBuffer(ext.data() + offset + 2 + 2, named_group_len); + hybrid_offset[nextHybrid] = KYBER768_PUBLIC_KEY_BYTES; + hybrid_ec_type[nextHybrid] = ssl_grp_ec_curve25519; + nextHybrid++; + break; + case ssl_grp_kem_secp256r1mlkem768: + hybrid_share[nextHybrid] = DataBuffer(ext.data() + offset + 2 + 2, named_group_len); + hybrid_offset[nextHybrid] = 0; + hybrid_ec_type[nextHybrid] = ssl_grp_ec_secp256r1; + nextHybrid++; + case ssl_grp_ec_curve25519: + case ssl_grp_ec_secp256r1: + ecdh_share[nextECDH] = DataBuffer(ext.data() + offset + 2 + 2, named_group_len); + ec_type[nextECDH] = (SSLNamedGroup) named_group; + nextECDH++; } offset += 2 + 2 + named_group_len; ext.Read(offset, 2, &named_group); @@ -124,11 +197,29 @@ static void CheckECDHShareReuse( } EXPECT_EQ(offset, ext.len()); - ASSERT_TRUE(hybrid_share.data()); - ASSERT_TRUE(x25519_share.data()); - ASSERT_GT(hybrid_share.len(), x25519_share.len()); - EXPECT_EQ(0, memcmp(hybrid_share.data() + KYBER768_PUBLIC_KEY_BYTES, - x25519_share.data(), x25519_share.len())); + ASSERT_TRUE(nextECDH > 0); + ASSERT_TRUE(nextHybrid > 0); + /* setup the hybrid ecdh indeces */ + for (int i=0; i < nextHybrid; i++) { + ecdh_index[i] = -1; + for (int j=0; j < nextECDH; j++) { + if (hybrid_ec_type[i] == ec_type[j]) { + ecdh_index[i] = j; + break; + } + } + ASSERT_TRUE(ecdh_index[i] != -1); + } + for (int i=0; i < nextECDH; i++) { + ASSERT_TRUE(ecdh_share[i].data()); + } + for (int i=0; i < nextHybrid; i++) { + int j = ecdh_index[i]; + ASSERT_TRUE(hybrid_share[i].data()); + ASSERT_GT(hybrid_share[i].len(), ecdh_share[j].len()); + EXPECT_EQ(0, memcmp(hybrid_share[i].data() + hybrid_offset[i], + ecdh_share[j].data(), ecdh_share[j].len())); + } } TEST_P(TlsKeyExchangeTest13, Mlkem768x25519ShareReuseFirst) { @@ -155,6 +246,30 @@ TEST_P(TlsKeyExchangeTest13, Mlkem768x25 CheckECDHShareReuse(shares_capture_); } +TEST_P(TlsKeyExchangeTest13, Mlkem768Secp256r1ShareReuseFirst) { + EnsureKeyShareSetup(); + ConfigNamedGroups({ssl_grp_kem_secp256r1mlkem768, ssl_grp_ec_secp256r1}); + EXPECT_EQ(SECSuccess, SSL_SendAdditionalKeyShares(client_->ssl_fd(), 1)); + + Connect(); + + CheckKEXDetails({ssl_grp_kem_secp256r1mlkem768, ssl_grp_ec_secp256r1}, + {ssl_grp_kem_secp256r1mlkem768, ssl_grp_ec_secp256r1}); + CheckECDHShareReuse(shares_capture_); +} + +TEST_P(TlsKeyExchangeTest13, Mlkem768Secp256r1ShareReuseSecond) { + EnsureKeyShareSetup(); + ConfigNamedGroups({ssl_grp_ec_secp256r1, ssl_grp_kem_secp256r1mlkem768}); + EXPECT_EQ(SECSuccess, SSL_SendAdditionalKeyShares(client_->ssl_fd(), 1)); + + Connect(); + + CheckKEXDetails({ssl_grp_ec_secp256r1, ssl_grp_kem_secp256r1mlkem768}, + {ssl_grp_ec_secp256r1, ssl_grp_kem_secp256r1mlkem768}); + CheckECDHShareReuse(shares_capture_); +} + class Mlkem768x25519ShareDamager : public TlsExtensionFilter { public: typedef enum { diff -up ./gtests/ssl_gtest/tls_psk_unittest.cc.mlkem_p256 ./gtests/ssl_gtest/tls_psk_unittest.cc --- ./gtests/ssl_gtest/tls_psk_unittest.cc.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./gtests/ssl_gtest/tls_psk_unittest.cc 2025-06-25 16:47:19.377362953 -0700 @@ -68,7 +68,7 @@ TEST_P(Tls13PskTest, NormalExternal) { AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_); Connect(); SendReceive(); - CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none); + CheckKeys(ssl_kea_ecdh_hybrid, ssl_grp_kem_mlkem768x25519, ssl_auth_psk, ssl_sig_none); client_->RemovePsk(kPskDummyLabel_); server_->RemovePsk(kPskDummyLabel_); @@ -91,7 +91,7 @@ TEST_P(Tls13PskTest, KeyTooLarge) { AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_); Connect(); SendReceive(); - CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none); + CheckKeys(ssl_kea_ecdh_hybrid, ssl_grp_kem_mlkem768x25519, ssl_auth_psk, ssl_sig_none); } // Attempt to use a PSK with the wrong PRF hash. @@ -117,7 +117,7 @@ TEST_P(Tls13PskTest, LabelMismatch) { client_->AddPsk(scoped_psk_, std::string("foo"), kPskHash_); server_->AddPsk(scoped_psk_, std::string("bar"), kPskHash_); Connect(); - CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign); + CheckKeys(); } SSLHelloRetryRequestAction RetryFirstHello( @@ -148,7 +148,7 @@ TEST_P(Tls13PskTest, ResPskRetryStateles Handshake(); CheckConnected(); EXPECT_EQ(2U, cb_called); - CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign); + CheckKeys(); SendReceive(); } @@ -174,7 +174,7 @@ TEST_P(Tls13PskTest, ExtPskRetryStateles Handshake(); CheckConnected(); SendReceive(); - CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none); + CheckKeys(ssl_kea_ecdh_hybrid, ssl_grp_kem_mlkem768x25519, ssl_auth_psk, ssl_sig_none); } // Server not configured with PSK and sends a certificate instead of @@ -182,7 +182,7 @@ TEST_P(Tls13PskTest, ExtPskRetryStateles TEST_P(Tls13PskTest, ClientOnly) { client_->AddPsk(scoped_psk_, kPskDummyLabel_, kPskHash_); Connect(); - CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign); + CheckKeys(); } // Set a PSK, remove psk_key_exchange_modes. @@ -247,7 +247,7 @@ TEST_P(Tls13PskTest, PreferEpsk) { Handshake(); CheckConnected(); SendReceive(); - CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none); + CheckKeys(ssl_kea_ecdh_hybrid, ssl_grp_kem_mlkem768x25519, ssl_auth_psk, ssl_sig_none); } // Enable resumption, but connect (initially) with an EPSK. @@ -260,7 +260,7 @@ TEST_P(Tls13PskTest, SuppressNewSessionT nst_capture->EnableDecryption(); Connect(); SendReceive(); - CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none); + CheckKeys(ssl_kea_ecdh_hybrid, ssl_grp_kem_mlkem768x25519, ssl_auth_psk, ssl_sig_none); EXPECT_EQ(SECFailure, SSL_SendSessionTicket(server_->ssl_fd(), nullptr, 0)); EXPECT_EQ(0U, nst_capture->buffer().len()); if (variant_ == ssl_variant_stream) { @@ -275,7 +275,7 @@ TEST_P(Tls13PskTest, SuppressNewSessionT ExpectResumption(RESUME_NONE); Connect(); SendReceive(); - CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none); + CheckKeys(ssl_kea_ecdh_hybrid, ssl_grp_kem_mlkem768x25519, ssl_auth_psk, ssl_sig_none); } TEST_P(Tls13PskTest, BadConfigValues) { @@ -314,7 +314,7 @@ TEST_P(Tls13PskTest, FallbackUnsupported client_->EnableSingleCipher(TLS_AES_128_GCM_SHA256); Connect(); SendReceive(); - CheckKeys(ssl_kea_ecdh, ssl_auth_rsa_sign); + CheckKeys(); } // That fallback should not occur if there is no cipher overlap. @@ -342,7 +342,7 @@ TEST_P(Tls13PskTest, SuppressHandshakeCe Connect(); SendReceive(); - CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none); + CheckKeys(ssl_kea_ecdh_hybrid, ssl_grp_kem_mlkem768x25519, ssl_auth_psk, ssl_sig_none); EXPECT_EQ(0U, cr_cert_capture->buffer().len()); } @@ -422,7 +422,7 @@ TEST_P(Tls13PskTestWithCiphers, 0RttCiph ExpectEarlyDataAccepted(true); CheckConnected(); SendReceive(); - CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none); + CheckKeys(ssl_kea_ecdh_hybrid, ssl_grp_kem_mlkem768x25519, ssl_auth_psk, ssl_sig_none); } TEST_P(Tls13PskTestWithCiphers, 0RttMaxEarlyData) { diff -up ./gtests/ssl_gtest/tls_subcerts_unittest.cc.mlkem_p256 ./gtests/ssl_gtest/tls_subcerts_unittest.cc --- ./gtests/ssl_gtest/tls_subcerts_unittest.cc.mlkem_p256 2025-06-25 16:47:19.357362696 -0700 +++ ./gtests/ssl_gtest/tls_subcerts_unittest.cc 2025-06-25 16:47:19.378362966 -0700 @@ -449,6 +449,7 @@ class ReplaceDCSigScheme : public TlsHan // Aborted because of incorrect DC signature algorithm indication. TEST_P(TlsConnectTls13, DCAbortBadExpectedCertVerifyAlg) { Reset(kEcdsaDelegatorId); + client_->ConfigNamedGroups(kNonPQDHEGroups); client_->EnableDelegatedCredentials(); server_->AddDelegatedCredential(TlsAgent::kServerEcdsa256, ssl_sig_ecdsa_secp256r1_sha256, kDCValidFor, @@ -569,6 +570,7 @@ TEST_P(TlsConnectTls13, DCConnectClientN // Connected without DC because server doesn't support TLS 1.3. TEST_P(TlsConnectTls13, DCConnectServerNoTls13) { Reset(kEcdsaDelegatorId); + client_->ConfigNamedGroups(kNonPQDHEGroups); client_->EnableDelegatedCredentials(); server_->AddDelegatedCredential(kDCId, kDCScheme, kDCValidFor, now()); @@ -613,6 +615,7 @@ TEST_P(TlsConnectTls13, DCConnectExpecte TEST_P(TlsConnectTls13, DCCheckPreliminaryInfo) { Reset(kEcdsaDelegatorId); EnsureTlsSetup(); + client_->ConfigNamedGroups(kNonPQDHEGroups); client_->EnableDelegatedCredentials(); server_->AddDelegatedCredential(TlsAgent::kServerEcdsa256, ssl_sig_ecdsa_secp256r1_sha256, kDCValidFor, @@ -638,6 +641,7 @@ TEST_P(TlsConnectTls13, DCCheckPrelimina TEST_P(TlsConnectTls13, DCCheckPreliminaryInfoNoDC) { Reset(kEcdsaDelegatorId); EnsureTlsSetup(); + client_->ConfigNamedGroups(kNonPQDHEGroups); client_->EnableDelegatedCredentials(); auto filter = MakeTlsFilter(server_); filter->SetHandshakeTypes( diff -up ./lib/pk11wrap/pk11akey.c.mlkem_p256 ./lib/pk11wrap/pk11akey.c --- ./lib/pk11wrap/pk11akey.c.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./lib/pk11wrap/pk11akey.c 2025-06-25 16:47:19.378362966 -0700 @@ -249,24 +249,31 @@ PK11_ImportPublicKey(PK11SlotInfo *slot, } break; case kyberKey: + /*fprintf(stderr, "PK11_ImportPublic key kyber, params=%d\n", + pubKey->u.kyber.params);*/ switch (pubKey->u.kyber.params) { +#ifndef NSS_DISABLE_KYBER case params_kyber768_round3: case params_kyber768_round3_test_mode: keyType = CKK_NSS_KYBER; kemParams = CKP_NSS_KYBER_768_ROUND3; break; +#endif case params_ml_kem768: case params_ml_kem768_test_mode: - keyType = CKK_NSS_ML_KEM; - kemParams = CKP_NSS_ML_KEM_768; + keyType = CKK_ML_KEM; + kemParams = CKP_ML_KEM_768; break; default: kemParams = CKP_INVALID_ID; break; } - PK11_SETATTRS(attrs, CKA_NSS_PARAMETER_SET, + /*fprintf(stderr, "PK11_ImportPublic KEY_TYPE=0x%08lx, kem_params=0x%08lxd\n", + keyType, kemParams); */ + + PK11_SETATTRS(attrs, CKA_PARAMETER_SET, &kemParams, - sizeof(CK_NSS_KEM_PARAMETER_SET_TYPE)); + sizeof(CK_ML_KEM_PARAMETER_SET_TYPE)); attrs++; PK11_SETATTRS(attrs, CKA_VALUE, pubKey->u.kyber.publicValue.data, pubKey->u.kyber.publicValue.len); @@ -298,6 +305,7 @@ PK11_ImportPublicKey(PK11SlotInfo *slot, SECITEM_FreeItem(pubValue, PR_TRUE); } if (rv != SECSuccess) { + /* fprintf(stderr, "PK11_CreatNewObject failed %d\n", PORT_GetError()); */ return CK_INVALID_HANDLE; } } @@ -684,8 +692,11 @@ PK11_ExtractPublicKey(PK11SlotInfo *slot case CKK_EC_EDWARDS: keyType = edKey; break; +#ifndef NSS_DISABLE_KYBER case CKK_NSS_KYBER: +#endif case CKK_NSS_ML_KEM: + case CKK_ML_KEM: keyType = kyberKey; break; default: @@ -852,35 +863,54 @@ PK11_ExtractPublicKey(PK11SlotInfo *slot PK11_SETATTRS(attrs, CKA_VALUE, NULL, 0); attrs++; kemParams = attrs; - PK11_SETATTRS(attrs, CKA_NSS_PARAMETER_SET, NULL, 0); + PK11_SETATTRS(attrs, CKA_PARAMETER_SET, NULL, 0); attrs++; templateCount = attrs - template; PR_ASSERT(templateCount <= sizeof(template) / sizeof(CK_ATTRIBUTE)); crv = PK11_GetAttributes(arena, slot, id, template, templateCount); - if (crv != CKR_OK) - break; - + if (crv != CKR_OK) { + /* try to fetch with the vendor specific + * CKA_NSS_PARAMETER_SET */ + kemParams->type = CKA_NSS_PARAMETER_SET; + crv = PK11_GetAttributes(arena, slot, id, template, + templateCount); + if (crv != CKR_OK) { + break; + } + } if (keyClass != CKO_PUBLIC_KEY) { crv = CKR_OBJECT_HANDLE_INVALID; break; } - - if (pk11KeyType != CKK_NSS_KYBER && pk11KeyType != CKK_NSS_ML_KEM) { - crv = CKR_OBJECT_HANDLE_INVALID; + switch (pk11KeyType) { +#ifndef NSS_DISABLE_KYBER + case CKK_NSS_KYBER: +#endif + case CKK_NSS_ML_KEM: + case CKK_ML_KEM: + break; + default: + crv = CKR_OBJECT_HANDLE_INVALID; + break; + } + if (crv != CKR_OK) { break; } - if (kemParams->ulValueLen != sizeof(CK_NSS_KEM_PARAMETER_SET_TYPE)) { + if (kemParams->ulValueLen != sizeof(CK_ML_KEM_PARAMETER_SET_TYPE)) { crv = CKR_OBJECT_HANDLE_INVALID; break; } - CK_NSS_KEM_PARAMETER_SET_TYPE *pPK11Params = kemParams->pValue; + CK_ML_KEM_PARAMETER_SET_TYPE *pPK11Params = kemParams->pValue; switch (*pPK11Params) { +#ifdef NSS_DISABLE_KYBER case CKP_NSS_KYBER_768_ROUND3: pubKey->u.kyber.params = params_kyber768_round3; break; +#endif case CKP_NSS_ML_KEM_768: + case CKP_ML_KEM_768: pubKey->u.kyber.params = params_ml_kem768; break; default: @@ -948,8 +978,11 @@ PK11_MakePrivKey(PK11SlotInfo *slot, Key case CKK_EC_EDWARDS: keyType = edKey; break; +#ifndef NSS_DISABLE_KYBER case CKK_NSS_KYBER: +#endif case CKK_NSS_ML_KEM: + case CKK_ML_KEM: keyType = kyberKey; break; default: @@ -1293,6 +1326,7 @@ PK11_GenerateKeyPairWithOpFlags(PK11Slot { CKA_DECRYPT, NULL, 0 }, { CKA_EXTRACTABLE, NULL, 0 }, { CKA_MODIFIABLE, NULL, 0 }, + { CKA_DECAPSULATE, NULL, 0 }, }; CK_ATTRIBUTE rsaPubTemplate[] = { { CKA_MODULUS_BITS, NULL, 0 }, @@ -1304,6 +1338,7 @@ PK11_GenerateKeyPairWithOpFlags(PK11Slot { CKA_VERIFY_RECOVER, NULL, 0 }, { CKA_ENCRYPT, NULL, 0 }, { CKA_MODIFIABLE, NULL, 0 }, + { CKA_ENCAPSULATE, NULL, 0 }, }; CK_ATTRIBUTE dsaPubTemplate[] = { { CKA_PRIME, NULL, 0 }, @@ -1316,6 +1351,7 @@ PK11_GenerateKeyPairWithOpFlags(PK11Slot { CKA_VERIFY_RECOVER, NULL, 0 }, { CKA_ENCRYPT, NULL, 0 }, { CKA_MODIFIABLE, NULL, 0 }, + { CKA_ENCAPSULATE, NULL, 0 }, }; CK_ATTRIBUTE dhPubTemplate[] = { { CKA_PRIME, NULL, 0 }, @@ -1327,6 +1363,7 @@ PK11_GenerateKeyPairWithOpFlags(PK11Slot { CKA_VERIFY_RECOVER, NULL, 0 }, { CKA_ENCRYPT, NULL, 0 }, { CKA_MODIFIABLE, NULL, 0 }, + { CKA_ENCAPSULATE, NULL, 0 }, }; CK_ATTRIBUTE ecPubTemplate[] = { { CKA_EC_PARAMS, NULL, 0 }, @@ -1337,6 +1374,7 @@ PK11_GenerateKeyPairWithOpFlags(PK11Slot { CKA_VERIFY_RECOVER, NULL, 0 }, { CKA_ENCRYPT, NULL, 0 }, { CKA_MODIFIABLE, NULL, 0 }, + { CKA_ENCAPSULATE, NULL, 0 }, }; SECKEYECParams *ecParams; @@ -1349,6 +1387,7 @@ PK11_GenerateKeyPairWithOpFlags(PK11Slot { CKA_VERIFY_RECOVER, NULL, 0 }, { CKA_ENCRYPT, NULL, 0 }, { CKA_MODIFIABLE, NULL, 0 }, + { CKA_ENCAPSULATE, NULL, 0 }, }; /*CK_ULONG key_size = 0;*/ @@ -1557,18 +1596,17 @@ PK11_GenerateKeyPairWithOpFlags(PK11Slot test_mech2.mechanism = CKM_ECDSA; } break; +#ifndef NSS_DISABLE_KYBER case CKM_NSS_KYBER_KEY_PAIR_GEN: - kemParams = (CK_NSS_KEM_PARAMETER_SET_TYPE *)param; - attrs = kyberPubTemplate; - PK11_SETATTRS(attrs, CKA_NSS_PARAMETER_SET, - kemParams, - sizeof(CK_NSS_KEM_PARAMETER_SET_TYPE)); - attrs++; - pubTemplate = kyberPubTemplate; - keyType = kyberKey; test_mech.mechanism = CKM_NSS_KYBER; - break; + goto ml_kem_gen; +#endif case CKM_NSS_ML_KEM_KEY_PAIR_GEN: + test_mech.mechanism = CKM_NSS_ML_KEM; + goto ml_kem_gen; + case CKM_ML_KEM_KEY_PAIR_GEN: + test_mech.mechanism = CKM_ML_KEM; +ml_kem_gen: kemParams = (CK_NSS_KEM_PARAMETER_SET_TYPE *)param; attrs = kyberPubTemplate; PK11_SETATTRS(attrs, CKA_NSS_PARAMETER_SET, @@ -1577,7 +1615,6 @@ PK11_GenerateKeyPairWithOpFlags(PK11Slot attrs++; pubTemplate = kyberPubTemplate; keyType = kyberKey; - test_mech.mechanism = CKM_NSS_ML_KEM; break; case CKM_EC_MONTGOMERY_KEY_PAIR_GEN: ecParams = (SECKEYECParams *)param; @@ -1658,7 +1695,13 @@ PK11_GenerateKeyPairWithOpFlags(PK11Slot case CKM_EDDSA: mechanism_info.flags = CKF_SIGN | CKF_VERIFY; break; - +#ifndef NSS_DISABLE_KYBER + case CKM_NSS_KYBER: +#endif + case CKM_NSS_ML_KEM: + case CKM_ML_KEM: + mechanism_info.flags = CKF_ENCAPSULATE|CKF_DECAPSULATE; + break; default: break; } @@ -1688,6 +1731,12 @@ PK11_GenerateKeyPairWithOpFlags(PK11Slot mechanism_info.flags & CKF_ENCRYPT ? &cktrue : &ckfalse, sizeof(CK_BBOOL)); attrs++; + /* only set encapsulate if it's requested and shows up in the mechanism + * list, that way we don't confuse pre-3.2 PKCS #11 modules */ + if (mechanism_info.flags & CKF_ENCAPSULATE) { + PK11_SETATTRS(attrs, CKA_ENCAPSULATE, &cktrue, sizeof(CK_BBOOL)); + attrs++; + } /* set the private key attributes */ PK11_SETATTRS(privattrs, CKA_DERIVE, mechanism_info.flags & CKF_DERIVE ? &cktrue : &ckfalse, @@ -1705,6 +1754,10 @@ PK11_GenerateKeyPairWithOpFlags(PK11Slot mechanism_info.flags & CKF_DECRYPT ? &cktrue : &ckfalse, sizeof(CK_BBOOL)); privattrs++; + if (mechanism_info.flags & CKF_DECAPSULATE) { + PK11_SETATTRS(privattrs, CKA_DECAPSULATE, &cktrue, sizeof(CK_BBOOL)); + privattrs++; + } if (token) { session_handle = PK11_GetRWSession(slot); diff -up ./lib/pk11wrap/pk11pars.c.mlkem_p256 ./lib/pk11wrap/pk11pars.c --- ./lib/pk11wrap/pk11pars.c.mlkem_p256 2025-06-25 16:47:19.354362658 -0700 +++ ./lib/pk11wrap/pk11pars.c 2025-06-25 16:47:19.378362966 -0700 @@ -245,8 +245,12 @@ static const oidValDef curveOptList[] = NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("CURVE25519"), SEC_OID_CURVE25519, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, - { CIPHER_NAME("XYBER768D00"), SEC_OID_XYBER768D00, 0 }, - { CIPHER_NAME("MLKEM768X25519"), SEC_OID_MLKEM768X25519, 0 }, + { CIPHER_NAME("XYBER768D00"), SEC_OID_XYBER768D00, + NSS_USE_ALG_IN_SSL_KX }, + { CIPHER_NAME("MLKEM768X25519"), SEC_OID_MLKEM768X25519, + NSS_USE_ALG_IN_SSL_KX }, + { CIPHER_NAME("MLKEM768SECP256R1"), SEC_OID_SECP256R1MLKEM768, + NSS_USE_ALG_IN_SSL_KX }, /* ANSI X9.62 named elliptic curves (characteristic two field) */ { CIPHER_NAME("C2PNB163V1"), SEC_OID_ANSIX962_EC_C2PNB163V1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, diff -up ./lib/pk11wrap/pk11skey.c.mlkem_p256 ./lib/pk11wrap/pk11skey.c --- ./lib/pk11wrap/pk11skey.c.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./lib/pk11wrap/pk11skey.c 2025-06-25 16:47:19.378362966 -0700 @@ -3107,6 +3107,30 @@ pk11_KEMCiphertextLength(SECKEYPublicKey } } +/* only one of privKey and pubKey is set. */ +static SECStatus +pk11_GetKEMMechanism(KeyType keyType, CK_MECHANISM_PTR mechp, + SECKEYPrivateKey *privKey, SECKEYPublicKey *pubKey) +{ + switch (keyType) { + case kyberKey: + /* we don't need to set the parameter set for the official + * mlkem interface, it can it it from the key. If we have + * kyber support, this still works because CKM_ML_KEM + * will see the CP_NSS_KYBER_786_ROUND3 parameter and use + * kyber.*/ + mechp->mechanism = CKM_ML_KEM; + mechp->pParameter = NULL; + mechp->ulParameterLen = 0; + break; + /* add other kems here */ + default: + PORT_SetError(SEC_ERROR_INVALID_KEY); + return SECFailure; + } + return SECSuccess; +} + SECStatus PK11_Encapsulate(SECKEYPublicKey *pubKey, CK_MECHANISM_TYPE target, PK11AttrFlags attrFlags, CK_FLAGS opFlags, PK11SymKey **outKey, SECItem **outCiphertext) { @@ -3138,20 +3162,11 @@ PK11_Encapsulate(SECKEYPublicKey *pubKey *outKey = NULL; *outCiphertext = NULL; - CK_MECHANISM_TYPE kemType; - CK_NSS_KEM_PARAMETER_SET_TYPE kemParameterSet = PK11_ReadULongAttribute(slot, pubKey->pkcs11ID, CKA_NSS_PARAMETER_SET); - switch (kemParameterSet) { - case CKP_NSS_KYBER_768_ROUND3: - kemType = CKM_NSS_KYBER; - break; - case CKP_NSS_ML_KEM_768: - kemType = CKM_NSS_ML_KEM; - break; - default: - PORT_SetError(SEC_ERROR_INVALID_KEY); - return SECFailure; + CK_MECHANISM mech; + SECStatus rv = pk11_GetKEMMechanism(pubKey->keyType, &mech, NULL, pubKey); + if (rv != SECSuccess) { + return rv; } - CK_MECHANISM mech = { kemType, &kemParameterSet, sizeof(kemParameterSet) }; sharedSecret = pk11_CreateSymKey(slot, target, PR_TRUE, PR_TRUE, NULL); if (sharedSecret == NULL) { @@ -3243,20 +3258,11 @@ PK11_Decapsulate(SECKEYPrivateKey *privK *outKey = NULL; - CK_MECHANISM_TYPE kemType; - CK_NSS_KEM_PARAMETER_SET_TYPE kemParameterSet = PK11_ReadULongAttribute(slot, privKey->pkcs11ID, CKA_NSS_PARAMETER_SET); - switch (kemParameterSet) { - case CKP_NSS_KYBER_768_ROUND3: - kemType = CKM_NSS_KYBER; - break; - case CKP_NSS_ML_KEM_768: - kemType = CKM_NSS_ML_KEM; - break; - default: - PORT_SetError(SEC_ERROR_INVALID_KEY); - return SECFailure; + CK_MECHANISM mech; + SECStatus rv = pk11_GetKEMMechanism(privKey->keyType, &mech, privKey, NULL); + if (rv != SECSuccess) { + return rv; } - CK_MECHANISM mech = { kemType, &kemParameterSet, sizeof(kemParameterSet) }; sharedSecret = pk11_CreateSymKey(slot, target, PR_TRUE, PR_TRUE, NULL); if (sharedSecret == NULL) { diff -up ./lib/pk11wrap/pk11slot.c.mlkem_p256 ./lib/pk11wrap/pk11slot.c --- ./lib/pk11wrap/pk11slot.c.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./lib/pk11wrap/pk11slot.c 2025-06-25 16:47:19.378362966 -0700 @@ -54,6 +54,7 @@ const PK11DefaultArrayEntry PK11_Default { "MD2", SECMOD_MD2_FLAG, CKM_MD2 }, { "SSL", SECMOD_SSL_FLAG, CKM_SSL3_PRE_MASTER_KEY_GEN }, { "TLS", SECMOD_TLS_FLAG, CKM_TLS_MASTER_KEY_DERIVE }, + { "MLKEM", SECMOD_MLKEM_FLAG, CKM_ML_KEM }, { "SKIPJACK", SECMOD_FORTEZZA_FLAG, CKM_SKIPJACK_CBC64 }, { "Publicly-readable certs", SECMOD_FRIENDLY_FLAG, CKM_INVALID_MECHANISM }, { "Random Num Generator", SECMOD_RANDOM_FLAG, CKM_FAKE_RANDOM }, @@ -89,6 +90,7 @@ static PK11SlotList pk11_dsaSlotList, pk11_dhSlotList, pk11_ecSlotList, + pk11_mlkemSlotList, pk11_ideaSlotList, pk11_sslSlotList, pk11_tlsSlotList, @@ -843,6 +845,7 @@ PK11_InitSlotLists(void) pk11_InitSlotListStatic(&pk11_dsaSlotList); pk11_InitSlotListStatic(&pk11_dhSlotList); pk11_InitSlotListStatic(&pk11_ecSlotList); + pk11_InitSlotListStatic(&pk11_mlkemSlotList); pk11_InitSlotListStatic(&pk11_ideaSlotList); pk11_InitSlotListStatic(&pk11_sslSlotList); pk11_InitSlotListStatic(&pk11_tlsSlotList); @@ -869,6 +872,7 @@ PK11_DestroySlotLists(void) pk11_FreeSlotListStatic(&pk11_dsaSlotList); pk11_FreeSlotListStatic(&pk11_dhSlotList); pk11_FreeSlotListStatic(&pk11_ecSlotList); + pk11_FreeSlotListStatic(&pk11_mlkemSlotList); pk11_FreeSlotListStatic(&pk11_ideaSlotList); pk11_FreeSlotListStatic(&pk11_sslSlotList); pk11_FreeSlotListStatic(&pk11_tlsSlotList); @@ -945,11 +949,14 @@ PK11_GetSlotList(CK_MECHANISM_TYPE type) case CKM_EC_KEY_PAIR_GEN: /* aka CKM_ECDSA_KEY_PAIR_GEN */ case CKM_NSS_ECDHE_NO_PAIRWISE_CHECK_KEY_PAIR_GEN: case CKM_ECDH1_DERIVE: + return &pk11_ecSlotList; case CKM_NSS_KYBER_KEY_PAIR_GEN: /* Bug 1893029 */ case CKM_NSS_KYBER: case CKM_NSS_ML_KEM_KEY_PAIR_GEN: /* Bug 1893029 */ case CKM_NSS_ML_KEM: - return &pk11_ecSlotList; + case CKM_ML_KEM_KEY_PAIR_GEN: /* Bug 1893029 */ + case CKM_ML_KEM: + return &pk11_mlkemSlotList; case CKM_SSL3_PRE_MASTER_KEY_GEN: case CKM_SSL3_MASTER_KEY_DERIVE: case CKM_SSL3_SHA1_MAC: diff -up ./lib/pk11wrap/secmodti.h.mlkem_p256 ./lib/pk11wrap/secmodti.h --- ./lib/pk11wrap/secmodti.h.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./lib/pk11wrap/secmodti.h 2025-06-25 16:47:19.378362966 -0700 @@ -202,4 +202,11 @@ struct PK11GenericObjectStr { /* This mask includes all CK_FLAGs with an equivalent CKA_ attribute. */ #define CKF_KEY_OPERATION_FLAGS 0x000e7b00UL +/* + * private header file, so we can set real names for oids that aren't upstream + * yet, so we applications don't try to use them and get hosed when they change + */ +#define SEC_OID_SECP256R1MLKEM768 SEC_OID_PRIVATE_1 +#define SEC_OID_MLKEM1024SECP256R1 SEC_OID_PRIVATE_2 + #endif /* _SECMODTI_H_ */ diff -up ./lib/softoken/fipsaudt.c.mlkem_p256 ./lib/softoken/fipsaudt.c --- ./lib/softoken/fipsaudt.c.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./lib/softoken/fipsaudt.c 2025-06-25 16:47:19.378362966 -0700 @@ -319,3 +319,51 @@ sftk_AuditDigestKey(CK_SESSION_HANDLE hS (PRUint32)hSession, (PRUint32)hKey, (PRUint32)rv); sftk_LogAuditMessage(severity, NSS_AUDIT_DIGEST_KEY, msg); } + +void +sftk_AuditEncapsulate(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hPublicKey, + CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, + CK_OBJECT_HANDLE_PTR phKey, CK_BYTE_PTR pCiphertext, + CK_ULONG_PTR pulCiphertextLen, CK_RV rv) +{ + char msg[256]; + char mech[MECHANISM_BUFSIZE]; + char shKey[32]; + NSSAuditSeverity severity = (rv == CKR_OK) ? NSS_AUDIT_INFO : NSS_AUDIT_ERROR; + + sftk_PrintMechanism(mech, sizeof mech, pMechanism); + sftk_PrintReturnedObjectHandle(shKey, sizeof shKey, "phKey", phKey, rv); + PR_snprintf(msg, sizeof msg, + "C_Encapsulate(hSession=0x%08lX, pMechanism=%s, " + "hPublicKey=0x%08lX, pTemplate=%p, ulAttributeCount=%lu, " + "phKey=%p pCipherText=%p, pulCiphertextLen=%p)=0x%08lX%s", + (PRUint32)hSession, mech, + (PRUint32)hPublicKey, pTemplate, (PRUint32)ulAttributeCount, + phKey, pCiphertext, pulCiphertextLen, (PRUint32)rv, shKey); + sftk_LogAuditMessage(severity, NSS_AUDIT_ENCAPSULATE_KEY, msg); +} + +void +sftk_AuditDecapsulate(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hPrivateKey, + CK_BYTE_PTR pCiphertext, CK_ULONG ulCiphertextLen, + CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, + CK_OBJECT_HANDLE_PTR phKey, CK_RV rv) +{ + char msg[256]; + char mech[MECHANISM_BUFSIZE]; + char shKey[32]; + NSSAuditSeverity severity = (rv == CKR_OK) ? NSS_AUDIT_INFO : NSS_AUDIT_ERROR; + + sftk_PrintMechanism(mech, sizeof mech, pMechanism); + sftk_PrintReturnedObjectHandle(shKey, sizeof shKey, "phKey", phKey, rv); + PR_snprintf(msg, sizeof msg, + "C_Decapsulate(hSession=0x%08lX, pMechanism=%s, " + "hPrivateKey=0x%08lX, pCipherText=%p, ulCiphertextLen=%lu,", + "pTemplate=%p, ulAttributeCount=%lu, phKey=%p)=0x%08lX%s", + (PRUint32)hSession, mech, + (PRUint32)hPrivateKey, pCiphertext, (PRUint32)ulCiphertextLen, + pTemplate, (PRUint32)ulAttributeCount, phKey, (PRUint32)rv, shKey); + sftk_LogAuditMessage(severity, NSS_AUDIT_DECAPSULATE_KEY, msg); +} diff -up ./lib/softoken/fipstokn.c.mlkem_p256 ./lib/softoken/fipstokn.c --- ./lib/softoken/fipstokn.c.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./lib/softoken/fipstokn.c 2025-06-25 16:47:19.379362979 -0700 @@ -24,6 +24,7 @@ #include "pkcs11i.h" #include "prenv.h" #include "prprf.h" +#include "kem.h" #include @@ -261,6 +262,30 @@ static CK_FUNCTION_LIST_3_0 sftk_fipsTab }; +CK_RV FC_Encapsulate(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hPublicKey, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + CK_OBJECT_HANDLE_PTR phKey, + CK_BYTE_PTR pCiphertext, + CK_ULONG_PTR pulCiphertextLen); + +CK_RV FC_Decapsulate(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hPrivateKey, + CK_BYTE_PTR pCiphertext, + CK_ULONG ulCiphertextLen, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + CK_OBJECT_HANDLE_PTR phKey); + +CK_NSS_KEM_FUNCTIONS sftk_fips_kem_funcList = { + { 1, 0 }, + FC_Encapsulate, + FC_Decapsulate +}; + /* forward declaration of special GetInfo functions */ CK_RV FC_GetInfoV2(CK_INFO_PTR pInfo); CK_RV NSC_GetInfoV2(CK_INFO_PTR pInfo); @@ -302,10 +327,11 @@ static CK_INTERFACE fips_interfaces[] = { (CK_UTF8CHAR_PTR) "PKCS 11", &sftk_fipsTable, NSS_INTERFACE_FLAGS }, { (CK_UTF8CHAR_PTR) "PKCS 11", &sftk_fipsTable_v2, NSS_INTERFACE_FLAGS }, { (CK_UTF8CHAR_PTR) "Vendor NSS Module Interface", &sftk_module_funcList, NSS_INTERFACE_FLAGS }, - { (CK_UTF8CHAR_PTR) "Vendor NSS FIPS Interface", &sftk_fips_funcList, NSS_INTERFACE_FLAGS } + { (CK_UTF8CHAR_PTR) "Vendor NSS FIPS Interface", &sftk_fips_funcList, NSS_INTERFACE_FLAGS }, + { (CK_UTF8CHAR_PTR) "Vendor NSS KEM Interface", &sftk_fips_kem_funcList, NSS_INTERFACE_FLAGS } }; /* must match the count of interfaces in fips_interfaces above*/ -#define FIPS_INTERFACE_COUNT 4 +#define FIPS_INTERFACE_COUNT PR_ARRAY_SIZE(fips_interfaces) /* CKO_NOT_A_KEY can be any object class that's not a key object. */ #define CKO_NOT_A_KEY CKO_DATA @@ -350,6 +376,8 @@ sftk_mapLinuxAuditType(NSSAuditSeverity case NSS_AUDIT_LOAD_KEY: case NSS_AUDIT_UNWRAP_KEY: case NSS_AUDIT_WRAP_KEY: + case NSS_AUDIT_ENCAPSULATE_KEY: + case NSS_AUDIT_DECAPSULATE_KEY: return AUDIT_CRYPTO_KEY_USER; case NSS_AUDIT_CRYPT: return (severity == NSS_AUDIT_ERROR) ? AUDIT_CRYPTO_FAILURE_USER : AUDIT_CRYPTO_KEY_USER; @@ -2078,3 +2106,75 @@ FC_MessageVerifyFinal(CK_SESSION_HANDLE CHECK_FORK(); return NSC_MessageVerifyFinal(hSession); } + +/* + * vendor specific encapsulate/decapsulate + */ +CK_RV +FC_Encapsulate(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hPublicKey, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + CK_OBJECT_HANDLE_PTR phKey, + CK_BYTE_PTR pCiphertext, + CK_ULONG_PTR pulCiphertextLen) +{ + CK_BBOOL *boolptr; + SFTK_FIPSCHECK(); + CHECK_FORK(); + + /* all secret keys must be sensitive, if the upper level code tries to say + * otherwise, reject it. */ + boolptr = (CK_BBOOL *)fc_getAttribute(pTemplate, + ulAttributeCount, CKA_SENSITIVE); + if (boolptr != NULL) { + if (!(*boolptr)) { + return CKR_ATTRIBUTE_VALUE_INVALID; + } + } + + rv = NSC_Encapsulate(hSession, pMechanism, hPublicKey, pTemplate, + ulAttributeCount, phKey, pCiphertext, + pulCiphertextLen); + if (sftk_audit_enabled) { + sftk_AuditEncapsulate(hSession, pMechanism, hPublicKey, pTemplate, + ulAttributeCount, phKey, pCiphertext, + pulCiphertextLen, rv); + } + return rv; +} + +CK_RV +FC_Decapsulate(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hPrivateKey, + CK_BYTE_PTR pCiphertext, + CK_ULONG ulCiphertextLen, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + CK_OBJECT_HANDLE_PTR phKey) +{ + CK_BBOOL *boolptr; + SFTK_FIPSCHECK(); + CHECK_FORK(); + + /* all secret keys must be sensitive, if the upper level code tries to say + * otherwise, reject it. */ + boolptr = (CK_BBOOL *)fc_getAttribute(pTemplate, + ulAttributeCount, CKA_SENSITIVE); + if (boolptr != NULL) { + if (!(*boolptr)) { + return CKR_ATTRIBUTE_VALUE_INVALID; + } + } + + rv = NSC_Decapsulate(hSession, pMechanism, hPrivateKey, pCiphertext, + ulCiphertextLen, pTemplate, ulAttributeCount, phKey); + if (sftk_audit_enabled) { + sftk_AuditDecapsulate(hSession, pMechanism, hPrivateKey, pCiphertext, + ulCiphertextLen, pTemplate, ulAttributeCount, + phKey, rv); + } + return rv; +} diff -up ./lib/softoken/kem.c.mlkem_p256 ./lib/softoken/kem.c --- ./lib/softoken/kem.c.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./lib/softoken/kem.c 2025-06-25 16:56:05.884129793 -0700 @@ -11,13 +11,19 @@ #include "secport.h" #include "softoken.h" +/* change to the largest KEM Secret Bytes value supported */ +#define MAX_SHARED_SECRET_BYTES KYBER_SHARED_SECRET_BYTES + KyberParams -sftk_kyber_PK11ParamToInternal(CK_NSS_KEM_PARAMETER_SET_TYPE pk11ParamSet) +sftk_kyber_PK11ParamToInternal(CK_ML_KEM_PARAMETER_SET_TYPE pk11ParamSet) { switch (pk11ParamSet) { +#ifndef NSS_DISABLE_KYBER case CKP_NSS_KYBER_768_ROUND3: return params_kyber768_round3; +#endif case CKP_NSS_ML_KEM_768: + case CKP_ML_KEM_768: return params_ml_kem768; default: return params_kyber_invalid; @@ -28,8 +34,10 @@ SECItem * sftk_kyber_AllocPubKeyItem(KyberParams params, SECItem *pubkey) { switch (params) { +#ifndef NSS_DISABLE_KYBER case params_kyber768_round3: case params_kyber768_round3_test_mode: +#endif case params_ml_kem768: case params_ml_kem768_test_mode: return SECITEM_AllocItem(NULL, pubkey, KYBER768_PUBLIC_KEY_BYTES); @@ -42,8 +50,10 @@ SECItem * sftk_kyber_AllocPrivKeyItem(KyberParams params, SECItem *privkey) { switch (params) { +#ifndef NSS_DISABLE_KYBER case params_kyber768_round3: case params_kyber768_round3_test_mode: +#endif case params_ml_kem768: case params_ml_kem768_test_mode: return SECITEM_AllocItem(NULL, privkey, KYBER768_PRIVATE_KEY_BYTES); @@ -56,8 +66,10 @@ SECItem * sftk_kyber_AllocCiphertextItem(KyberParams params, SECItem *ciphertext) { switch (params) { +#ifndef NSS_DISABLE_KYBER case params_kyber768_round3: case params_kyber768_round3_test_mode: +#endif case params_ml_kem768: case params_ml_kem768_test_mode: return SECITEM_AllocItem(NULL, ciphertext, KYBER768_CIPHERTEXT_BYTES); @@ -67,70 +79,100 @@ sftk_kyber_AllocCiphertextItem(KyberPara } static PRBool -sftk_kyber_ValidateParams(const CK_NSS_KEM_PARAMETER_SET_TYPE *params) +sftk_kem_ValidateMechanism(CK_MECHANISM_PTR pMechanism) { - if (!params) { + if (!pMechanism) { return PR_FALSE; } - switch (*params) { - case CKP_NSS_KYBER_768_ROUND3: - case CKP_NSS_ML_KEM_768: + switch (pMechanism->mechanism) { +#ifndef NSS_DISABLE_KYBER + case CKM_NSS_KYBER: +#endif + case CKM_NSS_ML_KEM: + case CKM_ML_KEM: return PR_TRUE; default: return PR_FALSE; } } -static PRBool -sftk_kem_ValidateMechanism(CK_MECHANISM_PTR pMechanism) +/* this is a generic call the returns the paramSet as a CKU_LONG. if the + * param set is in a different attribute or mechanism structure, that would + * be based on the mechanism. The meaning of the paramter set is alway + * mechanism specific */ +static CK_RV +sftk_kem_getParamSet(CK_MECHANISM_PTR pMechanism, SFTKObject *key, + CK_ULONG *paramSet) { - if (!pMechanism) { - return PR_FALSE; - } + CK_RV crv = CKR_MECHANISM_INVALID; + switch (pMechanism->mechanism) { +#ifndef NSS_DISABLE_KYBER case CKM_NSS_KYBER: +#endif case CKM_NSS_ML_KEM: - return pMechanism->ulParameterLen == sizeof(CK_NSS_KEM_PARAMETER_SET_TYPE) && sftk_kyber_ValidateParams(pMechanism->pParameter); + if ((pMechanism->pParameter) && + pMechanism->ulParameterLen == sizeof(CK_ML_KEM_PARAMETER_SET_TYPE)) { + PR_STATIC_ASSERT(sizeof(CK_ML_KEM_PARAMETER_SET_TYPE) == sizeof(CK_LONG)); + *paramSet = *(CK_ULONG *)pMechanism->pParameter; + crv = CKR_OK; + break; + } + crv = sftk_GetULongAttribute(key, CKA_NSS_PARAMETER_SET, paramSet); + if (crv == CKR_OK) { + break; + } + case CKM_ML_KEM: + crv = sftk_GetULongAttribute(key, CKA_PARAMETER_SET, paramSet); + break; default: - return PR_FALSE; + break; } + return crv; } static CK_ULONG -sftk_kem_CiphertextLen(CK_MECHANISM_PTR pMechanism) +sftk_kem_CiphertextLen(CK_MECHANISM_PTR pMechanism, CK_ULONG paramSet) { -#ifdef DEBUG - if (!sftk_kem_ValidateMechanism(pMechanism)) { - PORT_Assert(0); - return 0; - } + /* the switch here is redundant now, but we will eventually have + * other unrelated KEM operations and the meaning of paramSet + * is dependent of the mechanism type (in general). Since there + * is not overlap between the Vendor specific mechanisms here + * and the stand ones, we'll just accept them all here. */ + switch (pMechanism->mechanism) { +#ifndef NSS_DISABLE_KYBER + case CKM_NSS_KYBER: #endif - - /* Assumes pMechanism has been validated with sftk_kem_ValidateMechanism */ - CK_NSS_KEM_PARAMETER_SET_TYPE *pParameterSet = pMechanism->pParameter; - switch (*pParameterSet) { - case CKP_NSS_KYBER_768_ROUND3: - case CKP_NSS_ML_KEM_768: - return KYBER768_CIPHERTEXT_BYTES; + case CKM_NSS_ML_KEM: + case CKM_ML_KEM: + switch (paramSet) { +#ifndef NSS_DISABLE_KYBER + case CKP_NSS_KYBER_768_ROUND3: +#endif + case CKP_NSS_ML_KEM_768: + case CKP_ML_KEM_768: + return KYBER768_CIPHERTEXT_BYTES; + default: + break; + } default: - /* unreachable if pMechanism has been validated */ - PORT_Assert(0); - return 0; + break; } + return 0; } /* C_Encapsulate takes a public encapsulation key hPublicKey, a secret * phKey, and outputs a ciphertext (i.e. encapsulaton) of this secret in * pCiphertext. */ CK_RV -NSC_Encapsulate(CK_SESSION_HANDLE hSession, - CK_MECHANISM_PTR pMechanism, - CK_OBJECT_HANDLE hPublicKey, - CK_ATTRIBUTE_PTR pTemplate, - CK_ULONG ulAttributeCount, - /* out */ CK_OBJECT_HANDLE_PTR phKey, - /* out */ CK_BYTE_PTR pCiphertext, - /* out */ CK_ULONG_PTR pulCiphertextLen) +NSC_EncapsulateKey(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hPublicKey, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + /* out */ CK_BYTE_PTR pCiphertext, + /* out */ CK_ULONG_PTR pulCiphertextLen, + /* out */ CK_OBJECT_HANDLE_PTR phKey) { SFTKSession *session = NULL; SFTKSlot *slot = NULL; @@ -142,6 +184,10 @@ NSC_Encapsulate(CK_SESSION_HANDLE hSessi CK_RV crv; SFTKFreeStatus status; + CK_ULONG paramSet = 0; /* use a generic U_LONG so we can handle + * different param set types based on the + * Mechanism value */ + KyberParams kyberParams; CHECK_FORK(); @@ -152,12 +198,6 @@ NSC_Encapsulate(CK_SESSION_HANDLE hSessi if (!sftk_kem_ValidateMechanism(pMechanism)) { return CKR_MECHANISM_INVALID; } - - CK_ULONG ciphertextLen = sftk_kem_CiphertextLen(pMechanism); - if (!pCiphertext || *pulCiphertextLen < ciphertextLen) { - *pulCiphertextLen = ciphertextLen; - return CKR_KEY_SIZE_RANGE; - } *phKey = CK_INVALID_HANDLE; session = sftk_SessionFromHandle(hSession); @@ -193,20 +233,39 @@ NSC_Encapsulate(CK_SESSION_HANDLE hSessi goto cleanup; } + crv = sftk_kem_getParamSet(pMechanism, encapsulationKeyObject, ¶mSet); + if (crv != CKR_OK) { + goto cleanup; + } + + CK_ULONG ciphertextLen = sftk_kem_CiphertextLen(pMechanism, paramSet); + if (!pCiphertext || *pulCiphertextLen < ciphertextLen || ciphertextLen == 0) { + *pulCiphertextLen = ciphertextLen; + crv = CKR_KEY_SIZE_RANGE; + goto cleanup; + } + SECItem ciphertext = { siBuffer, pCiphertext, ciphertextLen }; SECItem pubKey = { siBuffer, encapsulationKey->attrib.pValue, encapsulationKey->attrib.ulValueLen }; - /* The length of secretBuf can be increased if we ever support other KEMs */ - uint8_t secretBuf[KYBER_SHARED_SECRET_BYTES] = { 0 }; + /* The length of secretBuf can be increased if we ever support other KEMs + * by changing the define at the top of this file */ + uint8_t secretBuf[MAX_SHARED_SECRET_BYTES] = { 0 }; SECItem secret = { siBuffer, secretBuf, sizeof secretBuf }; + key->isFIPS = sftk_operationIsFIPS(slot, pMechanism, CKA_ENCAPSULATE, + key, 0); + key->source = SFTK_SOURCE_KEA; switch (pMechanism->mechanism) { +#ifndef NSS_DISABLE_KYBER case CKM_NSS_KYBER: +#endif case CKM_NSS_ML_KEM: - PORT_Assert(secret.len == KYBER_SHARED_SECRET_BYTES); - CK_NSS_KEM_PARAMETER_SET_TYPE *pParameter = pMechanism->pParameter; - KyberParams kyberParams = sftk_kyber_PK11ParamToInternal(*pParameter); - SECStatus rv = Kyber_Encapsulate(kyberParams, /* seed */ NULL, &pubKey, &ciphertext, &secret); + case CKM_ML_KEM: + PORT_Assert(secret.len >= KYBER_SHARED_SECRET_BYTES); + kyberParams = sftk_kyber_PK11ParamToInternal(paramSet); + SECStatus rv = Kyber_Encapsulate(kyberParams, /* seed */ NULL, + &pubKey, &ciphertext, &secret); if (rv != SECSuccess) { crv = (PORT_GetError() == SEC_ERROR_INVALID_ARGS) ? CKR_ARGUMENTS_BAD : CKR_FUNCTION_FAILED; goto cleanup; @@ -232,6 +291,7 @@ NSC_Encapsulate(CK_SESSION_HANDLE hSessi } cleanup: + PORT_SafeZero(secretBuf, sizeof(secretBuf)); if (session) { sftk_FreeSession(session); } @@ -254,14 +314,14 @@ cleanup: } CK_RV -NSC_Decapsulate(CK_SESSION_HANDLE hSession, - CK_MECHANISM_PTR pMechanism, - CK_OBJECT_HANDLE hPrivateKey, - CK_BYTE_PTR pCiphertext, - CK_ULONG ulCiphertextLen, - CK_ATTRIBUTE_PTR pTemplate, - CK_ULONG ulAttributeCount, - /* out */ CK_OBJECT_HANDLE_PTR phKey) +NSC_DecapsulateKey(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hPrivateKey, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + CK_BYTE_PTR pCiphertext, + CK_ULONG ulCiphertextLen, + /* out */ CK_OBJECT_HANDLE_PTR phKey) { SFTKSession *session = NULL; SFTKSlot *slot = NULL; @@ -270,9 +330,13 @@ NSC_Decapsulate(CK_SESSION_HANDLE hSessi SFTKObject *decapsulationKeyObject = NULL; SFTKAttribute *decapsulationKey = NULL; + CK_ULONG paramSet = 0; /* use a generic U_LONG so we can handle + * different param set types based on the + * Mechanism value */ CK_RV crv; SFTKFreeStatus status; + KyberParams kyberParams; CHECK_FORK(); @@ -283,11 +347,6 @@ NSC_Decapsulate(CK_SESSION_HANDLE hSessi if (!sftk_kem_ValidateMechanism(pMechanism)) { return CKR_MECHANISM_INVALID; } - - CK_ULONG ciphertextLen = sftk_kem_CiphertextLen(pMechanism); - if (ulCiphertextLen < ciphertextLen) { - return CKR_ARGUMENTS_BAD; - } *phKey = CK_INVALID_HANDLE; session = sftk_SessionFromHandle(hSession); @@ -323,20 +382,39 @@ NSC_Decapsulate(CK_SESSION_HANDLE hSessi goto cleanup; } - SECItem privKey = { siBuffer, decapsulationKey->attrib.pValue, decapsulationKey->attrib.ulValueLen }; + crv = sftk_kem_getParamSet(pMechanism, decapsulationKeyObject, ¶mSet); + if (crv != CKR_OK) { + goto cleanup; + } + + CK_ULONG ciphertextLen = sftk_kem_CiphertextLen(pMechanism, paramSet); + if (!pCiphertext || ulCiphertextLen != ciphertextLen || ciphertextLen == 0) { + crv = CKR_ARGUMENTS_BAD; + goto cleanup; + } + + SECItem privKey = { siBuffer, decapsulationKey->attrib.pValue, + decapsulationKey->attrib.ulValueLen }; SECItem ciphertext = { siBuffer, pCiphertext, ulCiphertextLen }; - /* The length of secretBuf can be increased if we ever support other KEMs */ - uint8_t secretBuf[KYBER_SHARED_SECRET_BYTES] = { 0 }; + /* The length of secretBuf can be increased if we ever support other KEMs + * by changing the define at the top of this file */ + uint8_t secretBuf[MAX_SHARED_SECRET_BYTES] = { 0 }; SECItem secret = { siBuffer, secretBuf, sizeof secretBuf }; + key->isFIPS = sftk_operationIsFIPS(slot, pMechanism, CKA_DECAPSULATE, + key, 0); + key->source = SFTK_SOURCE_KEA; switch (pMechanism->mechanism) { +#ifndef NSS_DISABLE_KYBER case CKM_NSS_KYBER: +#endif case CKM_NSS_ML_KEM: - PORT_Assert(secret.len == KYBER_SHARED_SECRET_BYTES); - CK_NSS_KEM_PARAMETER_SET_TYPE *pParameter = pMechanism->pParameter; - KyberParams kyberParams = sftk_kyber_PK11ParamToInternal(*pParameter); - SECStatus rv = Kyber_Decapsulate(kyberParams, &privKey, &ciphertext, &secret); + case CKM_ML_KEM: + kyberParams = sftk_kyber_PK11ParamToInternal(paramSet); + PORT_Assert(secret.len >= KYBER_SHARED_SECRET_BYTES); + SECStatus rv = Kyber_Decapsulate(kyberParams, &privKey, + &ciphertext, &secret); if (rv != SECSuccess) { crv = (PORT_GetError() == SEC_ERROR_INVALID_ARGS) ? CKR_ARGUMENTS_BAD : CKR_FUNCTION_FAILED; goto cleanup; @@ -359,6 +437,7 @@ NSC_Decapsulate(CK_SESSION_HANDLE hSessi } cleanup: + PORT_SafeZero(secretBuf, sizeof(secretBuf)); if (session) { sftk_FreeSession(session); } @@ -379,3 +458,36 @@ cleanup: } return crv; } + +/* PKCS #11 final spec moved som the the arguments around (to make + * NSC_EncapsulateKey and NSC_DecapsulateKey match, keep the old version for + * the vendor defined for backward compatibility */ +CK_RV +NSC_Encapsulate(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hPublicKey, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + /* out */ CK_OBJECT_HANDLE_PTR phKey, + /* out */ CK_BYTE_PTR pCiphertext, + /* out */ CK_ULONG_PTR pulCiphertextLen) +{ + return NSC_EncapsulateKey(hSession, pMechanism, hPublicKey, + pTemplate, ulAttributeCount, + pCiphertext, pulCiphertextLen, phKey); +} + +CK_RV +NSC_Decapsulate(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hPrivateKey, + CK_BYTE_PTR pCiphertext, + CK_ULONG ulCiphertextLen, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + /* out */ CK_OBJECT_HANDLE_PTR phKey) +{ + return NSC_DecapsulateKey(hSession, pMechanism, hPrivateKey, pTemplate, + ulAttributeCount, pCiphertext, ulCiphertextLen, + phKey); +} diff -up ./lib/softoken/pkcs11c.c.mlkem_p256 ./lib/softoken/pkcs11c.c --- ./lib/softoken/pkcs11c.c.mlkem_p256 2025-06-25 16:47:19.369362850 -0700 +++ ./lib/softoken/pkcs11c.c 2025-06-25 16:47:19.380362992 -0700 @@ -5029,6 +5029,64 @@ loser: return crv; } +PRBool +sftk_compareKeysEqual(CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE key1, CK_OBJECT_HANDLE key2) +{ + PRBool result = PR_FALSE; + SFTKSession *session; + SFTKObject *key1obj = NULL; + SFTKObject *key2obj = NULL; + SFTKAttribute *att1 = NULL; + SFTKAttribute *att2 = NULL; + + /* fetch the pkcs11 objects from the handles */ + session = sftk_SessionFromHandle(hSession); + if (session == NULL) { + return CKR_SESSION_HANDLE_INVALID; + } + + key1obj = sftk_ObjectFromHandle(key1, session); + key2obj = sftk_ObjectFromHandle(key2, session); + sftk_FreeSession(session); + if ((key1obj == NULL) || (key2obj == NULL)) { + goto loser; + } + /* fetch the value attributes */ + att1 = sftk_FindAttribute(key1obj, CKA_VALUE); + if (att1 == NULL) { + goto loser; + } + att2 = sftk_FindAttribute(key2obj, CKA_VALUE); + if (att2 == NULL) { + goto loser; + } + /* make sure that they are equal */ + if (att1->attrib.ulValueLen != att2->attrib.ulValueLen) { + goto loser; + } + if (PORT_Memcmp(att1->attrib.pValue, att2->attrib.pValue, + att1->attrib.ulValueLen) != 0) { + goto loser; + } + result = PR_TRUE; +loser: + if (key1obj) { + sftk_FreeObject(key1obj); + } + if (key2obj) { + sftk_FreeObject(key1obj); + } + if (att1) { + sftk_FreeAttribute(att1); + } + if (att2) { + sftk_FreeAttribute(att2); + } + return result; +} + + #define PAIRWISE_DIGEST_LENGTH SHA1_LENGTH /* 160-bits */ #define PAIRWISE_MESSAGE_LENGTH 20 /* 160-bits */ @@ -5043,7 +5101,8 @@ loser: */ static CK_RV sftk_PairwiseConsistencyCheck(CK_SESSION_HANDLE hSession, SFTKSlot *slot, - SFTKObject *publicKey, SFTKObject *privateKey, CK_KEY_TYPE keyType) + SFTKObject *publicKey, SFTKObject *privateKey, + CK_KEY_TYPE keyType) { /* * Key type Mechanism type @@ -5058,11 +5117,15 @@ sftk_PairwiseConsistencyCheck(CK_SESSION * * None of these mechanisms has a parameter. * - * For derive CKK_DH => CKM_DH_PKCS_DERIVE - * CKK_EC => CKM_ECDH1_DERIVE - * CKK_EC_MONTGOMERY => CKM_ECDH1_DERIVE + * For derive: regenerate public key from the private key + * CKK_DH => DH_Derive + * CKK_EC => EC_NewKeyFromSeed + * CKK_EC_MONTGOMERY => EC_NewKeyFromSeed * others => CKM_INVALID_MECHANISM * + * For KEM mechanisms: + * CKK_NSS_KYBER => don't + * * The parameters for these mechanisms is the public key. */ CK_MECHANISM mech = { 0, NULL, 0 }; @@ -5072,6 +5135,7 @@ sftk_PairwiseConsistencyCheck(CK_SESSION PRBool isEncryptable = PR_FALSE; PRBool canSignVerify = PR_FALSE; PRBool isDerivable = PR_FALSE; + PRBool isKEM = PR_FALSE; CK_RV crv; /* Variables used for Encrypt/Decrypt functions. */ @@ -5089,35 +5153,42 @@ sftk_PairwiseConsistencyCheck(CK_SESSION unsigned char *known_digest = (unsigned char *)"Mozilla Rules the World through NSS!"; unsigned char *signature; CK_ULONG signature_length; + SFTKAttribute *attribute; - if (keyType == CKK_RSA) { - SFTKAttribute *attribute; - - /* Get modulus length of private key. */ - attribute = sftk_FindAttribute(privateKey, CKA_MODULUS); - if (attribute == NULL) { - return CKR_DEVICE_ERROR; - } - modulusLen = attribute->attrib.ulValueLen; - if (*(unsigned char *)attribute->attrib.pValue == 0) { - modulusLen--; - } - sftk_FreeAttribute(attribute); - } else if (keyType == CKK_DSA) { - SFTKAttribute *attribute; - - /* Get subprime length of private key. */ - attribute = sftk_FindAttribute(privateKey, CKA_SUBPRIME); - if (attribute == NULL) { - return CKR_DEVICE_ERROR; - } - subPrimeLen = attribute->attrib.ulValueLen; - if (subPrimeLen > 1 && *(unsigned char *)attribute->attrib.pValue == 0) { - subPrimeLen--; - } - sftk_FreeAttribute(attribute); + switch (keyType) { + case CKK_RSA: + /* Get modulus length of private key. */ + attribute = sftk_FindAttribute(privateKey, CKA_MODULUS); + if (attribute == NULL) { + return CKR_DEVICE_ERROR; + } + modulusLen = attribute->attrib.ulValueLen; + if (*(unsigned char *)attribute->attrib.pValue == 0) { + modulusLen--; + } + sftk_FreeAttribute(attribute); + break; + case CKK_DSA: + /* Get subprime length of private key. */ + attribute = sftk_FindAttribute(privateKey, CKA_SUBPRIME); + if (attribute == NULL) { + return CKR_DEVICE_ERROR; + } + subPrimeLen = attribute->attrib.ulValueLen; + if (subPrimeLen > 1 && + *(unsigned char *)attribute->attrib.pValue == 0) { + subPrimeLen--; + } + break; + case CKK_NSS_KYBER: + case CKK_NSS_ML_KEM: + /* these aren't fips. we use them to generate key without a + * pairwise consistency check */ + return CKR_OK; } + + /**************************************************/ /* Pairwise Consistency Check of Encrypt/Decrypt. */ /**************************************************/ @@ -5473,6 +5544,79 @@ sftk_PairwiseConsistencyCheck(CK_SESSION return crv; } } + isKEM = sftk_isTrue(privateKey, CKA_ENCAPSULATE); + if (isKEM) { + unsigned char *cipher_text = NULL; + CK_ULONG cipher_text_length = 0; + CK_OBJECT_HANDLE key1 = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE key2 = CK_INVALID_HANDLE; + CK_KEY_TYPE genType = CKO_SECRET_KEY; + CK_ATTRIBUTE template = { CKA_KEY_TYPE, NULL, 0 }; + + template.pValue = &genType; + template.ulValueLen = sizeof(genType); + crv = CKR_OK; + switch (keyType) { + case CKK_ML_KEM: + cipher_text_length = KYBER_SHARED_SECRET_BYTES; + mech.mechanism = CKM_ML_KEM; + break; + case CKK_RSA: + if (!isEncryptable) { + /* already handled the pairwise test, no need to + * do it again */ + goto kem_done; + } + cipher_text_length = modulusLen; + mech.mechanism = CKM_RSA_PKCS; + break; + case CKK_EC: + case CKK_EC_MONTGOMERY: + if (!isDerivable) { + /* again, already handled above, no need to check again + */ + goto kem_done; + } + cipher_text_length = MAX_ECKEY_LEN; + mech.mechanism = CKM_ECDH1_DERIVE; + break; + default: + return CKR_DEVICE_ERROR; + } + + /* Allocate space for signature data. */ + cipher_text = (unsigned char *)PORT_ZAlloc(cipher_text_length); + if (cipher_text == NULL) { + return CKR_HOST_MEMORY; + } + crv = NSC_Encapsulate(hSession, &mech, publicKey->handle, &template, 1, + &key1, cipher_text, &cipher_text_length); + if (crv != CKR_OK) { + goto kem_done; + } + crv = NSC_Decapsulate(hSession, &mech, privateKey->handle, + cipher_text, cipher_text_length, &template, 1, + &key2); + if (crv != CKR_OK) { + goto kem_done; + } + if (!sftk_compareKeysEqual(hSession, key1, key2)) { + crv = CKR_DEVICE_ERROR; + goto kem_done; + } +kem_done: + /* PORT_Free already checks for NULL */ + PORT_Free(cipher_text); + if (key1 != CK_INVALID_HANDLE) { + NSC_DestroyObject(hSession, key1); + } + if (key2 != CK_INVALID_HANDLE) { + NSC_DestroyObject(hSession, key2); + } + if (crv != CKR_OK) { + return CKR_DEVICE_ERROR; + } + } return CKR_OK; } @@ -5547,6 +5691,10 @@ NSC_GenerateKeyPair(CK_SESSION_HANDLE hS ckKyberParamSet = *(CK_NSS_KEM_PARAMETER_SET_TYPE *)pPublicKeyTemplate[i].pValue; continue; } + if (pPublicKeyTemplate[i].type == CKA_PARAMETER_SET) { + ckKyberParamSet = *(CK_ML_KEM_PARAMETER_SET_TYPE *)pPublicKeyTemplate[i].pValue; + continue; + } crv = sftk_AddAttributeType(publicKey, sftk_attr_expand(&pPublicKeyTemplate[i])); @@ -5883,8 +6031,9 @@ NSC_GenerateKeyPair(CK_SESSION_HANDLE hS /* extract the necessary parameters and copy them to private keys */ crv = sftk_Attribute2SSecItem(NULL, &ecEncodedParams, publicKey, CKA_EC_PARAMS); - if (crv != CKR_OK) + if (crv != CKR_OK) { break; + } crv = sftk_AddAttributeType(privateKey, CKA_EC_PARAMS, sftk_item_expand(&ecEncodedParams)); @@ -5895,11 +6044,12 @@ NSC_GenerateKeyPair(CK_SESSION_HANDLE hS /* Decode ec params before calling EC_NewKey */ rv = EC_DecodeParams(&ecEncodedParams, &ecParams); - SECITEM_ZfreeItem(&ecEncodedParams, PR_FALSE); if (rv != SECSuccess) { crv = sftk_MapCryptError(PORT_GetError()); + SECITEM_ZfreeItem(&ecEncodedParams, PR_FALSE); break; } + SECITEM_ZfreeItem(&ecEncodedParams, PR_FALSE); rv = EC_NewKey(ecParams, &ecPriv); if (rv != SECSuccess) { if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { @@ -5943,10 +6093,18 @@ NSC_GenerateKeyPair(CK_SESSION_HANDLE hS PORT_FreeArena(ecPriv->ecParams.arena, PR_TRUE); break; +#ifndef NSS_DISABLE_KYBER case CKM_NSS_KYBER_KEY_PAIR_GEN: + key_type = CKK_NSS_KYBER; + goto generate_mlkem; +#endif case CKM_NSS_ML_KEM_KEY_PAIR_GEN: + key_type = CKK_NSS_ML_KEM; + goto generate_mlkem; + case CKM_ML_KEM_KEY_PAIR_GEN: + key_type = CKK_ML_KEM; +generate_mlkem: sftk_DeleteAttributeType(privateKey, CKA_NSS_DB); - key_type = CKK_NSS_KYBER; SECItem privKey = { siBuffer, NULL, 0 }; SECItem pubKey = { siBuffer, NULL, 0 }; @@ -5969,8 +6127,8 @@ NSC_GenerateKeyPair(CK_SESSION_HANDLE hS if (crv != CKR_OK) { goto kyber_done; } - crv = sftk_AddAttributeType(publicKey, CKA_NSS_PARAMETER_SET, - &ckKyberParamSet, sizeof(CK_NSS_KEM_PARAMETER_SET_TYPE)); + crv = sftk_AddAttributeType(publicKey, CKA_PARAMETER_SET, + &ckKyberParamSet, sizeof(CK_ML_KEM_PARAMETER_SET_TYPE)); if (crv != CKR_OK) { goto kyber_done; } @@ -5979,8 +6137,9 @@ NSC_GenerateKeyPair(CK_SESSION_HANDLE hS if (crv != CKR_OK) { goto kyber_done; } - crv = sftk_AddAttributeType(privateKey, CKA_NSS_PARAMETER_SET, - &ckKyberParamSet, sizeof(CK_NSS_KEM_PARAMETER_SET_TYPE)); + + crv = sftk_AddAttributeType(privateKey, CKA_PARAMETER_SET, + &ckKyberParamSet, sizeof(CK_ML_KEM_PARAMETER_SET_TYPE)); if (crv != CKR_OK) { goto kyber_done; } @@ -6130,7 +6289,8 @@ NSC_GenerateKeyPair(CK_SESSION_HANDLE hS &cktrue, sizeof(CK_BBOOL)); } - if (crv == CKR_OK && pMechanism->mechanism != CKM_NSS_ECDHE_NO_PAIRWISE_CHECK_KEY_PAIR_GEN && key_type != CKK_NSS_KYBER) { + if (crv == CKR_OK && + pMechanism->mechanism != CKM_NSS_ECDHE_NO_PAIRWISE_CHECK_KEY_PAIR_GEN) { /* Perform FIPS 140-2 pairwise consistency check. */ crv = sftk_PairwiseConsistencyCheck(hSession, slot, publicKey, privateKey, key_type); @@ -8598,6 +8758,8 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession crv = sftk_forceAttribute(key, CKA_VALUE, buf, keySize); PORT_ZFree(buf, tmpKeySize); + /* preserve the source of the original base key */ + /* key->source = sourceKey->source; */ sftk_FreeAttribute(att2); sftk_FreeObject(paramKey); break; diff -up ./lib/softoken/pkcs11.c.mlkem_p256 ./lib/softoken/pkcs11.c --- ./lib/softoken/pkcs11.c.mlkem_p256 2025-06-25 16:47:19.369362850 -0700 +++ ./lib/softoken/pkcs11.c 2025-06-25 16:47:19.379362979 -0700 @@ -302,6 +302,7 @@ struct mechanismList { #define CKF_SN_VR CKF_SIGN | CKF_VERIFY #define CKF_SN_RE CKF_SIGN_RECOVER | CKF_VERIFY_RECOVER #define CKF_EN_DE_MSG CKF_ENCRYPT | CKF_DECRYPT | CKF_MESSAGE_ENCRYPT | CKF_MESSAGE_DECRYPT +#define CKF_KEM CKF_ENCAPSULATE | CKF_DECAPSULATE #define CKF_EN_DE_WR_UN CKF_EN_DE | CKF_WR_UN #define CKF_SN_VR_RE CKF_SN_VR | CKF_SN_RE @@ -651,10 +652,14 @@ static const struct mechanismList mechan { CKM_NSS_IKE1_PRF_DERIVE, { 8, 64, CKF_DERIVE }, PR_TRUE }, { CKM_NSS_IKE1_APP_B_PRF_DERIVE, { 8, 255 * 64, CKF_DERIVE }, PR_TRUE }, /* -------------------- Kyber Operations ----------------------- */ +#ifndef NSS_DISABLE_KYBER { CKM_NSS_KYBER_KEY_PAIR_GEN, { 0, 0, CKF_GENERATE_KEY_PAIR }, PR_TRUE }, - { CKM_NSS_KYBER, { 0, 0, 0 }, PR_TRUE }, + { CKM_NSS_KYBER, { 0, 0, CKF_KEM }, PR_TRUE }, +#endif { CKM_NSS_ML_KEM_KEY_PAIR_GEN, { 0, 0, CKF_GENERATE_KEY_PAIR }, PR_TRUE }, - { CKM_NSS_ML_KEM, { 0, 0, 0 }, PR_TRUE }, + { CKM_NSS_ML_KEM, { 0, 0, CKF_KEM }, PR_TRUE }, + { CKM_ML_KEM_KEY_PAIR_GEN, { 0, 0, CKF_GENERATE_KEY_PAIR }, PR_TRUE }, + { CKM_ML_KEM, { 0, 0, CKF_KEM }, PR_TRUE }, }; static const CK_ULONG mechanismCount = sizeof(mechanisms) / sizeof(mechanisms[0]); @@ -1036,6 +1041,7 @@ sftk_handlePublicKeyObject(SFTKSession * CK_BBOOL wrap = CK_TRUE; CK_BBOOL derive = CK_FALSE; CK_BBOOL verify = CK_TRUE; + CK_BBOOL encapsulate = CK_FALSE; CK_RV crv; switch (key_type) { @@ -1109,16 +1115,22 @@ sftk_handlePublicKeyObject(SFTKSession * recover = CK_FALSE; wrap = CK_FALSE; break; +#ifndef NSS_DISABLE_KYBER case CKK_NSS_KYBER: +#endif case CKK_NSS_ML_KEM: - if (!sftk_hasAttribute(object, CKA_NSS_PARAMETER_SET)) { - return CKR_TEMPLATE_INCOMPLETE; + case CKK_ML_KEM: + if (!sftk_hasAttribute(object, CKA_PARAMETER_SET)) { + if (!sftk_hasAttribute(object, CKA_NSS_PARAMETER_SET)) { + return CKR_TEMPLATE_INCOMPLETE; + } } derive = CK_FALSE; verify = CK_FALSE; encrypt = CK_FALSE; recover = CK_FALSE; wrap = CK_FALSE; + encapsulate = CK_TRUE; break; default: return CKR_ATTRIBUTE_VALUE_INVALID; @@ -1144,6 +1156,10 @@ sftk_handlePublicKeyObject(SFTKSession * crv = sftk_defaultAttribute(object, CKA_DERIVE, &derive, sizeof(CK_BBOOL)); if (crv != CKR_OK) return crv; + crv = sftk_defaultAttribute(object, CKA_ENCAPSULATE, &encapsulate, + sizeof(CK_BBOOL)); + if (crv != CKR_OK) + return crv; object->objectInfo = sftk_GetPubKey(object, key_type, &crv); if (object->objectInfo == NULL) { @@ -1196,6 +1212,7 @@ sftk_handlePrivateKeyObject(SFTKSession CK_BBOOL recover = CK_TRUE; CK_BBOOL wrap = CK_TRUE; CK_BBOOL derive = CK_TRUE; + CK_BBOOL encapsulate = CK_FALSE; CK_BBOOL ckfalse = CK_FALSE; PRBool createObjectInfo = PR_TRUE; PRBool fillPrivateKey = PR_FALSE; @@ -1323,15 +1340,24 @@ sftk_handlePrivateKeyObject(SFTKSession derive = CK_TRUE; createObjectInfo = PR_FALSE; break; +#ifndef NSS_DISABLE_KYBER case CKK_NSS_KYBER: +#endif case CKK_NSS_ML_KEM: + case CKK_ML_KEM: if (!sftk_hasAttribute(object, CKA_KEY_TYPE)) { return CKR_TEMPLATE_INCOMPLETE; } if (!sftk_hasAttribute(object, CKA_VALUE)) { return CKR_TEMPLATE_INCOMPLETE; } + if (!sftk_hasAttribute(object, CKA_PARAMETER_SET)) { + if (!sftk_hasAttribute(object, CKA_NSS_PARAMETER_SET)) { + return CKR_TEMPLATE_INCOMPLETE; + } + } encrypt = sign = recover = wrap = CK_FALSE; + encapsulate = CK_TRUE; break; default: return CKR_ATTRIBUTE_VALUE_INVALID; @@ -1361,6 +1387,9 @@ sftk_handlePrivateKeyObject(SFTKSession crv = sftk_defaultAttribute(object, CKA_DERIVE, &derive, sizeof(CK_BBOOL)); if (crv != CKR_OK) return crv; + crv = sftk_defaultAttribute(object, CKA_DECAPSULATE, &encapsulate, sizeof(CK_BBOOL)); + if (crv != CKR_OK) + return crv; /* the next two bits get modified only in the key gen and token cases */ crv = sftk_forceAttribute(object, CKA_ALWAYS_SENSITIVE, &ckfalse, sizeof(CK_BBOOL)); @@ -1372,7 +1401,6 @@ sftk_handlePrivateKeyObject(SFTKSession return crv; /* should we check the non-token RSA private keys? */ - if (sftk_isTrue(object, CKA_TOKEN)) { SFTKSlot *slot = session->slot; SFTKDBHandle *keyHandle = sftk_getKeyDB(slot); @@ -2017,8 +2045,11 @@ sftk_GetPubKey(SFTKObject *object, CK_KE crv = CKR_ATTRIBUTE_VALUE_INVALID; } break; +#ifndef NSS_DISABLE_KYBER case CKK_NSS_KYBER: +#endif case CKK_NSS_ML_KEM: + case CKK_ML_KEM: crv = CKR_OK; break; default: @@ -2187,8 +2218,11 @@ sftk_mkPrivKey(SFTKObject *object, CK_KE } break; +#ifndef NSS_DISABLE_KYBER case CKK_NSS_KYBER: +#endif case CKK_NSS_ML_KEM: + case CKK_ML_KEM: break; default: diff -up ./lib/softoken/pkcs11u.c.mlkem_p256 ./lib/softoken/pkcs11u.c --- ./lib/softoken/pkcs11u.c.mlkem_p256 2025-06-25 16:47:19.361362747 -0700 +++ ./lib/softoken/pkcs11u.c 2025-06-25 16:47:19.380362992 -0700 @@ -555,6 +555,11 @@ sftk_isTrue(SFTKObject *object, CK_ATTRI if (attribute == NULL) { return PR_FALSE; } + if ((attribute->attrib.pValue == NULL) || + (attribute->attrib.ulValueLen != sizeof(CK_BBOOL))) { + return PR_FALSE; + } + tok = (PRBool)(*(CK_BBOOL *)attribute->attrib.pValue); sftk_FreeAttribute(attribute); diff -up ./lib/softoken/sdb.c.mlkem_p256 ./lib/softoken/sdb.c --- ./lib/softoken/sdb.c.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./lib/softoken/sdb.c 2025-06-25 16:47:19.380362992 -0700 @@ -113,6 +113,7 @@ static const CK_ATTRIBUTE_TYPE known_att CKA_RESOLUTION, CKA_CHAR_ROWS, CKA_CHAR_COLUMNS, CKA_COLOR, CKA_BITS_PER_PIXEL, CKA_CHAR_SETS, CKA_ENCODING_METHODS, CKA_MIME_TYPES, CKA_MECHANISM_TYPE, CKA_REQUIRED_CMS_ATTRIBUTES, + CKA_ENCAPSULATE, CKA_DECAPSULATE, CKA_PARAMETER_SET, CKA_DEFAULT_CMS_ATTRIBUTES, CKA_SUPPORTED_CMS_ATTRIBUTES, CKA_WRAP_TEMPLATE, CKA_UNWRAP_TEMPLATE, CKA_NSS_TRUST, CKA_NSS_URL, CKA_NSS_EMAIL, CKA_NSS_SMIME_INFO, CKA_NSS_SMIME_TIMESTAMP, diff -up ./lib/softoken/sftkdb.c.mlkem_p256 ./lib/softoken/sftkdb.c --- ./lib/softoken/sftkdb.c.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./lib/softoken/sftkdb.c 2025-06-25 16:47:19.380362992 -0700 @@ -1763,6 +1763,7 @@ static const CK_ATTRIBUTE_TYPE known_att CKA_CHAR_COLUMNS, CKA_COLOR, CKA_BITS_PER_PIXEL, CKA_CHAR_SETS, CKA_ENCODING_METHODS, CKA_MIME_TYPES, CKA_MECHANISM_TYPE, CKA_REQUIRED_CMS_ATTRIBUTES, CKA_DEFAULT_CMS_ATTRIBUTES, + CKA_ENCAPSULATE, CKA_DECAPSULATE, CKA_PARAMETER_SET, CKA_SUPPORTED_CMS_ATTRIBUTES, CKA_NSS_URL, CKA_NSS_EMAIL, CKA_NSS_SMIME_INFO, CKA_NSS_SMIME_TIMESTAMP, CKA_NSS_PKCS8_SALT, CKA_NSS_PASSWORD_CHECK, CKA_NSS_EXPIRES, diff -up ./lib/softoken/softoken.h.mlkem_p256 ./lib/softoken/softoken.h --- ./lib/softoken/softoken.h.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./lib/softoken/softoken.h 2025-06-25 16:47:19.380362992 -0700 @@ -137,6 +137,24 @@ extern void sftk_AuditDeriveKey(CK_SESSI extern void sftk_AuditDigestKey(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey, CK_RV rv); +extern void sftk_AuditEncapsulate(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hPublicKey, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + CK_OBJECT_HANDLE_PTR phKey, + CK_BYTE_PTR pCiphertext, + CK_ULONG_PTR pulCiphertextLen, CK_RV rv); + +extern void sftk_AuditDecapsulate(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hPrivateKey, + CK_BYTE_PTR pCiphertext, + CK_ULONG ulCiphertextLen, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, + CK_OBJECT_HANDLE_PTR phKey, CK_RV rv); + /* ** FIPS 140-2 Error state */ diff -up ./lib/softoken/softoknt.h.mlkem_p256 ./lib/softoken/softoknt.h --- ./lib/softoken/softoknt.h.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./lib/softoken/softoknt.h 2025-06-25 16:47:19.380362992 -0700 @@ -40,7 +40,9 @@ typedef enum { NSS_AUDIT_SELF_TEST, NSS_AUDIT_SET_PIN, NSS_AUDIT_UNWRAP_KEY, - NSS_AUDIT_WRAP_KEY + NSS_AUDIT_WRAP_KEY, + NSS_AUDIT_ENCAPSULATE_KEY, + NSS_AUDIT_DECAPSULATE_KEY } NSSAuditType; #endif /* _SOFTOKNT_H_ */ diff -up ./lib/ssl/sslimpl.h.mlkem_p256 ./lib/ssl/sslimpl.h --- ./lib/ssl/sslimpl.h.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./lib/ssl/sslimpl.h 2025-06-25 16:47:19.381363005 -0700 @@ -130,7 +130,13 @@ typedef enum { SSLAppOpRead = 0, #define DTLS_RETRANSMIT_FINISHED_MS 30000 /* default number of entries in namedGroupPreferences */ +#ifndef NSS_DISABLE_KYBER +/* this define is checked against the namedGroup table + * and compile time asserts kick in if it doesn't match */ +#define SSL_NAMED_GROUP_COUNT 34 +#else #define SSL_NAMED_GROUP_COUNT 33 +#endif /* The maximum DH and RSA bit-length supported. */ #define SSL_MAX_DH_KEY_BITS 8192 diff -up ./lib/ssl/sslsock.c.mlkem_p256 ./lib/ssl/sslsock.c --- ./lib/ssl/sslsock.c.mlkem_p256 2025-06-25 16:47:19.358362709 -0700 +++ ./lib/ssl/sslsock.c 2025-06-25 16:47:19.381363005 -0700 @@ -22,6 +22,7 @@ #include "tls13ech.h" #include "tls13psk.h" #include "tls13subcerts.h" +#include "secmodti.h" /* until SEC_OID_SECP256R1MLKEM768 is upstream */ static const sslSocketOps ssl_default_ops = { /* No SSL. */ ssl_DefConnect, @@ -158,16 +159,24 @@ static const PRUint16 srtpCiphers[] = { ssl_grp_ffdhe_##size, size, ssl_kea_dh, \ SEC_OID_TLS_FFDHE_##size, PR_TRUE \ } +#define HYGROUP(kem, ec, size, kem_oid, ec_oid, assumeSupported) \ + { \ + ssl_grp_kem_##kem##ec, size, ssl_kea_ecdh_hybrid, \ + SEC_OID_##kem_oid##ec_oid, assumeSupported \ + } const sslNamedGroupDef ssl_named_groups[] = { - /* Note that 256 for 25519 is a lie, but we only use it for checking bit - * security and expect 256 bits there (not 255). */ + /* Note that 256 for 25519 and hybrid is a lie, but we only use it for + * checking bit security and expect 256 bits there (not 255). */ + HYGROUP(mlkem768, x25519, 256, MLKEM768, X25519, PR_TRUE), + HYGROUP(mlkem768, secp256r1, 256, MLKEM768, SECP256R1, PR_TRUE), { ssl_grp_ec_curve25519, 256, ssl_kea_ecdh, SEC_OID_CURVE25519, PR_TRUE }, ECGROUP(secp256r1, 256, SECP256R1, PR_TRUE), ECGROUP(secp384r1, 384, SECP384R1, PR_TRUE), ECGROUP(secp521r1, 521, SECP521R1, PR_TRUE), +#ifndef NSS_DISABLE_KYBER { ssl_grp_kem_xyber768d00, 256, ssl_kea_ecdh_hybrid, SEC_OID_XYBER768D00, PR_TRUE }, - { ssl_grp_kem_mlkem768x25519, 256, ssl_kea_ecdh_hybrid, SEC_OID_MLKEM768X25519, PR_TRUE }, +#endif FFGROUP(2048), FFGROUP(3072), FFGROUP(4096), diff -up ./lib/ssl/sslt.h.mlkem_p256 ./lib/ssl/sslt.h --- ./lib/ssl/sslt.h.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./lib/ssl/sslt.h 2025-06-25 16:47:19.381363005 -0700 @@ -261,7 +261,10 @@ typedef enum { ssl_grp_ffdhe_6144 = 259, ssl_grp_ffdhe_8192 = 260, ssl_grp_kem_mlkem768x25519 = 4588, + ssl_grp_kem_secp256r1mlkem768 = 4587, +#ifndef NSS_DISABLE_KYBER ssl_grp_kem_xyber768d00 = 25497, /* draft-tls-westerbaan-xyber768d00-02 */ +#endif ssl_grp_none = 65537, /* special value */ ssl_grp_ffdhe_custom = 65538 /* special value */ } SSLNamedGroup; diff -up ./lib/ssl/tls13con.c.mlkem_p256 ./lib/ssl/tls13con.c --- ./lib/ssl/tls13con.c.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./lib/ssl/tls13con.c 2025-06-25 16:47:19.382363018 -0700 @@ -382,13 +382,16 @@ tls13_CreateKEMKeyPair(sslSocket *ss, co CK_NSS_KEM_PARAMETER_SET_TYPE paramSet; switch (groupDef->name) { +#ifndef NSS_DISABLE_KYBER case ssl_grp_kem_xyber768d00: mechanism = CKM_NSS_KYBER_KEY_PAIR_GEN; paramSet = CKP_NSS_KYBER_768_ROUND3; break; +#endif case ssl_grp_kem_mlkem768x25519: + case ssl_grp_kem_secp256r1mlkem768: mechanism = CKM_NSS_ML_KEM_KEY_PAIR_GEN; - paramSet = CKP_NSS_ML_KEM_768; + paramSet = CKP_ML_KEM_768; break; default: PORT_Assert(0); @@ -400,15 +403,33 @@ tls13_CreateKEMKeyPair(sslSocket *ss, co if (!slot) { goto loser; } + /* avoid pairwise check in non-FIPS mode */ + /* the only difference between CKM_ML_KEM_KEY_PAIR_GEN and + * CKM_NSS_ML_KEM_KEY_PAIR_GEN is the latter skips the pairwise consistency + * check and is only supported by softoken */ + if ((mechanism == CKM_ML_KEM_KEY_PAIR_GEN) && !PK11_IsFIPS() && + PK11_DoesMechanism(slot, CKM_NSS_ML_KEM_KEY_PAIR_GEN)) { + mechanism = CKM_NSS_ML_KEM_KEY_PAIR_GEN; + } privKey = PK11_GenerateKeyPairWithOpFlags(slot, mechanism, - ¶mSet, &pubKey, PK11_ATTR_SESSION | PK11_ATTR_INSENSITIVE | PK11_ATTR_PUBLIC, - CKF_DERIVE, CKF_DERIVE, ss->pkcs11PinArg); + ¶mSet, &pubKey, + PK11_ATTR_SESSION | + PK11_ATTR_INSENSITIVE | + PK11_ATTR_PUBLIC, + CKF_ENCAPSULATE|CKF_DECAPSULATE, + CKF_ENCAPSULATE|CKF_DECAPSULATE, + ss->pkcs11PinArg); if (!privKey) { privKey = PK11_GenerateKeyPairWithOpFlags(slot, mechanism, - ¶mSet, &pubKey, PK11_ATTR_SESSION | PK11_ATTR_SENSITIVE | PK11_ATTR_PRIVATE, - CKF_DERIVE, CKF_DERIVE, ss->pkcs11PinArg); + ¶mSet, &pubKey, + PK11_ATTR_SESSION | + PK11_ATTR_SENSITIVE | + PK11_ATTR_PRIVATE, + CKF_ENCAPSULATE|CKF_DECAPSULATE, + CKF_ENCAPSULATE|CKF_DECAPSULATE, + ss->pkcs11PinArg); } PK11_FreeSlot(slot); @@ -448,6 +469,89 @@ loser: return SECFailure; } +/* only copy the ECDh component of an ephemeral KeyPair */ +sslEphemeralKeyPair * +tls13_CopyDHEphemeralKeyPair(sslEphemeralKeyPair *copyKeyPair, + const sslNamedGroupDef *groupDef) { + /* We could use ssl_CopyEphemeralKeyPair here, but we would need to free + * the KEM components. So we only copy the ECDH keys */ + sslEphemeralKeyPair *keyPair = PORT_ZNew(sslEphemeralKeyPair); + if (!keyPair) { + return NULL; + } + PR_INIT_CLIST(&keyPair->link); + keyPair->group = groupDef; + keyPair->keys = ssl_GetKeyPairRef(copyKeyPair->keys); + return keyPair; +} + +/* + * find a hybrid key Pair they might contain the same ecdh key so we + * can reuse them. Each ec group can map to more than one hybrid Pair + */ +sslEphemeralKeyPair * +tls13_FindHybridKeyPair(sslSocket *ss, const sslNamedGroupDef *groupDef) +{ + sslEphemeralKeyPair *hybridPair = NULL; + switch (groupDef->name) { + case ssl_grp_ec_secp256r1: + /* future, this may be a loop to check multiple named groups */ + hybridPair = ssl_LookupEphemeralKeyPair(ss, + ssl_LookupNamedGroup(ssl_grp_kem_secp256r1mlkem768)); + break; + case ssl_grp_ec_curve25519: +#ifndef NSS_DISABLE_KYBER + /* a loop to check multiple named groups */ + SSLNamedGroup gnames[] = { ssl_grp_kem_xyber768d00, + ssl_grp_kem_mlkem768x25519 }; + for (int i=0; i < PR_ARRAY_SIZE(gnames); i++) { + hybridPair = ssl_LookupEphemeralKeyPair(ss, + ssl_LookupNamedGroup(gnames[i])); + if (hybridPair != NULL) { + break; + } + } +#else + /* future, this will be a loop to check multiple named groups */ + hybridPair = ssl_LookupEphemeralKeyPair(ss, + ssl_LookupNamedGroup(ssl_grp_kem_mlkem768x25519)); +#endif + break; + default: + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return NULL; + } + return hybridPair; +} + +/* + * Find a corresponding KEM form another hybrid key + */ +#ifdef notdef +sslKeyPair * +tls13_FindKEMKeyPairFromHybrid(sslSocket *ss, const sslNamedGroupDef *groupDef) +{ + sslEphemeralKeyPair *hybridPair = NULL; + switch (groupDef->name) { + case ssl_grp_kem_mlkem768x25519: + /* future, this may be a loop to check multiple named groups */ + hybridPair = ssl_LookupEphemeralKeyPair(ss, + ssl_LookupNamedGroup(ssl_grp_kem_secp256r1mlkem768)); + break; + case ssl_grp_kem_secp256r1mlkem768: + /* future, this will be a loop to check multiple named groups */ + hybridPair = ssl_LookupEphemeralKeyPair(ss, + ssl_LookupNamedGroup(ssl_grp_kem_mlkem768x25519)); + break; + default: + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return NULL; + } + return hybridPair->kemKeys; +} +#endif + + SECStatus tls13_CreateKeyShare(sslSocket *ss, const sslNamedGroupDef *groupDef, sslEphemeralKeyPair **outKeyPair) @@ -455,21 +559,35 @@ tls13_CreateKeyShare(sslSocket *ss, cons SECStatus rv; const ssl3DHParams *params; sslEphemeralKeyPair *keyPair = NULL; + const sslNamedGroupDef *dhGroup = NULL; PORT_Assert(groupDef); switch (groupDef->keaType) { case ssl_kea_ecdh_hybrid: - if (groupDef->name != ssl_grp_kem_xyber768d00 && groupDef->name != ssl_grp_kem_mlkem768x25519) { + switch (groupDef->name) { + case ssl_grp_kem_secp256r1mlkem768: + dhGroup = ssl_LookupNamedGroup(ssl_grp_ec_secp256r1); + break; +#ifndef NSS_DISABLE_KYBER + case ssl_grp_kem_xyber768d00: +#endif + case ssl_grp_kem_mlkem768x25519: + dhGroup = ssl_LookupNamedGroup(ssl_grp_ec_curve25519); + break; + default: + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + if (dhGroup == NULL) { PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure; } - const sslNamedGroupDef *x25519 = ssl_LookupNamedGroup(ssl_grp_ec_curve25519); - sslEphemeralKeyPair *x25519Pair = ssl_LookupEphemeralKeyPair(ss, x25519); - if (x25519Pair) { - keyPair = ssl_CopyEphemeralKeyPair(x25519Pair); + keyPair = ssl_LookupEphemeralKeyPair(ss, dhGroup); + if (keyPair) { + keyPair= ssl_CopyEphemeralKeyPair(keyPair); } if (!keyPair) { - rv = ssl_CreateECDHEphemeralKeyPair(ss, x25519, &keyPair); + rv = ssl_CreateECDHEphemeralKeyPair(ss, dhGroup, &keyPair); if (rv != SECSuccess) { return SECFailure; } @@ -477,23 +595,9 @@ tls13_CreateKeyShare(sslSocket *ss, cons keyPair->group = groupDef; break; case ssl_kea_ecdh: - if (groupDef->name == ssl_grp_ec_curve25519) { - sslEphemeralKeyPair *hybridPair = ssl_LookupEphemeralKeyPair(ss, ssl_LookupNamedGroup(ssl_grp_kem_mlkem768x25519)); - if (!hybridPair) { - hybridPair = ssl_LookupEphemeralKeyPair(ss, ssl_LookupNamedGroup(ssl_grp_kem_xyber768d00)); - } - if (hybridPair) { - // We could use ssl_CopyEphemeralKeyPair here, but we would need to free - // the KEM components. We should pull this out into a utility function when - // we refactor to support multiple hybrid mechanisms. - keyPair = PORT_ZNew(sslEphemeralKeyPair); - if (!keyPair) { - return SECFailure; - } - PR_INIT_CLIST(&keyPair->link); - keyPair->group = groupDef; - keyPair->keys = ssl_GetKeyPairRef(hybridPair->keys); - } + keyPair = tls13_FindHybridKeyPair(ss, groupDef); + if (keyPair) { + keyPair = tls13_CopyDHEphemeralKeyPair(keyPair, groupDef); } if (!keyPair) { rv = ssl_CreateECDHEphemeralKeyPair(ss, groupDef, &keyPair); @@ -519,11 +623,27 @@ tls13_CreateKeyShare(sslSocket *ss, cons // If we're creating an ECDH + KEM hybrid share and we're the client, then // we still need to generate the KEM key pair. Otherwise we're done. if (groupDef->keaType == ssl_kea_ecdh_hybrid && !ss->sec.isServer) { +#ifdef notdef + sslKeyPair *kemPair = NULL; + kemPair = tls13_FindKEMKeyPairFromHybrid(ss, groupDef); + if (kemPair) { + kemPair = ssl_GetKeyPairRef(kemPair); + } + if (!kemPair) { + rv = tls13_CreateKEMKeyPair(ss, groupDef, &kemPair); + if (rv != SECSuccess) { + ssl_FreeEphemeralKeyPair(keyPair); + return SECFailure; + } + } + keyPair->kemKeys = kemPair; +#else rv = tls13_CreateKEMKeyPair(ss, groupDef, &keyPair->kemKeys); if (rv != SECSuccess) { ssl_FreeEphemeralKeyPair(keyPair); return SECFailure; } +#endif } *outKeyPair = keyPair; @@ -706,12 +826,17 @@ tls13_ImportKEMKeyShare(SECKEYPublicKey size_t expected_len; switch (entry->group->name) { +#ifndef NSS_DISABLE_KYBER case ssl_grp_kem_xyber768d00: expected_len = X25519_PUBLIC_KEY_BYTES + KYBER768_PUBLIC_KEY_BYTES; break; +#endif case ssl_grp_kem_mlkem768x25519: expected_len = X25519_PUBLIC_KEY_BYTES + KYBER768_PUBLIC_KEY_BYTES; break; + case ssl_grp_kem_secp256r1mlkem768: + expected_len = SECP256_PUBLIC_KEY_BYTES + KYBER768_PUBLIC_KEY_BYTES; + break; default: PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG); return SECFailure; @@ -723,6 +848,7 @@ tls13_ImportKEMKeyShare(SECKEYPublicKey } switch (entry->group->name) { +#ifndef NSS_DISABLE_KYBER case ssl_grp_kem_xyber768d00: peerKey->keyType = kyberKey; peerKey->u.kyber.params = params_kyber768_round3; @@ -730,6 +856,7 @@ tls13_ImportKEMKeyShare(SECKEYPublicKey pk.data = entry->key_exchange.data + X25519_PUBLIC_KEY_BYTES; pk.len = KYBER768_PUBLIC_KEY_BYTES; break; +#endif case ssl_grp_kem_mlkem768x25519: peerKey->keyType = kyberKey; peerKey->u.kyber.params = params_ml_kem768; @@ -737,6 +864,13 @@ tls13_ImportKEMKeyShare(SECKEYPublicKey pk.data = entry->key_exchange.data; pk.len = KYBER768_PUBLIC_KEY_BYTES; break; + case ssl_grp_kem_secp256r1mlkem768: + peerKey->keyType = kyberKey; + peerKey->u.kyber.params = params_ml_kem768; + /* key_exchange.data is `secp256 || mlkem768` */ + pk.data = entry->key_exchange.data + SECP256_PUBLIC_KEY_BYTES; + pk.len = KYBER768_PUBLIC_KEY_BYTES; + break; default: PORT_Assert(0); PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); @@ -759,6 +893,7 @@ tls13_HandleKEMCiphertext(sslSocket *ss, SECStatus rv; switch (entry->group->name) { +#ifndef NSS_DISABLE_KYBER case ssl_grp_kem_xyber768d00: if (entry->key_exchange.len != X25519_PUBLIC_KEY_BYTES + KYBER768_CIPHERTEXT_BYTES) { ssl_MapLowLevelError(SSL_ERROR_RX_MALFORMED_HYBRID_KEY_SHARE); @@ -767,6 +902,7 @@ tls13_HandleKEMCiphertext(sslSocket *ss, ct.data = entry->key_exchange.data + X25519_PUBLIC_KEY_BYTES; ct.len = KYBER768_CIPHERTEXT_BYTES; break; +#endif case ssl_grp_kem_mlkem768x25519: if (entry->key_exchange.len != X25519_PUBLIC_KEY_BYTES + KYBER768_CIPHERTEXT_BYTES) { ssl_MapLowLevelError(SSL_ERROR_RX_MALFORMED_HYBRID_KEY_SHARE); @@ -775,13 +911,22 @@ tls13_HandleKEMCiphertext(sslSocket *ss, ct.data = entry->key_exchange.data; ct.len = KYBER768_CIPHERTEXT_BYTES; break; + case ssl_grp_kem_secp256r1mlkem768: + if (entry->key_exchange.len != SECP256_PUBLIC_KEY_BYTES + KYBER768_CIPHERTEXT_BYTES) { + ssl_MapLowLevelError(SSL_ERROR_RX_MALFORMED_HYBRID_KEY_SHARE); + return SECFailure; + } + ct.data = entry->key_exchange.data + SECP256_PUBLIC_KEY_BYTES; + ct.len = KYBER768_CIPHERTEXT_BYTES; + break; default: PORT_Assert(0); ssl_MapLowLevelError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure; } - rv = PK11_Decapsulate(keyPair->privKey, &ct, CKM_HKDF_DERIVE, PK11_ATTR_SESSION | PK11_ATTR_INSENSITIVE, CKF_DERIVE, outKey); + rv = PK11_Decapsulate(keyPair->privKey, &ct, CKM_HKDF_DERIVE, PK11_ATTR_SESSION, + CKF_DERIVE, outKey); if (rv != SECSuccess) { ssl_MapLowLevelError(SSL_ERROR_KEY_EXCHANGE_FAILURE); } @@ -813,9 +958,12 @@ tls13_HandleKEMKey(sslSocket *ss, goto loser; } - PK11SlotInfo *slot = PK11_GetBestSlot(CKM_NSS_KYBER, ss->pkcs11PinArg); + PK11SlotInfo *slot = PK11_GetBestSlot(CKM_ML_KEM, ss->pkcs11PinArg); if (!slot) { - goto loser; + slot = PK11_GetBestSlot(CKM_NSS_ML_KEM, ss->pkcs11PinArg); + if (!slot) { + goto loser; + } } handle = PK11_ImportPublicKey(slot, peerKey, PR_FALSE); @@ -825,7 +973,7 @@ tls13_HandleKEMKey(sslSocket *ss, } rv = PK11_Encapsulate(peerKey, - CKM_HKDF_DERIVE, PK11_ATTR_SESSION | PK11_ATTR_INSENSITIVE | PK11_ATTR_PUBLIC, + CKM_HKDF_DERIVE, PK11_ATTR_SESSION, CKF_DERIVE, key, ciphertext); /* Destroy the imported public key */ @@ -855,6 +1003,8 @@ tls13_HandleKeyShare(sslSocket *ss, unsigned char *ec_data; SECStatus rv; int keySize = 0; + const sslNamedGroupDef *dhGroup = NULL; + int dhLen = 0; PORT_InitCheapArena(&arena, DER_DEFAULT_CHUNKSIZE); peerKey = PORT_ArenaZNew(&arena.arena, SECKEYPublicKey); @@ -868,17 +1018,31 @@ tls13_HandleKeyShare(sslSocket *ss, switch (entry->group->keaType) { case ssl_kea_ecdh_hybrid: switch (entry->group->name) { +#ifndef NSS_DISABLE_KYBER case ssl_grp_kem_xyber768d00: + dhLen = X25519_PUBLIC_KEY_BYTES; // x25519 share is at the beginning - ec_data = entry->key_exchange.len < X25519_PUBLIC_KEY_BYTES + ec_data = entry->key_exchange.len < dhLen ? NULL : entry->key_exchange.data; + dhGroup = ssl_LookupNamedGroup(ssl_grp_ec_curve25519); break; +#endif case ssl_grp_kem_mlkem768x25519: + dhLen = X25519_PUBLIC_KEY_BYTES; // x25519 share is at the end - ec_data = entry->key_exchange.len < X25519_PUBLIC_KEY_BYTES + ec_data = entry->key_exchange.len < dhLen ? NULL - : entry->key_exchange.data + entry->key_exchange.len - X25519_PUBLIC_KEY_BYTES; + : entry->key_exchange.data + entry->key_exchange.len - dhLen; + dhGroup = ssl_LookupNamedGroup(ssl_grp_ec_curve25519); + break; + case ssl_grp_kem_secp256r1mlkem768: + dhLen = SECP256_PUBLIC_KEY_BYTES; + /* secp256 share is at the beginning */ + ec_data = entry->key_exchange.len < dhLen + ? NULL + : entry->key_exchange.data; + dhGroup = ssl_LookupNamedGroup(ssl_grp_ec_secp256r1); break; default: ec_data = NULL; @@ -888,10 +1052,7 @@ tls13_HandleKeyShare(sslSocket *ss, PORT_SetError(SSL_ERROR_RX_MALFORMED_HYBRID_KEY_SHARE); goto loser; } - rv = ssl_ImportECDHKeyShare(peerKey, - ec_data, - X25519_PUBLIC_KEY_BYTES, - ssl_LookupNamedGroup(ssl_grp_ec_curve25519)); + rv = ssl_ImportECDHKeyShare(peerKey, ec_data, dhLen, dhGroup); mechanism = CKM_ECDH1_DERIVE; break; case ssl_kea_ecdh: @@ -927,7 +1088,7 @@ tls13_HandleKeyShare(sslSocket *ss, *out = key; PORT_DestroyCheapArena(&arena); - return SECSuccess; + return rv; loser: PORT_DestroyCheapArena(&arena); @@ -2762,12 +2923,17 @@ tls13_HandleClientKeyShare(sslSocket *ss goto loser; /* Error set by tls13_HandleKEMKey */ } switch (peerShare->group->name) { +#ifndef NSS_DISABLE_KYBER case ssl_grp_kem_xyber768d00: ss->ssl3.hs.dheSecret = PK11_ConcatSymKeys(dheSecret, kemSecret, CKM_HKDF_DERIVE, CKA_DERIVE); break; +#endif case ssl_grp_kem_mlkem768x25519: ss->ssl3.hs.dheSecret = PK11_ConcatSymKeys(kemSecret, dheSecret, CKM_HKDF_DERIVE, CKA_DERIVE); break; + case ssl_grp_kem_secp256r1mlkem768: + ss->ssl3.hs.dheSecret = PK11_ConcatSymKeys(dheSecret, kemSecret, CKM_HKDF_DERIVE, CKA_DERIVE); + break; default: PORT_Assert(0); PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); @@ -3621,12 +3787,17 @@ tls13_HandleServerKeyShare(sslSocket *ss goto loser; /* Error set by tls13_HandleKEMCiphertext */ } switch (entry->group->name) { +#ifndef NSS_DISABLE_KYBER case ssl_grp_kem_xyber768d00: ss->ssl3.hs.dheSecret = PK11_ConcatSymKeys(dheSecret, kemSecret, CKM_HKDF_DERIVE, CKA_DERIVE); break; +#endif case ssl_grp_kem_mlkem768x25519: ss->ssl3.hs.dheSecret = PK11_ConcatSymKeys(kemSecret, dheSecret, CKM_HKDF_DERIVE, CKA_DERIVE); break; + case ssl_grp_kem_secp256r1mlkem768: + ss->ssl3.hs.dheSecret = PK11_ConcatSymKeys(dheSecret, kemSecret, CKM_HKDF_DERIVE, CKA_DERIVE); + break; default: PORT_Assert(0); PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); diff -up ./lib/ssl/tls13exthandle.c.mlkem_p256 ./lib/ssl/tls13exthandle.c --- ./lib/ssl/tls13exthandle.c.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./lib/ssl/tls13exthandle.c 2025-06-25 16:47:19.382363018 -0700 @@ -80,19 +80,30 @@ tls13_SizeOfKeyShareEntry(const sslEphem if (keyPair->kemKeys) { PORT_Assert(!keyPair->kemCt); - PORT_Assert(keyPair->group->name == ssl_grp_kem_xyber768d00 || keyPair->group->name == ssl_grp_kem_mlkem768x25519); + PORT_Assert( +#ifndef NSS_DISABLE_KYBER + keyPair->group->name == ssl_grp_kem_xyber768d00 || +#endif + keyPair->group->name == ssl_grp_kem_mlkem768x25519 || + keyPair->group->name == ssl_grp_kem_secp256r1mlkem768); pubKey = keyPair->kemKeys->pubKey; size += pubKey->u.kyber.publicValue.len; } if (keyPair->kemCt) { PORT_Assert(!keyPair->kemKeys); - PORT_Assert(keyPair->group->name == ssl_grp_kem_xyber768d00 || keyPair->group->name == ssl_grp_kem_mlkem768x25519); + PORT_Assert( +#ifndef NSS_DISABLE_KYBER + keyPair->group->name == ssl_grp_kem_xyber768d00 || +#endif + keyPair->group->name == ssl_grp_kem_mlkem768x25519 || + keyPair->group->name == ssl_grp_kem_secp256r1mlkem768); size += keyPair->kemCt->len; } return size; } +#ifndef NSS_DISABLE_KYBER static SECStatus tls13_WriteXyber768D00KeyExchangeInfo(sslBuffer *buf, sslEphemeralKeyPair *keyPair) { @@ -119,6 +130,34 @@ tls13_WriteXyber768D00KeyExchangeInfo(ss } return rv; } +#endif + +static SECStatus +tls13_WriteMLKEM768Secp256r1KeyExchangeInfo(sslBuffer *buf, sslEphemeralKeyPair *keyPair) + { + PORT_Assert(keyPair->group->name == ssl_grp_kem_secp256r1mlkem768); + PORT_Assert(keyPair->keys->pubKey->keyType == ecKey); + + // Encode the p256 key first, then the Kyber768 key or ciphertext. + SECStatus rv; + rv = sslBuffer_Append(buf, keyPair->keys->pubKey->u.ec.publicValue.data, + keyPair->keys->pubKey->u.ec.publicValue.len); + if (rv != SECSuccess) { + return rv; + } + + if (keyPair->kemKeys) { + PORT_Assert(!keyPair->kemCt); + rv = sslBuffer_Append(buf, keyPair->kemKeys->pubKey->u.kyber.publicValue.data, keyPair->kemKeys->pubKey->u.kyber.publicValue.len); + } else if (keyPair->kemCt) { + rv = sslBuffer_Append(buf, keyPair->kemCt->data, keyPair->kemCt->len); + } else { + PORT_Assert(0); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + rv = SECFailure; + } + return rv; +} static SECStatus tls13_WriteMLKEM768X25519KeyExchangeInfo(sslBuffer *buf, sslEphemeralKeyPair *keyPair) @@ -187,12 +226,18 @@ tls13_EncodeKeyShareEntry(sslBuffer *buf } switch (keyPair->group->name) { +#ifndef NSS_DISABLE_KYBER + case ssl_grp_kem_xyber768d00: + rv = tls13_WriteXyber768D00KeyExchangeInfo(buf, keyPair); + break; +#endif case ssl_grp_kem_mlkem768x25519: rv = tls13_WriteMLKEM768X25519KeyExchangeInfo(buf, keyPair); break; - case ssl_grp_kem_xyber768d00: - rv = tls13_WriteXyber768D00KeyExchangeInfo(buf, keyPair); + case ssl_grp_kem_secp256r1mlkem768: + rv = tls13_WriteMLKEM768Secp256r1KeyExchangeInfo(buf, keyPair); break; + default: rv = tls13_WriteKeyExchangeInfo(buf, keyPair); break; diff -up ./lib/util/eccutil.h.mlkem_p256 ./lib/util/eccutil.h --- ./lib/util/eccutil.h.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./lib/util/eccutil.h 2025-06-25 16:47:19.382363018 -0700 @@ -6,6 +6,7 @@ #define _FREEBL_H_ #define X25519_PUBLIC_KEY_BYTES 32U +#define SECP256_PUBLIC_KEY_BYTES 65U /* deprecated */ typedef enum { diff -up ./lib/util/pkcs11n.h.mlkem_p256 ./lib/util/pkcs11n.h diff -up ./lib/util/pkcs11t.h.mlkem_p256 ./lib/util/pkcs11t.h --- ./lib/util/pkcs11t.h.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./lib/util/pkcs11t.h 2025-06-25 16:47:19.382363018 -0700 @@ -755,6 +755,21 @@ typedef CK_ULONG CK_MECHANISM_TYPE; #define CKM_DSA_SHA3_384 0x0000001AUL #define CKM_DSA_SHA3_512 0x0000001BUL +/* subset of pkcs11v3.2 defines needed for + * mlkem support */ +#define CKM_ML_KEM 0x00000017UL +#define CKM_ML_KEM_KEY_PAIR_GEN 0x0000000fUL +#define CKK_ML_KEM 0x00000049UL +typedef CK_ULONG CK_ML_KEM_PARAMETER_SET_TYPE; +#define CKP_ML_KEM_512 0x00000001UL +#define CKP_ML_KEM_768 0x00000002UL +#define CKP_ML_KEM_1024 0x00000003UL +#define CKA_PARAMETER_SET 0x0000061dUL +#define CKA_ENCAPSULATE 0x00000633UL +#define CKA_DECAPSULATE 0x00000634UL +#define CKF_DECAPSULATE 0x20000000UL +#define CKF_ENCAPSULATE 0x10000000UL + #define CKM_DH_PKCS_KEY_PAIR_GEN 0x00000020UL #define CKM_DH_PKCS_DERIVE 0x00000021UL diff -up ./lib/util/secoid.c.mlkem_p256 ./lib/util/secoid.c --- ./lib/util/secoid.c.mlkem_p256 2025-06-25 16:47:19.354362658 -0700 +++ ./lib/util/secoid.c 2025-06-25 16:47:19.382363018 -0700 @@ -1900,6 +1900,12 @@ const static SECOidData oids[SEC_OID_TOT "ML-KEM-768+X25519 key exchange", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION), ODE(SEC_OID_TLS_REQUIRE_EMS, "TLS Require EMS", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION), +/* this will change upstream. for now apps shouldn't use it */ +/* we need it for the policy code. */ + ODE(SEC_OID_PRIVATE_1, + "ML-KEM-768+SECP256 key exchange", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION), + ODE(SEC_OID_PRIVATE_2, + "ML-KEM-1024+SECP256 key exchange", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION), }; diff -up ./lib/util/secoidt.h.mlkem_p256 ./lib/util/secoidt.h --- ./lib/util/secoidt.h.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./lib/util/secoidt.h 2025-06-25 16:47:19.382363018 -0700 @@ -536,6 +536,12 @@ typedef enum { SEC_OID_TLS_REQUIRE_EMS = 390, + /* these will change upstream. for now apps shouldn't use it */ + /* give it an obscure name here */ + + SEC_OID_PRIVATE_1 = 391, + SEC_OID_PRIVATE_2 = 392, + SEC_OID_TOTAL } SECOidTag; diff -up ./lib/util/utilmodt.h.mlkem_p256 ./lib/util/utilmodt.h --- ./lib/util/utilmodt.h.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./lib/util/utilmodt.h 2025-06-25 16:47:19.382363018 -0700 @@ -28,6 +28,7 @@ #define SECMOD_CAMELLIA_FLAG 0x00010000L /* = PUBLIC_MECH_CAMELLIA_FLAG */ #define SECMOD_SEED_FLAG 0x00020000L #define SECMOD_ECC_FLAG 0x00040000L +#define SECMOD_MLKEM_FLAG 0x00080000L /* reserved bit for future, do not use */ #define SECMOD_RESERVED_FLAG 0X08000000L #define SECMOD_FRIENDLY_FLAG 0x10000000L diff -up ./lib/util/utilpars.c.mlkem_p256 ./lib/util/utilpars.c --- ./lib/util/utilpars.c.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./lib/util/utilpars.c 2025-06-25 16:47:19.382363018 -0700 @@ -609,6 +609,7 @@ static struct nssutilArgSlotFlagTable ns NSSUTIL_ARG_ENTRY(SEED, SECMOD_SEED_FLAG), NSSUTIL_ARG_ENTRY(PublicCerts, SECMOD_FRIENDLY_FLAG), NSSUTIL_ARG_ENTRY(RANDOM, SECMOD_RANDOM_FLAG), + NSSUTIL_ARG_ENTRY(MLKEM, SECMOD_MLKEM_FLAG), NSSUTIL_ARG_ENTRY(Disable, SECMOD_DISABLE_FLAG), }; diff -up ./lib/util/utilparst.h.mlkem_p256 ./lib/util/utilparst.h --- ./lib/util/utilparst.h.mlkem_p256 2025-05-23 06:07:49.000000000 -0700 +++ ./lib/util/utilparst.h 2025-06-25 16:47:19.382363018 -0700 @@ -43,7 +43,7 @@ #define NSSUTIL_DEFAULT_INTERNAL_INIT3 \ " askpw=any timeout=30})\"" #define NSSUTIL_DEFAULT_SFTKN_FLAGS \ - "slotFlags=[ECC,RSA,DSA,DH,RC2,RC4,DES,RANDOM,SHA1,MD5,MD2,SSL,TLS,AES,Camellia,SEED,SHA256,SHA512]" + "slotFlags=[ECC,RSA,DSA,DH,RC2,RC4,DES,RANDOM,SHA1,MD5,MD2,SSL,TLS,AES,Camellia,SEED,MLKEM,SHA256,SHA512]" #define NSSUTIL_DEFAULT_CIPHER_ORDER 0 #define NSSUTIL_DEFAULT_TRUST_ORDER 50 diff -up ./tests/ssl/sslcov.txt.mlkem_p256 ./tests/ssl/sslcov.txt --- ./tests/ssl/sslcov.txt.mlkem_p256 2025-06-25 16:47:19.370362863 -0700 +++ ./tests/ssl/sslcov.txt 2025-06-25 16:47:19.383363031 -0700 @@ -4,7 +4,7 @@ # # This file enables test coverage of the various SSL ciphers # -# Enable Enable Cipher Test Name +# Enable Enable Cipher Test Name # EC TLS # noECC SSL3 c SSL3_RSA_WITH_RC4_128_MD5 @@ -147,3 +147,9 @@ ECC TLS13 :1301 TLS13_ECDHE_WITH_AES_128_GCM_SHA256 ECC TLS13 :1302 TLS13_ECDHE_WITH_AES_256_GCM_SHA384 ECC TLS13 :1303 TLS13_ECDHE_WITH_CHACHA20_POLY1305_SHA256 +MLKEM256 TLS13 :1301 TLS13_MLKEMP256_WITH_AES_128_GCM_SHA256 +MLKEM256 TLS13 :1302 TLS13_MLKEMP256_WITH_AES_256_GCM_SHA384 +MLKEM256 TLS13 :1303 TLS13_MLKEMP256_WITH_CHACHA20_POLY1305_SHA256 +MLKEM219 TLS13 :1301 TLS13_MLKEMX25519_WITH_AES_128_GCM_SHA256 +MLKEM219 TLS13 :1302 TLS13_MLKEMX25519_WITH_AES_256_GCM_SHA384 +MLKEM219 TLS13 :1303 TLS13_MLKEMX25519_WITH_CHACHA20_POLY1305_SHA256 diff -up ./tests/ssl/ssl.sh.mlkem_p256 ./tests/ssl/ssl.sh --- ./tests/ssl/ssl.sh.mlkem_p256 2025-06-25 16:47:19.370362863 -0700 +++ ./tests/ssl/ssl.sh 2025-06-25 16:47:19.383363031 -0700 @@ -121,8 +121,12 @@ ssl_init() CIPHER_SUITES="-c ${EC_SUITES}${NON_EC_SUITES}" TLS13_CIPHER_SUITES="-c ${TLS13_SUITES}${EC_SUITES}${NON_EC_SUITES}" + # FIPS specific options for both clients and servers + FIPS_OPTIONS="" # in fips mode, turn off curve25519 until it's NIST approved - FIPS_OPTIONS="-I P256,P384,P521,FF2048,FF3072,FF4096,FF6144,FF8192" + ALL_GROUPS="P256,P384,P521,x25519,FF2048,FF3072,FF4096,FF6144,FF8192,mlkem768secp256r1,mlkem768x25519" + NON_PQ_GROUPS="P256,P384,P521,x25519,FF2048,FF3072,FF4096,FF6144,FF8192" + FIPS_GROUPS="P256,P384,P521,FF2048,FF3072,FF4096,FF6144,FF8192,mlkem768secp256r1,mlkem768x25519" # in non-fips mode, tstclnt may run without the db password in some # cases, but in fips mode it's always needed @@ -312,10 +316,12 @@ ssl_cov() SAVE_SERVER_OPTIONS=${SERVER_OPTIONS} if [ "${SERVER_MODE}" = "fips" ] ; then - SERVER_OPTIONS="${SERVER_OPTIONS} ${FIPS_OPTIONS}" + SERVER_OPTIONS="${SERVER_OPTIONS} -I ${FIPS_GROUPS} ${FIPS_OPTIONS}" fi SAVE_CLIENT_OPTIONS=${CLIENT_OPTIONS} + CLIENT_GROUPS=${NON_PQ_GROUPS} if [ "${CLIENT_MODE}" = "fips" ] ; then + CLIENT_GROUPS=${FIPS_GROUPS} CLIENT_OPTIONS="${CLIENT_OPTIONS} ${FIPS_OPTIONS}" fi @@ -340,7 +346,7 @@ ssl_cov() ;; esac - echo "$SCRIPTNAME: running $testname ----------------------------" + echo "$SCRIPTNAME: running $testname ------------------" VMAX="ssl3" if [ "$testmax" = "TLS10" ]; then VMAX="tls1.0" @@ -375,13 +381,19 @@ ssl_cov() VMIN="ssl3" fi + TLS_GROUPS=${CLIENT_GROUPS} + if [ "$ectype" = "MLKEM256" ]; then + TLS_GROUPS="mlkem768secp256r1" + elif [ "$ectype" = "MLKEM219" ]; then + TLS_GROUPS="mlkem768x25519" + fi + echo "TLS_GROUPS=${TLS_GROUPS}" - - echo "tstclnt -4 -p ${PORT} -h ${HOSTADDR} -c ${param} -V ${VMIN}:${VMAX} ${CLIENT_OPTIONS} \\" + echo "tstclnt -4 -p ${PORT} -h ${HOSTADDR} -c ${param} -I \"${TLS_GROUPS}\" -V ${VMIN}:${VMAX} ${CLIENT_OPTIONS} \\" echo " -f -d ${P_R_CLIENTDIR} $verbose -w nss < ${REQUEST_FILE}" rm ${TMP}/$HOST.tmp.$$ 2>/dev/null - ${PROFTOOL} ${BINDIR}/tstclnt -4 -p ${PORT} -h ${HOSTADDR} -c ${param} -V ${VMIN}:${VMAX} ${CLIENT_OPTIONS} -f \ + ${PROFTOOL} ${BINDIR}/tstclnt -4 -p ${PORT} -h ${HOSTADDR} -c ${param} -I "${TLS_GROUPS}" -V ${VMIN}:${VMAX} ${CLIENT_OPTIONS} -f \ -d ${P_R_CLIENTDIR} $verbose -w nss < ${REQUEST_FILE} \ >${TMP}/$HOST.tmp.$$ 2>&1 ret=$?