diff --git a/crypto/s2n_fips.h b/crypto/s2n_fips.h index e082e34f3b0..f1047d52ae6 100644 --- a/crypto/s2n_fips.h +++ b/crypto/s2n_fips.h @@ -16,9 +16,18 @@ #include #include "api/s2n.h" +#include "utils/s2n_result.h" #pragma once int s2n_fips_init(void); int s2n_is_in_fips_mode(void); bool s2n_libcrypto_is_fips(void); + +struct s2n_cipher_suite; +S2N_RESULT s2n_fips_validate_cipher_suite(const struct s2n_cipher_suite *cipher_suite, bool *valid); +struct s2n_signature_scheme; +S2N_RESULT s2n_fips_validate_signature_scheme(const struct s2n_signature_scheme *sig_alg, bool *valid); +struct s2n_ecc_named_curve; +S2N_RESULT s2n_fips_validate_curve(const struct s2n_ecc_named_curve *curve, bool *valid); +S2N_RESULT s2n_fips_validate_version(uint8_t version, bool *valid); diff --git a/crypto/s2n_fips_rules.c b/crypto/s2n_fips_rules.c new file mode 100644 index 00000000000..042b5fb8825 --- /dev/null +++ b/crypto/s2n_fips_rules.c @@ -0,0 +1,125 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "crypto/s2n_fips.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_tls_parameters.h" +#include "utils/s2n_result.h" + +/* FIPS requires at least 112 bits of security. + * https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-107r1.pdf */ +const s2n_hash_algorithm fips_hash_algs[] = { + S2N_HASH_SHA224, + S2N_HASH_SHA256, + S2N_HASH_SHA384, + S2N_HASH_SHA512, +}; +S2N_RESULT s2n_fips_validate_hash_algorithm(s2n_hash_algorithm hash_alg, bool *valid) +{ + RESULT_ENSURE_REF(valid); + *valid = false; + for (size_t i = 0; i < s2n_array_len(fips_hash_algs); i++) { + if (fips_hash_algs[i] == hash_alg) { + *valid = true; + return S2N_RESULT_OK; + } + } + return S2N_RESULT_OK; +} + +/* https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-52r2.pdf */ +const uint8_t fips_cipher_suite_ianas[][2] = { + /* 3.3.1.1.1 Cipher Suites for ECDSA Certificates */ + { TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 }, + { TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 }, + { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 }, + { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 }, + { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA }, + { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA }, + + /* 3.3.1.1.2 Cipher Suites for RSA Certificates */ + { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 }, + { TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 }, + { TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 }, + { TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 }, + { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 }, + { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 }, + { TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 }, + { TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 }, + { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA }, + { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA }, + { TLS_DHE_RSA_WITH_AES_128_CBC_SHA }, + { TLS_DHE_RSA_WITH_AES_256_CBC_SHA }, + + /* 3.3.1.2 Cipher Suites for TLS 1.3 */ + { TLS_AES_128_GCM_SHA256 }, + { TLS_AES_256_GCM_SHA384 }, +}; + +S2N_RESULT s2n_fips_validate_cipher_suite(const struct s2n_cipher_suite *cipher_suite, bool *valid) +{ + RESULT_ENSURE_REF(cipher_suite); + RESULT_ENSURE_REF(valid); + + *valid = false; + for (size_t i = 0; i < s2n_array_len(fips_cipher_suite_ianas); i++) { + if (fips_cipher_suite_ianas[i][0] != cipher_suite->iana_value[0]) { + continue; + } + if (fips_cipher_suite_ianas[i][1] != cipher_suite->iana_value[1]) { + continue; + } + *valid = true; + return S2N_RESULT_OK; + } + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_fips_validate_signature_scheme(const struct s2n_signature_scheme *sig_alg, bool *valid) +{ + RESULT_ENSURE_REF(sig_alg); + RESULT_GUARD(s2n_fips_validate_hash_algorithm(sig_alg->hash_alg, valid)); + return S2N_RESULT_OK; +} + +/* https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar3.pdf */ +const struct s2n_ecc_named_curve *fips_curves[] = { + &s2n_ecc_curve_secp256r1, + &s2n_ecc_curve_secp384r1, + &s2n_ecc_curve_secp521r1, +}; +S2N_RESULT s2n_fips_validate_curve(const struct s2n_ecc_named_curve *curve, bool *valid) +{ + RESULT_ENSURE_REF(curve); + RESULT_ENSURE_REF(valid); + *valid = false; + for (size_t i = 0; i < s2n_array_len(fips_curves); i++) { + if (fips_curves[i] == curve) { + *valid = true; + return S2N_RESULT_OK; + } + } + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_fips_validate_version(uint8_t version, bool *valid) +{ + RESULT_ENSURE_REF(valid); + /* Technically FIPS 140-3 still allows TLS1.0 and TLS1.1 for some use cases, + * but for simplicity s2n-tls does not. + */ + *valid = (version >= S2N_TLS12); + return S2N_RESULT_OK; +} diff --git a/tests/unit/s2n_fips_rules_test.c b/tests/unit/s2n_fips_rules_test.c new file mode 100644 index 00000000000..059f3f10db9 --- /dev/null +++ b/tests/unit/s2n_fips_rules_test.c @@ -0,0 +1,251 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +S2N_RESULT s2n_fips_validate_hash_algorithm(s2n_hash_algorithm hash_alg, bool *valid); + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Test s2n_fips_validate_cipher_suite */ + { + /* Safety */ + { + bool is_valid = false; + EXPECT_ERROR_WITH_ERRNO( + s2n_fips_validate_cipher_suite(NULL, &is_valid), + S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO( + s2n_fips_validate_cipher_suite(&s2n_null_cipher_suite, NULL), + S2N_ERR_NULL); + } + + /* Test: Valid */ + const struct s2n_cipher_suite *valid[] = { + &s2n_tls13_aes_256_gcm_sha384, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + }; + for (size_t i = 0; i < s2n_array_len(valid); i++) { + bool is_valid = false; + EXPECT_OK(s2n_fips_validate_cipher_suite(valid[i], &is_valid)); + EXPECT_TRUE(is_valid); + } + + /* Test: Invalid */ + const struct s2n_cipher_suite *invalid[] = { + &s2n_null_cipher_suite, + &s2n_rsa_with_rc4_128_md5, + &s2n_rsa_with_aes_128_gcm_sha256, + }; + for (size_t i = 0; i < s2n_array_len(invalid); i++) { + bool is_valid = true; + EXPECT_OK(s2n_fips_validate_cipher_suite(invalid[i], &is_valid)); + EXPECT_FALSE(is_valid); + } + + /* Test: check all */ + for (size_t i = 0; i < cipher_preferences_test_all.count; i++) { + const struct s2n_cipher_suite *cipher_suite = cipher_preferences_test_all.suites[i]; + + bool is_valid = false; + EXPECT_OK(s2n_fips_validate_cipher_suite(cipher_suite, &is_valid)); + if (!is_valid) { + continue; + } + + /* Must be in the "test_all_fips" security policy */ + const struct s2n_cipher_preferences *test_all_fips_prefs = + security_policy_test_all_fips.cipher_preferences; + bool is_in_test_all_fips_prefs = false; + for (size_t j = 0; j < test_all_fips_prefs->count; j++) { + if (cipher_suite == test_all_fips_prefs->suites[j]) { + is_in_test_all_fips_prefs = true; + } + } + EXPECT_TRUE(is_in_test_all_fips_prefs); + + /* We copy our lists of allowed cipher suites directly from the standards, + * but we should double check any invariants we can just in case. + */ + + /* RSA key exchange is disallowed after 2023 + * https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf */ + EXPECT_NOT_EQUAL(cipher_suite->key_exchange_alg, &s2n_rsa); + + /* AES is required. + * Multiple s2n_ciphers represent AES, so just check the name. + */ + EXPECT_NOT_NULL(strstr(cipher_suite->name, "AES")); + + /* Must use valid prf hash algorithm */ + bool hash_is_valid = false; + s2n_hash_algorithm hash_alg = 0; + EXPECT_SUCCESS(s2n_hmac_hash_alg(cipher_suite->prf_alg, &hash_alg)); + EXPECT_OK(s2n_fips_validate_hash_algorithm(hash_alg, &hash_is_valid)); + EXPECT_TRUE(hash_is_valid); + } + }; + + /* Test s2n_fips_validate_signature_scheme */ + { + /* Safety */ + { + bool is_valid = false; + EXPECT_ERROR_WITH_ERRNO( + s2n_fips_validate_signature_scheme(NULL, &is_valid), + S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO( + s2n_fips_validate_signature_scheme(&s2n_null_sig_scheme, NULL), + S2N_ERR_NULL); + } + + /* Test: Valid */ + const struct s2n_signature_scheme *valid[] = { + &s2n_ecdsa_sha256, + &s2n_rsa_pkcs1_sha384, + &s2n_ecdsa_secp521r1_sha512, + &s2n_rsa_pss_pss_sha256, + }; + for (size_t i = 0; i < s2n_array_len(valid); i++) { + bool is_valid = false; + EXPECT_OK(s2n_fips_validate_signature_scheme(valid[i], &is_valid)); + EXPECT_TRUE(is_valid); + } + + /* Test: Invalid */ + const struct s2n_signature_scheme *invalid[] = { + &s2n_rsa_pkcs1_md5_sha1, + &s2n_rsa_pkcs1_sha1, + &s2n_ecdsa_sha1, + &s2n_null_sig_scheme, + }; + for (size_t i = 0; i < s2n_array_len(invalid); i++) { + bool is_valid = true; + EXPECT_OK(s2n_fips_validate_signature_scheme(invalid[i], &is_valid)); + EXPECT_FALSE(is_valid); + } + + /* Test: check all */ + const struct s2n_signature_preferences *all_sig_schemes = + security_policy_test_all.signature_preferences; + for (size_t i = 0; i < all_sig_schemes->count; i++) { + const struct s2n_signature_scheme *sig_scheme = all_sig_schemes->signature_schemes[i]; + + bool is_valid = false; + EXPECT_OK(s2n_fips_validate_signature_scheme(sig_scheme, &is_valid)); + if (!is_valid) { + continue; + } + + /* Must be in the "test_all_fips" security policy */ + const struct s2n_signature_preferences *test_all_fips_prefs = + security_policy_test_all_fips.signature_preferences; + bool is_in_test_all_fips_prefs = false; + for (size_t j = 0; j < test_all_fips_prefs->count; j++) { + if (sig_scheme == test_all_fips_prefs->signature_schemes[j]) { + is_in_test_all_fips_prefs = true; + } + } + EXPECT_TRUE(is_in_test_all_fips_prefs); + } + }; + + /* Test s2n_fips_validate_curve */ + { + /* Safety */ + { + bool is_valid = false; + EXPECT_ERROR_WITH_ERRNO( + s2n_fips_validate_curve(NULL, &is_valid), + S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO( + s2n_fips_validate_curve(&s2n_ecc_curve_secp256r1, NULL), + S2N_ERR_NULL); + } + + /* Test: Valid */ + const struct s2n_ecc_named_curve *valid[] = { &s2n_ecc_curve_secp256r1 }; + for (size_t i = 0; i < s2n_array_len(valid); i++) { + bool is_valid = false; + EXPECT_OK(s2n_fips_validate_curve(valid[i], &is_valid)); + EXPECT_TRUE(is_valid); + } + + /* Test: Invalid */ + const struct s2n_ecc_named_curve *invalid[] = { &s2n_ecc_curve_x25519 }; + for (size_t i = 0; i < s2n_array_len(invalid); i++) { + bool is_valid = true; + EXPECT_OK(s2n_fips_validate_curve(invalid[i], &is_valid)); + EXPECT_FALSE(is_valid); + } + + /* Test: check all */ + for (size_t i = 0; i < s2n_ecc_preferences_test_all.count; i++) { + const struct s2n_ecc_named_curve *curve = s2n_ecc_preferences_test_all.ecc_curves[i]; + + bool is_valid = false; + EXPECT_OK(s2n_fips_validate_curve(curve, &is_valid)); + if (!is_valid) { + continue; + } + + /* Must be in the "test_all_fips" security policy */ + const struct s2n_ecc_preferences *test_all_fips_prefs = + security_policy_test_all_fips.ecc_preferences; + bool is_in_test_all_fips_prefs = false; + for (size_t j = 0; j < test_all_fips_prefs->count; j++) { + if (curve == test_all_fips_prefs->ecc_curves[j]) { + is_in_test_all_fips_prefs = true; + } + } + EXPECT_TRUE(is_in_test_all_fips_prefs); + } + }; + + /* Test s2n_fips_validate_version */ + { + /* Safety */ + EXPECT_ERROR_WITH_ERRNO(s2n_fips_validate_version(0, NULL), S2N_ERR_NULL); + + /* Test: Valid */ + uint8_t valid[] = { S2N_TLS12, S2N_TLS13 }; + for (size_t i = 0; i < s2n_array_len(valid); i++) { + bool is_valid = false; + EXPECT_OK(s2n_fips_validate_version(valid[i], &is_valid)); + EXPECT_TRUE(is_valid); + } + + /* Test: Invalid */ + uint8_t invalid[] = { 0, 1, S2N_SSLv2, S2N_SSLv3, S2N_TLS11 }; + for (size_t i = 0; i < s2n_array_len(invalid); i++) { + bool is_valid = true; + EXPECT_OK(s2n_fips_validate_version(invalid[i], &is_valid)); + EXPECT_FALSE(is_valid); + } + + /* Test: check all */ + for (size_t version = 0; version < UINT8_MAX; version++) { + bool is_valid = false; + EXPECT_OK(s2n_fips_validate_version(version, &is_valid)); + } + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_security_policies_rules_test.c b/tests/unit/s2n_security_policies_rules_test.c index 0aea4dcbc1a..3fe4c48c84c 100644 --- a/tests/unit/s2n_security_policies_rules_test.c +++ b/tests/unit/s2n_security_policies_rules_test.c @@ -34,7 +34,7 @@ int main(int argc, char **argv) int output_size = s2n_stuffer_data_available(&result.output); char *output_str = s2n_stuffer_raw_read(&result.output, output_size); EXPECT_NOT_NULL(output_str); - fprintf(stdout, "%.*s", output_size, output_str); + fprintf(stdout, "\n%.*s", output_size, output_str); FAIL_MSG("Security policies violate configured policy rules. See stdout for details."); } diff --git a/tests/unit/s2n_security_policies_test.c b/tests/unit/s2n_security_policies_test.c index 9a6301326aa..8a3c1b0989f 100644 --- a/tests/unit/s2n_security_policies_test.c +++ b/tests/unit/s2n_security_policies_test.c @@ -460,7 +460,6 @@ int main(int argc, char **argv) "20190121", "20190122", "20201021", - "test_all_fips", "test_all_ecdsa", "test_ecdsa_priority", "test_all_tls12", diff --git a/tests/unit/s2n_security_rules_test.c b/tests/unit/s2n_security_rules_test.c index 51d7c6d54eb..5c69d9f2824 100644 --- a/tests/unit/s2n_security_rules_test.c +++ b/tests/unit/s2n_security_rules_test.c @@ -63,6 +63,19 @@ static S2N_RESULT s2n_test_curve_rule(const struct s2n_ecc_named_curve *curve, b return S2N_RESULT_OK; } +const uint8_t VALID_VERSION = S2N_TLS12; +const uint8_t EXAMPLE_INVALID_VERSION = S2N_TLS11; +static S2N_RESULT s2n_test_version(uint8_t version, bool *valid) +{ + RESULT_ENSURE_REF(valid); + if (version == VALID_VERSION) { + *valid = true; + } else { + *valid = false; + } + return S2N_RESULT_OK; +} + int main(int argc, char **argv) { BEGIN_TEST(); @@ -73,6 +86,7 @@ int main(int argc, char **argv) .validate_sig_scheme = s2n_test_sig_scheme_rule, .validate_cert_sig_scheme = s2n_test_sig_scheme_rule, .validate_curve = s2n_test_curve_rule, + .validate_version = s2n_test_version, }; const struct s2n_cipher_preferences valid_cipher_prefs = { @@ -112,12 +126,14 @@ int main(int argc, char **argv) .signature_preferences = &valid_sig_prefs, .certificate_signature_preferences = &valid_sig_prefs, .ecc_preferences = &valid_ecc_prefs, + .minimum_protocol_version = VALID_VERSION, }; const struct s2n_security_policy invalid_policy = { .cipher_preferences = &invalid_cipher_prefs, .signature_preferences = &invalid_sig_prefs, .certificate_signature_preferences = &invalid_sig_prefs, .ecc_preferences = &invalid_ecc_prefs, + .minimum_protocol_version = EXAMPLE_INVALID_VERSION, }; /* Test s2n_security_rule_validate_policy */ @@ -183,6 +199,17 @@ int main(int argc, char **argv) &test_rule, &test_policy, &result)); EXPECT_TRUE(result.found_error); }; + + /* Test: only version invalid */ + { + struct s2n_security_policy test_policy = valid_policy; + test_policy.minimum_protocol_version = EXAMPLE_INVALID_VERSION; + + struct s2n_security_rule_result result = { 0 }; + EXPECT_OK(s2n_security_rule_validate_policy( + &test_rule, &test_policy, &result)); + EXPECT_TRUE(result.found_error); + }; }; /* Test: skips certificate signature preferences if not present */ diff --git a/tls/s2n_cipher_suites.c b/tls/s2n_cipher_suites.c index bbf8dbcd20e..81e08a8126b 100644 --- a/tls/s2n_cipher_suites.c +++ b/tls/s2n_cipher_suites.c @@ -837,17 +837,18 @@ const struct s2n_cipher_preferences cipher_preferences_test_all_tls12 = { * in order of IANA value. Exposed for the "test_all_fips" cipher preference list. */ static struct s2n_cipher_suite *s2n_all_fips_cipher_suites[] = { - &s2n_rsa_with_3des_ede_cbc_sha, /* 0x00,0x0A */ - &s2n_rsa_with_aes_128_cbc_sha, /* 0x00,0x2F */ - &s2n_rsa_with_aes_256_cbc_sha, /* 0x00,0x35 */ - &s2n_rsa_with_aes_128_cbc_sha256, /* 0x00,0x3C */ - &s2n_rsa_with_aes_256_cbc_sha256, /* 0x00,0x3D */ + &s2n_dhe_rsa_with_aes_128_cbc_sha, /* 0x00,0x33 */ + &s2n_dhe_rsa_with_aes_256_cbc_sha, /* 0x00,0x39 */ &s2n_dhe_rsa_with_aes_128_cbc_sha256, /* 0x00,0x67 */ &s2n_dhe_rsa_with_aes_256_cbc_sha256, /* 0x00,0x6B */ - &s2n_rsa_with_aes_128_gcm_sha256, /* 0x00,0x9C */ - &s2n_rsa_with_aes_256_gcm_sha384, /* 0x00,0x9D */ &s2n_dhe_rsa_with_aes_128_gcm_sha256, /* 0x00,0x9E */ &s2n_dhe_rsa_with_aes_256_gcm_sha384, /* 0x00,0x9F */ + &s2n_tls13_aes_128_gcm_sha256, /* 0x13,0x01 */ + &s2n_tls13_aes_256_gcm_sha384, /* 0x13,0x02 */ + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, /* 0xC0,0x09 */ + &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha, /* 0xC0,0x0A */ + &s2n_ecdhe_rsa_with_aes_128_cbc_sha, /* 0xC0,0x13 */ + &s2n_ecdhe_rsa_with_aes_256_cbc_sha, /* 0xC0,0x14 */ &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, /* 0xC0,0x23 */ &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha384, /* 0xC0,0x24 */ &s2n_ecdhe_rsa_with_aes_128_cbc_sha256, /* 0xC0,0x27 */ diff --git a/tls/s2n_security_policies.c b/tls/s2n_security_policies.c index 57f90290e15..2b53f6bd9e9 100644 --- a/tls/s2n_security_policies.c +++ b/tls/s2n_security_policies.c @@ -51,6 +51,7 @@ const struct s2n_security_policy security_policy_default_fips = { .ecc_preferences = &s2n_ecc_preferences_default_fips, .rules = { [S2N_PERFECT_FORWARD_SECRECY] = true, + [S2N_FIPS_140_3] = true, }, }; @@ -63,6 +64,7 @@ const struct s2n_security_policy security_policy_20230317 = { .ecc_preferences = &s2n_ecc_preferences_20201021, .rules = { [S2N_PERFECT_FORWARD_SECRECY] = true, + [S2N_FIPS_140_3] = true, }, }; @@ -919,6 +921,7 @@ const struct s2n_security_policy security_policy_20210816 = { .ecc_preferences = &s2n_ecc_preferences_20210816, .rules = { [S2N_PERFECT_FORWARD_SECRECY] = true, + [S2N_FIPS_140_3] = true, }, }; @@ -930,6 +933,7 @@ const struct s2n_security_policy security_policy_20210816_gcm = { .ecc_preferences = &s2n_ecc_preferences_20210816, .rules = { [S2N_PERFECT_FORWARD_SECRECY] = true, + [S2N_FIPS_140_3] = true, }, }; @@ -963,11 +967,14 @@ const struct s2n_security_policy security_policy_test_all_tls12 = { }; const struct s2n_security_policy security_policy_test_all_fips = { - .minimum_protocol_version = S2N_TLS10, + .minimum_protocol_version = S2N_TLS12, .cipher_preferences = &cipher_preferences_test_all_fips, .kem_preferences = &kem_preferences_null, - .signature_preferences = &s2n_signature_preferences_20201021, + .signature_preferences = &s2n_signature_preferences_test_all_fips, .ecc_preferences = &s2n_ecc_preferences_20201021, + .rules = { + [S2N_FIPS_140_3] = true, + }, }; const struct s2n_security_policy security_policy_test_all_ecdsa = { diff --git a/tls/s2n_security_rules.c b/tls/s2n_security_rules.c index 093af115fdc..9d85199d51b 100644 --- a/tls/s2n_security_rules.c +++ b/tls/s2n_security_rules.c @@ -17,6 +17,7 @@ #include +#include "crypto/s2n_fips.h" #include "tls/s2n_cipher_suites.h" #include "tls/s2n_signature_scheme.h" #include "utils/s2n_result.h" @@ -69,6 +70,13 @@ static S2N_RESULT s2n_security_rule_all_curves( return S2N_RESULT_OK; } +static S2N_RESULT s2n_security_rule_all_versions(uint8_t version, bool *valid) +{ + RESULT_ENSURE_REF(valid); + *valid = true; + return S2N_RESULT_OK; +} + const struct s2n_security_rule security_rule_definitions[] = { [S2N_PERFECT_FORWARD_SECRECY] = { .name = "Perfect Forward Secrecy", @@ -76,6 +84,15 @@ const struct s2n_security_rule security_rule_definitions[] = { .validate_sig_scheme = s2n_security_rule_all_sig_schemes, .validate_cert_sig_scheme = s2n_security_rule_all_sig_schemes, .validate_curve = s2n_security_rule_all_curves, + .validate_version = s2n_security_rule_all_versions, + }, + [S2N_FIPS_140_3] = { + .name = "FIPS 140-3 (2019)", + .validate_cipher_suite = s2n_fips_validate_cipher_suite, + .validate_sig_scheme = s2n_fips_validate_signature_scheme, + .validate_cert_sig_scheme = s2n_fips_validate_signature_scheme, + .validate_curve = s2n_fips_validate_curve, + .validate_version = s2n_fips_validate_version, }, }; @@ -94,6 +111,7 @@ S2N_RESULT s2n_security_rule_validate_policy(const struct s2n_security_rule *rul const char *error_msg_format_name = "%s: policy %s: %s: %s (#%i)"; const char *error_msg_format_iana = "%s: policy %s: %s: %x (#%i)"; + const char *error_msg_format_basic = "%s: policy %s: %s: %i"; const struct s2n_cipher_preferences *cipher_prefs = policy->cipher_preferences; RESULT_ENSURE_REF(cipher_prefs); @@ -101,6 +119,7 @@ S2N_RESULT s2n_security_rule_validate_policy(const struct s2n_security_rule *rul const struct s2n_cipher_suite *cipher_suite = cipher_prefs->suites[i]; RESULT_ENSURE_REF(cipher_suite); bool is_valid = false; + RESULT_ENSURE_REF(rule->validate_cipher_suite); RESULT_GUARD(rule->validate_cipher_suite(cipher_suite, &is_valid)); RESULT_GUARD(s2n_security_rule_result_process(result, is_valid, error_msg_format_name, rule->name, policy_name, @@ -113,6 +132,7 @@ S2N_RESULT s2n_security_rule_validate_policy(const struct s2n_security_rule *rul const struct s2n_signature_scheme *sig_scheme = sig_prefs->signature_schemes[i]; RESULT_ENSURE_REF(sig_scheme); bool is_valid = false; + RESULT_ENSURE_REF(rule->validate_sig_scheme); RESULT_GUARD(rule->validate_sig_scheme(sig_scheme, &is_valid)); RESULT_GUARD(s2n_security_rule_result_process(result, is_valid, error_msg_format_iana, rule->name, policy_name, @@ -125,6 +145,7 @@ S2N_RESULT s2n_security_rule_validate_policy(const struct s2n_security_rule *rul const struct s2n_signature_scheme *sig_scheme = cert_sig_prefs->signature_schemes[i]; RESULT_ENSURE_REF(sig_scheme); bool is_valid = false; + RESULT_ENSURE_REF(rule->validate_cert_sig_scheme); RESULT_GUARD(rule->validate_cert_sig_scheme(sig_scheme, &is_valid)); RESULT_GUARD(s2n_security_rule_result_process(result, is_valid, error_msg_format_iana, rule->name, policy_name, @@ -138,12 +159,20 @@ S2N_RESULT s2n_security_rule_validate_policy(const struct s2n_security_rule *rul const struct s2n_ecc_named_curve *curve = ecc_prefs->ecc_curves[i]; RESULT_ENSURE_REF(curve); bool is_valid = false; + RESULT_ENSURE_REF(rule->validate_curve); RESULT_GUARD(rule->validate_curve(curve, &is_valid)); RESULT_GUARD(s2n_security_rule_result_process(result, is_valid, error_msg_format_name, rule->name, policy_name, "curve", curve->name, i + 1)); } + bool is_valid = false; + RESULT_ENSURE_REF(rule->validate_version); + RESULT_GUARD(rule->validate_version(policy->minimum_protocol_version, &is_valid)); + RESULT_GUARD(s2n_security_rule_result_process(result, is_valid, + error_msg_format_basic, rule->name, policy_name, + "min version", policy->minimum_protocol_version)); + return S2N_RESULT_OK; } diff --git a/tls/s2n_security_rules.h b/tls/s2n_security_rules.h index f060d778a88..587bfb84c7f 100644 --- a/tls/s2n_security_rules.h +++ b/tls/s2n_security_rules.h @@ -20,6 +20,7 @@ typedef enum { S2N_PERFECT_FORWARD_SECRECY = 0, + S2N_FIPS_140_3, S2N_SECURITY_RULES_COUNT, } s2n_security_rule_id; @@ -42,6 +43,7 @@ struct s2n_security_rule { S2N_RESULT (*validate_sig_scheme)(const struct s2n_signature_scheme *sig_scheme, bool *valid); S2N_RESULT (*validate_cert_sig_scheme)(const struct s2n_signature_scheme *sig_scheme, bool *valid); S2N_RESULT (*validate_curve)(const struct s2n_ecc_named_curve *curve, bool *valid); + S2N_RESULT (*validate_version)(uint8_t version, bool *valid); }; S2N_RESULT s2n_security_policy_validate_security_rules( diff --git a/tls/s2n_signature_scheme.c b/tls/s2n_signature_scheme.c index f5ac033a229..c4bab44d865 100644 --- a/tls/s2n_signature_scheme.c +++ b/tls/s2n_signature_scheme.c @@ -470,3 +470,33 @@ const struct s2n_signature_preferences s2n_certificate_signature_preferences_rfc .count = s2n_array_len(s2n_cert_sig_scheme_pref_list_rfc9151), .signature_schemes = s2n_cert_sig_scheme_pref_list_rfc9151 }; + +const struct s2n_signature_scheme* const s2n_sig_scheme_pref_list_test_all_fips[] = { + /* RSA PSS */ + &s2n_rsa_pss_pss_sha256, + &s2n_rsa_pss_pss_sha384, + &s2n_rsa_pss_pss_sha512, + &s2n_rsa_pss_rsae_sha256, + &s2n_rsa_pss_rsae_sha384, + &s2n_rsa_pss_rsae_sha512, + + /* RSA PKCS1 */ + &s2n_rsa_pkcs1_sha256, + &s2n_rsa_pkcs1_sha384, + &s2n_rsa_pkcs1_sha512, + &s2n_rsa_pkcs1_sha224, + + /* ECDSA */ + &s2n_ecdsa_sha256, /* same iana value as TLS 1.3 s2n_ecdsa_secp256r1_sha256 */ + &s2n_ecdsa_secp256r1_sha256, + &s2n_ecdsa_sha384, /* same iana value as TLS 1.3 s2n_ecdsa_secp384r1_sha384 */ + &s2n_ecdsa_secp384r1_sha384, + &s2n_ecdsa_sha512, /* same iana value as TLS 1.3 s2n_ecdsa_secp521r1_sha512 */ + &s2n_ecdsa_secp521r1_sha512, + &s2n_ecdsa_sha224, +}; + +const struct s2n_signature_preferences s2n_signature_preferences_test_all_fips = { + .count = s2n_array_len(s2n_sig_scheme_pref_list_test_all_fips), + .signature_schemes = s2n_sig_scheme_pref_list_test_all_fips, +}; diff --git a/tls/s2n_signature_scheme.h b/tls/s2n_signature_scheme.h index 03828a27e89..c8143f452aa 100644 --- a/tls/s2n_signature_scheme.h +++ b/tls/s2n_signature_scheme.h @@ -84,5 +84,6 @@ extern const struct s2n_signature_preferences s2n_signature_preferences_rfc9151; extern const struct s2n_signature_preferences s2n_certificate_signature_preferences_rfc9151; extern const struct s2n_signature_preferences s2n_signature_preferences_default_fips; extern const struct s2n_signature_preferences s2n_signature_preferences_null; +extern const struct s2n_signature_preferences s2n_signature_preferences_test_all_fips; extern const struct s2n_signature_preferences s2n_certificate_signature_preferences_20201110;