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 : 5806 : {
65 [ - + ][ # # ]: 5806 : POSIX_ENSURE_REF(conn);
66 : 5806 : const struct s2n_ecc_preferences *ecc_pref = NULL;
67 [ - + ]: 5806 : POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_pref));
68 [ - + ][ # # ]: 5806 : POSIX_ENSURE_REF(ecc_pref);
69 : :
70 : : /* Skip sending classical ECC curves for PQ only policies. */
71 [ - + ]: 5806 : 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 : 5806 : struct s2n_ecc_evp_params *client_params = &conn->kex_params.client_ecc_evp_params;
79 [ + + ]: 5806 : if (s2n_is_hello_retry_handshake(conn)) {
80 : 647 : 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 [ - + ]: 647 : 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 [ + - ]: 647 : if (server_curve != client_params->negotiated_curve) {
89 [ - + ]: 647 : POSIX_GUARD(s2n_ecc_evp_params_free(client_params));
90 : 647 : }
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 : 647 : client_params->negotiated_curve = server_curve;
100 : 5159 : } else {
101 : 5159 : client_params->negotiated_curve = ecc_pref->ecc_curves[0];
102 : 5159 : }
103 [ - + ]: 5806 : POSIX_GUARD(s2n_ecdhe_parameters_send(client_params, out));
104 : :
105 : 5806 : return S2N_SUCCESS;
106 : 5806 : }
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));
136 : 0 : } else {
137 [ # # ]: 0 : POSIX_GUARD_RESULT(s2n_ecdhe_send_public_key(ecc_params, out));
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 : 5806 : {
149 [ # # ][ - + ]: 5806 : POSIX_ENSURE_REF(conn);
150 [ # # ][ - + ]: 5806 : POSIX_ENSURE_REF(out);
151 : :
152 : : /* Client should skip sending PQ groups/key shares if PQ is disabled */
153 [ + - ]: 5806 : if (!s2n_pq_is_enabled()) {
154 : 5806 : return S2N_SUCCESS;
155 : 5806 : }
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 : }
197 : :
198 [ # # ]: 0 : POSIX_GUARD(s2n_generate_pq_key_share(out, client_params));
199 : :
200 : 0 : return S2N_SUCCESS;
201 : 0 : }
202 : :
203 : : static int s2n_client_key_share_send(struct s2n_connection *conn, struct s2n_stuffer *out)
204 : 5808 : {
205 [ + + ]: 5808 : if (s2n_is_hello_retry_handshake(conn)) {
206 : 649 : const struct s2n_ecc_named_curve *server_curve = conn->kex_params.server_ecc_evp_params.negotiated_curve;
207 : 649 : const struct s2n_ecc_named_curve *client_curve = conn->kex_params.client_ecc_evp_params.negotiated_curve;
208 : 649 : const struct s2n_kem_group *server_group = conn->kex_params.server_kem_group_params.kem_group;
209 : 649 : const struct s2n_kem_group *client_group = conn->kex_params.client_kem_group_params.kem_group;
210 : :
211 : : /* Ensure a new key share will be sent after a hello retry request */
212 [ + - ][ + + ]: 649 : POSIX_ENSURE(server_curve != client_curve || server_group != client_group, S2N_ERR_BAD_KEY_SHARE);
[ - + ]
213 : 649 : }
214 : :
215 : 5806 : struct s2n_stuffer_reservation shares_size = { 0 };
216 [ - + ]: 5806 : POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &shares_size));
217 [ - + ]: 5806 : POSIX_GUARD(s2n_generate_default_pq_key_share(conn, out));
218 [ - + ]: 5806 : POSIX_GUARD(s2n_generate_default_ecc_key_share(conn, out));
219 [ - + ]: 5806 : POSIX_GUARD(s2n_stuffer_write_vector_size(&shares_size));
220 : :
221 : : /* We must have written at least one share */
222 [ - + ][ # # ]: 5806 : POSIX_ENSURE(s2n_stuffer_data_available(out) > shares_size.length, S2N_ERR_BAD_KEY_SHARE);
223 : :
224 : 5806 : return S2N_SUCCESS;
225 : 5806 : }
226 : :
227 : : static int s2n_client_key_share_parse_ecc(struct s2n_stuffer *key_share, const struct s2n_ecc_named_curve *curve,
228 : : struct s2n_ecc_evp_params *ecc_params)
229 : 4775 : {
230 [ # # ][ - + ]: 4775 : POSIX_ENSURE_REF(key_share);
231 [ - + ][ # # ]: 4775 : POSIX_ENSURE_REF(curve);
232 [ # # ][ - + ]: 4775 : POSIX_ENSURE_REF(ecc_params);
233 : :
234 : 4775 : struct s2n_blob point_blob = { 0 };
235 [ - + ]: 4775 : POSIX_GUARD(s2n_ecc_evp_read_params_point(key_share, curve->share_size, &point_blob));
236 : :
237 : : /* Ignore curves with points we can't parse */
238 : 4775 : ecc_params->negotiated_curve = curve;
239 [ + + ]: 4775 : if (s2n_ecc_evp_parse_params_point(&point_blob, ecc_params) != S2N_SUCCESS) {
240 : 2 : ecc_params->negotiated_curve = NULL;
241 [ - + ]: 2 : POSIX_GUARD(s2n_ecc_evp_params_free(ecc_params));
242 : 2 : }
243 : :
244 : 4775 : return S2N_SUCCESS;
245 : 4775 : }
246 : :
247 : : static int s2n_client_key_share_recv_ecc(struct s2n_connection *conn, struct s2n_stuffer *key_share, uint16_t curve_iana_id)
248 : 5437 : {
249 [ # # ][ - + ]: 5437 : POSIX_ENSURE_REF(conn);
250 [ # # ][ - + ]: 5437 : POSIX_ENSURE_REF(key_share);
251 : :
252 : 5437 : const struct s2n_ecc_preferences *ecc_pref = NULL;
253 [ - + ]: 5437 : POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_pref));
254 [ # # ][ - + ]: 5437 : POSIX_ENSURE_REF(ecc_pref);
255 : :
256 : 5437 : struct s2n_ecc_evp_params *client_params = &conn->kex_params.client_ecc_evp_params;
257 : :
258 : 5437 : const struct s2n_ecc_named_curve *curve = NULL;
259 [ + + ]: 8056 : for (size_t i = 0; i < ecc_pref->count; i++) {
260 : 7401 : const struct s2n_ecc_named_curve *supported_curve = ecc_pref->ecc_curves[i];
261 [ - + ][ # # ]: 7401 : POSIX_ENSURE_REF(supported_curve);
262 : :
263 : : /* Stop if we reach the current highest priority share.
264 : : * Any share of lower priority is discarded.
265 : : */
266 [ + + ]: 7401 : if (client_params->negotiated_curve == supported_curve) {
267 : 6 : break;
268 : 6 : }
269 : :
270 : : /* Skip if not supported by the client.
271 : : * The client must not send shares it doesn't support, but the server
272 : : * is not required to error if they are encountered.
273 : : */
274 [ + + ]: 7395 : if (!conn->kex_params.mutually_supported_curves[i]) {
275 : 13 : continue;
276 : 13 : }
277 : :
278 : : /* Stop if we find a match */
279 [ + + ]: 7382 : if (curve_iana_id == supported_curve->iana_id) {
280 : 4776 : curve = supported_curve;
281 : 4776 : break;
282 : 4776 : }
283 : 7382 : }
284 : :
285 : : /* Ignore unsupported curves */
286 [ + + ]: 5437 : if (!curve) {
287 : 661 : return S2N_SUCCESS;
288 : 661 : }
289 : :
290 : : /* Ignore curves with unexpected share sizes */
291 [ + + ]: 4776 : if (key_share->blob.size != curve->share_size) {
292 : 1 : return S2N_SUCCESS;
293 : 1 : }
294 : :
295 : 4775 : DEFER_CLEANUP(struct s2n_ecc_evp_params new_client_params = { 0 }, s2n_ecc_evp_params_free);
296 : :
297 [ - + ]: 4775 : POSIX_GUARD(s2n_client_key_share_parse_ecc(key_share, curve, &new_client_params));
298 : : /* negotiated_curve will be NULL if the key share was not parsed successfully */
299 [ + + ]: 4775 : if (!new_client_params.negotiated_curve) {
300 : 2 : return S2N_SUCCESS;
301 : 2 : }
302 : :
303 [ - + ]: 4773 : POSIX_GUARD(s2n_ecc_evp_params_free(client_params));
304 : 4773 : *client_params = new_client_params;
305 : :
306 : 4773 : ZERO_TO_DISABLE_DEFER_CLEANUP(new_client_params);
307 : 4773 : return S2N_SUCCESS;
308 : 4773 : }
309 : :
310 : : static int s2n_client_key_share_recv_hybrid_partial_ecc(struct s2n_stuffer *key_share, struct s2n_kem_group_params *new_client_params)
311 : 0 : {
312 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(new_client_params);
313 : 0 : const struct s2n_kem_group *kem_group = new_client_params->kem_group;
314 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(kem_group);
315 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(kem_group->curve);
316 : :
317 [ # # ]: 0 : POSIX_GUARD(s2n_client_key_share_parse_ecc(key_share, kem_group->curve, &new_client_params->ecc_params));
318 : :
319 : : /* If we were unable to parse the EC portion of the share, negotiated_curve
320 : : * will be NULL, and we should ignore the entire key share. */
321 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(new_client_params->ecc_params.negotiated_curve);
322 : :
323 : 0 : return S2N_SUCCESS;
324 : 0 : }
325 : :
326 : : static int s2n_client_key_share_recv_pq(struct s2n_connection *conn, struct s2n_stuffer *key_share, uint16_t kem_group_iana_id)
327 : 5437 : {
328 [ # # ][ - + ]: 5437 : POSIX_ENSURE_REF(conn);
329 [ - + ][ # # ]: 5437 : POSIX_ENSURE_REF(key_share);
330 : :
331 : 5437 : const struct s2n_kem_preferences *kem_pref = NULL;
332 [ - + ]: 5437 : POSIX_GUARD(s2n_connection_get_kem_preferences(conn, &kem_pref));
333 [ - + ][ # # ]: 5437 : POSIX_ENSURE_REF(kem_pref);
334 : :
335 : : /* Ignore key share if PQ is not enabled */
336 [ + - ]: 5437 : if (!s2n_pq_is_enabled()) {
337 : 5437 : return S2N_SUCCESS;
338 : 5437 : }
339 : :
340 : 0 : struct s2n_kem_group_params *client_params = &conn->kex_params.client_kem_group_params;
341 : :
342 : 0 : const struct s2n_kem_group *kem_group = NULL;
343 [ # # ]: 0 : for (size_t i = 0; i < kem_pref->tls13_kem_group_count; i++) {
344 : 0 : const struct s2n_kem_group *supported_group = kem_pref->tls13_kem_groups[i];
345 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(supported_group);
346 : :
347 : : /* Skip if the group is not available */
348 [ # # ]: 0 : if (!s2n_kem_group_is_available(supported_group)) {
349 : 0 : continue;
350 : 0 : }
351 : :
352 : : /* Stop if we reach the current highest priority share.
353 : : * Any share of lower priority is discarded.
354 : : */
355 [ # # ]: 0 : if (client_params->kem_group == supported_group) {
356 : 0 : break;
357 : 0 : }
358 : :
359 : : /* Skip if not supported by the client.
360 : : * The client must not send shares it doesn't support, but the server
361 : : * is not required to error if they are encountered.
362 : : */
363 [ # # ]: 0 : if (!conn->kex_params.mutually_supported_kem_groups[i]) {
364 : 0 : continue;
365 : 0 : }
366 : :
367 : : /* Stop if we find a match */
368 [ # # ]: 0 : if (kem_group_iana_id == supported_group->iana_id) {
369 : 0 : kem_group = supported_group;
370 : 0 : break;
371 : 0 : }
372 : 0 : }
373 : :
374 : : /* Ignore unsupported KEM groups */
375 [ # # ]: 0 : if (!kem_group) {
376 : 0 : return S2N_SUCCESS;
377 : 0 : }
378 : :
379 : 0 : uint16_t actual_share_size = key_share->blob.size;
380 : 0 : uint16_t expected_share_size = kem_group->curve->share_size + kem_group->kem->public_key_length;
381 : :
382 : : /* Ignore KEM groups with unexpected overall total share sizes */
383 [ # # ]: 0 : if (actual_share_size != expected_share_size) {
384 : 0 : return S2N_SUCCESS;
385 : 0 : }
386 : :
387 : 0 : DEFER_CLEANUP(struct s2n_kem_group_params new_client_params = { 0 }, s2n_kem_group_free);
388 : 0 : new_client_params.kem_group = kem_group;
389 : :
390 : 0 : new_client_params.kem_params.kem = kem_group->kem;
391 : :
392 : : /* Note: the PQ share size is validated in s2n_kem_recv_public_key() */
393 : : /* Ignore PQ and ECC groups with public keys we can't parse */
394 [ # # ]: 0 : if (kem_group->curve == &s2n_ecc_curve_none) { /* Pure PQ */
395 [ # # ]: 0 : if (s2n_kem_recv_public_key(key_share, &new_client_params.kem_params) != S2N_SUCCESS) {
396 : 0 : return S2N_SUCCESS;
397 : 0 : }
398 : 0 : } else { /* Hybrid PQ */
399 [ # # ]: 0 : if (kem_group->send_kem_first) {
400 [ # # ]: 0 : if (s2n_kem_recv_public_key(key_share, &new_client_params.kem_params) != S2N_SUCCESS) {
401 : 0 : return S2N_SUCCESS;
402 : 0 : }
403 [ # # ]: 0 : if (s2n_client_key_share_recv_hybrid_partial_ecc(key_share, &new_client_params) != S2N_SUCCESS) {
404 : 0 : return S2N_SUCCESS;
405 : 0 : }
406 : 0 : } else {
407 [ # # ]: 0 : if (s2n_client_key_share_recv_hybrid_partial_ecc(key_share, &new_client_params) != S2N_SUCCESS) {
408 : 0 : return S2N_SUCCESS;
409 : 0 : }
410 [ # # ]: 0 : if (s2n_kem_recv_public_key(key_share, &new_client_params.kem_params) != S2N_SUCCESS) {
411 : 0 : return S2N_SUCCESS;
412 : 0 : }
413 : 0 : }
414 : 0 : }
415 : :
416 [ # # ]: 0 : POSIX_GUARD(s2n_kem_group_free(client_params));
417 : 0 : *client_params = new_client_params;
418 : :
419 : 0 : ZERO_TO_DISABLE_DEFER_CLEANUP(new_client_params);
420 : 0 : return S2N_SUCCESS;
421 : 0 : }
422 : :
423 : : /*
424 : : * We chose our most preferred group of the mutually supported groups while processing the
425 : : * supported_groups extension. However, our true most preferred group is always the
426 : : * group that we already have a key share for, since retries are expensive.
427 : : *
428 : : * This method modifies our group selection based on what keyshares are available.
429 : : * It then stores the client keyshare for the selected group, or initiates a retry
430 : : * if no valid keyshares are available.
431 : : */
432 : : static int s2n_client_key_share_recv(struct s2n_connection *conn, struct s2n_stuffer *extension)
433 : 5432 : {
434 [ # # ][ - + ]: 5432 : POSIX_ENSURE_REF(conn);
435 [ # # ][ - + ]: 5432 : POSIX_ENSURE_REF(extension);
436 : :
437 : 5432 : uint16_t key_shares_size = 0;
438 [ - + ]: 5432 : POSIX_GUARD(s2n_stuffer_read_uint16(extension, &key_shares_size));
439 [ + + ][ + - ]: 5432 : POSIX_ENSURE(s2n_stuffer_data_available(extension) == key_shares_size, S2N_ERR_BAD_MESSAGE);
440 : :
441 : 5431 : uint16_t named_group = 0, share_size = 0;
442 : 5431 : struct s2n_blob key_share_blob = { 0 };
443 : 5431 : struct s2n_stuffer key_share = { 0 };
444 : :
445 : 5431 : uint16_t keyshare_count = 0;
446 [ + + ]: 10868 : while (s2n_stuffer_data_available(extension) > 0) {
447 [ - + ]: 5438 : POSIX_GUARD(s2n_stuffer_read_uint16(extension, &named_group));
448 [ - + ]: 5438 : POSIX_GUARD(s2n_stuffer_read_uint16(extension, &share_size));
449 [ + - ][ + + ]: 5438 : POSIX_ENSURE(s2n_stuffer_data_available(extension) >= share_size, S2N_ERR_BAD_MESSAGE);
450 : :
451 [ - + ]: 5437 : POSIX_GUARD(s2n_blob_init(&key_share_blob,
452 : 5437 : s2n_stuffer_raw_read(extension, share_size), share_size));
453 [ - + ]: 5437 : POSIX_GUARD(s2n_stuffer_init(&key_share, &key_share_blob));
454 [ - + ]: 5437 : POSIX_GUARD(s2n_stuffer_skip_write(&key_share, share_size));
455 : 5437 : keyshare_count++;
456 : :
457 : : /* Try to parse the share as ECC, then as PQ; will ignore
458 : : * shares for unrecognized groups. */
459 [ - + ]: 5437 : POSIX_GUARD(s2n_client_key_share_recv_ecc(conn, &key_share, named_group));
460 [ - + ]: 5437 : POSIX_GUARD(s2n_client_key_share_recv_pq(conn, &key_share, named_group));
461 : 5437 : }
462 : :
463 : : /* During a retry, the client should only have sent one keyshare */
464 [ # # ][ + - ]: 5430 : POSIX_ENSURE(!s2n_is_hello_retry_handshake(conn) || keyshare_count == 1, S2N_ERR_BAD_MESSAGE);
[ + + ]
465 : :
466 : : /**
467 : : * If there were no matching key shares, then we received an empty key share extension
468 : : * or we didn't match a key share with a supported group. We should send a retry.
469 : : *
470 : : *= https://www.rfc-editor.org/rfc/rfc8446#4.1.1
471 : : *# If the server selects an (EC)DHE group and the client did not offer a
472 : : *# compatible "key_share" extension in the initial ClientHello, the
473 : : *# server MUST respond with a HelloRetryRequest (Section 4.1.4) message.
474 : : **/
475 : 5430 : struct s2n_ecc_evp_params *client_ecc_params = &conn->kex_params.client_ecc_evp_params;
476 : 5430 : struct s2n_kem_group_params *client_pq_params = &conn->kex_params.client_kem_group_params;
477 [ + - ][ + + ]: 5430 : if (!client_pq_params->kem_group && !client_ecc_params->negotiated_curve) {
478 [ - + ]: 656 : POSIX_GUARD(s2n_set_hello_retry_required(conn));
479 : 656 : }
480 : :
481 : 5430 : return S2N_SUCCESS;
482 : 5430 : }
483 : :
484 : : /* Old-style extension functions -- remove after extensions refactor is complete */
485 : :
486 : : int s2n_extensions_client_key_share_recv(struct s2n_connection *conn, struct s2n_stuffer *extension)
487 : 0 : {
488 : 0 : return s2n_extension_recv(&s2n_client_key_share_extension, conn, extension);
489 : 0 : }
|