diff --git a/tests/unit/s2n_extensions_server_key_share_select_test.c b/tests/unit/s2n_extensions_server_key_share_select_test.c index efd707c1c5b..9591a792fc6 100644 --- a/tests/unit/s2n_extensions_server_key_share_select_test.c +++ b/tests/unit/s2n_extensions_server_key_share_select_test.c @@ -383,8 +383,7 @@ int main() EXPECT_SUCCESS(s2n_connection_free(server_conn)); }; - /* When client sent valid keyshares - * only for ECC, server should choose curves[0] and not send HRR. */ + /* When client sent KeyShares only for ECC but also supports PQ, server should choose PQ and send HRR. */ { struct s2n_connection *server_conn = NULL; EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); @@ -422,13 +421,12 @@ int main() EXPECT_SUCCESS(s2n_extensions_server_key_share_select(server_conn)); - /* Server should update its choice to curve[0], no HRR */ - EXPECT_EQUAL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve, ecc_pref->ecc_curves[0]); - EXPECT_NULL(server_params->kem_group); - EXPECT_NULL(server_params->kem_params.kem); - EXPECT_NULL(server_params->ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_EQUAL(server_params->kem_group, kem_group0); + EXPECT_EQUAL(server_params->kem_params.kem, kem_group0->kem); + EXPECT_EQUAL(server_params->ecc_params.negotiated_curve, kem_group0->curve); EXPECT_NULL(server_conn->kex_params.client_kem_group_params.kem_group); - EXPECT_FALSE(s2n_is_hello_retry_handshake(server_conn)); + EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); EXPECT_SUCCESS(s2n_connection_free(server_conn)); }; diff --git a/tests/unit/s2n_tls13_pq_handshake_test.c b/tests/unit/s2n_tls13_pq_handshake_test.c index 74f26f0c288..d8301c761de 100644 --- a/tests/unit/s2n_tls13_pq_handshake_test.c +++ b/tests/unit/s2n_tls13_pq_handshake_test.c @@ -26,31 +26,94 @@ /* Include C file directly to access static functions */ #include "tls/s2n_handshake_io.c" -const struct s2n_kem_group *s2n_get_highest_priority_shared_kem_group(const struct s2n_kem_preferences *client_prefs, const struct s2n_kem_preferences *server_prefs) +const struct s2n_kem_group *s2n_get_predicted_negotiated_kem_group(const struct s2n_kem_preferences *client_prefs, const struct s2n_kem_preferences *server_prefs) { PTR_ENSURE_REF(client_prefs); PTR_ENSURE_REF(server_prefs); - for (int i = 0; i < client_prefs->tls13_kem_group_count; i++) { - for (int j = 0; j < server_prefs->tls13_kem_group_count; j++) { - const struct s2n_kem_group *client_group = client_prefs->tls13_kem_groups[i]; - const struct s2n_kem_group *server_group = server_prefs->tls13_kem_groups[j]; + + /* Client will offer their highest priority PQ KeyShare in their ClientHello. This PQ KeyShare + * will be most preferred since it can be negotiated in 1-RTT (even if there are other mutually + * supported PQ KeyShares that the server would prefer over this one but would require 2-RTT's). + */ + const struct s2n_kem_group *client_default = client_prefs->tls13_kem_groups[0]; + PTR_ENSURE_REF(client_default); + + for (int i = 0; i < server_prefs->tls13_kem_group_count; i++) { + const struct s2n_kem_group *server_group = server_prefs->tls13_kem_groups[i]; + PTR_ENSURE_REF(server_group); + if (s2n_kem_group_is_available(client_default) && s2n_kem_group_is_available(server_group) + && client_default->iana_id == server_group->iana_id + && s2n_kem_group_is_available(client_default)) { + return client_default; + } + } + + /* Otherwise, if the client's default isn't supported, and a 2-RTT PQ handshake is required, the server will choose + * whichever mutually supported PQ KeyShare that is highest on the server's preference list. */ + for (int i = 0; i < server_prefs->tls13_kem_group_count; i++) { + const struct s2n_kem_group *server_group = server_prefs->tls13_kem_groups[i]; + + /* j starts at 1 since we already checked client_prefs->tls13_kem_groups[0] above */ + for (int j = 1; j < client_prefs->tls13_kem_group_count; j++) { + const struct s2n_kem_group *client_group = client_prefs->tls13_kem_groups[j]; PTR_ENSURE_REF(client_group); PTR_ENSURE_REF(server_group); if (s2n_kem_group_is_available(client_group) && s2n_kem_group_is_available(server_group) - && s2n_kem_group_is_available(client_group) == s2n_kem_group_is_available(server_group)) { + && client_group->iana_id == server_group->iana_id + && s2n_kem_group_is_available(client_group)) { return client_group; } } } + + return NULL; +} + +const struct s2n_ecc_named_curve *s2n_get_predicted_negotiated_ecdhe_curve(const struct s2n_security_policy *client_sec_policy, + const struct s2n_security_policy *server_sec_policy) +{ + PTR_ENSURE_REF(client_sec_policy); + PTR_ENSURE_REF(server_sec_policy); + + /* Client will offer their highest priority ECDHE KeyShare in their ClientHello. This KeyShare + * will be most preferred since it can be negotiated in 1-RTT (even if there are other mutually + * supported ECDHE KeyShares that the server would prefer over this one but would require 2-RTT's). + */ + const struct s2n_ecc_named_curve *client_default = client_sec_policy->ecc_preferences->ecc_curves[0]; + PTR_ENSURE_REF(client_default); + + for (int i = 0; i < server_sec_policy->ecc_preferences->count; i++) { + const struct s2n_ecc_named_curve *server_curve = server_sec_policy->ecc_preferences->ecc_curves[i]; + PTR_ENSURE_REF(server_curve); + if (server_curve->iana_id == client_default->iana_id) { + return client_default; + } + } + + /* Otherwise, if the client's default isn't supported, and a 2-RTT handshake is required, the server will choose + * whichever mutually supported PQ KeyShare that is highest on the server's preference list. */ + for (int i = 0; i < server_sec_policy->ecc_preferences->count; i++) { + const struct s2n_ecc_named_curve *server_curve = server_sec_policy->ecc_preferences->ecc_curves[i]; + + /* j starts at 1 since we already checked client_sec_policy->ecc_preferences->ecc_curves[0] above */ + for (int j = 1; j < client_sec_policy->ecc_preferences->count; j++) { + const struct s2n_ecc_named_curve *client_curve = client_sec_policy->ecc_preferences->ecc_curves[j]; + PTR_ENSURE_REF(client_curve); + PTR_ENSURE_REF(server_curve); + if (client_curve->iana_id == server_curve->iana_id) { + return client_curve; + } + } + } + return NULL; } int s2n_test_tls13_pq_handshake(const struct s2n_security_policy *client_sec_policy, - const struct s2n_security_policy *server_sec_policy, + const struct s2n_security_policy *server_sec_policy, const struct s2n_kem_group *expected_kem_group, const struct s2n_ecc_named_curve *expected_curve, bool hrr_expected, bool len_prefix_expected) { /* XOR check: can expect to negotiate either a KEM group, or a classic EC curve, but not both/neither */ - const struct s2n_kem_group *expected_kem_group = s2n_get_highest_priority_shared_kem_group(client_sec_policy->kem_preferences, server_sec_policy->kem_preferences); POSIX_ENSURE((expected_kem_group == NULL) != (expected_curve == NULL), S2N_ERR_SAFETY); /* Set up connections */ @@ -118,11 +181,7 @@ int s2n_test_tls13_pq_handshake(const struct s2n_security_policy *client_sec_pol /* Server sends ServerHello or HRR */ POSIX_GUARD(s2n_conn_set_handshake_type(server_conn)); - if (hrr_expected) { - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), HELLO_RETRY_MSG); - } else { - POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), SERVER_HELLO); - } + POSIX_ENSURE_EQ(hrr_expected, s2n_handshake_type_check_tls13_flag(server_conn, HELLO_RETRY_REQUEST)); POSIX_GUARD(s2n_handshake_write_io(server_conn)); /* Server sends CCS */ @@ -343,15 +402,16 @@ int main() .ecc_preferences = security_policy_test_tls13_retry.ecc_preferences, }; - const struct s2n_ecc_named_curve *expected_curve = &s2n_ecc_curve_x25519; + const struct s2n_ecc_named_curve *default_curve = &s2n_ecc_curve_x25519; if (!s2n_is_evp_apis_supported()) { - expected_curve = &s2n_ecc_curve_secp256r1; + default_curve = &s2n_ecc_curve_secp256r1; } struct pq_handshake_test_vector { const struct s2n_security_policy *client_policy; const struct s2n_security_policy *server_policy; + const struct s2n_kem_group *expected_kem_group; const struct s2n_ecc_named_curve *expected_curve; bool hrr_expected; bool len_prefix_expected; @@ -361,10 +421,19 @@ int main() * If PQ is disabled, the expected negotiation outcome is overridden below * before performing the handshake test. */ const struct pq_handshake_test_vector test_vectors[] = { + { + .client_policy = &security_policy_pq_tls_1_3_2023_06_01, + .server_policy = &security_policy_pq_tls_1_0_2021_05_24, + .expected_kem_group = &s2n_x25519_kyber_512_r3, + .expected_curve = NULL, + .hrr_expected = s2n_pq_is_enabled(), + .len_prefix_expected = false, + }, /* Server and Client both support PQ and TLS 1.3 */ { .client_policy = &security_policy_pq_tls_1_1_2021_05_21, .server_policy = &security_policy_pq_tls_1_1_2021_05_21, + .expected_kem_group = &s2n_x25519_kyber_512_r3, .expected_curve = NULL, .hrr_expected = false, .len_prefix_expected = true, @@ -372,6 +441,7 @@ int main() { .client_policy = &security_policy_pq_tls_1_0_2021_05_22, .server_policy = &security_policy_pq_tls_1_0_2021_05_22, + .expected_kem_group = &s2n_x25519_kyber_512_r3, .expected_curve = NULL, .hrr_expected = false, .len_prefix_expected = true, @@ -379,6 +449,7 @@ int main() { .client_policy = &security_policy_pq_tls_1_0_2021_05_23, .server_policy = &security_policy_pq_tls_1_0_2021_05_23, + .expected_kem_group = &s2n_x25519_kyber_512_r3, .expected_curve = NULL, .hrr_expected = false, .len_prefix_expected = true, @@ -386,6 +457,7 @@ int main() { .client_policy = &security_policy_pq_tls_1_0_2021_05_24, .server_policy = &security_policy_pq_tls_1_0_2021_05_24, + .expected_kem_group = &s2n_x25519_kyber_512_r3, .expected_curve = NULL, .hrr_expected = false, .len_prefix_expected = true, @@ -393,6 +465,7 @@ int main() { .client_policy = &security_policy_pq_tls_1_0_2021_05_26, .server_policy = &security_policy_pq_tls_1_0_2021_05_26, + .expected_kem_group = &s2n_x25519_kyber_512_r3, .expected_curve = NULL, .hrr_expected = false, .len_prefix_expected = true, @@ -400,6 +473,7 @@ int main() { .client_policy = &security_policy_pq_tls_1_0_2023_01_24, .server_policy = &security_policy_pq_tls_1_0_2023_01_24, + .expected_kem_group = &s2n_x25519_kyber_512_r3, .expected_curve = NULL, .hrr_expected = false, .len_prefix_expected = false, @@ -411,6 +485,7 @@ int main() { .client_policy = &security_policy_pq_tls_1_3_2023_06_01, .server_policy = &security_policy_pq_tls_1_3_2023_06_01, + .expected_kem_group = &s2n_secp256r1_kyber_768_r3, .expected_curve = NULL, .hrr_expected = false, .len_prefix_expected = false, @@ -418,6 +493,7 @@ int main() { .client_policy = &kyber1024_test_policy, .server_policy = &security_policy_pq_tls_1_3_2023_06_01, + .expected_kem_group = &s2n_secp521r1_kyber_1024_r3, .expected_curve = NULL, .hrr_expected = false, .len_prefix_expected = false, @@ -425,6 +501,7 @@ int main() { .client_policy = &kyber768_test_policy, .server_policy = &security_policy_pq_tls_1_3_2023_06_01, + .expected_kem_group = &s2n_secp384r1_kyber_768_r3, .expected_curve = NULL, .hrr_expected = false, .len_prefix_expected = false, @@ -436,6 +513,7 @@ int main() { .client_policy = &security_policy_pq_tls_1_1_2021_05_21, .server_policy = &security_policy_pq_tls_1_3_2023_06_01, + .expected_kem_group = &s2n_x25519_kyber_512_r3, .expected_curve = NULL, .hrr_expected = !s2n_pq_is_enabled(), .len_prefix_expected = true, @@ -444,6 +522,7 @@ int main() { .client_policy = &kyber_test_policy_draft0, .server_policy = &kyber_test_policy_draft5, + .expected_kem_group = &s2n_x25519_kyber_512_r3, .expected_curve = NULL, .hrr_expected = false, .len_prefix_expected = true, @@ -451,6 +530,7 @@ int main() { .client_policy = &kyber_test_policy_draft5, .server_policy = &kyber_test_policy_draft0, + .expected_kem_group = &s2n_x25519_kyber_512_r3, .expected_curve = NULL, .hrr_expected = false, .len_prefix_expected = false, @@ -458,6 +538,7 @@ int main() { .client_policy = &security_policy_pq_tls_1_0_2021_05_24, .server_policy = &security_policy_pq_tls_1_0_2023_01_24, + .expected_kem_group = &s2n_x25519_kyber_512_r3, .expected_curve = NULL, .hrr_expected = false, .len_prefix_expected = true, @@ -465,6 +546,7 @@ int main() { .client_policy = &security_policy_pq_tls_1_0_2023_01_24, .server_policy = &security_policy_pq_tls_1_0_2021_05_24, + .expected_kem_group = &s2n_x25519_kyber_512_r3, .expected_curve = NULL, .hrr_expected = false, .len_prefix_expected = false, @@ -475,6 +557,7 @@ int main() { .client_policy = &security_policy_pq_tls_1_0_2020_12, .server_policy = &security_policy_pq_tls_1_0_2020_12, + .expected_kem_group = &s2n_x25519_kyber_512_r3, .expected_curve = NULL, .hrr_expected = false, .len_prefix_expected = true, @@ -486,6 +569,7 @@ int main() { .client_policy = &security_policy_pq_tls_1_0_2020_12, .server_policy = &kyber_test_policy_draft0, + .expected_kem_group = &s2n_x25519_kyber_512_r3, .expected_curve = NULL, .hrr_expected = false, .len_prefix_expected = true, @@ -497,6 +581,7 @@ int main() { .client_policy = &security_policy_pq_tls_1_0_2020_12, .server_policy = &kyber_test_policy_draft5, + .expected_kem_group = &s2n_x25519_kyber_512_r3, .expected_curve = NULL, .hrr_expected = false, .len_prefix_expected = true, @@ -507,7 +592,8 @@ int main() { .client_policy = &security_policy_pq_tls_1_0_2020_12, .server_policy = &security_policy_test_all_tls13, - .expected_curve = expected_curve, + .expected_kem_group = NULL, + .expected_curve = default_curve, .hrr_expected = false, .len_prefix_expected = true, }, @@ -517,7 +603,8 @@ int main() { .client_policy = &ecc_retry_policy, .server_policy = &security_policy_test_all_tls13, - .expected_curve = expected_curve, + .expected_kem_group = NULL, + .expected_curve = default_curve, .hrr_expected = true, .len_prefix_expected = true, }, @@ -527,7 +614,8 @@ int main() { .client_policy = &security_policy_test_all_tls13, .server_policy = &security_policy_pq_tls_1_0_2020_12, - .expected_curve = expected_curve, + .expected_kem_group = NULL, + .expected_curve = default_curve, .hrr_expected = false, .len_prefix_expected = true, }, @@ -537,7 +625,8 @@ int main() { .client_policy = &security_policy_test_tls13_retry, .server_policy = &security_policy_pq_tls_1_0_2020_12, - .expected_curve = expected_curve, + .expected_kem_group = NULL, + .expected_curve = default_curve, .hrr_expected = true, .len_prefix_expected = true, }, @@ -547,24 +636,50 @@ int main() const struct pq_handshake_test_vector *vector = &test_vectors[i]; const struct s2n_security_policy *client_policy = vector->client_policy; const struct s2n_security_policy *server_policy = vector->server_policy; + const struct s2n_kem_group *kem_group = vector->expected_kem_group; const struct s2n_ecc_named_curve *curve = vector->expected_curve; bool hrr_expected = vector->hrr_expected; bool len_prefix_expected = vector->len_prefix_expected; if (!s2n_pq_is_enabled()) { - /* If PQ is disabled, for older policies we expect to negotiate - * x25519 ECDH if available. Newer policies only include NIST - * curves, so if the server policy doesn't contain x25519, modify - * that expectation to a NIST curve. - */ - if (!s2n_ecc_preferences_includes_curve(server_policy->ecc_preferences, expected_curve->iana_id)) { + EXPECT_TRUE(client_policy->ecc_preferences->count > 0); + const struct s2n_ecc_named_curve *client_default = client_policy->ecc_preferences->ecc_curves[0]; + const struct s2n_ecc_named_curve *predicted_curve = s2n_get_predicted_negotiated_ecdhe_curve(client_policy, server_policy); + + /* If either policy doesn't support the default curve, fall back to p256 as it should + * be in common with every ECC preference list. */ + if (!s2n_ecc_preferences_includes_curve(client_policy->ecc_preferences, default_curve->iana_id) + || !s2n_ecc_preferences_includes_curve(server_policy->ecc_preferences, default_curve->iana_id)) { + EXPECT_TRUE(s2n_ecc_preferences_includes_curve(client_policy->ecc_preferences, s2n_ecc_curve_secp256r1.iana_id)); + EXPECT_TRUE(s2n_ecc_preferences_includes_curve(server_policy->ecc_preferences, s2n_ecc_curve_secp256r1.iana_id)); curve = &s2n_ecc_curve_secp256r1; - } else { - curve = expected_curve; } + + /* The client's preferred curve will be a higher priority than the default if both sides + * support TLS 1.3, and if the client's default can be chosen by the server in 1-RTT. */ + if (s2n_security_policy_supports_tls13(client_policy) && s2n_security_policy_supports_tls13(server_policy) + && s2n_ecc_preferences_includes_curve(server_policy->ecc_preferences, client_default->iana_id)) { + curve = client_default; + } + + /* Finally, confirm that the expected curve listed in the test vector matches the output of s2n_get_predicted_negotiated_ecdhe_curve() */ + EXPECT_EQUAL(curve->iana_id, predicted_curve->iana_id); + } + + if (!s2n_kem_group_is_available(kem_group)) { + kem_group = NULL; + } + + if (kem_group != NULL) { + const struct s2n_kem_group *predicted_kem_group = s2n_get_predicted_negotiated_kem_group(client_policy->kem_preferences, server_policy->kem_preferences); + POSIX_ENSURE_REF(predicted_kem_group); + + /* Confirm that the expected KEM Group listed in the test vector matches the output of + * s2n_get_predicted_negotiated_kem_group() */ + POSIX_ENSURE_EQ(kem_group->iana_id, predicted_kem_group->iana_id); } - EXPECT_SUCCESS(s2n_test_tls13_pq_handshake(client_policy, server_policy, curve, hrr_expected, len_prefix_expected)); + EXPECT_SUCCESS(s2n_test_tls13_pq_handshake(client_policy, server_policy, kem_group, curve, hrr_expected, len_prefix_expected)); } END_TEST(); diff --git a/tls/extensions/s2n_server_key_share.c b/tls/extensions/s2n_server_key_share.c index 08128c8df34..1e0ecf84b71 100644 --- a/tls/extensions/s2n_server_key_share.c +++ b/tls/extensions/s2n_server_key_share.c @@ -346,13 +346,13 @@ int s2n_extensions_server_key_share_select(struct s2n_connection *conn) { POSIX_ENSURE_REF(conn); - const struct s2n_ecc_preferences *ecc_pref = NULL; - POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); - POSIX_ENSURE_REF(ecc_pref); + /* Get the client's preferred groups for the KeyShares that were actually sent by the client */ + const struct s2n_ecc_named_curve *client_curve = conn->kex_params.client_ecc_evp_params.negotiated_curve; + const struct s2n_kem_group *client_kem_group = conn->kex_params.client_kem_group_params.kem_group; - const struct s2n_kem_preferences *kem_pref = NULL; - POSIX_GUARD(s2n_connection_get_kem_preferences(conn, &kem_pref)); - POSIX_ENSURE_REF(kem_pref); + /* Get the server's preferred groups (which may or may not have been sent in the KeyShare by the client) */ + const struct s2n_ecc_named_curve *server_curve = conn->kex_params.server_ecc_evp_params.negotiated_curve; + const struct s2n_kem_group *server_kem_group = conn->kex_params.server_kem_group_params.kem_group; /* Boolean XOR check. When receiving the supported_groups extension, s2n server * should (exclusively) set either server_curve or server_kem_group based on the @@ -361,37 +361,48 @@ int s2n_extensions_server_key_share_select(struct s2n_connection *conn) * groups; key negotiation is not possible and the handshake should be aborted * without sending HRR. (The case of both being non-NULL should never occur, and * is an error.) */ - const struct s2n_ecc_named_curve *server_curve = conn->kex_params.server_ecc_evp_params.negotiated_curve; - const struct s2n_kem_group *server_kem_group = conn->kex_params.server_kem_group_params.kem_group; POSIX_ENSURE((server_curve == NULL) != (server_kem_group == NULL), S2N_ERR_ECDHE_UNSUPPORTED_CURVE); /* To avoid extra round trips, we prefer to negotiate a group for which we have already * received a key share (even if it is different than the group previously chosen). In * general, we prefer to negotiate PQ over ECDHE; however, if both client and server * support PQ, but the client sent only EC key shares, then we will negotiate ECHDE. */ - if (conn->kex_params.client_kem_group_params.kem_group) { + + /* Option 1: Select the best mutually supported PQ Hybrid Group that can be negotiated in 1-RTT */ + if (client_kem_group != NULL) { POSIX_ENSURE_REF(conn->kex_params.client_kem_group_params.ecc_params.negotiated_curve); POSIX_ENSURE_REF(conn->kex_params.client_kem_group_params.kem_params.kem); conn->kex_params.server_kem_group_params.kem_group = conn->kex_params.client_kem_group_params.kem_group; conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve = conn->kex_params.client_kem_group_params.ecc_params.negotiated_curve; conn->kex_params.server_kem_group_params.kem_params.kem = conn->kex_params.client_kem_group_params.kem_params.kem; + conn->kex_params.server_ecc_evp_params.negotiated_curve = NULL; + return S2N_SUCCESS; + } + /* Option 2: Otherwise, if any PQ Hybrid Groups can be negotiated in 2-RTT's select that one. This ensures that + * clients who offer PQ (and presumably therefore have concerns about quantum computing impacting the long term + * confidentiality of their data), have their choice to offer PQ respected, even if they predict the server-side + * supports a different PQ KeyShare algorithms. This ensures clients with PQ support are never downgraded to non-PQ + * algorithms. */ + if (server_kem_group != NULL) { + /* Null out any available ECC curves so that they won't be sent in the ClientHelloRetry */ conn->kex_params.server_ecc_evp_params.negotiated_curve = NULL; + POSIX_GUARD(s2n_set_hello_retry_required(conn)); return S2N_SUCCESS; } - if (conn->kex_params.client_ecc_evp_params.negotiated_curve) { + /* Option 3: Otherwise, if there is a mutually supported classical ECDHE-only group can be negotiated in 1-RTT, select that one */ + if (client_curve) { conn->kex_params.server_ecc_evp_params.negotiated_curve = conn->kex_params.client_ecc_evp_params.negotiated_curve; - conn->kex_params.server_kem_group_params.kem_group = NULL; conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve = NULL; conn->kex_params.server_kem_group_params.kem_params.kem = NULL; return S2N_SUCCESS; } - /* Server and client have mutually supported groups, but the client did not send key - * shares for any of them. Send HRR indicating the server's preference. */ + /* Option 4: Server and client have at least 1 mutually supported group, but the client did not send key shares for + * any of them. Send a HelloRetryRequest indicating the server's preference. */ POSIX_GUARD(s2n_set_hello_retry_required(conn)); return S2N_SUCCESS; } diff --git a/tls/s2n_server_hello_retry.c b/tls/s2n_server_hello_retry.c index fd05ca07236..702c4362c8d 100644 --- a/tls/s2n_server_hello_retry.c +++ b/tls/s2n_server_hello_retry.c @@ -69,10 +69,11 @@ int s2n_server_hello_retry_recv(struct s2n_connection *conn) POSIX_ENSURE_REF(kem_pref); const struct s2n_ecc_named_curve *named_curve = conn->kex_params.server_ecc_evp_params.negotiated_curve; - const struct s2n_kem_group *kem_group = conn->kex_params.server_kem_group_params.kem_group; + const struct s2n_kem_group *server_preferred_kem_group = conn->kex_params.server_kem_group_params.kem_group; + const struct s2n_kem_group *client_preferred_kem_group = conn->kex_params.client_kem_group_params.kem_group; /* Boolean XOR check: exactly one of {named_curve, kem_group} should be non-null. */ - POSIX_ENSURE((named_curve != NULL) != (kem_group != NULL), S2N_ERR_INVALID_HELLO_RETRY); + POSIX_ENSURE((named_curve != NULL) != (server_preferred_kem_group != NULL), S2N_ERR_INVALID_HELLO_RETRY); /** *= https://www.rfc-editor.org/rfc/rfc8446#4.2.8 @@ -85,7 +86,9 @@ int s2n_server_hello_retry_recv(struct s2n_connection *conn) if (named_curve != NULL && s2n_ecc_preferences_includes_curve(ecc_pref, named_curve->iana_id)) { selected_group_in_supported_groups = true; } - if (kem_group != NULL && s2n_kem_group_is_available(kem_group) && s2n_kem_preferences_includes_tls13_kem_group(kem_pref, kem_group->iana_id)) { + if (server_preferred_kem_group != NULL + && s2n_kem_group_is_available(server_preferred_kem_group) + && s2n_kem_preferences_includes_tls13_kem_group(kem_pref, server_preferred_kem_group->iana_id)) { selected_group_in_supported_groups = true; } @@ -96,14 +99,16 @@ int s2n_server_hello_retry_recv(struct s2n_connection *conn) *# in the original ClientHello. **/ bool new_key_share_requested = false; + if (named_curve != NULL) { new_key_share_requested = (named_curve != conn->kex_params.client_ecc_evp_params.negotiated_curve); } - if (kem_group != NULL) { + + if (server_preferred_kem_group != NULL) { /* If PQ is disabled, the client should not have sent any PQ IDs * in the supported_groups list of the initial ClientHello */ POSIX_ENSURE(s2n_pq_is_enabled(), S2N_ERR_NO_SUPPORTED_LIBCRYPTO_API); - new_key_share_requested = (kem_group != conn->kex_params.client_kem_group_params.kem_group); + new_key_share_requested = (server_preferred_kem_group != client_preferred_kem_group); } /**