Skip to content

Commit 46272f7

Browse files
committed
Bug 2052296 - add a pref and enterprise policy for CNSA2.0 key agreement. r=keeler,fluent-reviewers,mkaply,flod
Differential Revision: https://phabricator.services.mozilla.com/D310120
1 parent 1fdc3ca commit 46272f7

10 files changed

Lines changed: 110 additions & 25 deletions

File tree

‎browser/components/enterprisepolicies/Policies.sys.mjs‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,12 @@ export var Policies = {
663663
},
664664
},
665665

666+
CNSA2KeyAgreementEnabled: {
667+
onBeforeAddons(manager, param) {
668+
lazy.PoliciesUtils.setAndLockPref("security.tls.enable_mlkem1024", param);
669+
},
670+
},
671+
666672
Containers: {
667673
// Queried directly by ContextualIdentityService.sys.mjs
668674
},

‎browser/components/enterprisepolicies/schemas/policies-schema.json‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,10 @@
431431
}
432432
},
433433

434+
"CNSA2KeyAgreementEnabled": {
435+
"type": "boolean"
436+
},
437+
434438
"Containers": {
435439
"type": "object",
436440
"x-category": "Extensions",

‎browser/components/enterprisepolicies/tests/xpcshell/test_simple_pref_policies.js‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,6 +1214,16 @@ const POLICIES_TESTS = [
12141214
},
12151215
},
12161216

