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 : 5321 : {
65 [ - + ][ # # ]: 5321 : POSIX_ENSURE_REF(conn);
66 : 5321 : const struct s2n_ecc_preferences *ecc_pref = NULL;
67 [ - + ]: 5321 : POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_pref));
68 [ # # ][ - + ]: 5321 : POSIX_ENSURE_REF(ecc_pref);
69 : :
70 : : /* We only ever send a single EC key share: either the share requested by the server
71 : : * during a retry, or the most preferred share according to local preferences.
72 : : */
73 : 5321 : struct s2n_ecc_evp_params *client_params = &conn->kex_params.client_ecc_evp_params;
74 [ + + ]: 5321 : if (s2n_is_hello_retry_handshake(conn)) {
75 : 644 : const struct s2n_ecc_named_curve *server_curve = conn->kex_params.server_ecc_evp_params.negotiated_curve;
76 : :
77 : : /* If the server did not request a specific ECC keyshare, don't send one */
78 [ - + ]: 644 : if (!server_curve) {
79 : 0 : return S2N_SUCCESS;
80 : 0 : }
81 : :
82 : : /* If the server requested a new ECC keyshare, free the old one */
83 [ + - ]: 644 : if (server_curve != client_params->negotiated_curve) {
84 [ - + ]: 644 : POSIX_GUARD(s2n_ecc_evp_params_free(client_params));
85 : 644 : }
86 : :
87 : : /**
88 : : *= https://www.rfc-editor.org/rfc/rfc8446#4.2.8
89 : : *# Otherwise, when sending the new ClientHello, the client MUST
90 : : *# replace the original "key_share" extension with one containing only a
91 : : *# new KeyShareEntry for the group indicated in the selected_group field
92 : : *# of the triggering HelloRetryRequest.
93 : : **/
94 : 644 : client_params->negotiated_curve = server_curve;
95 : 4677 : } else {
96 : 4677 : client_params->negotiated_curve = ecc_pref->ecc_curves[0];
97 : 4677 : }
98 [ - + ]: 5321 : POSIX_GUARD(s2n_ecdhe_parameters_send(client_params, out));
99 : :
100 : 5321 : return S2N_SUCCESS;
101 : 5321 : }
102 : :
103 : : static int s2n_generate_pq_hybrid_key_share(struct s2n_stuffer *out, struct s2n_kem_group_params *kem_group_params)
104 : 0 : {
105 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(out);
106 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(kem_group_params);
107 : :
108 : : /* This function should never be called when PQ is disabled */
109 [ # # ][ # # ]: 0 : POSIX_ENSURE(s2n_pq_is_enabled(), S2N_ERR_UNIMPLEMENTED);
110 : :
111 : 0 : const struct s2n_kem_group *kem_group = kem_group_params->kem_group;
112 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(kem_group);
113 : :
114 [ # # ]: 0 : POSIX_GUARD(s2n_stuffer_write_uint16(out, kem_group->iana_id));
115 : :
116 : 0 : struct s2n_stuffer_reservation total_share_size = { 0 };
117 [ # # ]: 0 : POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &total_share_size));
118 : :
119 : 0 : struct s2n_ecc_evp_params *ecc_params = &kem_group_params->ecc_params;
120 : 0 : ecc_params->negotiated_curve = kem_group->curve;
121 : :
122 : 0 : struct s2n_kem_params *kem_params = &kem_group_params->kem_params;
123 : 0 : kem_params->kem = kem_group->kem;
124 : :
125 [ # # ]: 0 : if (kem_group->send_kem_first) {
126 [ # # ]: 0 : POSIX_GUARD(s2n_kem_send_public_key(out, kem_params));
127 [ # # ]: 0 : POSIX_GUARD_RESULT(s2n_ecdhe_send_public_key(ecc_params, out, kem_params->len_prefixed));
128 : 0 : } else {
129 [ # # ]: 0 : POSIX_GUARD_RESULT(s2n_ecdhe_send_public_key(ecc_params, out, kem_params->len_prefixed));
130 [ # # ]: 0 : POSIX_GUARD(s2n_kem_send_public_key(out, kem_params));
131 : 0 : }
132 : :
133 [ # # ]: 0 : POSIX_GUARD(s2n_stuffer_write_vector_size(&total_share_size));
134 : :
135 : 0 : return S2N_SUCCESS;
136 : 0 : }
137 : :
138 : : static int s2n_generate_default_pq_hybrid_key_share(struct s2n_connection *conn, struct s2n_stuffer *out)
139 : 5321 : {
140 [ - + ][ # # ]: 5321 : POSIX_ENSURE_REF(conn);
141 [ # # ][ - + ]: 5321 : POSIX_ENSURE_REF(out);
142 : :
143 : : /* Client should skip sending PQ groups/key shares if PQ is disabled */
144 [ + - ]: 5321 : if (!s2n_pq_is_enabled()) {
145 : 5321 : return S2N_SUCCESS;
146 : 5321 : }
147 : :
148 : 0 : const struct s2n_kem_preferences *kem_pref = NULL;
149 [ # # ]: 0 : POSIX_GUARD(s2n_connection_get_kem_preferences(conn, &kem_pref));
150 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(kem_pref);
151 : :
152 : 0 : uint32_t available_groups = 0;
153 [ # # ]: 0 : POSIX_GUARD_RESULT(s2n_kem_preferences_groups_available(kem_pref, &available_groups));
154 [ # # ]: 0 : if (available_groups == 0) {
155 : 0 : return S2N_SUCCESS;
156 : 0 : }
157 : :
158 : : /* We only ever send a single PQ key share: either the share requested by the server
159 : : * during a retry, or the most preferred share according to local preferences.
160 : : */
161 : 0 : struct s2n_kem_group_params *client_params = &conn->kex_params.client_kem_group_params;
162 : :
163 [ # # ]: 0 : if (s2n_is_hello_retry_handshake(conn)) {
164 : 0 : const struct s2n_kem_group *server_group = conn->kex_params.server_kem_group_params.kem_group;
165 : :
166 : : /* If the server did not request a specific PQ keyshare, don't send one */
167 [ # # ]: 0 : if (!server_group) {
168 : 0 : return S2N_SUCCESS;
169 : 0 : }
170 : :
171 : : /* If the server requested a new PQ keyshare, free the old one */
172 [ # # ]: 0 : if (client_params->kem_group != server_group) {
173 [ # # ]: 0 : POSIX_GUARD(s2n_kem_group_free(client_params));
174 : 0 : }
175 : :
176 : : /**
177 : : *= https://www.rfc-editor.org/rfc/rfc8446#4.2.8
178 : : *# Otherwise, when sending the new ClientHello, the client MUST
179 : : *# replace the original "key_share" extension with one containing only a
180 : : *# new KeyShareEntry for the group indicated in the selected_group field
181 : : *# of the triggering HelloRetryRequest.
182 : : **/
183 : 0 : client_params->kem_group = server_group;
184 : 0 : } else {
185 : 0 : client_params->kem_group = s2n_kem_preferences_get_highest_priority_group(kem_pref);
186 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(client_params->kem_group);
187 : 0 : client_params->kem_params.len_prefixed = s2n_tls13_client_must_use_hybrid_kem_length_prefix(kem_pref);
188 : 0 : }
189 : :
190 [ # # ]: 0 : POSIX_GUARD(s2n_generate_pq_hybrid_key_share(out, client_params));
191 : :
192 : 0 : return S2N_SUCCESS;
193 : 0 : }
194 : :
195 : : static int s2n_client_key_share_send(struct s2n_connection *conn, struct s2n_stuffer *out)
196 : 5323 : {
197 [ + + ]: 5323 : if (s2n_is_hello_retry_handshake(conn)) {
198 : 646 : const struct s2n_ecc_named_curve *server_curve = conn->kex_params.server_ecc_evp_params.negotiated_curve;
199 : 646 : const struct s2n_ecc_named_curve *client_curve = conn->kex_params.client_ecc_evp_params.negotiated_curve;
200 : 646 : const struct s2n_kem_group *server_group = conn->kex_params.server_kem_group_params.kem_group;
201 : 646 : const struct s2n_kem_group *client_group = conn->kex_params.client_kem_group_params.kem_group;
202 : :
203 : : /* Ensure a new key share will be sent after a hello retry request */
204 [ + - ][ + + ]: 646 : POSIX_ENSURE(server_curve != client_curve || server_group != client_group, S2N_ERR_BAD_KEY_SHARE);
[ - + ]
205 : 646 : }
206 : :
207 : 5321 : struct s2n_stuffer_reservation shares_size = { 0 };
208 [ - + ]: 5321 : POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &shares_size));
209 [ - + ]: 5321 : POSIX_GUARD(s2n_generate_default_pq_hybrid_key_share(conn, out));
210 [ - + ]: 5321 : POSIX_GUARD(s2n_generate_default_ecc_key_share(conn, out));
211 [ - + ]: 5321 : POSIX_GUARD(s2n_stuffer_write_vector_size(&shares_size));
212 : :
213 : : /* We must have written at least one share */
214 [ - + ][ # # ]: 5321 : POSIX_ENSURE(s2n_stuffer_data_available(out) > shares_size.length, S2N_ERR_BAD_KEY_SHARE);
215 : :
216 : 5321 : return S2N_SUCCESS;
217 : 5321 : }
218 : :
219 : : static int s2n_client_key_share_parse_ecc(struct s2n_stuffer *key_share, const struct s2n_ecc_named_curve *curve,
220 : : struct s2n_ecc_evp_params *ecc_params)
221 : 4283 : {
222 [ # # ][ - + ]: 4283 : POSIX_ENSURE_REF(key_share);
223 [ - + ][ # # ]: 4283 : POSIX_ENSURE_REF(curve);
224 [ - + ][ # # ]: 4283 : POSIX_ENSURE_REF(ecc_params);
225 : :
226 : 4283 : struct s2n_blob point_blob = { 0 };
227 [ - + ]: 4283 : POSIX_GUARD(s2n_ecc_evp_read_params_point(key_share, curve->share_size, &point_blob));
228 : :
229 : : /* Ignore curves with points we can't parse */
230 : 4283 : ecc_params->negotiated_curve = curve;
231 [ + + ]: 4283 : if (s2n_ecc_evp_parse_params_point(&point_blob, ecc_params) != S2N_SUCCESS) {
232 : 2 : ecc_params->negotiated_curve = NULL;
233 [ - + ]: 2 : POSIX_GUARD(s2n_ecc_evp_params_free(ecc_params));
234 : 2 : }
235 : :
236 : 4283 : return S2N_SUCCESS;
237 : 4283 : }
238 : :
239 : : static int s2n_client_key_share_recv_ecc(struct s2n_connection *conn, struct s2n_stuffer *key_share, uint16_t curve_iana_id)
240 : 4947 : {
241 [ - + ][ # # ]: 4947 : POSIX_ENSURE_REF(conn);
242 [ - + ][ # # ]: 4947 : POSIX_ENSURE_REF(key_share);
243 : :
244 : 4947 : const struct s2n_ecc_preferences *ecc_pref = NULL;
245 [ - + ]: 4947 : POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_pref));
246 [ # # ][ - + ]: 4947 : POSIX_ENSURE_REF(ecc_pref);
247 : :
248 : 4947 : struct s2n_ecc_evp_params *client_params = &conn->kex_params.client_ecc_evp_params;
249 : :
250 : 4947 : const struct s2n_ecc_named_curve *curve = NULL;
251 [ + + ]: 7554 : for (size_t i = 0; i < ecc_pref->count; i++) {
252 : 6897 : const struct s2n_ecc_named_curve *supported_curve = ecc_pref->ecc_curves[i];
253 [ # # ][ - + ]: 6897 : POSIX_ENSURE_REF(supported_curve);
254 : :
255 : : /* Stop if we reach the current highest priority share.
256 : : * Any share of lower priority is discarded.
257 : : */
258 [ + + ]: 6897 : if (client_params->negotiated_curve == supported_curve) {
259 : 6 : break;
260 : 6 : }
261 : :
262 : : /* Skip if not supported by the client.
263 : : * The client must not send shares it doesn't support, but the server
264 : : * is not required to error if they are encountered.
265 : : */
266 [ + + ]: 6891 : if (!conn->kex_params.mutually_supported_curves[i]) {
267 : 13 : continue;
268 : 13 : }
269 : :
270 : : /* Stop if we find a match */
271 [ + + ]: 6878 : if (curve_iana_id == supported_curve->iana_id) {
272 : 4284 : curve = supported_curve;
273 : 4284 : break;
274 : 4284 : }
275 : 6878 : }
276 : :
277 : : /* Ignore unsupported curves */
278 [ + + ]: 4947 : if (!curve) {
279 : 663 : return S2N_SUCCESS;
280 : 663 : }
281 : :
282 : : /* Ignore curves with unexpected share sizes */
283 [ + + ]: 4284 : if (key_share->blob.size != curve->share_size) {
284 : 1 : return S2N_SUCCESS;
285 : 1 : }
286 : :
287 : 4283 : DEFER_CLEANUP(struct s2n_ecc_evp_params new_client_params = { 0 }, s2n_ecc_evp_params_free);
288 : :
289 [ - + ]: 4283 : POSIX_GUARD(s2n_client_key_share_parse_ecc(key_share, curve, &new_client_params));
290 : : /* negotiated_curve will be NULL if the key share was not parsed successfully */
291 [ + + ]: 4283 : if (!new_client_params.negotiated_curve) {
292 : 2 : return S2N_SUCCESS;
293 : 2 : }
294 : :
295 [ - + ]: 4281 : POSIX_GUARD(s2n_ecc_evp_params_free(client_params));
296 : 4281 : *client_params = new_client_params;
297 : :
298 : 4281 : ZERO_TO_DISABLE_DEFER_CLEANUP(new_client_params);
299 : 4281 : return S2N_SUCCESS;
300 : 4281 : }
301 : :
302 : : static int s2n_client_key_share_recv_hybrid_partial_ecc(struct s2n_stuffer *key_share, struct s2n_kem_group_params *new_client_params)
303 : 0 : {
304 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(new_client_params);
305 : 0 : const struct s2n_kem_group *kem_group = new_client_params->kem_group;
306 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(kem_group);
307 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(kem_group->curve);
308 : :
309 [ # # ]: 0 : if (new_client_params->kem_params.len_prefixed) {
310 : 0 : uint16_t ec_share_size = 0;
311 [ # # ]: 0 : POSIX_GUARD(s2n_stuffer_read_uint16(key_share, &ec_share_size));
312 [ # # ][ # # ]: 0 : POSIX_ENSURE(ec_share_size == kem_group->curve->share_size, S2N_ERR_SIZE_MISMATCH);
313 : 0 : }
314 : :
315 [ # # ]: 0 : POSIX_GUARD(s2n_client_key_share_parse_ecc(key_share, kem_group->curve, &new_client_params->ecc_params));
316 : :
317 : : /* If we were unable to parse the EC portion of the share, negotiated_curve
318 : : * will be NULL, and we should ignore the entire key share. */
319 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(new_client_params->ecc_params.negotiated_curve);
320 : :
321 : 0 : return S2N_SUCCESS;
322 : 0 : }
323 : :
324 : : static int s2n_client_key_share_recv_pq_hybrid(struct s2n_connection *conn, struct s2n_stuffer *key_share, uint16_t kem_group_iana_id)
325 : 4947 : {
326 [ - + ][ # # ]: 4947 : POSIX_ENSURE_REF(conn);
327 [ - + ][ # # ]: 4947 : POSIX_ENSURE_REF(key_share);
328 : :
329 : 4947 : const struct s2n_kem_preferences *kem_pref = NULL;
330 [ - + ]: 4947 : POSIX_GUARD(s2n_connection_get_kem_preferences(conn, &kem_pref));
331 [ - + ][ # # ]: 4947 : POSIX_ENSURE_REF(kem_pref);
332 : :
333 : : /* Ignore key share if PQ is not enabled */
334 [ + - ]: 4947 : if (!s2n_pq_is_enabled()) {
335 : 4947 : return S2N_SUCCESS;
336 : 4947 : }
337 : :
338 : 0 : struct s2n_kem_group_params *client_params = &conn->kex_params.client_kem_group_params;
339 : :
340 : 0 : const struct s2n_kem_group *kem_group = NULL;
341 [ # # ]: 0 : for (size_t i = 0; i < kem_pref->tls13_kem_group_count; i++) {
342 : 0 : const struct s2n_kem_group *supported_group = kem_pref->tls13_kem_groups[i];
343 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(supported_group);
344 : :
345 : : /* Skip if the group is not available */
346 [ # # ]: 0 : if (!s2n_kem_group_is_available(supported_group)) {
347 : 0 : continue;
348 : 0 : }
349 : :
350 : : /* Stop if we reach the current highest priority share.
351 : : * Any share of lower priority is discarded.
352 : : */
353 [ # # ]: 0 : if (client_params->kem_group == supported_group) {
354 : 0 : break;
355 : 0 : }
356 : :
357 : : /* Skip if not supported by the client.
358 : : * The client must not send shares it doesn't support, but the server
359 : : * is not required to error if they are encountered.
360 : : */
361 [ # # ]: 0 : if (!conn->kex_params.mutually_supported_kem_groups[i]) {
362 : 0 : continue;
363 : 0 : }
364 : :
365 : : /* Stop if we find a match */
366 [ # # ]: 0 : if (kem_group_iana_id == supported_group->iana_id) {
367 : 0 : kem_group = supported_group;
368 : 0 : break;
369 : 0 : }
370 : 0 : }
371 : :
372 : : /* Ignore unsupported KEM groups */
373 [ # # ]: 0 : if (!kem_group) {
374 : 0 : return S2N_SUCCESS;
375 : 0 : }
376 : :
377 : : /* The length of the hybrid key share must be one of two possible lengths. Its internal values are either length
378 : : * prefixed, or they are not. */
379 : 0 : uint16_t actual_hybrid_share_size = key_share->blob.size;
380 : 0 : uint16_t unprefixed_hybrid_share_size = kem_group->curve->share_size + kem_group->kem->public_key_length;
381 : 0 : uint16_t prefixed_hybrid_share_size = (2 * S2N_SIZE_OF_KEY_SHARE_SIZE) + unprefixed_hybrid_share_size;
382 : :
383 : : /* Ignore KEM groups with unexpected overall total share sizes */
384 [ # # ][ # # ]: 0 : if ((actual_hybrid_share_size != unprefixed_hybrid_share_size) && (actual_hybrid_share_size != prefixed_hybrid_share_size)) {
385 : 0 : return S2N_SUCCESS;
386 : 0 : }
387 : :
388 : 0 : bool is_hybrid_share_length_prefixed = (actual_hybrid_share_size == prefixed_hybrid_share_size);
389 : :
390 : 0 : DEFER_CLEANUP(struct s2n_kem_group_params new_client_params = { 0 }, s2n_kem_group_free);
391 : 0 : new_client_params.kem_group = kem_group;
392 : :
393 : : /* Need to save whether the client included the length prefix so that we can match their behavior in our response. */
394 : 0 : new_client_params.kem_params.len_prefixed = is_hybrid_share_length_prefixed;
395 : 0 : new_client_params.kem_params.kem = kem_group->kem;
396 : :
397 : : /* Note: the PQ share size is validated in s2n_kem_recv_public_key() */
398 : : /* Ignore PQ and ECC groups with public keys we can't parse */
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 : :
415 [ # # ]: 0 : POSIX_GUARD(s2n_kem_group_free(client_params));
416 : 0 : *client_params = new_client_params;
417 : :
418 : 0 : ZERO_TO_DISABLE_DEFER_CLEANUP(new_client_params);
419 : 0 : return S2N_SUCCESS;
420 : 0 : }
421 : :
422 : : /*
423 : : * We chose our most preferred group of the mutually supported groups while processing the
424 : : * supported_groups extension. However, our true most preferred group is always the
425 : : * group that we already have a key share for, since retries are expensive.
426 : : *
427 : : * This method modifies our group selection based on what keyshares are available.
428 : : * It then stores the client keyshare for the selected group, or initiates a retry
429 : : * if no valid keyshares are available.
430 : : */
431 : : static int s2n_client_key_share_recv(struct s2n_connection *conn, struct s2n_stuffer *extension)
432 : 4942 : {
433 [ - + ][ # # ]: 4942 : POSIX_ENSURE_REF(conn);
434 [ - + ][ # # ]: 4942 : POSIX_ENSURE_REF(extension);
435 : :
436 : 4942 : uint16_t key_shares_size = 0;
437 [ - + ]: 4942 : POSIX_GUARD(s2n_stuffer_read_uint16(extension, &key_shares_size));
438 [ + + ][ + - ]: 4942 : POSIX_ENSURE(s2n_stuffer_data_available(extension) == key_shares_size, S2N_ERR_BAD_MESSAGE);
439 : :
440 : 4941 : uint16_t named_group = 0, share_size = 0;
441 : 4941 : struct s2n_blob key_share_blob = { 0 };
442 : 4941 : struct s2n_stuffer key_share = { 0 };
443 : :
444 : 4941 : uint16_t keyshare_count = 0;
445 [ + + ]: 9888 : while (s2n_stuffer_data_available(extension) > 0) {
446 [ - + ]: 4948 : POSIX_GUARD(s2n_stuffer_read_uint16(extension, &named_group));
447 [ - + ]: 4948 : POSIX_GUARD(s2n_stuffer_read_uint16(extension, &share_size));
448 [ + - ][ + + ]: 4948 : POSIX_ENSURE(s2n_stuffer_data_available(extension) >= share_size, S2N_ERR_BAD_MESSAGE);
449 : :
450 [ - + ]: 4947 : POSIX_GUARD(s2n_blob_init(&key_share_blob,
451 : 4947 : s2n_stuffer_raw_read(extension, share_size), share_size));
452 [ - + ]: 4947 : POSIX_GUARD(s2n_stuffer_init(&key_share, &key_share_blob));
453 [ - + ]: 4947 : POSIX_GUARD(s2n_stuffer_skip_write(&key_share, share_size));
454 : 4947 : keyshare_count++;
455 : :
456 : : /* Try to parse the share as ECC, then as PQ/hybrid; will ignore
457 : : * shares for unrecognized groups. */
458 [ - + ]: 4947 : POSIX_GUARD(s2n_client_key_share_recv_ecc(conn, &key_share, named_group));
459 [ - + ]: 4947 : POSIX_GUARD(s2n_client_key_share_recv_pq_hybrid(conn, &key_share, named_group));
460 : 4947 : }
461 : :
462 : : /* During a retry, the client should only have sent one keyshare */
463 [ # # ][ + - ]: 4940 : POSIX_ENSURE(!s2n_is_hello_retry_handshake(conn) || keyshare_count == 1, S2N_ERR_BAD_MESSAGE);
[ + + ]
464 : :
465 : : /**
466 : : * If there were no matching key shares, then we received an empty key share extension
467 : : * or we didn't match a key share with a supported group. We should send a retry.
468 : : *
469 : : *= https://www.rfc-editor.org/rfc/rfc8446#4.1.1
470 : : *# If the server selects an (EC)DHE group and the client did not offer a
471 : : *# compatible "key_share" extension in the initial ClientHello, the
472 : : *# server MUST respond with a HelloRetryRequest (Section 4.1.4) message.
473 : : **/
474 : 4940 : struct s2n_ecc_evp_params *client_ecc_params = &conn->kex_params.client_ecc_evp_params;
475 : 4940 : struct s2n_kem_group_params *client_pq_params = &conn->kex_params.client_kem_group_params;
476 [ + - ][ + + ]: 4940 : if (!client_pq_params->kem_group && !client_ecc_params->negotiated_curve) {
477 [ - + ]: 658 : POSIX_GUARD(s2n_set_hello_retry_required(conn));
478 : 658 : }
479 : :
480 : 4940 : return S2N_SUCCESS;
481 : 4940 : }
482 : :
483 : : /* Old-style extension functions -- remove after extensions refactor is complete */
484 : :
485 : : int s2n_extensions_client_key_share_recv(struct s2n_connection *conn, struct s2n_stuffer *extension)
486 : 0 : {
487 : 0 : return s2n_extension_recv(&s2n_client_key_share_extension, conn, extension);
488 : 0 : }
|