Add SSL_[CTX_]_set_compliance_policy.

These functions aid in meeting specific compliance goals and allows
configuration of things like TLS 1.3 cipher suites, which are otherwise
not configurable.

Change-Id: I668afc734a19ecd4b996eaa23be73ce259b13fa2
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/52625
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
chromium-5359
Adam Langley 3 years ago committed by Boringssl LUCI CQ
parent 828b2d26ca
commit 451ea3ca3e
  1. 38
      include/openssl/ssl.h
  2. 10
      ssl/handshake_client.cc
  3. 19
      ssl/internal.h
  4. 24
      ssl/s3_both.cc
  5. 95
      ssl/ssl_lib.cc
  6. 192
      ssl/test/runner/runner.go
  7. 6
      ssl/test/test_config.cc
  8. 1
      ssl/test/test_config.h
  9. 10
      ssl/tls13_client.cc
  10. 3
      ssl/tls13_server.cc

@ -5104,6 +5104,44 @@ OPENSSL_EXPORT int SSL_CTX_set_tlsext_status_arg(SSL_CTX *ctx, void *arg);
OPENSSL_EXPORT uint16_t SSL_CIPHER_get_value(const SSL_CIPHER *cipher);
// Compliance policy configurations
//
// A TLS connection has a large number of different parameters. Some are well
// known, like cipher suites, but many are obscure and configuration functions
// for them may not exist. These policy controls allow broad configuration
// goals to be specified so that they can flow down to all the different
// parameters of a TLS connection.
enum ssl_compliance_policy_t BORINGSSL_ENUM_INT {
// ssl_policy_fips_202205 configures a TLS connection to use:
// * TLS 1.2 or 1.3
// * For TLS 1.2, only ECDHE_[RSA|ECDSA]_WITH_AES_*_GCM_SHA*.
// * For TLS 1.3, only AES-GCM
// * P-256 or P-384 for key agreement.
// * For server signatures, only PKCS#1/PSS with SHA256/384/512, or ECDSA
// with P-256 or P-384.
//
// Note: this policy can be configured even if BoringSSL has not been built in
// FIPS mode. Call |FIPS_mode| to check that.
//
// Note: this setting aids with compliance with NIST requirements but does not
// guarantee it. Careful reading of SP 800-52r2 is recommended.
ssl_compliance_policy_fips_202205,
};
// SSL_CTX_set_compliance_policy configures various aspects of |ctx| based on
// the given policy requirements. Subsequently calling other functions that
// configure |ctx| may override |policy|, or may not. This should be the final
// configuration function called in order to have defined behaviour.
OPENSSL_EXPORT int SSL_CTX_set_compliance_policy(
SSL_CTX *ctx, enum ssl_compliance_policy_t policy);
// SSL_set_compliance_policy acts the same as |SSL_CTX_set_compliance_policy|,
// but only configures a single |SSL*|.
OPENSSL_EXPORT int SSL_set_compliance_policy(
SSL *ssl, enum ssl_compliance_policy_t policy);
// Nodejs compatibility section (hidden).
//
// These defines exist for node.js, with the hope that we can eliminate the

