From cab862738998b5b910e133fceeabef7783502434 Mon Sep 17 00:00:00 2001 From: Alex Weibel Date: Tue, 8 Oct 2024 11:23:00 -0700 Subject: [PATCH] Update FIPS rules for ML-KEM --- crypto/s2n_fips.h | 2 ++ crypto/s2n_fips_rules.c | 44 ++++++++++++++++++++++++++++ tests/unit/s2n_security_rules_test.c | 44 ++++++++++++++++++++++++++-- tls/s2n_security_rules.c | 19 ++++++++---- tls/s2n_security_rules.h | 2 ++ 5 files changed, 104 insertions(+), 7 deletions(-) diff --git a/crypto/s2n_fips.h b/crypto/s2n_fips.h index a353589199d..f9eafef5907 100644 --- a/crypto/s2n_fips.h +++ b/crypto/s2n_fips.h @@ -16,6 +16,7 @@ #include #include "api/s2n.h" +#include "tls/s2n_kem.h" #include "utils/s2n_result.h" #pragma once @@ -30,4 +31,5 @@ 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_hybrid_group(const struct s2n_kem_group *hybrid_group, 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 index 042b5fb8825..31bc1e20ddb 100644 --- a/crypto/s2n_fips_rules.c +++ b/crypto/s2n_fips_rules.c @@ -114,6 +114,50 @@ S2N_RESULT s2n_fips_validate_curve(const struct s2n_ecc_named_curve *curve, bool return S2N_RESULT_OK; } +/* https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.pdf */ +const struct s2n_kem *fips_kems[] = { + &s2n_mlkem_768 +}; + +S2N_RESULT s2n_fips_validate_kem(const struct s2n_kem *kem, bool *valid) +{ + RESULT_ENSURE_REF(kem); + RESULT_ENSURE_REF(valid); + *valid = false; + + for (size_t i = 0; i < s2n_array_len(fips_kems); i++) { + if (fips_kems[i] == kem) { + *valid = true; + return S2N_RESULT_OK; + } + } + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_fips_validate_hybrid_group(const struct s2n_kem_group *hybrid_group, bool *valid) +{ + RESULT_ENSURE_REF(hybrid_group); + RESULT_ENSURE_REF(valid); + *valid = false; + + /* The first share in a Hybrid Group must be FIPS-approved, see page 33 of NIST 800-56Cr2. + * + * "Recommendation is expanded to permit the use of “hybrid” shared secrets of the form Z' = Z || T, + * which is a concatenation consisting of a “standard” shared secret Z that was generated during the + * execution of a key-establishment scheme as currently specified in [SP 800-56A] or [SP 800-56B], + * followed by an auxiliary shared secret T that has been generated using some other method." + * + * https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Cr2.pdf + */ + if (hybrid_group->send_kem_first) { + RESULT_GUARD(s2n_fips_validate_kem(hybrid_group->kem, valid)); + } else { + RESULT_GUARD(s2n_fips_validate_curve(hybrid_group->curve, valid)); + } + + return S2N_RESULT_OK; +} + S2N_RESULT s2n_fips_validate_version(uint8_t version, bool *valid) { RESULT_ENSURE_REF(valid); diff --git a/tests/unit/s2n_security_rules_test.c b/tests/unit/s2n_security_rules_test.c index 60d304f4699..67cb8afd2f9 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 struct s2n_kem_group *VALID_HYBRID_GROUP = &s2n_secp256r1_mlkem_768; +const struct s2n_kem_group *EXAMPLE_INVALID_HYBRID_GROUP = &s2n_x25519_kyber_512_r3; +static S2N_RESULT s2n_test_hybrid_group_rule(const struct s2n_kem_group *hybrid_group, bool *valid) +{ + RESULT_ENSURE_REF(valid); + if (hybrid_group == VALID_HYBRID_GROUP) { + *valid = true; + } else { + *valid = false; + } + 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) @@ -86,6 +99,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_hybrid_group = s2n_test_hybrid_group_rule, .validate_version = s2n_test_version, }; @@ -121,20 +135,35 @@ int main(int argc, char **argv) .count = 1, }; + const struct s2n_kem_preferences valid_kem_preferences = { + .kem_count = 0, + .kems = NULL, + .tls13_kem_groups = &VALID_HYBRID_GROUP, + .tls13_kem_group_count = 1, + }; + + const struct s2n_kem_preferences invalid_kem_preferences = { + .kem_count = 0, + .kems = NULL, + .tls13_kem_groups = &EXAMPLE_INVALID_HYBRID_GROUP, + .tls13_kem_group_count = 1, + }; + const struct s2n_security_policy valid_policy = { .cipher_preferences = &valid_cipher_prefs, .signature_preferences = &valid_sig_prefs, .certificate_signature_preferences = &valid_sig_prefs, .ecc_preferences = &valid_ecc_prefs, - .kem_preferences = &kem_preferences_null, + .kem_preferences = &valid_kem_preferences, .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, - .kem_preferences = &kem_preferences_null, + .kem_preferences = &invalid_kem_preferences, .minimum_protocol_version = EXAMPLE_INVALID_VERSION, }; @@ -202,6 +231,17 @@ int main(int argc, char **argv) EXPECT_TRUE(result.found_error); }; + /* Test: only hybrid group invalid */ + { + struct s2n_security_policy test_policy = valid_policy; + test_policy.kem_preferences = &invalid_kem_preferences; + + 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: only version invalid */ { struct s2n_security_policy test_policy = valid_policy; diff --git a/tls/s2n_security_rules.c b/tls/s2n_security_rules.c index b6bdab4bfdc..92be99ea39c 100644 --- a/tls/s2n_security_rules.c +++ b/tls/s2n_security_rules.c @@ -70,6 +70,14 @@ static S2N_RESULT s2n_security_rule_all_curves( return S2N_RESULT_OK; } +static S2N_RESULT s2n_security_rule_all_hybrid_groups( + const struct s2n_kem_group *hybrid_group, bool *valid) +{ + RESULT_ENSURE_REF(valid); + *valid = true; + return S2N_RESULT_OK; +} + static S2N_RESULT s2n_security_rule_all_versions(uint8_t version, bool *valid) { RESULT_ENSURE_REF(valid); @@ -84,6 +92,7 @@ const struct s2n_security_rule security_rule_definitions[S2N_SECURITY_RULES_COUN .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_hybrid_group = s2n_security_rule_all_hybrid_groups, .validate_version = s2n_security_rule_all_versions, }, [S2N_FIPS_140_3] = { @@ -92,6 +101,7 @@ const struct s2n_security_rule security_rule_definitions[S2N_SECURITY_RULES_COUN .validate_sig_scheme = s2n_fips_validate_signature_scheme, .validate_cert_sig_scheme = s2n_fips_validate_signature_scheme, .validate_curve = s2n_fips_validate_curve, + .validate_hybrid_group = s2n_fips_validate_hybrid_group, .validate_version = s2n_fips_validate_version, }, }; @@ -169,14 +179,13 @@ S2N_RESULT s2n_security_rule_validate_policy(const struct s2n_security_rule *rul RESULT_ENSURE_REF(kem_prefs); for (size_t i = 0; i < kem_prefs->tls13_kem_group_count; i++) { const struct s2n_kem_group *kem_group = kem_prefs->tls13_kem_groups[i]; - const struct s2n_ecc_named_curve *curve = kem_group->curve; - RESULT_ENSURE_REF(curve); + RESULT_ENSURE_REF(kem_group); bool is_valid = false; - RESULT_ENSURE_REF(rule->validate_curve); - RESULT_GUARD(rule->validate_curve(curve, &is_valid)); + RESULT_ENSURE_REF(rule->validate_hybrid_group); + RESULT_GUARD(rule->validate_hybrid_group(kem_group, &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)); + "kem_group", kem_group->name, i + 1)); } bool is_valid = false; diff --git a/tls/s2n_security_rules.h b/tls/s2n_security_rules.h index 587bfb84c7f..140f8cf2e22 100644 --- a/tls/s2n_security_rules.h +++ b/tls/s2n_security_rules.h @@ -16,6 +16,7 @@ #pragma once #include "stuffer/s2n_stuffer.h" +#include "tls/s2n_kem.h" #include "utils/s2n_result.h" typedef enum { @@ -43,6 +44,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_hybrid_group)(const struct s2n_kem_group *hybrid_group, bool *valid); S2N_RESULT (*validate_version)(uint8_t version, bool *valid); };