Branch data Line data Source code
1 : : /*
2 : : * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 : : *
4 : : * Licensed under the Apache License, Version 2.0 (the "License").
5 : : * You may not use this file except in compliance with the License.
6 : : * A copy of the License is located at
7 : : *
8 : : * http://aws.amazon.com/apache2.0
9 : : *
10 : : * or in the "license" file accompanying this file. This file is distributed
11 : : * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 : : * express or implied. See the License for the specific language governing
13 : : * permissions and limitations under the License.
14 : : */
15 : :
16 : : #include "tls/extensions/s2n_client_key_share.h"
17 : :
18 : : #include "crypto/s2n_pq.h"
19 : : #include "error/s2n_errno.h"
20 : : #include "stuffer/s2n_stuffer.h"
21 : : #include "tls/extensions/s2n_key_share.h"
22 : : #include "tls/s2n_kem_preferences.h"
23 : : #include "tls/s2n_security_policies.h"
24 : : #include "tls/s2n_tls13.h"
25 : : #include "utils/s2n_safety.h"
26 : :
27 : : /**
28 : : * Specified in https://tools.ietf.org/html/rfc8446#section-4.2.8
29 : : * "The "key_share" extension contains the endpoint's cryptographic parameters."
30 : : *
31 : : * Structure:
32 : : * Extension type (2 bytes)
33 : : * Extension data size (2 bytes)
34 : : * Client shares size (2 bytes)
35 : : * Client shares:
36 : : * Named group (2 bytes)
37 : : * Key share size (2 bytes)
38 : : * Key share (variable size)
39 : : *
40 : : * This extension only modifies the connection's client ecc_evp_params. It does
41 : : * not make any decisions about which set of params to use.
42 : : *
43 : : * The server will NOT alert when processing a client extension that violates the RFC.
44 : : * So the server will accept:
45 : : * - Multiple key shares for the same named group. The server will accept the first
46 : : * key share for the group and ignore any duplicates.
47 : : * - Key shares for named groups not in the client's supported_groups extension.
48 : : **/
49 : :
50 : : static int s2n_client_key_share_send(struct s2n_connection *conn, struct s2n_stuffer *out);
51 : : static int s2n_client_key_share_recv(struct s2n_connection *conn, struct s2n_stuffer *extension);
52 : :
53 : : const s2n_extension_type s2n_client_key_share_extension = {
54 : : .iana_value = TLS_EXTENSION_KEY_SHARE,
55 : : .minimum_version = S2N_TLS13,
56 : : .is_response = false,
57 : : .send = s2n_client_key_share_send,
58 : : .recv = s2n_client_key_share_recv,
59 : : .should_send = s2n_extension_always_send,
60 : : .if_missing = s2n_extension_noop_if_missing,
61 : : };
62 : :
63 : : static int s2n_generate_default_ecc_key_share(struct s2n_connection *conn, struct s2n_stuffer *out)
64 : 5584 : {
65 [ - + ][ # # ]: 5584 : POSIX_ENSURE_REF(conn);
66 : 5584 : const struct s2n_ecc_preferences *ecc_pref = NULL;
67 [ - + ]: 5584 : POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_pref));
68 [ - + ][ # # ]: 5584 : POSIX_ENSURE_REF(ecc_pref);
69 : :
70 : : /* Skip sending classical ECC curves for PQ only policies. */
71 [ - + ]: 5584 : if (ecc_pref->count == 0) {
72 : 0 : return S2N_SUCCESS;
73 : 0 : }
74 : :
75 : : /* We only ever send a single EC key share: either the share requested by the server
76 : : * during a retry, or the most preferred share according to local preferences.
77 : : */
78 : 5584 : struct s2n_ecc_evp_params *client_params = &conn->kex_params.client_ecc_evp_params;
79 [ + + ]: 5584 : if (s2n_is_hello_retry_handshake(conn)) {
80 : 660 : const struct s2n_ecc_named_curve *server_curve = conn->kex_params.server_ecc_evp_params.negotiated_curve;
81 : :
82 : : /* If the server did not request a specific ECC keyshare, don't send one */
83 [ - + ]: 660 : if (!server_curve) {
84 : 0 : return S2N_SUCCESS;
85 : 0 : }
86 : :
87 : : /* If the server requested a new ECC keyshare, free the old one */
88 [ + - ]: 660 : if (server_curve != client_params->negotiated_curve) {
89 [ - + ]: 660 : POSIX_GUARD(s2n_ecc_evp_params_free(client_params));
90 : 660 : }
91 : :
92 : : /**
93 : : *= https://www.rfc-editor.org/rfc/rfc8446#4.2.8
94 : : *# Otherwise, when sending the new ClientHello, the client MUST
95 : : *# replace the original "key_share" extension with one containing only a
96 : : *# new KeyShareEntry for the group indicated in the selected_group field
97 : : *# of the triggering HelloRetryRequest.
98 : : **/
99 : 660 : client_params->negotiated_curve = server_curve;
100 : 4924 : } else {
101 : 4924 : client_params->negotiated_curve = ecc_pref->ecc_curves[0];
102 : 4924 : }
103 [ - + ]: 5584 : POSIX_GUARD(s2n_ecdhe_parameters_send(client_params, out));
104 : :
105 : 5584 : return S2N_SUCCESS;
106 : 5584 : }
107 : :
108 : : static int s2n_generate_pq_key_share(struct s2n_stuffer *out, struct s2n_kem_group_params *kem_group_params)
109 : 0 : {
110 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(out);
111 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(kem_group_params);
112 : :
113 : : /* This function should never be called when PQ is disabled */
114 [ # # ][ # # ]: 0 : POSIX_ENSURE(s2n_pq_is_enabled(), S2N_ERR_UNIMPLEMENTED);
115 : :
116 : 0 : const struct s2n_kem_group *kem_group = kem_group_params->kem_group;
117 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(kem_group);
118 : :
119 [ # # ]: 0 : POSIX_GUARD(s2n_stuffer_write_uint16(out, kem_group->iana_id));
120 : :
121 : 0 : struct s2n_stuffer_reservation total_share_size = { 0 };
122 [ # # ]: 0 : POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &total_share_size));
123 : :
124 : 0 : struct s2n_ecc_evp_params *ecc_params = &kem_group_params->ecc_params;
125 : 0 : ecc_params->negotiated_curve = kem_group->curve;
126 : :
127 : 0 : struct s2n_kem_params *kem_params = &kem_group_params->kem_params;
128 : 0 : kem_params->kem = kem_group->kem;
129 : :
130 [ # # ]: 0 : if (kem_group->curve == &s2n_ecc_curve_none) { /* Pure PQ */
131 [ # # ]: 0 : POSIX_GUARD(s2n_kem_send_public_key(out, kem_params));
132 : 0 : } else { /* Hybrid PQ */
133 [ # # ]: 0 : if (kem_group->send_kem_first) {
134 [ # # ]: 0 : POSIX_GUARD(s2n_kem_send_public_key(out, kem_params));
135 [ # # ]: 0 : POSIX_GUARD_RESULT(s2n_ecdhe_send_public_key(ecc_params, out, kem_params->len_prefixed));
136 : 0 : } else {
137 [ # # ]: 0 : POSIX_GUARD_RESULT(s2n_ecdhe_send_public_key(ecc_params, out, kem_params->len_prefixed));
138 [ # # ]: 0 : POSIX_GUARD(s2n_kem_send_public_key(out, kem_params));
139 : 0 : }
140 : 0 : }
141 : :
142 [ # # ]: 0 : POSIX_GUARD(s2n_stuffer_write_vector_size(&total_share_size));
143 : :
144 : 0 : return S2N_SUCCESS;
145 : 0 : }
146 : :
147 : : static int s2n_generate_default_pq_key_share(struct s2n_connection *conn, struct s2n_stuffer *out)
148 : 5584 : {
149 [ # # ][ - + ]: 5584 : POSIX_ENSURE_REF(conn);
150 [ - + ][ # # ]: 5584 : POSIX_ENSURE_REF(out);
151 : :
152 : : /* Client should skip sending PQ groups/key shares if PQ is disabled */
153 [ + - ]: 5584 : if (!s2n_pq_is_enabled()) {
154 : 5584 : return S2N_SUCCESS;
155 : 5584 : }
156 : :
157 : 0 : const struct s2n_kem_preferences *kem_pref = NULL;
158 [ # # ]: 0 : POSIX_GUARD(s2n_connection_get_kem_preferences(conn, &kem_pref));
159 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(kem_pref);
160 : :
161 : 0 : uint32_t available_groups = 0;
162 [ # # ]: 0 : POSIX_GUARD_RESULT(s2n_kem_preferences_groups_available(kem_pref, &available_groups));
163 [ # # ]: 0 : if (available_groups == 0) {
164 : 0 : return S2N_SUCCESS;
165 : 0 : }
166 : :
167 : : /* We only ever send a single PQ key share: either the share requested by the server
168 : : * during a retry, or the most preferred share according to local preferences.
169 : : */
170 : 0 : struct s2n_kem_group_params *client_params = &conn->kex_params.client_kem_group_params;
171 : :
172 [ # # ]: 0 : if (s2n_is_hello_retry_handshake(conn)) {
173 : 0 : const struct s2n_kem_group *server_group = conn->kex_params.server_kem_group_params.kem_group;
174 : :
175 : : /* If the server did not request a specific PQ keyshare, don't send one */
176 [ # # ]: 0 : if (!server_group) {
177 : 0 : return S2N_SUCCESS;
178 : 0 : }
179 : :
180 : : /* If the server requested a new PQ keyshare, free the old one */
181 [ # # ]: 0 : if (client_params->kem_group != server_group) {
182 [ # # ]: 0 : POSIX_GUARD(s2n_kem_group_free(client_params));
183 : 0 : }
184 : :
185 : : /**
186 : : *= https://www.rfc-editor.org/rfc/rfc8446#4.2.8
187 : : *# Otherwise, when sending the new ClientHello, the client MUST
188 : : *# replace the original "key_share" extension with one containing only a
189 : : *# new KeyShareEntry for the group indicated in the selected_group field
190 : : *# of the triggering HelloRetryRequest.
191 : : **/
192 : 0 : client_params->kem_group = server_group;
193 : 0 : } else {
194 : 0 : client_params->kem_group = s2n_kem_preferences_get_highest_priority_group(kem_pref);
195 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(client_params->kem_group);
196 : 0 : client_params->kem_params.len_prefixed = s2n_tls13_client_must_use_hybrid_kem_length_prefix(kem_pref);
197 : 0 : }
198 : :
199 [ # # ]: 0 : POSIX_GUARD(s2n_generate_pq_key_share(out, client_params));
200 : :
201 : 0 : return S2N_SUCCESS;
202 : 0 : }
203 : :
204 : : static int s2n_client_key_share_send(struct s2n_connection *conn, struct s2n_stuffer *out)
205 : 5586 : {
206 [ + + ]: 5586 : if (s2n_is_hello_retry_handshake(conn)) {
207 : 662 : const struct s2n_ecc_named_curve *server_curve = conn->kex_params.server_ecc_evp_params.negotiated_curve;
208 : 662 : const struct s2n_ecc_named_curve *client_curve = conn->kex_params.client_ecc_evp_params.negotiated_curve;
209 : 662 : const struct s2n_kem_group *server_group = conn->kex_params.server_kem_group_params.kem_group;
210 : 662 : const struct s2n_kem_group *client_group = conn->kex_params.client_kem_group_params.kem_group;
211 : :
212 : : /* Ensure a new key share will be sent after a hello retry request */
213 [ + - ][ + + ]: 662 : POSIX_ENSURE(server_curve != client_curve || server_group != client_group, S2N_ERR_BAD_KEY_SHARE);
[ - + ]
214 : 662 : }
215 : :
216 : 5584 : struct s2n_stuffer_reservation shares_size = { 0 };
217 [ - + ]: 5584 : POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &shares_size));
218 [ - + ]: 5584 : POSIX_GUARD(s2n_generate_default_pq_key_share(conn, out));
219 [ - + ]: 5584 : POSIX_GUARD(s2n_generate_default_ecc_key_share(conn, out));
220 [ - + ]: 5584 : POSIX_GUARD(s2n_stuffer_write_vector_size(&shares_size));
221 : :
222 : : /* We must have written at least one share */
223 [ - + ][ # # ]: 5584 : POSIX_ENSURE(s2n_stuffer_data_available(out) > shares_size.length, S2N_ERR_BAD_KEY_SHARE);
224 : :
225 : 5584 : return S2N_SUCCESS;
226 : 5584 : }
227 : :
228 : : static int s2n_client_key_share_parse_ecc(struct s2n_stuffer *key_share, const struct s2n_ecc_named_curve *curve,
229 : : struct s2n_ecc_evp_params *ecc_params)
230 : 4535 : {
231 [ # # ][ - + ]: 4535 : POSIX_ENSURE_REF(key_share);
232 [ # # ][ - + ]: 4535 : POSIX_ENSURE_REF(curve);
233 [ - + ][ # # ]: 4535 : POSIX_ENSURE_REF(ecc_params);
234 : :
235 : 4535 : struct s2n_blob point_blob = { 0 };
236 [ - + ]: 4535 : POSIX_GUARD(s2n_ecc_evp_read_params_point(key_share, curve->share_size, &point_blob));
237 : :
238 : : /* Ignore curves with points we can't parse */
239 : 4535 : ecc_params->negotiated_curve = curve;
240 [ + + ]: 4535 : if (s2n_ecc_evp_parse_params_point(&point_blob, ecc_params) != S2N_SUCCESS) {
241 : 2 : ecc_params->negotiated_curve = NULL;
242 [ - + ]: 2 : POSIX_GUARD(s2n_ecc_evp_params_free(ecc_params));
243 : 2 : }
244 : :
245 : 4535 : return S2N_SUCCESS;
246 : 4535 : }
247 : :
248 : : static int s2n_client_key_share_recv_ecc(struct s2n_connection *conn, struct s2n_stuffer *key_share, uint16_t curve_iana_id)
249 : 5215 : {
250 [ - + ][ # # ]: 5215 : POSIX_ENSURE_REF(conn);
251 [ - + ][ # # ]: 5215 : POSIX_ENSURE_REF(key_share);
252 : :
253 : 5215 : const struct s2n_ecc_preferences *ecc_pref = NULL;
254 [ - + ]: 5215 : POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_pref));
255 [ - + ][ # # ]: 5215 : POSIX_ENSURE_REF(ecc_pref);
256 : :
257 : 5215 : struct s2n_ecc_evp_params *client_params = &conn->kex_params.client_ecc_evp_params;
258 : :
259 : 5215 : const struct s2n_ecc_named_curve *curve = NULL;
260 [ + + ]: 7885 : for (size_t i = 0; i < ecc_pref->count; i++) {
261 : 7212 : const struct s2n_ecc_named_curve *supported_curve = ecc_pref->ecc_curves[i];
262 [ - + ][ # # ]: 7212 : POSIX_ENSURE_REF(supported_curve);
263 : :
264 : : /* Stop if we reach the current highest priority share.
265 : : * Any share of lower priority is discarded.
266 : : */
267 [ + + ]: 7212 : if (client_params->negotiated_curve == supported_curve) {
268 : 6 : break;
269 : 6 : }
270 : :
271 : : /* Skip if not supported by the client.
272 : : * The client must not send shares it doesn't support, but the server
273 : : * is not required to error if they are encountered.
274 : : */
275 [ + + ]: 7206 : if (!conn->kex_params.mutually_supported_curves[i]) {
276 : 31 : continue;
277 : 31 : }
278 : :
279 : : /* Stop if we find a match */
280 [ + + ]: 7175 : if (curve_iana_id == supported_curve->iana_id) {
281 : 4536 : curve = supported_curve;
282 : 4536 : break;
283 : 4536 : }
284 : 7175 : }
285 : :
286 : : /* Ignore unsupported curves */
287 [ + + ]: 5215 : if (!curve) {
288 : 679 : return S2N_SUCCESS;
289 : 679 : }
290 : :
291 : : /* Ignore curves with unexpected share sizes */
292 [ + + ]: 4536 : if (key_share->blob.size != curve->share_size) {
293 : 1 : return S2N_SUCCESS;
294 : 1 : }
295 : :
296 : 4535 : DEFER_CLEANUP(struct s2n_ecc_evp_params new_client_params = { 0 }, s2n_ecc_evp_params_free);
297 : :
298 [ - + ]: 4535 : POSIX_GUARD(s2n_client_key_share_parse_ecc(key_share, curve, &new_client_params));
299 : : /* negotiated_curve will be NULL if the key share was not parsed successfully */
300 [ + + ]: 4535 : if (!new_client_params.negotiated_curve) {
301 : 2 : return S2N_SUCCESS;
302 : 2 : }
303 : :
304 [ - + ]: 4533 : POSIX_GUARD(s2n_ecc_evp_params_free(client_params));
305 : 4533 : *client_params = new_client_params;
306 : :
307 : 4533 : ZERO_TO_DISABLE_DEFER_CLEANUP(new_client_params);
308 : 4533 : return S2N_SUCCESS;
309 : 4533 : }
310 : :
311 : : static int s2n_client_key_share_recv_hybrid_partial_ecc(struct s2n_stuffer *key_share, struct s2n_kem_group_params *new_client_params)
312 : 0 : {
313 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(new_client_params);
314 : 0 : const struct s2n_kem_group *kem_group = new_client_params->kem_group;
315 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(kem_group);
316 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(kem_group->curve);
317 : :
318 [ # # ]: 0 : if (new_client_params->kem_params.len_prefixed) {
319 : 0 : uint16_t ec_share_size = 0;
320 [ # # ]: 0 : POSIX_GUARD(s2n_stuffer_read_uint16(key_share, &ec_share_size));
321 [ # # ][ # # ]: 0 : POSIX_ENSURE(ec_share_size == kem_group->curve->share_size, S2N_ERR_SIZE_MISMATCH);
322 : 0 : }
323 : :
324 [ # # ]: 0 : POSIX_GUARD(s2n_client_key_share_parse_ecc(key_share, kem_group->curve, &new_client_params->ecc_params));
325 : :
326 : : /* If we were unable to parse the EC portion of the share, negotiated_curve
327 : : * will be NULL, and we should ignore the entire key share. */
328 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(new_client_params->ecc_params.negotiated_curve);
329 : :
330 : 0 : return S2N_SUCCESS;
331 : 0 : }
332 : :
333 : : static int s2n_client_key_share_recv_pq(struct s2n_connection *conn, struct s2n_stuffer *key_share, uint16_t kem_group_iana_id)
334 : 5215 : {
335 [ - + ][ # # ]: 5215 : POSIX_ENSURE_REF(conn);
336 [ - + ][ # # ]: 5215 : POSIX_ENSURE_REF(key_share);
337 : :
338 : 5215 : const struct s2n_kem_preferences *kem_pref = NULL;
339 [ - + ]: 5215 : POSIX_GUARD(s2n_connection_get_kem_preferences(conn, &kem_pref));
340 [ - + ][ # # ]: 5215 : POSIX_ENSURE_REF(kem_pref);
341 : :
342 : : /* Ignore key share if PQ is not enabled */
343 [ + - ]: 5215 : if (!s2n_pq_is_enabled()) {
344 : 5215 : return S2N_SUCCESS;
345 : 5215 : }
346 : :
347 : 0 : struct s2n_kem_group_params *client_params = &conn->kex_params.client_kem_group_params;
348 : :
349 : 0 : const struct s2n_kem_group *kem_group = NULL;
350 [ # # ]: 0 : for (size_t i = 0; i < kem_pref->tls13_kem_group_count; i++) {
351 : 0 : const struct s2n_kem_group *supported_group = kem_pref->tls13_kem_groups[i];
352 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(supported_group);
353 : :
354 : : /* Skip if the group is not available */
355 [ # # ]: 0 : if (!s2n_kem_group_is_available(supported_group)) {
356 : 0 : continue;
357 : 0 : }
358 : :
359 : : /* Stop if we reach the current highest priority share.
360 : : * Any share of lower priority is discarded.
361 : : */
362 [ # # ]: 0 : if (client_params->kem_group == supported_group) {
363 : 0 : break;
364 : 0 : }
365 : :
366 : : /* Skip if not supported by the client.
367 : : * The client must not send shares it doesn't support, but the server
368 : : * is not required to error if they are encountered.
369 : : */
370 [ # # ]: 0 : if (!conn->kex_params.mutually_supported_kem_groups[i]) {
371 : 0 : continue;
372 : 0 : }
373 : :
374 : : /* Stop if we find a match */
375 [ # # ]: 0 : if (kem_group_iana_id == supported_group->iana_id) {
376 : 0 : kem_group = supported_group;
377 : 0 : break;
378 : 0 : }
379 : 0 : }
380 : :
381 : : /* Ignore unsupported KEM groups */
382 [ # # ]: 0 : if (!kem_group) {
383 : 0 : return S2N_SUCCESS;
384 : 0 : }
385 : :
386 : : /* The length of the key share must be one of two possible lengths. Its internal values are either length
387 : : * prefixed, or they are not. */
388 : 0 : uint16_t actual_share_size = key_share->blob.size;
389 : 0 : uint16_t unprefixed_share_size = kem_group->curve->share_size + kem_group->kem->public_key_length;
390 : 0 : uint16_t prefixed_share_size = (2 * S2N_SIZE_OF_KEY_SHARE_SIZE) + unprefixed_share_size;
391 : :
392 : : /* Ignore KEM groups with unexpected overall total share sizes */
393 [ # # ][ # # ]: 0 : if ((actual_share_size != unprefixed_share_size) && (actual_share_size != prefixed_share_size)) {
394 : 0 : return S2N_SUCCESS;
395 : 0 : }
396 : :
397 : 0 : bool is_share_length_prefixed = (actual_share_size == prefixed_share_size);
398 : :
399 : 0 : DEFER_CLEANUP(struct s2n_kem_group_params new_client_params = { 0 }, s2n_kem_group_free);
400 : 0 : new_client_params.kem_group = kem_group;
401 : :
402 : : /* Need to save whether the client included the length prefix so that we can match their behavior in our response. */
403 : 0 : new_client_params.kem_params.len_prefixed = is_share_length_prefixed;
404 : 0 : new_client_params.kem_params.kem = kem_group->kem;
405 : :
406 : : /* Note: the PQ share size is validated in s2n_kem_recv_public_key() */
407 : : /* Ignore PQ and ECC groups with public keys we can't parse */
408 [ # # ]: 0 : if (kem_group->curve == &s2n_ecc_curve_none) { /* Pure PQ */
409 : : /* we only support pure PQ for modern MLKEM groups, which are not length prefixed. */
410 [ # # ]: 0 : if (is_share_length_prefixed) {
411 : 0 : return S2N_SUCCESS;
412 : 0 : }
413 [ # # ]: 0 : if (s2n_kem_recv_public_key(key_share, &new_client_params.kem_params) != S2N_SUCCESS) {
414 : 0 : return S2N_SUCCESS;
415 : 0 : }
416 : 0 : } else { /* Hybrid PQ */
417 [ # # ]: 0 : if (kem_group->send_kem_first) {
418 [ # # ]: 0 : if (s2n_kem_recv_public_key(key_share, &new_client_params.kem_params) != S2N_SUCCESS) {
419 : 0 : return S2N_SUCCESS;
420 : 0 : }
421 [ # # ]: 0 : if (s2n_client_key_share_recv_hybrid_partial_ecc(key_share, &new_client_params) != S2N_SUCCESS) {
422 : 0 : return S2N_SUCCESS;
423 : 0 : }
424 : 0 : } else {
425 [ # # ]: 0 : if (s2n_client_key_share_recv_hybrid_partial_ecc(key_share, &new_client_params) != S2N_SUCCESS) {
426 : 0 : return S2N_SUCCESS;
427 : 0 : }
428 [ # # ]: 0 : if (s2n_kem_recv_public_key(key_share, &new_client_params.kem_params) != S2N_SUCCESS) {
429 : 0 : return S2N_SUCCESS;
430 : 0 : }
431 : 0 : }
432 : 0 : }
433 : :
434 [ # # ]: 0 : POSIX_GUARD(s2n_kem_group_free(client_params));
435 : 0 : *client_params = new_client_params;
436 : :
437 : 0 : ZERO_TO_DISABLE_DEFER_CLEANUP(new_client_params);
438 : 0 : return S2N_SUCCESS;
439 : 0 : }
440 : :
441 : : /*
442 : : * We chose our most preferred group of the mutually supported groups while processing the
443 : : * supported_groups extension. However, our true most preferred group is always the
444 : : * group that we already have a key share for, since retries are expensive.
445 : : *
446 : : * This method modifies our group selection based on what keyshares are available.
447 : : * It then stores the client keyshare for the selected group, or initiates a retry
448 : : * if no valid keyshares are available.
449 : : */
450 : : static int s2n_client_key_share_recv(struct s2n_connection *conn, struct s2n_stuffer *extension)
451 : 5210 : {
452 [ # # ][ - + ]: 5210 : POSIX_ENSURE_REF(conn);
453 [ - + ][ # # ]: 5210 : POSIX_ENSURE_REF(extension);
454 : :
455 : 5210 : uint16_t key_shares_size = 0;
456 [ - + ]: 5210 : POSIX_GUARD(s2n_stuffer_read_uint16(extension, &key_shares_size));
457 [ + - ][ + + ]: 5210 : POSIX_ENSURE(s2n_stuffer_data_available(extension) == key_shares_size, S2N_ERR_BAD_MESSAGE);
458 : :
459 : 5209 : uint16_t named_group = 0, share_size = 0;
460 : 5209 : struct s2n_blob key_share_blob = { 0 };
461 : 5209 : struct s2n_stuffer key_share = { 0 };
462 : :
463 : 5209 : uint16_t keyshare_count = 0;
464 [ + + ]: 10424 : while (s2n_stuffer_data_available(extension) > 0) {
465 [ - + ]: 5216 : POSIX_GUARD(s2n_stuffer_read_uint16(extension, &named_group));
466 [ - + ]: 5216 : POSIX_GUARD(s2n_stuffer_read_uint16(extension, &share_size));
467 [ + - ][ + + ]: 5216 : POSIX_ENSURE(s2n_stuffer_data_available(extension) >= share_size, S2N_ERR_BAD_MESSAGE);
468 : :
469 [ - + ]: 5215 : POSIX_GUARD(s2n_blob_init(&key_share_blob,
470 : 5215 : s2n_stuffer_raw_read(extension, share_size), share_size));
471 [ - + ]: 5215 : POSIX_GUARD(s2n_stuffer_init(&key_share, &key_share_blob));
472 [ - + ]: 5215 : POSIX_GUARD(s2n_stuffer_skip_write(&key_share, share_size));
473 : 5215 : keyshare_count++;
474 : :
475 : : /* Try to parse the share as ECC, then as PQ; will ignore
476 : : * shares for unrecognized groups. */
477 [ - + ]: 5215 : POSIX_GUARD(s2n_client_key_share_recv_ecc(conn, &key_share, named_group));
478 [ - + ]: 5215 : POSIX_GUARD(s2n_client_key_share_recv_pq(conn, &key_share, named_group));
479 : 5215 : }
480 : :
481 : : /* During a retry, the client should only have sent one keyshare */
482 [ # # ][ + - ]: 5208 : POSIX_ENSURE(!s2n_is_hello_retry_handshake(conn) || keyshare_count == 1, S2N_ERR_BAD_MESSAGE);
[ + + ]
483 : :
484 : : /**
485 : : * If there were no matching key shares, then we received an empty key share extension
486 : : * or we didn't match a key share with a supported group. We should send a retry.
487 : : *
488 : : *= https://www.rfc-editor.org/rfc/rfc8446#4.1.1
489 : : *# If the server selects an (EC)DHE group and the client did not offer a
490 : : *# compatible "key_share" extension in the initial ClientHello, the
491 : : *# server MUST respond with a HelloRetryRequest (Section 4.1.4) message.
492 : : **/
493 : 5208 : struct s2n_ecc_evp_params *client_ecc_params = &conn->kex_params.client_ecc_evp_params;
494 : 5208 : struct s2n_kem_group_params *client_pq_params = &conn->kex_params.client_kem_group_params;
495 [ + - ][ + + ]: 5208 : if (!client_pq_params->kem_group && !client_ecc_params->negotiated_curve) {
496 [ - + ]: 674 : POSIX_GUARD(s2n_set_hello_retry_required(conn));
497 : 674 : }
498 : :
499 : 5208 : return S2N_SUCCESS;
500 : 5208 : }
501 : :
502 : : /* Old-style extension functions -- remove after extensions refactor is complete */
503 : :
504 : : int s2n_extensions_client_key_share_recv(struct s2n_connection *conn, struct s2n_stuffer *extension)
505 : 0 : {
506 : 0 : return s2n_extension_recv(&s2n_client_key_share_extension, conn, extension);
507 : 0 : }
|