@ -235,7 +235,12 @@ static bool ssl_write_client_cipher_list(const SSL_HANDSHAKE *hs, CBB *out,
// Add TLS 1.3 ciphers. Order ChaCha20-Poly1305 relative to AES-GCM based on
// hardware support.
if (hs->max_version >= TLS1_3_VERSION) {
if (!EVP_has_aes_hardware() &&
const bool include_chacha20 = ssl_tls13_cipher_meets_policy(
TLS1_CK_CHACHA20_POLY1305_SHA256 & 0xffff,
ssl->config->only_fips_cipher_suites_in_tls13);
if (!EVP_has_aes_hardware() && //
include_chacha20 && //
!CBB_add_u16(&child, TLS1_CK_CHACHA20_POLY1305_SHA256 & 0xffff)) {
return false;
}
@ -243,7 +248,8 @@ static bool ssl_write_client_cipher_list(const SSL_HANDSHAKE *hs, CBB *out,
!CBB_add_u16(&child, TLS1_CK_AES_256_GCM_SHA384 & 0xffff)) {
return false;
}
if (EVP_has_aes_hardware() &&
if (EVP_has_aes_hardware() && //
include_chacha20 && //
!CBB_add_u16(&child, TLS1_CK_CHACHA20_POLY1305_SHA256 & 0xffff)) {
return false;
}

@ -660,10 +660,15 @@ bool ssl_cipher_requires_server_key_exchange(const SSL_CIPHER *cipher);
size_t ssl_cipher_get_record_split_len(const SSL_CIPHER *cipher);
// ssl_choose_tls13_cipher returns an |SSL_CIPHER| corresponding with the best
// available from |cipher_suites| compatible with |version| and |group_id|. It
// returns NULL if there isn't a compatible cipher.
// available from |cipher_suites| compatible with |version|, |group_id|, and
// |only_fips|. It returns NULL if there isn't a compatible cipher.
const SSL_CIPHER *ssl_choose_tls13_cipher(CBS cipher_suites, uint16_t version,
uint16_t group_id);
uint16_t group_id, bool only_fips);
// ssl_tls13_cipher_meets_policy returns true if |cipher_id| is acceptable given
// |only_fips|. (For now there's only a single policy and so the policy argument
// is just a bool.)
bool ssl_tls13_cipher_meets_policy(uint16_t cipher_id, bool only_fips);
// Transcript layer.
@ -3087,6 +3092,10 @@ struct SSL_CONFIG {
// permute_extensions is whether to permute extensions when sending messages.
bool permute_extensions : 1;
// only_fips_cipher_suites_in_tls13 constrains the selection of cipher suites
// in TLS 1.3 such that only FIPS approved ones will be selected.
bool only_fips_cipher_suites_in_tls13 : 1;
};
// From RFC 8446, used in determining PSK modes.
@ -3694,6 +3703,10 @@ struct ssl_ctx_st {
// If enable_early_data is true, early data can be sent and accepted.
bool enable_early_data : 1;
// only_fips_cipher_suites_in_tls13 constrains the selection of cipher suites
// in TLS 1.3 such that only FIPS approved ones will be selected.
bool only_fips_cipher_suites_in_tls13 : 1;
private:
~ssl_ctx_st();
friend OPENSSL_EXPORT void SSL_CTX_free(SSL_CTX *);

@ -691,8 +691,25 @@ class CipherScorer {
const bool security_128_is_fine_;
};
bool ssl_tls13_cipher_meets_policy(uint16_t cipher_id, bool only_fips) {
if (!only_fips) {
return true;
}
switch (cipher_id) {
case TLS1_CK_AES_128_GCM_SHA256 & 0xffff:
case TLS1_CK_AES_256_GCM_SHA384 & 0xffff:
return true;
case TLS1_CK_CHACHA20_POLY1305_SHA256 & 0xffff:
return false;
default:
assert(false);
return false;
}
}
const SSL_CIPHER *ssl_choose_tls13_cipher(CBS cipher_suites, uint16_t version,
uint16_t group_id) {
uint16_t group_id, bool only_fips) {
if (CBS_len(&cipher_suites) % 2 != 0) {
return nullptr;
}
@ -715,6 +732,11 @@ const SSL_CIPHER *ssl_choose_tls13_cipher(CBS cipher_suites, uint16_t version,
continue;
}
if (!ssl_tls13_cipher_meets_policy(SSL_CIPHER_get_protocol_id(candidate),
only_fips)) {
continue;
}
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.

@ -519,7 +519,8 @@ ssl_ctx_st::ssl_ctx_st(const SSL_METHOD *ssl_method)
allow_unknown_alpn_protos(false),
false_start_allowed_without_alpn(false),
handoff(false),
enable_early_data(false) {
enable_early_data(false),
only_fips_cipher_suites_in_tls13(false) {
CRYPTO_MUTEX_init(&lock);
CRYPTO_new_ex_data(&ex_data);
}
@ -639,6 +640,8 @@ SSL *SSL_new(SSL_CTX *ctx) {
ssl->config->retain_only_sha256_of_client_certs =
ctx->retain_only_sha256_of_client_certs;
ssl->config->permute_extensions = ctx->permute_extensions;
ssl->config->only_fips_cipher_suites_in_tls13 =
ctx->only_fips_cipher_suites_in_tls13;
if (!ssl->config->supported_group_list.CopyFrom(ctx->supported_group_list) ||
!ssl->config->alpn_client_proto_list.CopyFrom(
@ -3079,3 +3082,93 @@ int SSL_CTX_set_tlsext_status_arg(SSL_CTX *ctx, void *arg) {
ctx->legacy_ocsp_callback_arg = arg;
return 1;
}
namespace fips202205 {
// (References are to SP 800-52r2):
// Section 3.4.2.2
// "at least one of the NIST-approved curves, P-256 (secp256r1) and P384
// (secp384r1), shall be supported as described in RFC 8422."
//
// Section 3.3.1
// "The server shall be configured to only use cipher suites that are
// composed entirely of NIST approved algorithms"
static const int kCurves[] = {NID_X9_62_prime256v1, NID_secp384r1};
static const uint16_t kSigAlgs[] = {
SSL_SIGN_RSA_PKCS1_SHA256,
SSL_SIGN_RSA_PKCS1_SHA384,
SSL_SIGN_RSA_PKCS1_SHA512,
// Table 4.1:
// "The curve should be P-256 or P-384"
SSL_SIGN_ECDSA_SECP256R1_SHA256,
SSL_SIGN_ECDSA_SECP384R1_SHA384,
SSL_SIGN_RSA_PSS_RSAE_SHA256,
SSL_SIGN_RSA_PSS_RSAE_SHA384,
SSL_SIGN_RSA_PSS_RSAE_SHA512,
};
static const char kTLS12Ciphers[] =
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:"
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:"
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:"
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384";
static int Configure(SSL_CTX *ctx) {
ctx->only_fips_cipher_suites_in_tls13 = true;
return
// Section 3.1:
// "Servers that support government-only applications shall be
// configured to use TLS 1.2 and should be configured to use TLS 1.3
// as well. These servers should not be configured to use TLS 1.1 and
// shall not use TLS 1.0, SSL 3.0, or SSL 2.0.
SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION) &&
SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION) &&
// Sections 3.3.1.1.1 and 3.3.1.1.2 are ambiguous about whether
// HMAC-SHA-1 cipher suites are permitted with TLS 1.2. However, later the
// Encrypt-then-MAC extension is required for all CBC cipher suites and so
// it's easier to drop them.
SSL_CTX_set_strict_cipher_list(ctx, kTLS12Ciphers) &&
SSL_CTX_set1_curves(ctx, kCurves, OPENSSL_ARRAY_SIZE(kCurves)) &&
SSL_CTX_set_signing_algorithm_prefs(ctx, kSigAlgs,
OPENSSL_ARRAY_SIZE(kSigAlgs)) &&
SSL_CTX_set_verify_algorithm_prefs(ctx, kSigAlgs,
OPENSSL_ARRAY_SIZE(kSigAlgs));
}
static int Configure(SSL *ssl) {
ssl->config->only_fips_cipher_suites_in_tls13 = true;
// See |Configure(SSL_CTX)|, above, for reasoning.
return SSL_set_min_proto_version(ssl, TLS1_2_VERSION) &&
SSL_set_max_proto_version(ssl, TLS1_3_VERSION) &&
SSL_set_strict_cipher_list(ssl, kTLS12Ciphers) &&
SSL_set1_curves(ssl, kCurves, OPENSSL_ARRAY_SIZE(kCurves)) &&
SSL_set_signing_algorithm_prefs(ssl, kSigAlgs,
OPENSSL_ARRAY_SIZE(kSigAlgs)) &&
SSL_set_verify_algorithm_prefs(ssl, kSigAlgs,
OPENSSL_ARRAY_SIZE(kSigAlgs));
}
} // namespace fips202205
int SSL_CTX_set_compliance_policy(SSL_CTX *ctx,
enum ssl_compliance_policy_t policy) {
switch (policy) {
case ssl_compliance_policy_fips_202205:
return fips202205::Configure(ctx);
default:
return 0;
}
}
int SSL_set_compliance_policy(SSL *ssl, enum ssl_compliance_policy_t policy) {
switch (policy) {
case ssl_compliance_policy_fips_202205:
return fips202205::Configure(ssl);
default:
return 0;
}
}

@ -19032,6 +19032,197 @@ func addHintMismatchTests() {
}
}
func addCompliancePolicyTests() {
for _, protocol := range []protocol{tls, quic} {
for _, suite := range testCipherSuites {
var isFIPSCipherSuite bool
switch suite.id {
case TLS_AES_128_GCM_SHA256,
TLS_AES_256_GCM_SHA384,
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
isFIPSCipherSuite = true
}
var certFile string
var keyFile string
var certs []Certificate
if hasComponent(suite.name, "ECDSA") {
certFile = ecdsaP256CertificateFile
keyFile = ecdsaP256KeyFile
certs = []Certificate{ecdsaP256Certificate}
} else {
certFile = rsaCertificateFile
keyFile = rsaKeyFile
certs = []Certificate{rsaCertificate}
}
maxVersion := uint16(VersionTLS13)
if !isTLS13Suite(suite.name) {
if protocol == quic {
continue
}
maxVersion = VersionTLS12
}
testCases = append(testCases, testCase{
testType: serverTest,
protocol: protocol,
name: "Compliance-fips202205-" + protocol.String() + "-Server-" + suite.name,
config: Config{
MinVersion: VersionTLS12,
MaxVersion: maxVersion,
CipherSuites: []uint16{suite.id},
},
certFile: certFile,
keyFile: keyFile,
flags: []string{
"-fips-202205",
},
shouldFail: !isFIPSCipherSuite,
})
testCases = append(testCases, testCase{
testType: clientTest,
protocol: protocol,
name: "Compliance-fips202205-" + protocol.String() + "-Client-" + suite.name,
config: Config{
MinVersion: VersionTLS12,
MaxVersion: maxVersion,
CipherSuites: []uint16{suite.id},
Certificates: certs,
},
flags: []string{
"-fips-202205",
},
shouldFail: !isFIPSCipherSuite,
})
}
// Check that a TLS 1.3 client won't accept ChaCha20 even if the server
// picks it without it being in the client's cipher list.
testCases = append(testCases, testCase{
testType: clientTest,
protocol: protocol,
name: "Compliance-fips202205-" + protocol.String() + "-Client-ReallyWontAcceptChaCha",
config: Config{
MinVersion: VersionTLS12,
MaxVersion: maxVersion,
Bugs: ProtocolBugs{
SendCipherSuite: TLS_CHACHA20_POLY1305_SHA256,
},
},
flags: []string{
"-fips-202205",
},
shouldFail: true,
expectedError: ":WRONG_CIPHER_RETURNED:",
})
for _, curve := range testCurves {
var isFIPSCurve bool
switch curve.id {
case CurveP256, CurveP384:
isFIPSCurve = true
}
testCases = append(testCases, testCase{
testType: serverTest,
protocol: protocol,
name: "Compliance-fips202205-" + protocol.String() + "-Server-" + curve.name,
config: Config{
MinVersion: VersionTLS12,
MaxVersion: VersionTLS13,
CurvePreferences: []CurveID{curve.id},
},
flags: []string{
"-fips-202205",
},
shouldFail: !isFIPSCurve,
})
testCases = append(testCases, testCase{
testType: clientTest,
protocol: protocol,
name: "Compliance-fips202205-" + protocol.String() + "-Client-" + curve.name,
config: Config{
MinVersion: VersionTLS12,
MaxVersion: VersionTLS13,
CurvePreferences: []CurveID{curve.id},
},
flags: []string{
"-fips-202205",
},
shouldFail: !isFIPSCurve,
})
}
for _, sigalg := range testSignatureAlgorithms {
var isFIPSSigAlg bool
switch sigalg.id {
case signatureRSAPKCS1WithSHA256,
signatureRSAPKCS1WithSHA384,
signatureRSAPKCS1WithSHA512,
signatureECDSAWithP256AndSHA256,
signatureECDSAWithP384AndSHA384,
signatureRSAPSSWithSHA256,
signatureRSAPSSWithSHA384,
signatureRSAPSSWithSHA512:
isFIPSSigAlg = true
}
if sigalg.cert == testCertECDSAP224 {
// This can work in TLS 1.2, but not with TLS 1.3.
// For consistency it's not permitted in FIPS mode.
isFIPSSigAlg = false
}
maxVersion := uint16(VersionTLS13)
if hasComponent(sigalg.name, "PKCS1") {
if protocol == quic {
continue
}
maxVersion = VersionTLS12
}
testCases = append(testCases, testCase{
testType: serverTest,
protocol: protocol,
name: "Compliance-fips202205-" + protocol.String() + "-Server-" + sigalg.name,
config: Config{
MinVersion: VersionTLS12,
MaxVersion: maxVersion,
VerifySignatureAlgorithms: []signatureAlgorithm{sigalg.id},
},
flags: []string{
"-fips-202205",
"-cert-file", path.Join(*resourceDir, getShimCertificate(sigalg.cert)),
"-key-file", path.Join(*resourceDir, getShimKey(sigalg.cert)),
},
shouldFail: !isFIPSSigAlg,
})
testCases = append(testCases, testCase{
testType: clientTest,
protocol: protocol,
name: "Compliance-fips202205-" + protocol.String() + "-Client-" + sigalg.name,
config: Config{
MinVersion: VersionTLS12,
MaxVersion: maxVersion,
SignSignatureAlgorithms: []signatureAlgorithm{sigalg.id},
Certificates: []Certificate{getRunnerCertificate(sigalg.cert)},
},
flags: []string{
"-fips-202205",
},
shouldFail: !isFIPSSigAlg,
})
}
}
}
func worker(statusChan chan statusMsg, c chan *testCase, shimPath string, wg *sync.WaitGroup) {
defer wg.Done()
@ -19274,6 +19465,7 @@ func main() {
addDelegatedCredentialTests()
addEncryptedClientHelloTests()
addHintMismatchTests()
addCompliancePolicyTests()
toAppend, err := convertToSplitHandshakeTests(testCases)
if err != nil {

@ -385,6 +385,7 @@ std::vector<Flag> SortedFlags() {
&TestConfig::quic_early_data_context),
IntFlag("-early-write-after-message",
&TestConfig::early_write_after_message),
BoolFlag("-fips-202205", &TestConfig::fips_202205),
};
std::sort(flags.begin(), flags.end(), [](const Flag &a, const Flag &b) {
return strcmp(a.name, b.name) < 0;
@ -1765,6 +1766,11 @@ bssl::UniquePtr<SSL> TestConfig::NewSSL(
if (enable_ech_grease) {
SSL_set_enable_ech_grease(ssl.get(), 1);
}
if (fips_202205 && !SSL_set_compliance_policy(
ssl.get(), ssl_compliance_policy_fips_202205)) {
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()),

@ -193,6 +193,7 @@ struct TestConfig {
bool wait_for_debugger = false;
std::string quic_early_data_context;
int early_write_after_message = 0;
bool fips_202205 = false;
int argc;
char **argv;

@ -192,11 +192,15 @@ static enum ssl_hs_wait_t do_read_hello_retry_request(SSL_HANDSHAKE *hs) {
}
// The cipher suite must be one we offered. We currently offer all supported
// TLS 1.3 ciphers, so check the version.
// TLS 1.3 ciphers unless policy controls limited it. So we check the version
// and that it's ok per policy.
const SSL_CIPHER *cipher = SSL_get_cipher_by_value(server_hello.cipher_suite);
if (cipher == nullptr ||
SSL_CIPHER_get_min_version(cipher) > ssl_protocol_version(ssl) ||
SSL_CIPHER_get_max_version(cipher) < ssl_protocol_version(ssl)) {
SSL_CIPHER_get_max_version(cipher) < ssl_protocol_version(ssl) ||
!ssl_tls13_cipher_meets_policy(
SSL_CIPHER_get_value(cipher),
ssl->config->only_fips_cipher_suites_in_tls13)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
return ssl_hs_error;
@ -372,7 +376,7 @@ static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) {
}
// Check the cipher suite, in case this is after HelloRetryRequest.
if (SSL_CIPHER_get_value(hs->new_cipher) != server_hello.cipher_suite) {
if (SSL_CIPHER_get_protocol_id(hs->new_cipher) != server_hello.cipher_suite) {
OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
return ssl_hs_error;

@ -116,7 +116,8 @@ static const SSL_CIPHER *choose_tls13_cipher(
const uint16_t version = ssl_protocol_version(ssl);
return ssl_choose_tls13_cipher(cipher_suites, version, group_id);
return ssl_choose_tls13_cipher(cipher_suites, version, group_id,
ssl->config->only_fips_cipher_suites_in_tls13);
}
static bool add_new_session_tickets(SSL_HANDSHAKE *hs, bool *out_sent_tickets) {

Loading…
Cancel
Save