1217+
// POLICY: CNSA2KeyAgreementEnabled
1218+
{
1219+
policies: {
1220+
CNSA2KeyAgreementEnabled: true,
1221+
},
1222+
lockedPrefs: {
1223+
"security.tls.enable_mlkem1024": true,
1224+
},
1225+
},
1226+
12171227
// POLICY: HttpsOnlyMode
12181228
{
12191229
policies: {

‎browser/locales/en-US/browser/policies/policies-descriptions.ftl‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ policy-CaptivePortal = Enable or disable captive portal support.
4949
5050
policy-CertificatesDescription = Add certificates or use built-in certificates.
5151
52+
# CNSA 2.0 is the United States National Security Agency's Commercial National
53+
# Security Algorithm Suite 2.0. ML-KEM-1024 is a post-quantum key agreement
54+
# algorithm. Neither should be translated.
55+
policy-CNSA2KeyAgreementEnabled = Enable the CNSA 2.0 ML-KEM-1024 key agreement for TLS.
56+
5257
policy-ContentAnalysis = Enable or disable connection to data-loss-prevention agent.
5358
5459
policy-Cookies = Allow or deny websites to set cookies.

‎modules/libpref/init/StaticPrefList.yaml‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19022,6 +19022,12 @@
1902219022
mirror: always
1902319023
rust: true
1902419024

19025+
# Whether to enable the CNSA 2.0 ML-KEM-1024 key agreement in TLS.
19026+
- name: security.tls.enable_mlkem1024
19027+
type: RelaxedAtomicBool
19028+
value: false
19029+
mirror: always
19030+
1902519031
- name: security.tls.client_hello.send_p256_keyshare
1902619032
type: RelaxedAtomicBool
1902719033
value: @IS_NOT_NIGHTLY_BUILD@

‎security/manager/ssl/nsNSSCallbacks.cpp‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,9 @@ nsCString getKeaGroupName(uint32_t aKeaGroup) {
802802
case ssl_grp_kem_secp384r1mlkem1024:
803803
groupName = "secp384r1mlkem1024"_ns;
804804
break;
805+
case ssl_grp_kem_mlkem1024:
806+
groupName = "mlkem1024"_ns;
807+
break;
805808
case ssl_grp_ffdhe_2048:
806809
groupName = "FF 2048"_ns;
807810
break;
@@ -1163,6 +1166,7 @@ void HandshakeCallback(PRFileDesc* fd, void* client_data) {
11631166
ECCCurve(channelInfo.keaKeyBits));
11641167
break;
11651168
case ssl_kea_ecdh_hybrid:
1169+
case ssl_kea_kem:
11661170
break;
11671171
default:
11681172
MOZ_CRASH("impossible KEA");

‎security/manager/ssl/nsNSSComponent.cpp‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1000,14 +1000,20 @@ bool SetDeprecatedTLS1CipherPrefs(const char* aPref = nullptr) {
10001000

10011001
// static
10021002
bool SetKyberPolicy(const char* aPref = nullptr) {
1003-
if (aPref != nullptr && strcmp(aPref, "security.tls.enable_kyber") != 0) {
1003+
if (aPref != nullptr && strcmp(aPref, "security.tls.enable_kyber") != 0 &&
1004+
strcmp(aPref, "security.tls.enable_mlkem1024") != 0) {
10041005
return false;
10051006
}
10061007
if (StaticPrefs::security_tls_enable_kyber()) {
10071008
NSS_SetAlgorithmPolicy(SEC_OID_MLKEM768X25519, NSS_USE_ALG_IN_SSL_KX, 0);
10081009
} else {
10091010
NSS_SetAlgorithmPolicy(SEC_OID_MLKEM768X25519, 0, NSS_USE_ALG_IN_SSL_KX);
10101011
}
1012+
if (StaticPrefs::security_tls_enable_mlkem1024()) {
1013+
NSS_SetAlgorithmPolicy(SEC_OID_ML_KEM_1024, NSS_USE_ALG_IN_SSL_KX, 0);
1014+
} else {
1015+
NSS_SetAlgorithmPolicy(SEC_OID_ML_KEM_1024, 0, NSS_USE_ALG_IN_SSL_KX);
1016+
}
10111017
return true;
10121018
}
10131019

‎security/manager/ssl/nsNSSIOLayer.cpp‎

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1580,26 +1580,27 @@ static nsresult nsSSLIOLayerSetOptions(PRFileDesc* fd, bool forSTARTTLS,
15801580
// nsNSSCallbacks.cpp when changing the lists here.
15811581
unsigned int additional_shares =
15821582
StaticPrefs::security_tls_client_hello_send_p256_keyshare();
1583-
if (StaticPrefs::security_tls_enable_kyber() &&
1584-
range.max >= SSL_LIBRARY_VERSION_TLS_1_3) {
1585-
const SSLNamedGroup namedGroups[] = {
1586-
ssl_grp_kem_mlkem768x25519, ssl_grp_ec_curve25519, ssl_grp_ec_secp256r1,
1587-
ssl_grp_ec_secp384r1, ssl_grp_ec_secp521r1, ssl_grp_ffdhe_2048,
1588-
ssl_grp_ffdhe_3072};
1589-
if (SECSuccess !=
1590-
SSL_NamedGroupConfig(fd, namedGroups, std::size(namedGroups))) {
1591-
return NS_ERROR_FAILURE;
1592-
}
1583+
bool tls13 = range.max >= SSL_LIBRARY_VERSION_TLS_1_3;
1584+
1585+
AutoTArray<SSLNamedGroup, 8> namedGroups;
1586+
if (StaticPrefs::security_tls_enable_kyber() && tls13) {
1587+
namedGroups.AppendElement(ssl_grp_kem_mlkem768x25519);
15931588
additional_shares += 1;
1594-
} else {
1595-
const SSLNamedGroup namedGroups[] = {
1596-
ssl_grp_ec_curve25519, ssl_grp_ec_secp256r1, ssl_grp_ec_secp384r1,
1597-
ssl_grp_ec_secp521r1, ssl_grp_ffdhe_2048, ssl_grp_ffdhe_3072};
1598-
// Skip the |ssl_grp_kem_mlkem768x25519| entry.
1599-
if (SECSuccess !=
1600-
SSL_NamedGroupConfig(fd, namedGroups, std::size(namedGroups))) {
1601-
return NS_ERROR_FAILURE;
1602-
}
1589+
}
1590+
namedGroups.AppendElement(ssl_grp_ec_curve25519);
1591+
namedGroups.AppendElement(ssl_grp_ec_secp256r1);
1592+
namedGroups.AppendElement(ssl_grp_ec_secp384r1);
1593+
namedGroups.AppendElement(ssl_grp_ec_secp521r1);
1594+
namedGroups.AppendElement(ssl_grp_ffdhe_2048);
1595+
namedGroups.AppendElement(ssl_grp_ffdhe_3072);
1596+
1597+
if (StaticPrefs::security_tls_enable_mlkem1024() && tls13) {
1598+
namedGroups.AppendElement(ssl_grp_kem_mlkem1024);
1599+
}
1600+
1601+
if (SECSuccess !=
1602+
SSL_NamedGroupConfig(fd, namedGroups.Elements(), namedGroups.Length())) {
1603+
return NS_ERROR_FAILURE;
16031604
}
16041605

16051606
// If additional_shares == 2, send mlkem768x25519, x25519, and p256.
@@ -1610,7 +1611,7 @@ static nsresult nsSSLIOLayerSetOptions(PRFileDesc* fd, bool forSTARTTLS,
16101611
}
16111612

16121613
// Enabling Certificate Compression Decoding mechanisms.
1613-
if (range.max >= SSL_LIBRARY_VERSION_TLS_1_3 &&
1614+
if (tls13 &&
16141615
!(infoObject->GetProviderFlags() &
16151616
(nsISocketProvider::BE_CONSERVATIVE | nsISocketProvider::IS_RETRY))) {
16161617
SSLCertificateCompressionAlgorithm zlibAlg = {1, "zlib", nullptr,

‎security/manager/ssl/tests/unit/test_faulty_server.js‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,36 @@ add_task(
138138
);
139139
}
140140
);
141+
142+
// SSL_ERROR_BASE + 155. The client aborts when a HelloRetryRequest selects a
143+
// group it did not offer.
144+
const SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST = SSL_ERROR_BASE + 155;
145+
146+
// With the pref disabled, the client does not enable ML-KEM-1024. The server
147+
// offers only ML-KEM-1024 and requests it via a HelloRetryRequest, but since
148+
// the client did not advertise ML-KEM-1024 it rejects the retry.
149+
add_connection_test(
150+
"mlkem1024.example.com",
151+
SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST,
152+
function () {
153+
Services.prefs.setBoolPref("security.tls.enable_mlkem1024", false);
154+
}
155+
);
156+
157+
// With the pref enabled, the client advertises ML-KEM-1024 in supported_groups
158+
// without sending a key share for it. A server that offers only ML-KEM-1024
159+
// therefore completes the handshake via a HelloRetryRequest.
160+
add_connection_test(
161+
"mlkem1024.example.com",
162+
PRErrorCodeSuccess,
163+
function () {
164+
Services.prefs.setBoolPref("security.tls.enable_mlkem1024", true);
165+
},
166+
function (aSecurityInfo) {
167+
equal(
168+
aSecurityInfo.keaGroupName,
169+
"mlkem1024",
170+
"should have negotiated ML-KEM-1024 via HelloRetryRequest"
171+
);
172+
}
173+
);

‎security/manager/ssl/tests/unit/tlsserver/cmd/FaultyServer.cpp‎

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ enum FaultType {
2121
ZeroRtt,
2222
UnknownSNI,
2323
Mlkem768x25519,
24+
Mlkem1024,
2425
};
2526

2627
struct FaultyServerHost {
@@ -47,6 +48,8 @@ const char* kHostMlkem768x25519NetInterrupt =
4748
const char* kHostMlkem768x25519AlertAfterServerHello =
4849
"mlkem768x25519-alert-after-server-hello.example.com";
4950

51+
const char* kHostMlkem1024 = "mlkem1024.example.com";
52+
5053
const char* kCertWildcard = "default-ee";
5154

5255
/* Each type of failure gets a different SNI.
@@ -65,6 +68,7 @@ MOZ_RUNINIT const FaultyServerHost sFaultyServerHosts[]{
6568
{kHostPSKDecryptErrorNoEarlyData, kCertWildcard, ZeroRtt},
6669
{kHostMlkem768x25519NetInterrupt, kCertWildcard, Mlkem768x25519},
6770
{kHostMlkem768x25519AlertAfterServerHello, kCertWildcard, Mlkem768x25519},
71+
{kHostMlkem1024, kCertWildcard, Mlkem1024},
6872
{nullptr, nullptr},
6973
};
7074

@@ -228,17 +232,23 @@ int32_t DoSNISocketConfig(PRFileDesc* aFd, const SECItem* aSrvNameArr,
228232
fprintf(stderr, "found pre-defined host '%s'\n", host->mHostName);
229233
}
230234

231-
const SSLNamedGroup mlkemTestNamedGroups[] = {ssl_grp_kem_mlkem768x25519,
232-
ssl_grp_ec_curve25519};
235+
const SSLNamedGroup hybridTestNamedGroups[] = {ssl_grp_kem_mlkem768x25519,
236+
ssl_grp_ec_curve25519};
237+
238+
const SSLNamedGroup mlkem1024TestNamedGroups[] = {ssl_grp_kem_mlkem1024};
233239

234240
switch (host->mFaultType) {
235241
case ZeroRtt:
236242
SSL_SecretCallback(aFd, &SecretCallbackFailZeroRtt, (void*)host);
237243
break;
238244
case Mlkem768x25519:
239245
SSL_SecretCallback(aFd, &SecretCallbackFailMlkem768x25519, (void*)host);
240-
SSL_NamedGroupConfig(aFd, mlkemTestNamedGroups,
241-
std::size(mlkemTestNamedGroups));
246+
SSL_NamedGroupConfig(aFd, hybridTestNamedGroups,
247+
std::size(hybridTestNamedGroups));
248+
break;
249+
case Mlkem1024:
250+
SSL_NamedGroupConfig(aFd, mlkem1024TestNamedGroups,
251+
std::size(mlkem1024TestNamedGroups));
242252
break;
243253
case None:
244254
break;

0 commit comments

Comments
 (0)