nss/nss-3.112-replace-xyber-with-mlkem-256.patch
Robert Relyea 2a8572a8f9 Resolves: RHEL-103353
rebase NSS to 3.112
Include mlkem1024 support and ml-dsa support for tls
2025-07-14 09:01:26 -07:00

4028 lines
163 KiB
Diff

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<TlsReplaceSignatureSchemeFilter>(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<TlsReplaceSignatureSchemeFilter>(
@@ -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<BeforeFinished13>(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<TlsLastByteDamager>(
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<TlsLastByteDamager>(
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<TlsExtensionCapture>(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<SSLNamedGroup> 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<PRInt32>(strlen(k0RttData));
SetupForZeroRtt(); // initial handshake as normal
+ client_->ConfigNamedGroups(kNonPQDHEGroups);
static const std::vector<SSLNamedGroup> groups = {ssl_grp_ec_secp384r1,
ssl_grp_ec_secp521r1};
@@ -107,6 +108,7 @@ TEST_P(TlsConnectTls13, SecondClientHell
auto orig_client =
std::make_shared<TlsAgent>(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<SSLNamedGroup> 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<SSLNamedGroup> 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<TLSKeyUpdateDamager>(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<TLSKeyUpdateDamager>(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<TlsHandshakeRecorder>(client_, kTlsHandshakeClientHello);
+ client_->ConfigNamedGroups(kNonPQDHEGroups);
// Add PSK with label long enough to push CH length into [256, 511].
std::vector<uint8_t> 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<TlsExtensionCapture>(
@@ -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<CaptureSessionId>(client_);
auto server_sid = MakeTlsFilter<CaptureSessionId>(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<TlsExtensionCapture>(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<TlsExtensionCapture>(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<CaptureSessionId>(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<TlsRecordFilter> 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<TlsRecordRecorder>(client_);
auto server_records = MakeTlsFilter<TlsRecordRecorder>(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<TlsRecordRecorder>(client_);
client_->SetFilter(
std::make_shared<ChainedPacketFilter>(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<TlsExtensionCapture>(
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<SSLNamedGroup> 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<SSLNamedGroup> 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<SSLNamedGroup> 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<SSLNamedGroup> kFFDHEGroups = {
@@ -537,13 +550,19 @@ const std::vector<SSLNamedGroup> kFFDHEG
// Defined because the big DHE groups are ridiculously slow.
const std::vector<SSLNamedGroup> 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<SSLNamedGroup> 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<SSLNamedGroup> 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<SSLNamedGroup> kAllDHEGroups;
+const extern std::vector<SSLNamedGroup> kNonPQDHEGroups;
const extern std::vector<SSLNamedGroup> kECDHEGroups;
const extern std::vector<SSLNamedGroup> kFFDHEGroups;
const extern std::vector<SSLNamedGroup> 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<size_t>(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<SSLSignatureScheme>(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<TlsAgent>& 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<SSLNamedGroup> 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<TlsExtensionCapture>& 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<TlsHandshakeDropper>(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 <ctype.h>
@@ -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, &paramSet);
+ 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, &paramSet);
+ 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,
- &paramSet, &pubKey, PK11_ATTR_SESSION | PK11_ATTR_INSENSITIVE | PK11_ATTR_PUBLIC,
- CKF_DERIVE, CKF_DERIVE, ss->pkcs11PinArg);
+ &paramSet, &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,
- &paramSet, &pubKey, PK11_ATTR_SESSION | PK11_ATTR_SENSITIVE | PK11_ATTR_PRIVATE,
- CKF_DERIVE, CKF_DERIVE, ss->pkcs11PinArg);
+ &paramSet, &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=$?