Add ssl_compliance_policy_cnsa_202407
This policy implements a part of RFC 9151.
Change-Id: I2c0a0e922b9287c8e29bdce4633fdee2a9f07fb8
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/69887
Auto-Submit: Adam Langley <agl@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 2f14754..1dbc1e7 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -5637,6 +5637,14 @@
// implementation risks of using a more obscure primitive like P-384
// dominate other considerations.
ssl_compliance_policy_wpa3_192_202304,
+
+ // ssl_compliance_policy_cnsa_202407 confingures a TLS connection to use:
+ // * For TLS 1.3, AES-256-GCM over AES-128-GCM over ChaCha20-Poly1305.
+ //
+ // I.e. it ensures that AES-GCM will be used whenever the client supports it.
+ // The cipher suite configuration mini-language can be used to similarly
+ // configure prior TLS versions if they are enabled.
+ ssl_compliance_policy_cnsa_202407,
};
// SSL_CTX_set_compliance_policy configures various aspects of |ctx| based on
diff --git a/ssl/handshake_client.cc b/ssl/handshake_client.cc
index c532345..9788b46 100644
--- a/ssl/handshake_client.cc
+++ b/ssl/handshake_client.cc
@@ -244,23 +244,36 @@
// Add TLS 1.3 ciphers. Order ChaCha20-Poly1305 relative to AES-GCM based on
// hardware support.
if (hs->max_version >= TLS1_3_VERSION) {
+ static const uint16_t kCiphersNoAESHardware[] = {
+ TLS1_3_CK_CHACHA20_POLY1305_SHA256 & 0xffff,
+ TLS1_3_CK_AES_128_GCM_SHA256 & 0xffff,
+ TLS1_3_CK_AES_256_GCM_SHA384 & 0xffff,
+ };
+ static const uint16_t kCiphersAESHardware[] = {
+ TLS1_3_CK_AES_128_GCM_SHA256 & 0xffff,
+ TLS1_3_CK_AES_256_GCM_SHA384 & 0xffff,
+ TLS1_3_CK_CHACHA20_POLY1305_SHA256 & 0xffff,
+ };
+ static const uint16_t kCiphersCNSA[] = {
+ TLS1_3_CK_AES_256_GCM_SHA384 & 0xffff,
+ TLS1_3_CK_AES_128_GCM_SHA256 & 0xffff,
+ TLS1_3_CK_CHACHA20_POLY1305_SHA256 & 0xffff,
+ };
+
const bool has_aes_hw = ssl->config->aes_hw_override
? ssl->config->aes_hw_override_value
: EVP_has_aes_hardware();
+ const bssl::Span<const uint16_t> ciphers =
+ ssl->config->tls13_cipher_policy == ssl_compliance_policy_cnsa_202407
+ ? bssl::Span<const uint16_t>(kCiphersCNSA)
+ : (has_aes_hw ? bssl::Span<const uint16_t>(kCiphersAESHardware)
+ : bssl::Span<const uint16_t>(kCiphersNoAESHardware));
- if ((!has_aes_hw && //
- !ssl_add_tls13_cipher(&child,
- TLS1_3_CK_CHACHA20_POLY1305_SHA256 & 0xffff,
- ssl->config->tls13_cipher_policy)) ||
- !ssl_add_tls13_cipher(&child, TLS1_3_CK_AES_128_GCM_SHA256 & 0xffff,
- ssl->config->tls13_cipher_policy) ||
- !ssl_add_tls13_cipher(&child, TLS1_3_CK_AES_256_GCM_SHA384 & 0xffff,
- ssl->config->tls13_cipher_policy) ||
- (has_aes_hw && //
- !ssl_add_tls13_cipher(&child,
- TLS1_3_CK_CHACHA20_POLY1305_SHA256 & 0xffff,
- ssl->config->tls13_cipher_policy))) {
- return false;
+ for (auto cipher : ciphers) {
+ if (!ssl_add_tls13_cipher(&child, cipher,
+ ssl->config->tls13_cipher_policy)) {
+ return false;
+ }
}
}
diff --git a/ssl/s3_both.cc b/ssl/s3_both.cc
index 172de90..7db2730 100644
--- a/ssl/s3_both.cc
+++ b/ssl/s3_both.cc
@@ -659,36 +659,49 @@
}
}
-// CipherScorer produces a "score" for each possible cipher suite offered by
-// the client.
class CipherScorer {
public:
- CipherScorer(bool has_aes_hw) : aes_is_fine_(has_aes_hw) {}
+ using Score = int;
+ static constexpr Score kMinScore = 0;
- typedef std::tuple<bool, bool> Score;
+ virtual Score Evaluate(const SSL_CIPHER *cipher) const = 0;
+};
- // MinScore returns a |Score| that will compare less than the score of all
- // cipher suites.
- Score MinScore() const {
- return Score(false, false);
- }
+// AesHwCipherScorer scores cipher suites based on whether AES is supported in
+// hardware.
+class AesHwCipherScorer : public CipherScorer {
+ public:
+ explicit AesHwCipherScorer(bool has_aes_hw) : aes_is_fine_(has_aes_hw) {}
- Score Evaluate(const SSL_CIPHER *a) const {
- return Score(
+ Score Evaluate(const SSL_CIPHER *a) const override {
+ return
// Something is always preferable to nothing.
- true,
+ 1 +
// Either AES is fine, or else ChaCha20 is preferred.
- aes_is_fine_ || a->algorithm_enc == SSL_CHACHA20POLY1305);
+ ((aes_is_fine_ || a->algorithm_enc == SSL_CHACHA20POLY1305) ? 1 : 0);
}
private:
const bool aes_is_fine_;
};
+// CNsaCipherScorer prefers AES-256-GCM over AES-128-GCM over anything else.
+class CNsaCipherScorer : public CipherScorer {
+ Score Evaluate(const SSL_CIPHER *a) const override {
+ if (a->id == TLS1_3_CK_AES_256_GCM_SHA384) {
+ return 3;
+ } else if (a->id == TLS1_3_CK_AES_128_GCM_SHA256) {
+ return 2;
+ }
+ return 1;
+ }
+};
+
bool ssl_tls13_cipher_meets_policy(uint16_t cipher_id,
enum ssl_compliance_policy_t policy) {
switch (policy) {
case ssl_compliance_policy_none:
+ case ssl_compliance_policy_cnsa_202407:
return true;
case ssl_compliance_policy_fips_202205:
@@ -728,8 +741,12 @@
}
const SSL_CIPHER *best = nullptr;
- CipherScorer scorer(has_aes_hw);
- CipherScorer::Score best_score = scorer.MinScore();
+ AesHwCipherScorer aes_hw_scorer(has_aes_hw);
+ CNsaCipherScorer cnsa_scorer;
+ CipherScorer *const scorer = (policy == ssl_compliance_policy_cnsa_202407)
+ ? static_cast<CipherScorer*>(&cnsa_scorer)
+ : static_cast<CipherScorer*>(&aes_hw_scorer);
+ CipherScorer::Score best_score = CipherScorer::kMinScore;
while (CBS_len(&cipher_suites) > 0) {
uint16_t cipher_suite;
@@ -750,7 +767,7 @@
continue;
}
- const CipherScorer::Score candidate_score = scorer.Evaluate(candidate);
+ const CipherScorer::Score candidate_score = scorer->Evaluate(candidate);
// |candidate_score| must be larger to displace the current choice. That way
// the client's order controls between ciphers with an equal score.
if (candidate_score > best_score) {
diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc
index c86b51b..2e78599 100644
--- a/ssl/ssl_lib.cc
+++ b/ssl/ssl_lib.cc
@@ -3403,6 +3403,21 @@
} // namespace wpa202304
+namespace cnsa202407 {
+
+static int Configure(SSL_CTX *ctx) {
+ ctx->tls13_cipher_policy = ssl_compliance_policy_cnsa_202407;
+ return 1;
+}
+
+static int Configure(SSL *ssl) {
+ ssl->config->tls13_cipher_policy =
+ ssl_compliance_policy_cnsa_202407;
+ return 1;
+}
+
+}
+
int SSL_CTX_set_compliance_policy(SSL_CTX *ctx,
enum ssl_compliance_policy_t policy) {
switch (policy) {
@@ -3410,6 +3425,8 @@
return fips202205::Configure(ctx);
case ssl_compliance_policy_wpa3_192_202304:
return wpa202304::Configure(ctx);
+ case ssl_compliance_policy_cnsa_202407:
+ return cnsa202407::Configure(ctx);
default:
return 0;
}
@@ -3421,6 +3438,8 @@
return fips202205::Configure(ssl);
case ssl_compliance_policy_wpa3_192_202304:
return wpa202304::Configure(ssl);
+ case ssl_compliance_policy_cnsa_202407:
+ return cnsa202407::Configure(ssl);
default:
return 0;
}
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 2cd1549..1476b38 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -19928,6 +19928,38 @@
})
}
}
+
+ // AES-256-GCM is the most preferred.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ protocol: protocol,
+ name: "Compliance-cnsa202407-" + protocol.String() + "-AES-256-preferred",
+ config: Config{
+ MinVersion: VersionTLS13,
+ MaxVersion: VersionTLS13,
+ CipherSuites: []uint16{TLS_CHACHA20_POLY1305_SHA256, TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384},
+ },
+ flags: []string{
+ "-cnsa-202407",
+ },
+ expectations: connectionExpectations{cipher: TLS_AES_256_GCM_SHA384},
+ })
+
+ // AES-128-GCM is preferred over ChaCha20-Poly1305.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ protocol: protocol,
+ name: "Compliance-cnsa202407-" + protocol.String() + "-AES-128-preferred",
+ config: Config{
+ MinVersion: VersionTLS13,
+ MaxVersion: VersionTLS13,
+ CipherSuites: []uint16{TLS_CHACHA20_POLY1305_SHA256, TLS_AES_128_GCM_SHA256},
+ },
+ flags: []string{
+ "-cnsa-202407",
+ },
+ expectations: connectionExpectations{cipher: TLS_AES_128_GCM_SHA256},
+ })
}
}
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index ae4f87b..2db2a89 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -478,6 +478,7 @@
&TestConfig::early_write_after_message),
BoolFlag("-fips-202205", &TestConfig::fips_202205),
BoolFlag("-wpa-202304", &TestConfig::wpa_202304),
+ BoolFlag("-cnsa-202407", &TestConfig::cnsa_202407),
BoolFlag("-no-check-client-certificate-type",
&TestConfig::no_check_client_certificate_type),
BoolFlag("-no-check-ecdsa-curve", &TestConfig::no_check_ecdsa_curve),
@@ -2096,7 +2097,9 @@
if (enable_ech_grease) {
SSL_set_enable_ech_grease(ssl.get(), 1);
}
- if (static_cast<int>(fips_202205) + static_cast<int>(wpa_202304) > 1) {
+ if (static_cast<int>(fips_202205) + static_cast<int>(wpa_202304) +
+ static_cast<int>(cnsa_202407) >
+ 1) {
fprintf(stderr, "Multiple policy options given\n");
return nullptr;
}
@@ -2110,6 +2113,11 @@
fprintf(stderr, "SSL_set_compliance_policy failed\n");
return nullptr;
}
+ if (cnsa_202407 && !SSL_set_compliance_policy(
+ ssl.get(), ssl_compliance_policy_cnsa_202407)) {
+ fprintf(stderr, "SSL_set_compliance_policy failed\n");
+ return nullptr;
+ }
if (!ech_config_list.empty() &&
!SSL_set1_ech_config_list(
ssl.get(), reinterpret_cast<const uint8_t *>(ech_config_list.data()),
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index 607f58d..0afeb49 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -214,6 +214,7 @@
int early_write_after_message = 0;
bool fips_202205 = false;
bool wpa_202304 = false;
+ bool cnsa_202407 = false;
bool no_check_client_certificate_type = false;
bool no_check_ecdsa_curve = false;
int expect_selected_credential = -1;