Skip to content

Commit f488bf0

Browse files
authored
S2N client negotation of un-offered group fix (#3422)
Fix s2n client negotiation of un-offered group + unit tests
1 parent 9403585 commit f488bf0

File tree

4 files changed

+92
-26
lines changed

4 files changed

+92
-26
lines changed

crypto/s2n_ecc_evp.c

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
#include <stdint.h>
2525

26+
#include "tls/s2n_connection.h"
27+
#include "tls/s2n_ecc_preferences.h"
2628
#include "tls/s2n_tls_parameters.h"
2729
#include "utils/s2n_mem.h"
2830
#include "utils/s2n_safety.h"
@@ -478,21 +480,26 @@ int s2n_ecc_evp_parse_params_point(struct s2n_blob *point_blob, struct s2n_ecc_e
478480
return 0;
479481
}
480482

481-
int s2n_ecc_evp_parse_params(struct s2n_ecdhe_raw_server_params *raw_server_ecc_params,
482-
struct s2n_ecc_evp_params *ecc_evp_params) {
483-
S2N_ERROR_IF(
484-
s2n_ecc_evp_find_supported_curve(&raw_server_ecc_params->curve_blob, &ecc_evp_params->negotiated_curve) != 0,
485-
S2N_ERR_ECDHE_UNSUPPORTED_CURVE);
483+
int s2n_ecc_evp_parse_params(struct s2n_connection* conn,
484+
struct s2n_ecdhe_raw_server_params* raw_server_ecc_params,
485+
struct s2n_ecc_evp_params* ecc_evp_params) {
486+
POSIX_ENSURE(
487+
s2n_ecc_evp_find_supported_curve(conn, &raw_server_ecc_params->curve_blob, &ecc_evp_params->negotiated_curve) == 0,
488+
S2N_ERR_ECDHE_UNSUPPORTED_CURVE);
486489
return s2n_ecc_evp_parse_params_point(&raw_server_ecc_params->point_blob, ecc_evp_params);
487490
}
488491

489-
int s2n_ecc_evp_find_supported_curve(struct s2n_blob *iana_ids, const struct s2n_ecc_named_curve **found) {
492+
int s2n_ecc_evp_find_supported_curve(struct s2n_connection* conn, struct s2n_blob *iana_ids, const struct s2n_ecc_named_curve **found) {
493+
const struct s2n_ecc_preferences* ecc_prefs = NULL;
494+
POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_prefs));
495+
POSIX_ENSURE_REF(ecc_prefs);
496+
490497
struct s2n_stuffer iana_ids_in = {0};
491498

492499
POSIX_GUARD(s2n_stuffer_init(&iana_ids_in, iana_ids));
493500
POSIX_GUARD(s2n_stuffer_write(&iana_ids_in, iana_ids));
494-
for (size_t i = 0; i < s2n_all_supported_curves_list_len; i++) {
495-
const struct s2n_ecc_named_curve *supported_curve = s2n_all_supported_curves_list[i];
501+
for (size_t i = 0; i < ecc_prefs->count; i++) {
502+
const struct s2n_ecc_named_curve *supported_curve = ecc_prefs->ecc_curves[i];
496503
for (uint32_t j = 0; j < iana_ids->size / 2; j++) {
497504
uint16_t iana_id;
498505
POSIX_GUARD(s2n_stuffer_read_uint16(&iana_ids_in, &iana_id));

crypto/s2n_ecc_evp.h

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,7 @@ int s2n_ecc_evp_compute_shared_secret_from_params(struct s2n_ecc_evp_params *pri
7070
struct s2n_blob *shared_key);
7171
int s2n_ecc_evp_write_params_point(struct s2n_ecc_evp_params *ecc_evp_params, struct s2n_stuffer *out);
7272
int s2n_ecc_evp_read_params_point(struct s2n_stuffer *in, int point_size, struct s2n_blob *point_blob);
73-
int s2n_ecc_evp_compute_shared_secret_from_params(struct s2n_ecc_evp_params *private_ecc_evp_params,
74-
struct s2n_ecc_evp_params *public_ecc_evp_params,
75-
struct s2n_blob *shared_key);
76-
int s2n_ecc_evp_compute_shared_secret_as_server(struct s2n_ecc_evp_params *server_ecc_evp_params,
73+
int s2n_ecc_evp_compute_shared_secret_as_server(struct s2n_ecc_evp_params *server_ecc_evp_params,
7774
struct s2n_stuffer *Yc_in, struct s2n_blob *shared_key);
7875
int s2n_ecc_evp_compute_shared_secret_as_client(struct s2n_ecc_evp_params *server_ecc_evp_params,
7976
struct s2n_stuffer *Yc_out, struct s2n_blob *shared_key);
@@ -82,8 +79,9 @@ int s2n_ecc_evp_write_params(struct s2n_ecc_evp_params *ecc_evp_params, struct s
8279
struct s2n_blob *written);
8380
int s2n_ecc_evp_read_params(struct s2n_stuffer *in, struct s2n_blob *data_to_verify,
8481
struct s2n_ecdhe_raw_server_params *raw_server_ecc_params);
85-
int s2n_ecc_evp_parse_params(struct s2n_ecdhe_raw_server_params *raw_server_ecc_params,
86-
struct s2n_ecc_evp_params *ecc_evp_params);
87-
int s2n_ecc_evp_find_supported_curve(struct s2n_blob *iana_ids, const struct s2n_ecc_named_curve **found);
82+
int s2n_ecc_evp_parse_params(struct s2n_connection *conn,
83+
struct s2n_ecdhe_raw_server_params *raw_server_ecc_params,
84+
struct s2n_ecc_evp_params* ecc_evp_params);
85+
int s2n_ecc_evp_find_supported_curve(struct s2n_connection* conn, struct s2n_blob *iana_ids, const struct s2n_ecc_named_curve **found);
8886
int s2n_ecc_evp_params_free(struct s2n_ecc_evp_params *ecc_evp_params);
8987
int s2n_is_evp_apis_supported();

tests/unit/s2n_ecc_evp_test.c

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,14 @@
2020
#include "crypto/s2n_ecc_evp.h"
2121
#include "stuffer/s2n_stuffer.h"
2222
#include "testlib/s2n_testlib.h"
23+
#include "tls/s2n_connection.h"
24+
#include "tls/s2n_security_policies.h"
2325
#include "utils/s2n_mem.h"
2426

2527
#define ECDHE_PARAMS_LEGACY_FORM 4
2628

29+
extern const struct s2n_ecc_named_curve s2n_unsupported_curve;
30+
2731
int main(int argc, char **argv) {
2832
BEGIN_TEST();
2933
EXPECT_SUCCESS(s2n_disable_tls13_in_test());
@@ -89,7 +93,7 @@ int main(int argc, char **argv) {
8993
}
9094
{
9195
/* Test failure case for computing shared key for all supported curves when the server
92-
and client curves donot match */
96+
and client curves do not match */
9397
for (int i = 0; i < s2n_all_supported_curves_list_len; i++) {
9498
for (int j = 0; j < s2n_all_supported_curves_list_len; j++) {
9599
struct s2n_ecc_evp_params server_params = {0};
@@ -216,6 +220,10 @@ int main(int argc, char **argv) {
216220
}
217221
}
218222
{
223+
DEFER_CLEANUP(struct s2n_connection* conn = s2n_connection_new(S2N_CLIENT),
224+
s2n_connection_ptr_free);
225+
EXPECT_NOT_NULL(conn);
226+
EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all"));
219227
/* Test read/write/parse params for all supported curves */
220228
for (int i = 0; i < s2n_all_supported_curves_list_len; i++) {
221229
struct s2n_ecc_evp_params write_params = {0};
@@ -238,23 +246,26 @@ int main(int argc, char **argv) {
238246

239247
/* Read params points from the wire */
240248
EXPECT_SUCCESS(s2n_ecc_evp_read_params(&wire, &ecdh_params_received, &ecdhe_data));
241-
EXPECT_SUCCESS(s2n_ecc_evp_parse_params(&ecdhe_data, &read_params));
249+
EXPECT_SUCCESS(s2n_ecc_evp_parse_params(conn, &ecdhe_data, &read_params));
242250

243251
/* Check that the point we read is the same we wrote */
244252
EXPECT_TRUE(EVP_PKEY_cmp(write_params.evp_pkey, read_params.evp_pkey));
245253

246-
/* Clean up */
254+
/* Clean up */
247255
EXPECT_SUCCESS(s2n_stuffer_free(&wire));
248256
EXPECT_SUCCESS(s2n_ecc_evp_params_free(&write_params));
249257
EXPECT_SUCCESS(s2n_ecc_evp_params_free(&read_params));
250258
}
251259
}
252260
{
261+
DEFER_CLEANUP(struct s2n_connection* conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free);
262+
EXPECT_NOT_NULL(conn);
263+
EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all"));
253264
/* Test generate/read/write/parse and compute shared secrets for all supported curves */
254265
for (int i = 0; i < s2n_all_supported_curves_list_len; i++) {
255266
struct s2n_ecc_evp_params server_params = {0};
256267
struct s2n_ecc_evp_params read_params = {0};
257-
struct s2n_ecc_evp_params client_params = {0};
268+
struct s2n_ecc_evp_params client_params = {0};
258269
struct s2n_stuffer wire;
259270
struct s2n_blob ecdh_params_sent, ecdh_params_received;
260271
struct s2n_blob server_shared_secret, client_shared_secret;
@@ -274,7 +285,7 @@ int main(int argc, char **argv) {
274285
/* Client reads the public */
275286
struct s2n_ecdhe_raw_server_params ecdhe_data = {0};
276287
EXPECT_SUCCESS(s2n_ecc_evp_read_params(&wire, &ecdh_params_received, &ecdhe_data));
277-
EXPECT_SUCCESS(s2n_ecc_evp_parse_params(&ecdhe_data, &read_params));
288+
EXPECT_SUCCESS(s2n_ecc_evp_parse_params(conn, &ecdhe_data, &read_params));
278289

279290
/* Verify if the client correctly read the server public */
280291
EXPECT_TRUE(EVP_PKEY_cmp(server_params.evp_pkey, read_params.evp_pkey));
@@ -283,11 +294,11 @@ int main(int argc, char **argv) {
283294
client_params.negotiated_curve =s2n_all_supported_curves_list[i];
284295
EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_params));
285296
EXPECT_NOT_NULL(client_params.evp_pkey);
286-
297+
287298
/* Compute shared secret for the server */
288299
EXPECT_SUCCESS(
289300
s2n_ecc_evp_compute_shared_secret_from_params(&server_params, &client_params, &server_shared_secret));
290-
301+
291302
/* Compute shared secret for the client */
292303
EXPECT_SUCCESS(
293304
s2n_ecc_evp_compute_shared_secret_from_params(&client_params, &read_params, &client_shared_secret));
@@ -299,15 +310,18 @@ int main(int argc, char **argv) {
299310
/* Clean up */
300311
EXPECT_SUCCESS(s2n_stuffer_free(&wire));
301312
EXPECT_SUCCESS(s2n_free(&server_shared_secret));
302-
EXPECT_SUCCESS(s2n_free(&client_shared_secret));
313+
EXPECT_SUCCESS(s2n_free(&client_shared_secret));
303314
EXPECT_SUCCESS(s2n_ecc_evp_params_free(&server_params));
304315
EXPECT_SUCCESS(s2n_ecc_evp_params_free(&read_params));
305316
EXPECT_SUCCESS(s2n_ecc_evp_params_free(&client_params));
306317
}
307318
}
308319
{
320+
DEFER_CLEANUP(struct s2n_connection* conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free);
321+
EXPECT_NOT_NULL(conn);
322+
EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all"));
309323
/* Test generate->write->read->compute_shared with all supported curves */
310-
for (int i = 0; i < s2n_all_supported_curves_list_len; i++) {
324+
for (int i = 0; i < s2n_all_supported_curves_list_len; i++) {
311325
struct s2n_ecc_evp_params server_params = {0}, client_params = {0};
312326
struct s2n_stuffer wire;
313327
struct s2n_blob server_shared, client_shared, ecdh_params_sent, ecdh_params_received;
@@ -323,7 +337,7 @@ int main(int argc, char **argv) {
323337
/* Client reads the public */
324338
struct s2n_ecdhe_raw_server_params ecdhe_data = {0};
325339
EXPECT_SUCCESS(s2n_ecc_evp_read_params(&wire, &ecdh_params_received, &ecdhe_data));
326-
EXPECT_SUCCESS(s2n_ecc_evp_parse_params(&ecdhe_data, &client_params));
340+
EXPECT_SUCCESS(s2n_ecc_evp_parse_params(conn, &ecdhe_data, &client_params));
327341

328342
/* The client got the curve */
329343
EXPECT_EQUAL(client_params.negotiated_curve, server_params.negotiated_curve);
@@ -345,5 +359,52 @@ int main(int argc, char **argv) {
345359
}
346360
}
347361

362+
/* Test that the client does not negotiate a group that was not
363+
* offered in EC preferences */
364+
{
365+
const struct s2n_security_policy* security_policy = NULL;
366+
DEFER_CLEANUP(struct s2n_connection* conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free);
367+
EXPECT_NOT_NULL(conn);
368+
/* Version does not include the unsupported curve and secp521r1, which will be used by a malicious server */
369+
EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20190802"));
370+
EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy));
371+
372+
/* Setup & verify invalid curves, which will be selected by a malicious server */
373+
const struct s2n_ecc_named_curve* const unrequested_curves[] = {
374+
&s2n_unsupported_curve,
375+
&s2n_ecc_curve_secp521r1,
376+
};
377+
378+
/* Verify that the client errors when the server attempts to
379+
* negotiate a curve that was never offered */
380+
for (size_t i = 0; i < s2n_array_len(unrequested_curves); i++) {
381+
struct s2n_ecc_evp_params server_params = {0};
382+
struct s2n_ecc_evp_params client_params = {0};
383+
struct s2n_stuffer wire = {0};
384+
struct s2n_blob ecdh_params_sent = {0}, ecdh_params_received = {0};
385+
386+
EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&wire, 1024));
387+
388+
/* Server maliciously chooses an unsupported curve */
389+
server_params.negotiated_curve = unrequested_curves[i];
390+
EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_params));
391+
EXPECT_NOT_NULL(server_params.evp_pkey);
392+
/* Server sends the public */
393+
EXPECT_SUCCESS(s2n_ecc_evp_write_params(&server_params, &wire, &ecdh_params_sent));
394+
/* Client reads the public */
395+
struct s2n_ecdhe_raw_server_params ecdhe_data = {0};
396+
EXPECT_SUCCESS(s2n_ecc_evp_read_params(&wire, &ecdh_params_received, &ecdhe_data));
397+
EXPECT_FAILURE_WITH_ERRNO(
398+
s2n_ecc_evp_parse_params(conn, &ecdhe_data, &client_params), S2N_ERR_ECDHE_UNSUPPORTED_CURVE);
399+
400+
/* The client didn't agree on a curve */
401+
EXPECT_NULL(client_params.negotiated_curve);
402+
403+
/* Clean up */
404+
EXPECT_SUCCESS(s2n_stuffer_free(&wire));
405+
EXPECT_SUCCESS(s2n_ecc_evp_params_free(&server_params));
406+
EXPECT_SUCCESS(s2n_ecc_evp_params_free(&client_params));
407+
}
408+
}
348409
END_TEST();
349410
}

tls/s2n_server_key_exchange.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ int s2n_ecdhe_server_key_recv_read_data(struct s2n_connection *conn, struct s2n_
100100

101101
int s2n_ecdhe_server_key_recv_parse_data(struct s2n_connection *conn, struct s2n_kex_raw_server_data *raw_server_data)
102102
{
103-
POSIX_GUARD(s2n_ecc_evp_parse_params(&raw_server_data->ecdhe_data, &conn->kex_params.server_ecc_evp_params));
103+
POSIX_GUARD(s2n_ecc_evp_parse_params(conn, &raw_server_data->ecdhe_data, &conn->kex_params.server_ecc_evp_params));
104104

105105
return 0;
106106
}

0 commit comments

Comments
 (0)