File: s2n_ecc_evp_test.c

package info (click to toggle)
aws-crt-python 0.24.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 75,932 kB
  • sloc: ansic: 418,984; python: 23,626; makefile: 6,035; sh: 4,075; ruby: 208; java: 82; perl: 73; cpp: 25; xml: 11
file content (494 lines) | stat: -rw-r--r-- 24,004 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
/*
 * 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_ecc_evp.h"

#include "api/s2n.h"
#include "crypto/s2n_fips.h"
#include "crypto/s2n_libcrypto.h"
#include "s2n_test.h"
#include "stuffer/s2n_stuffer.h"
#include "testlib/s2n_testlib.h"
#include "tls/s2n_connection.h"
#include "tls/s2n_security_policies.h"
#include "utils/s2n_mem.h"

#define ECDHE_PARAMS_LEGACY_FORM 4

extern const struct s2n_ecc_named_curve s2n_unsupported_curve;

DEFINE_POINTER_CLEANUP_FUNC(EC_KEY*, EC_KEY_free);
DEFINE_POINTER_CLEANUP_FUNC(EC_POINT*, EC_POINT_free);

int main(int argc, char** argv)
{
    BEGIN_TEST();
    EXPECT_SUCCESS(s2n_disable_tls13_in_test());

    /* Test the EC_KEY_CHECK_FIPS feature probe. AWS-LC is a libcrypto known to support this feature. */
    if (s2n_libcrypto_is_awslc()) {
        EXPECT_TRUE(s2n_ecc_evp_supports_fips_check());
    }

    {
        /* Test generate ephemeral keys for all supported curves */
        for (size_t i = 0; i < s2n_all_supported_curves_list_len; i++) {
            struct s2n_ecc_evp_params evp_params = { 0 };
            /* Server generates a key */
            evp_params.negotiated_curve = s2n_all_supported_curves_list[i];
            EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&evp_params));
            EXPECT_NOT_NULL(evp_params.evp_pkey);
            EXPECT_SUCCESS(s2n_ecc_evp_params_free(&evp_params));
        }
    };
    {
        /* Test failure case for generate ephemeral key  when the negotiated curve is not set */
        for (size_t i = 0; i < s2n_all_supported_curves_list_len; i++) {
            struct s2n_ecc_evp_params evp_params = { 0 };
            /* Server generates a key */
            evp_params.negotiated_curve = NULL;
            EXPECT_FAILURE(s2n_ecc_evp_generate_ephemeral_key(&evp_params));
            EXPECT_NULL(evp_params.evp_pkey);
            EXPECT_SUCCESS(s2n_ecc_evp_params_free(&evp_params));
        }
    };
    {
        /* Test generate ephemeral key and compute shared key for all supported curves */
        for (size_t i = 0; i < s2n_all_supported_curves_list_len; i++) {
            struct s2n_ecc_evp_params server_params = { 0 };
            struct s2n_ecc_evp_params client_params = { 0 };
            struct s2n_blob server_shared = { 0 };
            struct s2n_blob client_shared = { 0 };

            /* Server generates a key */
            server_params.negotiated_curve = s2n_all_supported_curves_list[i];
            EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_params));
            EXPECT_NOT_NULL(server_params.evp_pkey);

            /* Client generates a key */
            client_params.negotiated_curve = s2n_all_supported_curves_list[i];
            EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_params));
            EXPECT_NOT_NULL(client_params.evp_pkey);

            /* Compute shared secret for server */
            EXPECT_SUCCESS(
                    s2n_ecc_evp_compute_shared_secret_from_params(&server_params, &client_params, &server_shared));

            /* Compute shared secret for client */
            EXPECT_SUCCESS(
                    s2n_ecc_evp_compute_shared_secret_from_params(&client_params, &server_params, &client_shared));

            /* Check if the shared secret computed is the same for the client
             * and the server */
            EXPECT_EQUAL(client_shared.size, server_shared.size);
            EXPECT_BYTEARRAY_EQUAL(client_shared.data, server_shared.data, client_shared.size);

            /* Clean up */
            EXPECT_SUCCESS(s2n_free(&server_shared));
            EXPECT_SUCCESS(s2n_free(&client_shared));
            EXPECT_SUCCESS(s2n_ecc_evp_params_free(&server_params));
            EXPECT_SUCCESS(s2n_ecc_evp_params_free(&client_params));
        }
    };
    {
        /* Test failure case for computing shared key for all supported curves when the server
        and client curves do not match */
        for (size_t i = 0; i < s2n_all_supported_curves_list_len; i++) {
            for (size_t j = 0; j < s2n_all_supported_curves_list_len; j++) {
                struct s2n_ecc_evp_params server_params = { 0 };
                struct s2n_ecc_evp_params client_params = { 0 };
                struct s2n_blob server_shared = { 0 };
                struct s2n_blob client_shared = { 0 };
                if (i == j) {
                    continue;
                }

                /* Server generates a key */
                server_params.negotiated_curve = s2n_all_supported_curves_list[j];

                EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_params));
                EXPECT_NOT_NULL(server_params.evp_pkey);

                /* Client generates a key */
                client_params.negotiated_curve = s2n_all_supported_curves_list[i];
                EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_params));
                EXPECT_NOT_NULL(client_params.evp_pkey);

                /* Compute shared secret for server */
                EXPECT_FAILURE(
                        s2n_ecc_evp_compute_shared_secret_from_params(&server_params, &client_params, &server_shared));

                /* Compute shared secret for client */
                EXPECT_FAILURE(
                        s2n_ecc_evp_compute_shared_secret_from_params(&client_params, &server_params, &client_shared));

                /* Clean up */
                EXPECT_SUCCESS(s2n_ecc_evp_params_free(&server_params));
                EXPECT_SUCCESS(s2n_ecc_evp_params_free(&client_params));
            }
        }
    };
    {
        /* Test s2n_ecc_evp_write_params_point for all supported curves */
        for (size_t i = 0; i < s2n_all_supported_curves_list_len; i++) {
            struct s2n_ecc_evp_params test_params = { 0 };
            struct s2n_stuffer wire = { 0 };
            uint8_t legacy_form = 0;

            EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&wire, 0));

            /* Server generates a key for a given curve */
            test_params.negotiated_curve = s2n_all_supported_curves_list[i];
            EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&test_params));
            EXPECT_NOT_NULL(test_params.evp_pkey);
            EXPECT_SUCCESS(s2n_ecc_evp_write_params_point(&test_params, &wire));

            /* Verify output is of the right length */
            uint32_t avail = s2n_stuffer_data_available(&wire);
            EXPECT_EQUAL(avail, s2n_all_supported_curves_list[i]->share_size);

            /* Verify output starts with the known legacy form for curves secp256r1
             * and secp384r1*/
            if (s2n_all_supported_curves_list[i]->iana_id == TLS_EC_CURVE_SECP_256_R1 || s2n_all_supported_curves_list[i]->iana_id == TLS_EC_CURVE_SECP_384_R1) {
                EXPECT_SUCCESS(s2n_stuffer_read_uint8(&wire, &legacy_form));
                EXPECT_EQUAL(legacy_form, ECDHE_PARAMS_LEGACY_FORM);
            }

            /* Clean up */
            EXPECT_SUCCESS(s2n_ecc_evp_params_free(&test_params));
            EXPECT_SUCCESS(s2n_stuffer_free(&wire));
        }
    };
    {
        /* TEST s2n_ecc_evp_read_params_point for all supported curves */
        for (size_t i = 0; i < s2n_all_supported_curves_list_len; i++) {
            struct s2n_ecc_evp_params write_params = { 0 };
            struct s2n_blob point_blob = { 0 };
            struct s2n_stuffer wire = { 0 };

            EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&wire, 0));

            /* Server generates a key for a given curve */
            write_params.negotiated_curve = s2n_all_supported_curves_list[i];
            EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&write_params));
            EXPECT_NOT_NULL(write_params.evp_pkey);
            EXPECT_SUCCESS(s2n_ecc_evp_write_params_point(&write_params, &wire));

            /* Read point back in */
            EXPECT_SUCCESS(
                    s2n_ecc_evp_read_params_point(&wire, s2n_all_supported_curves_list[i]->share_size, &point_blob));

            /* Check that the blob looks generally correct. */
            EXPECT_EQUAL(point_blob.size, s2n_all_supported_curves_list[i]->share_size);
            EXPECT_NOT_NULL(point_blob.data);

            EXPECT_SUCCESS(s2n_ecc_evp_params_free(&write_params));
            EXPECT_SUCCESS(s2n_stuffer_free(&wire));
        }
    };
    {
        /* TEST s2n_ecc_evp_parse_params_point for all supported curves */
        for (size_t i = 0; i < s2n_all_supported_curves_list_len; i++) {
            struct s2n_ecc_evp_params write_params = { 0 };
            struct s2n_ecc_evp_params read_params = { 0 };
            struct s2n_blob point_blob = { 0 };
            struct s2n_stuffer wire = { 0 };

            EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&wire, 0));

            write_params.negotiated_curve = s2n_all_supported_curves_list[i];
            read_params.negotiated_curve = s2n_all_supported_curves_list[i];

            /* Server generates a key for a given curve */
            EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&write_params));
            EXPECT_NOT_NULL(write_params.evp_pkey);
            EXPECT_SUCCESS(s2n_ecc_evp_write_params_point(&write_params, &wire));

            /* Read point back in */
            EXPECT_SUCCESS(
                    s2n_ecc_evp_read_params_point(&wire, s2n_all_supported_curves_list[i]->share_size, &point_blob));
            EXPECT_SUCCESS(s2n_ecc_evp_parse_params_point(&point_blob, &read_params));
            /* Check that the point we read is the same we wrote */
            EXPECT_TRUE(EVP_PKEY_cmp(write_params.evp_pkey, read_params.evp_pkey));

            /* Clean up */
            EXPECT_SUCCESS(s2n_ecc_evp_params_free(&write_params));
            EXPECT_SUCCESS(s2n_ecc_evp_params_free(&read_params));
            EXPECT_SUCCESS(s2n_stuffer_free(&wire));
        }
    };
    {
        DEFER_CLEANUP(struct s2n_connection* conn = s2n_connection_new(S2N_CLIENT),
                s2n_connection_ptr_free);
        EXPECT_NOT_NULL(conn);
        EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all"));
        /* Test read/write/parse params for all supported curves */
        for (size_t i = 0; i < s2n_all_supported_curves_list_len; i++) {
            struct s2n_ecc_evp_params write_params = { 0 };
            struct s2n_ecc_evp_params read_params = { 0 };
            struct s2n_stuffer wire = { 0 };
            struct s2n_blob ecdh_params_sent, ecdh_params_received;

            EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&wire, 1024));

            write_params.negotiated_curve = s2n_all_supported_curves_list[i];
            read_params.negotiated_curve = s2n_all_supported_curves_list[i];

            /* Server generates a key for a given curve */
            EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&write_params));
            EXPECT_NOT_NULL(write_params.evp_pkey);

            /* Write params points to wire */
            EXPECT_SUCCESS(s2n_ecc_evp_write_params(&write_params, &wire, &ecdh_params_sent));
            struct s2n_ecdhe_raw_server_params ecdhe_data = { 0 };

            /* Read params points from the wire */
            EXPECT_SUCCESS(s2n_ecc_evp_read_params(&wire, &ecdh_params_received, &ecdhe_data));
            EXPECT_SUCCESS(s2n_ecc_evp_parse_params(conn, &ecdhe_data, &read_params));

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

            /* Clean up */
            EXPECT_SUCCESS(s2n_stuffer_free(&wire));
            EXPECT_SUCCESS(s2n_ecc_evp_params_free(&write_params));
            EXPECT_SUCCESS(s2n_ecc_evp_params_free(&read_params));
        }
    };
    {
        DEFER_CLEANUP(struct s2n_connection* conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free);
        EXPECT_NOT_NULL(conn);
        EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all"));
        /* Test generate/read/write/parse and compute shared secrets for all supported curves */
        for (size_t i = 0; i < s2n_all_supported_curves_list_len; i++) {
            struct s2n_ecc_evp_params server_params = { 0 };
            struct s2n_ecc_evp_params read_params = { 0 };
            struct s2n_ecc_evp_params client_params = { 0 };
            struct s2n_stuffer wire = { 0 };
            struct s2n_blob ecdh_params_sent, ecdh_params_received;
            struct s2n_blob server_shared_secret, client_shared_secret;

            EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&wire, 1024));

            server_params.negotiated_curve = s2n_all_supported_curves_list[i];
            read_params.negotiated_curve = s2n_all_supported_curves_list[i];

            /* Server generates a key for a given curve */
            EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_params));
            EXPECT_NOT_NULL(server_params.evp_pkey);

            /* Server sends the public */
            EXPECT_SUCCESS(s2n_ecc_evp_write_params(&server_params, &wire, &ecdh_params_sent));

            /* Client reads the public */
            struct s2n_ecdhe_raw_server_params ecdhe_data = { 0 };
            EXPECT_SUCCESS(s2n_ecc_evp_read_params(&wire, &ecdh_params_received, &ecdhe_data));
            EXPECT_SUCCESS(s2n_ecc_evp_parse_params(conn, &ecdhe_data, &read_params));

            /* Verify if the client correctly read the server public */
            EXPECT_TRUE(EVP_PKEY_cmp(server_params.evp_pkey, read_params.evp_pkey));

            /* Client generates its key for the given curve */
            client_params.negotiated_curve = s2n_all_supported_curves_list[i];
            EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_params));
            EXPECT_NOT_NULL(client_params.evp_pkey);

            /* Compute shared secret for the server */
            EXPECT_SUCCESS(
                    s2n_ecc_evp_compute_shared_secret_from_params(&server_params, &client_params, &server_shared_secret));

            /* Compute shared secret for the client */
            EXPECT_SUCCESS(
                    s2n_ecc_evp_compute_shared_secret_from_params(&client_params, &read_params, &client_shared_secret));

            /* Verify that shared is the same for the client and the server */
            EXPECT_EQUAL(client_shared_secret.size, server_shared_secret.size);
            EXPECT_BYTEARRAY_EQUAL(client_shared_secret.data, server_shared_secret.data, client_shared_secret.size);

            /* Clean up */
            EXPECT_SUCCESS(s2n_stuffer_free(&wire));
            EXPECT_SUCCESS(s2n_free(&server_shared_secret));
            EXPECT_SUCCESS(s2n_free(&client_shared_secret));
            EXPECT_SUCCESS(s2n_ecc_evp_params_free(&server_params));
            EXPECT_SUCCESS(s2n_ecc_evp_params_free(&read_params));
            EXPECT_SUCCESS(s2n_ecc_evp_params_free(&client_params));
        }
    };
    {
        DEFER_CLEANUP(struct s2n_connection* conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free);
        EXPECT_NOT_NULL(conn);
        EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all"));
        /* Test generate->write->read->compute_shared with all supported curves */
        for (size_t i = 0; i < s2n_all_supported_curves_list_len; i++) {
            struct s2n_ecc_evp_params server_params = { 0 }, client_params = { 0 };
            struct s2n_stuffer wire = { 0 };
            struct s2n_blob server_shared, client_shared, ecdh_params_sent, ecdh_params_received;

            EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&wire, 1024));

            /* Server generates a key for a given curve */
            server_params.negotiated_curve = s2n_all_supported_curves_list[i];
            EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_params));
            EXPECT_NOT_NULL(server_params.evp_pkey);
            /* Server sends the public */
            EXPECT_SUCCESS(s2n_ecc_evp_write_params(&server_params, &wire, &ecdh_params_sent));
            /* Client reads the public */
            struct s2n_ecdhe_raw_server_params ecdhe_data = { 0 };
            EXPECT_SUCCESS(s2n_ecc_evp_read_params(&wire, &ecdh_params_received, &ecdhe_data));
            EXPECT_SUCCESS(s2n_ecc_evp_parse_params(conn, &ecdhe_data, &client_params));

            /* The client got the curve */
            EXPECT_EQUAL(client_params.negotiated_curve, server_params.negotiated_curve);

            /* Client sends its public */
            EXPECT_SUCCESS(s2n_ecc_evp_compute_shared_secret_as_client(&client_params, &wire, &client_shared));
            /* Server receives it */
            EXPECT_SUCCESS(s2n_ecc_evp_compute_shared_secret_as_server(&server_params, &wire, &server_shared));
            /* Shared is the same for the client and the server */
            EXPECT_EQUAL(client_shared.size, server_shared.size);
            EXPECT_BYTEARRAY_EQUAL(client_shared.data, server_shared.data, client_shared.size);

            /* Clean up */
            EXPECT_SUCCESS(s2n_stuffer_free(&wire));
            EXPECT_SUCCESS(s2n_free(&server_shared));
            EXPECT_SUCCESS(s2n_free(&client_shared));
            EXPECT_SUCCESS(s2n_ecc_evp_params_free(&server_params));
            EXPECT_SUCCESS(s2n_ecc_evp_params_free(&client_params));
        }
    };

    /* Test that the client does not negotiate a group that was not
     * offered in EC preferences */
    {
        const struct s2n_security_policy* security_policy = NULL;
        DEFER_CLEANUP(struct s2n_connection* conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free);
        EXPECT_NOT_NULL(conn);
        /* Version does not include the unsupported curve and secp521r1, which will be used by a malicious server */
        EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20190802"));
        EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy));

        /* Setup & verify invalid curves, which will be selected by a malicious server */
        const struct s2n_ecc_named_curve* const unrequested_curves[] = {
            &s2n_unsupported_curve,
            &s2n_ecc_curve_secp521r1,
        };

        /* Verify that the client errors when the server attempts to
         * negotiate a curve that was never offered */
        for (size_t i = 0; i < s2n_array_len(unrequested_curves); i++) {
            struct s2n_ecc_evp_params server_params = { 0 };
            struct s2n_ecc_evp_params client_params = { 0 };
            struct s2n_stuffer wire = { 0 };
            struct s2n_blob ecdh_params_sent = { 0 }, ecdh_params_received = { 0 };

            EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&wire, 1024));

            /* Server maliciously chooses an unsupported curve */
            server_params.negotiated_curve = unrequested_curves[i];
            EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_params));
            EXPECT_NOT_NULL(server_params.evp_pkey);
            /* Server sends the public */
            EXPECT_SUCCESS(s2n_ecc_evp_write_params(&server_params, &wire, &ecdh_params_sent));
            /* Client reads the public */
            struct s2n_ecdhe_raw_server_params ecdhe_data = { 0 };
            EXPECT_SUCCESS(s2n_ecc_evp_read_params(&wire, &ecdh_params_received, &ecdhe_data));
            EXPECT_FAILURE_WITH_ERRNO(
                    s2n_ecc_evp_parse_params(conn, &ecdhe_data, &client_params), S2N_ERR_ECDHE_UNSUPPORTED_CURVE);

            /* The client didn't agree on a curve */
            EXPECT_NULL(client_params.negotiated_curve);

            /* Clean up */
            EXPECT_SUCCESS(s2n_stuffer_free(&wire));
            EXPECT_SUCCESS(s2n_ecc_evp_params_free(&server_params));
            EXPECT_SUCCESS(s2n_ecc_evp_params_free(&client_params));
        }
    };

    /**
     *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.8.2
     *= type=test
     *# For the curves secp256r1, secp384r1, and secp521r1, peers MUST
     *# validate each other's public value Q by ensuring that the point is a
     *# valid point on the elliptic curve.  The appropriate validation
     *# procedures are defined in Section 4.3.7 of [ECDSA] and alternatively
     *# in Section 5.6.2.3 of [KEYAGREEMENT].  This process consists of three
     *# steps: (1) verify that Q is not the point at infinity (O), (2) verify
     *# that for Q = (x, y) both integers x and y are in the correct
     *# interval, and (3) ensure that (x, y) is a correct solution to the
     *# elliptic curve equation.  For these curves, implementors do not need
     *# to verify membership in the correct subgroup.
     *
     * s2n-tls performs this validation by invoking the libcrypto APIs: EC_KEY_check_key, and
     * EC_KEY_check_fips. To ensure that these APIs are properly called, step (1) is invalidated.
     */
    {
        const struct s2n_ecc_named_curve* const nist_curves[] = {
            &s2n_ecc_curve_secp256r1,
            &s2n_ecc_curve_secp384r1,
            &s2n_ecc_curve_secp521r1,
        };

        for (size_t i = 0; i < s2n_array_len(nist_curves); i++) {
            const struct s2n_ecc_named_curve* curve = nist_curves[i];

            DEFER_CLEANUP(struct s2n_ecc_evp_params server_params = { 0 }, s2n_ecc_evp_params_free);
            DEFER_CLEANUP(struct s2n_ecc_evp_params client_params = { 0 }, s2n_ecc_evp_params_free);
            DEFER_CLEANUP(struct s2n_blob shared_key = { 0 }, s2n_free);

            /* Create a server key. */
            server_params.negotiated_curve = curve;
            EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_params));
            EXPECT_NOT_NULL(server_params.evp_pkey);

            /* Create a client key. */
            client_params.negotiated_curve = curve;
            EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_params));
            EXPECT_NOT_NULL(client_params.evp_pkey);

            /* Retrieve the existing client public key. */
            DEFER_CLEANUP(EC_KEY* ec_key = EVP_PKEY_get1_EC_KEY(client_params.evp_pkey),
                    EC_KEY_free_pointer);
            EXPECT_NOT_NULL(ec_key);
            const EC_GROUP* group = EC_KEY_get0_group(ec_key);
            EXPECT_NOT_NULL(group);
            const EC_POINT* public_key = EC_KEY_get0_public_key(ec_key);
            EXPECT_NOT_NULL(public_key);

            /* Invalidate the public key by setting the coordinate to infinity. */
            DEFER_CLEANUP(EC_POINT* invalid_public_key = EC_POINT_dup(public_key, group),
                    EC_POINT_free_pointer);
            EXPECT_NOT_NULL(invalid_public_key);
            EXPECT_EQUAL(EC_POINT_set_to_infinity(group, invalid_public_key), 1);
            EXPECT_EQUAL(EC_KEY_set_public_key(ec_key, invalid_public_key), 1);
            EXPECT_EQUAL(EVP_PKEY_set1_EC_KEY(client_params.evp_pkey, ec_key), 1);

            /* Compute the server's shared secret. */
            int ret = s2n_ecc_evp_compute_shared_secret_from_params(&server_params,
                    &client_params, &shared_key);

            /* If s2n-tls is in FIPS mode and the libcrypto supports the EC_KEY_check_fips API,
             * ensure that this API is called by checking for the correct error.
             */
            if (s2n_is_in_fips_mode() && s2n_ecc_evp_supports_fips_check()) {
                EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_ECDHE_INVALID_PUBLIC_KEY_FIPS);
            } else {
                EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_ECDHE_INVALID_PUBLIC_KEY);
            }
        }
    }

    END_TEST();
}