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_server_key_share.h"
17 : :
18 : : #include "crypto/s2n_pq.h"
19 : : #include "tls/s2n_security_policies.h"
20 : : #include "tls/s2n_tls.h"
21 : : #include "tls/s2n_tls13.h"
22 : : #include "utils/s2n_safety.h"
23 : :
24 : : static int s2n_server_key_share_send(struct s2n_connection *conn, struct s2n_stuffer *out);
25 : : static int s2n_server_key_share_recv(struct s2n_connection *conn, struct s2n_stuffer *extension);
26 : :
27 : : const s2n_extension_type s2n_server_key_share_extension = {
28 : : .iana_value = TLS_EXTENSION_KEY_SHARE,
29 : : .minimum_version = S2N_TLS13,
30 : : .is_response = true,
31 : : .send = s2n_server_key_share_send,
32 : : .recv = s2n_server_key_share_recv,
33 : : .should_send = s2n_extension_always_send,
34 : : .if_missing = s2n_extension_noop_if_missing,
35 : : };
36 : :
37 : : static int s2n_server_key_share_send_hybrid_partial_ecc(struct s2n_connection *conn, struct s2n_stuffer *out)
38 : 0 : {
39 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(conn);
40 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(out);
41 : :
42 : 0 : struct s2n_kem_group_params *server_kem_group_params = &conn->kex_params.server_kem_group_params;
43 : 0 : struct s2n_kem_params *client_kem_params = &conn->kex_params.client_kem_group_params.kem_params;
44 : :
45 : 0 : struct s2n_ecc_evp_params *server_ecc_params = &server_kem_group_params->ecc_params;
46 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(server_ecc_params->negotiated_curve);
47 [ # # ]: 0 : if (client_kem_params->len_prefixed) {
48 [ # # ]: 0 : POSIX_GUARD(s2n_stuffer_write_uint16(out, server_ecc_params->negotiated_curve->share_size));
49 : 0 : }
50 [ # # ]: 0 : POSIX_GUARD(s2n_ecc_evp_generate_ephemeral_key(server_ecc_params));
51 [ # # ]: 0 : POSIX_GUARD(s2n_ecc_evp_write_params_point(server_ecc_params, out));
52 : :
53 : 0 : return S2N_SUCCESS;
54 : 0 : }
55 : :
56 : : static int s2n_server_key_share_generate_pq(struct s2n_connection *conn, struct s2n_stuffer *out)
57 : 0 : {
58 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(out);
59 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(conn);
60 : :
61 [ # # ][ # # ]: 0 : POSIX_ENSURE(s2n_pq_is_enabled(), S2N_ERR_UNIMPLEMENTED);
62 : :
63 : 0 : struct s2n_kem_group_params *server_kem_group_params = &conn->kex_params.server_kem_group_params;
64 : 0 : struct s2n_kem_params *client_kem_params = &conn->kex_params.client_kem_group_params.kem_params;
65 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(client_kem_params->public_key.data);
66 : :
67 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(server_kem_group_params->kem_group);
68 [ # # ]: 0 : POSIX_GUARD(s2n_stuffer_write_uint16(out, server_kem_group_params->kem_group->iana_id));
69 : :
70 : 0 : struct s2n_stuffer_reservation total_share_size = { 0 };
71 [ # # ]: 0 : POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &total_share_size));
72 : :
73 : : /* s2n_kem_send_ciphertext() will generate the PQ shared secret and use
74 : : * the client's public key to encapsulate; the PQ shared secret will be
75 : : * stored in client_kem_params, and will be used during the shared
76 : : * secret derivation. */
77 [ # # ]: 0 : if (server_kem_group_params->kem_group->curve == &s2n_ecc_curve_none) { /* Pure PQ */
78 [ # # ]: 0 : POSIX_GUARD(s2n_kem_send_ciphertext(out, client_kem_params));
79 : 0 : } else { /* Hybrid PQ */
80 [ # # ]: 0 : if (server_kem_group_params->kem_group->send_kem_first) {
81 [ # # ]: 0 : POSIX_GUARD(s2n_kem_send_ciphertext(out, client_kem_params));
82 [ # # ]: 0 : POSIX_GUARD(s2n_server_key_share_send_hybrid_partial_ecc(conn, out));
83 : 0 : } else {
84 [ # # ]: 0 : POSIX_GUARD(s2n_server_key_share_send_hybrid_partial_ecc(conn, out));
85 [ # # ]: 0 : POSIX_GUARD(s2n_kem_send_ciphertext(out, client_kem_params));
86 : 0 : }
87 : 0 : }
88 : :
89 [ # # ]: 0 : POSIX_GUARD(s2n_stuffer_write_vector_size(&total_share_size));
90 : 0 : return S2N_SUCCESS;
91 : 0 : }
92 : :
93 : : /* Check that client has sent a corresponding key share for the server's KEM group */
94 : : int s2n_server_key_share_send_check_pq(struct s2n_connection *conn)
95 : 2 : {
96 [ + + ][ + - ]: 2 : POSIX_ENSURE_REF(conn);
97 : :
98 [ + - ][ + - ]: 1 : POSIX_ENSURE(s2n_pq_is_enabled(), S2N_ERR_UNIMPLEMENTED);
99 : :
100 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(conn->kex_params.server_kem_group_params.kem_group);
101 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(conn->kex_params.server_kem_group_params.kem_params.kem);
102 : :
103 : 0 : const struct s2n_kem_preferences *kem_pref = NULL;
104 [ # # ]: 0 : POSIX_GUARD(s2n_connection_get_kem_preferences(conn, &kem_pref));
105 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(kem_pref);
106 : :
107 : 0 : const struct s2n_kem_group *server_kem_group = conn->kex_params.server_kem_group_params.kem_group;
108 [ # # ][ # # ]: 0 : POSIX_ENSURE(s2n_kem_preferences_includes_tls13_kem_group(kem_pref, server_kem_group->iana_id),
109 : 0 : S2N_ERR_KEM_UNSUPPORTED_PARAMS);
110 : :
111 : 0 : struct s2n_kem_group_params *client_params = &conn->kex_params.client_kem_group_params;
112 [ # # ][ # # ]: 0 : POSIX_ENSURE(client_params->kem_group == server_kem_group, S2N_ERR_BAD_KEY_SHARE);
113 : :
114 [ # # ][ # # ]: 0 : POSIX_ENSURE(client_params->kem_params.kem == server_kem_group->kem, S2N_ERR_BAD_KEY_SHARE);
115 [ # # ][ # # ]: 0 : POSIX_ENSURE(client_params->kem_params.public_key.size == server_kem_group->kem->public_key_length, S2N_ERR_BAD_KEY_SHARE);
116 [ # # ][ # # ]: 0 : POSIX_ENSURE(client_params->kem_params.public_key.data != NULL, S2N_ERR_BAD_KEY_SHARE);
117 : :
118 : 0 : return S2N_SUCCESS;
119 : 0 : }
120 : :
121 : : /* Check that client has sent a corresponding key share for the server's EC curve */
122 : : int s2n_server_key_share_send_check_ecdhe(struct s2n_connection *conn)
123 : 4515 : {
124 [ + + ][ + - ]: 4515 : POSIX_ENSURE_REF(conn);
125 : :
126 : 4514 : const struct s2n_ecc_preferences *ecc_pref = NULL;
127 [ - + ]: 4514 : POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_pref));
128 [ # # ][ - + ]: 4514 : POSIX_ENSURE_REF(ecc_pref);
129 : :
130 : 4514 : const struct s2n_ecc_named_curve *server_curve = conn->kex_params.server_ecc_evp_params.negotiated_curve;
131 [ + + ][ + - ]: 4514 : POSIX_ENSURE_REF(server_curve);
132 : :
133 : 4513 : struct s2n_ecc_evp_params *client_params = &conn->kex_params.client_ecc_evp_params;
134 [ + + ][ + - ]: 4513 : POSIX_ENSURE(client_params->negotiated_curve == server_curve, S2N_ERR_BAD_KEY_SHARE);
135 [ + - ][ + + ]: 4511 : POSIX_ENSURE(client_params->evp_pkey != NULL, S2N_ERR_BAD_KEY_SHARE);
136 : :
137 : 4505 : return S2N_SUCCESS;
138 : 4511 : }
139 : :
140 : : static int s2n_server_key_share_send(struct s2n_connection *conn, struct s2n_stuffer *out)
141 : 5187 : {
142 [ + - ][ + + ]: 5187 : POSIX_ENSURE_REF(conn);
143 [ + + ][ + - ]: 5186 : POSIX_ENSURE_REF(out);
144 : :
145 : 5185 : const struct s2n_ecc_named_curve *curve = conn->kex_params.server_ecc_evp_params.negotiated_curve;
146 : 5185 : const struct s2n_kem_group *kem_group = conn->kex_params.server_kem_group_params.kem_group;
147 : :
148 : : /* Boolean XOR: exactly one of {server_curve, server_kem_group} should be non-null. */
149 [ + + ][ + - ]: 5185 : POSIX_ENSURE((curve == NULL) != (kem_group == NULL), S2N_ERR_ECDHE_UNSUPPORTED_CURVE);
150 : :
151 : : /* Retry requests only require the selected named group, not an actual share.
152 : : * https://tools.ietf.org/html/rfc8446#section-4.2.8 */
153 [ + + ]: 5183 : if (s2n_is_hello_retry_message(conn)) {
154 : 674 : uint16_t named_group_id = 0;
155 [ + - ]: 674 : if (curve != NULL) {
156 : 674 : named_group_id = curve->iana_id;
157 : 674 : } else {
158 : 0 : named_group_id = kem_group->iana_id;
159 : 0 : }
160 : :
161 [ - + ]: 674 : POSIX_GUARD(s2n_stuffer_write_uint16(out, named_group_id));
162 : 674 : return S2N_SUCCESS;
163 : 674 : }
164 : :
165 [ + - ]: 4509 : if (curve != NULL) {
166 [ + + ]: 4509 : POSIX_GUARD(s2n_server_key_share_send_check_ecdhe(conn));
167 [ - + ]: 4504 : POSIX_GUARD(s2n_ecdhe_parameters_send(&conn->kex_params.server_ecc_evp_params, out));
168 : 4504 : } else {
169 [ # # ]: 0 : POSIX_GUARD(s2n_server_key_share_send_check_pq(conn));
170 [ # # ]: 0 : POSIX_GUARD(s2n_server_key_share_generate_pq(conn, out));
171 : 0 : }
172 : :
173 : 4504 : return S2N_SUCCESS;
174 : 4509 : }
175 : :
176 : : static int s2n_server_key_share_recv_hybrid_partial_ecc(struct s2n_connection *conn, struct s2n_stuffer *extension)
177 : 0 : {
178 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(conn);
179 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(extension);
180 : :
181 : 0 : struct s2n_kem_params *client_kem_params = &conn->kex_params.client_kem_group_params.kem_params;
182 : 0 : struct s2n_kem_group_params *server_kem_group_params = &conn->kex_params.server_kem_group_params;
183 : 0 : const struct s2n_kem_group *server_kem_group = server_kem_group_params->kem_group;
184 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(server_kem_group);
185 : 0 : uint16_t expected_ecc_share_size = server_kem_group->curve->share_size;
186 : :
187 : : /* Parse ECC key share */
188 [ # # ]: 0 : if (client_kem_params->len_prefixed) {
189 : 0 : uint16_t actual_ecc_share_size = 0;
190 [ # # ]: 0 : POSIX_GUARD(s2n_stuffer_read_uint16(extension, &actual_ecc_share_size));
191 [ # # ][ # # ]: 0 : POSIX_ENSURE(actual_ecc_share_size == expected_ecc_share_size, S2N_ERR_BAD_KEY_SHARE);
192 : 0 : }
193 : :
194 : 0 : struct s2n_blob point_blob = { 0 };
195 [ # # ][ # # ]: 0 : POSIX_ENSURE(s2n_ecc_evp_read_params_point(extension, expected_ecc_share_size, &point_blob) == S2N_SUCCESS, S2N_ERR_BAD_KEY_SHARE);
196 [ # # ][ # # ]: 0 : POSIX_ENSURE(s2n_ecc_evp_parse_params_point(&point_blob, &server_kem_group_params->ecc_params) == S2N_SUCCESS, S2N_ERR_BAD_KEY_SHARE);
197 [ # # ][ # # ]: 0 : POSIX_ENSURE(server_kem_group_params->ecc_params.evp_pkey != NULL, S2N_ERR_BAD_KEY_SHARE);
198 : :
199 : 0 : return S2N_SUCCESS;
200 : 0 : }
201 : :
202 : : static int s2n_server_key_share_recv_pq(struct s2n_connection *conn, uint16_t named_group_iana,
203 : : struct s2n_stuffer *extension)
204 : 1 : {
205 [ - + ][ # # ]: 1 : POSIX_ENSURE_REF(conn);
206 [ # # ][ - + ]: 1 : POSIX_ENSURE_REF(extension);
207 : :
208 : : /* If PQ is disabled, the client should not have sent any PQ IDs
209 : : * in the supported_groups list of the initial ClientHello */
210 [ + - ][ + - ]: 1 : POSIX_ENSURE(s2n_pq_is_enabled(), S2N_ERR_ECDHE_UNSUPPORTED_CURVE);
211 : :
212 : 0 : const struct s2n_kem_preferences *kem_pref = NULL;
213 [ # # ]: 0 : POSIX_GUARD(s2n_connection_get_kem_preferences(conn, &kem_pref));
214 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(kem_pref);
215 : :
216 : : /* This check should have been done higher up, but including it here as well for extra defense.
217 : : * Uses S2N_ERR_ECDHE_UNSUPPORTED_CURVE for backward compatibility. */
218 [ # # ][ # # ]: 0 : POSIX_ENSURE(s2n_kem_preferences_includes_tls13_kem_group(kem_pref, named_group_iana), S2N_ERR_ECDHE_UNSUPPORTED_CURVE);
219 : :
220 : 0 : size_t kem_group_index = 0;
221 [ # # ]: 0 : for (size_t i = 0; i < kem_pref->tls13_kem_group_count; i++) {
222 [ # # ]: 0 : if (named_group_iana == kem_pref->tls13_kem_groups[i]->iana_id
223 [ # # ]: 0 : && s2n_kem_group_is_available(kem_pref->tls13_kem_groups[i])) {
224 : 0 : kem_group_index = i;
225 : 0 : break;
226 : 0 : }
227 : 0 : }
228 : :
229 : 0 : struct s2n_kem_group_params *server_kem_group_params = &conn->kex_params.server_kem_group_params;
230 : 0 : server_kem_group_params->kem_group = kem_pref->tls13_kem_groups[kem_group_index];
231 : 0 : server_kem_group_params->kem_params.kem = kem_pref->tls13_kem_groups[kem_group_index]->kem;
232 : 0 : server_kem_group_params->ecc_params.negotiated_curve = kem_pref->tls13_kem_groups[kem_group_index]->curve;
233 : :
234 : : /* If this a HRR, the server will only have sent the named group ID. We assign the
235 : : * appropriate KEM group params above, then exit early so that the client can
236 : : * generate the correct key share. */
237 [ # # ]: 0 : if (s2n_is_hello_retry_message(conn)) {
238 : 0 : return S2N_SUCCESS;
239 : 0 : }
240 : :
241 : : /* Ensure that the server's key share corresponds with a key share previously sent by the client */
242 : 0 : struct s2n_kem_group_params *client_kem_group_params = &conn->kex_params.client_kem_group_params;
243 [ # # ][ # # ]: 0 : POSIX_ENSURE(client_kem_group_params->kem_params.private_key.data, S2N_ERR_BAD_KEY_SHARE);
244 [ # # ][ # # ]: 0 : POSIX_ENSURE(client_kem_group_params->kem_group == server_kem_group_params->kem_group, S2N_ERR_BAD_KEY_SHARE);
245 : :
246 : 0 : uint16_t actual_hybrid_share_size = 0;
247 [ # # ]: 0 : POSIX_GUARD(s2n_stuffer_read_uint16(extension, &actual_hybrid_share_size));
248 [ # # ][ # # ]: 0 : POSIX_ENSURE(s2n_stuffer_data_available(extension) == actual_hybrid_share_size, S2N_ERR_BAD_KEY_SHARE);
249 : :
250 : 0 : struct s2n_kem_params *client_kem_params = &conn->kex_params.client_kem_group_params.kem_params;
251 : :
252 : : /* Don't need to set client_kem_params->len_prefixed since we are the client;
253 : : * server-side should auto-detect hybrid share size and match our behavior. */
254 : :
255 [ # # ]: 0 : if (server_kem_group_params->kem_group->curve == &s2n_ecc_curve_none) { /* Pure PQ */
256 [ # # ][ # # ]: 0 : POSIX_ENSURE(s2n_kem_recv_ciphertext(extension, client_kem_params) == S2N_SUCCESS, S2N_ERR_BAD_KEY_SHARE);
257 : 0 : } else { /* Hybrid PQ */
258 [ # # ]: 0 : if (!server_kem_group_params->kem_group->send_kem_first) {
259 [ # # ][ # # ]: 0 : POSIX_ENSURE(s2n_server_key_share_recv_hybrid_partial_ecc(conn, extension) == S2N_SUCCESS, S2N_ERR_BAD_KEY_SHARE);
260 [ # # ][ # # ]: 0 : POSIX_ENSURE(s2n_kem_recv_ciphertext(extension, client_kem_params) == S2N_SUCCESS, S2N_ERR_BAD_KEY_SHARE);
261 : 0 : } else {
262 [ # # ][ # # ]: 0 : POSIX_ENSURE(s2n_kem_recv_ciphertext(extension, client_kem_params) == S2N_SUCCESS, S2N_ERR_BAD_KEY_SHARE);
263 [ # # ][ # # ]: 0 : POSIX_ENSURE(s2n_server_key_share_recv_hybrid_partial_ecc(conn, extension) == S2N_SUCCESS, S2N_ERR_BAD_KEY_SHARE);
264 : 0 : }
265 : 0 : }
266 : :
267 : 0 : return S2N_SUCCESS;
268 : 0 : }
269 : :
270 : : static int s2n_server_key_share_recv_ecc(struct s2n_connection *conn, uint16_t named_group_iana,
271 : : struct s2n_stuffer *extension)
272 : 5148 : {
273 [ - + ][ # # ]: 5148 : POSIX_ENSURE_REF(conn);
274 [ - + ][ # # ]: 5148 : POSIX_ENSURE_REF(extension);
275 : :
276 : 5148 : const struct s2n_ecc_preferences *ecc_pref = NULL;
277 [ - + ]: 5148 : POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_pref));
278 [ # # ][ - + ]: 5148 : POSIX_ENSURE_REF(ecc_pref);
279 : :
280 : : /* This check should have been done higher up, but including it here as well for extra defense. */
281 [ # # ][ - + ]: 5148 : POSIX_ENSURE(s2n_ecc_preferences_includes_curve(ecc_pref, named_group_iana),
282 : 5148 : S2N_ERR_ECDHE_UNSUPPORTED_CURVE);
283 : :
284 : 5148 : size_t supported_curve_index = 0;
285 : :
286 [ + - ]: 7683 : for (size_t i = 0; i < ecc_pref->count; i++) {
287 [ + + ]: 7683 : if (named_group_iana == ecc_pref->ecc_curves[i]->iana_id) {
288 : 5148 : supported_curve_index = i;
289 : 5148 : break;
290 : 5148 : }
291 : 7683 : }
292 : :
293 : 5148 : struct s2n_ecc_evp_params *server_ecc_evp_params = &conn->kex_params.server_ecc_evp_params;
294 : 5148 : const struct s2n_ecc_named_curve *negotiated_curve = ecc_pref->ecc_curves[supported_curve_index];
295 : :
296 : : /**
297 : : *= https://www.rfc-editor.org/rfc/rfc8446#4.2.8
298 : : *# If using (EC)DHE key establishment and a HelloRetryRequest containing a
299 : : *# "key_share" extension was received by the client, the client MUST
300 : : *# verify that the selected NamedGroup in the ServerHello is the same as
301 : : *# that in the HelloRetryRequest. If this check fails, the client MUST
302 : : *# abort the handshake with an "illegal_parameter" alert.
303 : : **/
304 [ + + ][ + + ]: 5148 : if (s2n_is_hello_retry_handshake(conn) && !s2n_is_hello_retry_message(conn)) {
305 [ - + ][ # # ]: 636 : POSIX_ENSURE_REF(server_ecc_evp_params->negotiated_curve);
306 : 636 : const struct s2n_ecc_named_curve *previous_negotiated_curve = server_ecc_evp_params->negotiated_curve;
307 [ + + ][ + - ]: 636 : POSIX_ENSURE(negotiated_curve == previous_negotiated_curve,
308 : 636 : S2N_ERR_BAD_MESSAGE);
309 : 636 : }
310 : :
311 : 5147 : server_ecc_evp_params->negotiated_curve = negotiated_curve;
312 : :
313 : : /* Now that ECC has been negotiated, null out this connection's preferred Hybrid KEMs. They will not be used any
314 : : * more during this TLS connection, but can still be printed by s2nc's client debugging output. */
315 : 5147 : conn->kex_params.client_kem_group_params.kem_group = NULL;
316 : 5147 : conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve = NULL;
317 : 5147 : conn->kex_params.client_kem_group_params.kem_params.kem = NULL;
318 : :
319 : : /* If this is a HelloRetryRequest, we won't have a key share. We just have the selected group.
320 : : * Set the server negotiated curve and exit early so a proper keyshare can be generated. */
321 [ + + ]: 5147 : if (s2n_is_hello_retry_message(conn)) {
322 : 667 : return S2N_SUCCESS;
323 : 667 : }
324 : :
325 : : /* Verify key share sent by client */
326 : 4480 : struct s2n_ecc_evp_params *client_ecc_evp_params = &conn->kex_params.client_ecc_evp_params;
327 [ + + ][ + - ]: 4480 : POSIX_ENSURE(client_ecc_evp_params->negotiated_curve == server_ecc_evp_params->negotiated_curve, S2N_ERR_BAD_KEY_SHARE);
328 [ - + ][ # # ]: 4478 : POSIX_ENSURE(client_ecc_evp_params->evp_pkey, S2N_ERR_BAD_KEY_SHARE);
329 : :
330 : 4478 : uint16_t share_size = 0;
331 [ - + ][ # # ]: 4478 : S2N_ERROR_IF(s2n_stuffer_data_available(extension) < sizeof(share_size), S2N_ERR_BAD_KEY_SHARE);
332 [ - + ]: 4478 : POSIX_GUARD(s2n_stuffer_read_uint16(extension, &share_size));
333 [ - + ][ # # ]: 4478 : S2N_ERROR_IF(s2n_stuffer_data_available(extension) < share_size, S2N_ERR_BAD_KEY_SHARE);
334 : :
335 : : /* Proceed to parse share */
336 : 4478 : struct s2n_blob point_blob = { 0 };
337 [ - + ][ # # ]: 4478 : S2N_ERROR_IF(s2n_ecc_evp_read_params_point(extension, share_size, &point_blob) < 0, S2N_ERR_BAD_KEY_SHARE);
338 [ - + ][ # # ]: 4478 : S2N_ERROR_IF(s2n_ecc_evp_parse_params_point(&point_blob, server_ecc_evp_params) < 0, S2N_ERR_BAD_KEY_SHARE);
339 [ - + ][ # # ]: 4478 : S2N_ERROR_IF(server_ecc_evp_params->evp_pkey == NULL, S2N_ERR_BAD_KEY_SHARE);
340 : :
341 : 4478 : return S2N_SUCCESS;
342 : 4478 : }
343 : :
344 : : /*
345 : : * From https://tools.ietf.org/html/rfc8446#section-4.2.8
346 : : *
347 : : * If using (EC)DHE key establishment, servers offer exactly one
348 : : * KeyShareEntry in the ServerHello. This value MUST be in the same
349 : : * group as the KeyShareEntry value offered by the client that the
350 : : * server has selected for the negotiated key exchange.
351 : : */
352 : : static int s2n_server_key_share_recv(struct s2n_connection *conn, struct s2n_stuffer *extension)
353 : 5151 : {
354 [ - + ][ # # ]: 5151 : POSIX_ENSURE_REF(conn);
355 [ # # ][ - + ]: 5151 : POSIX_ENSURE_REF(extension);
356 : :
357 : 5151 : uint16_t negotiated_named_group_iana = 0;
358 [ - + ][ # # ]: 5151 : S2N_ERROR_IF(s2n_stuffer_data_available(extension) < sizeof(negotiated_named_group_iana), S2N_ERR_BAD_KEY_SHARE);
359 [ - + ]: 5151 : POSIX_GUARD(s2n_stuffer_read_uint16(extension, &negotiated_named_group_iana));
360 : :
361 : 5151 : const struct s2n_kem_preferences *kem_pref = NULL;
362 [ - + ]: 5151 : POSIX_GUARD(s2n_connection_get_kem_preferences(conn, &kem_pref));
363 [ # # ][ - + ]: 5151 : POSIX_ENSURE_REF(kem_pref);
364 : :
365 : 5151 : const struct s2n_ecc_preferences *ecc_pref = NULL;
366 [ - + ]: 5151 : POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_pref));
367 [ - + ][ # # ]: 5151 : POSIX_ENSURE_REF(ecc_pref);
368 : :
369 [ + + ]: 5151 : if (s2n_ecc_preferences_includes_curve(ecc_pref, negotiated_named_group_iana)) {
370 [ + + ]: 5148 : POSIX_GUARD(s2n_server_key_share_recv_ecc(conn, negotiated_named_group_iana, extension));
371 [ + + ]: 5148 : } else if (s2n_kem_preferences_includes_tls13_kem_group(kem_pref, negotiated_named_group_iana)) {
372 [ + - ]: 1 : POSIX_GUARD(s2n_server_key_share_recv_pq(conn, negotiated_named_group_iana, extension));
373 : 2 : } else {
374 [ + - ]: 2 : POSIX_BAIL(S2N_ERR_ECDHE_UNSUPPORTED_CURVE);
375 : 2 : }
376 : :
377 : 5145 : return S2N_SUCCESS;
378 : 5151 : }
379 : :
380 : : /* Selects highest priority mutually supported key share, or indicates need for HRR */
381 : : int s2n_extensions_server_key_share_select(struct s2n_connection *conn)
382 : 5186 : {
383 [ # # ][ - + ]: 5186 : POSIX_ENSURE_REF(conn);
384 : :
385 : : /* Get the client's preferred groups for the KeyShares that were actually sent by the client */
386 : 5186 : const struct s2n_ecc_named_curve *client_curve = conn->kex_params.client_ecc_evp_params.negotiated_curve;
387 : 5186 : const struct s2n_kem_group *client_kem_group = conn->kex_params.client_kem_group_params.kem_group;
388 : :
389 : : /* Get the server's preferred groups (which may or may not have been sent in the KeyShare by the client) */
390 : 5186 : const struct s2n_ecc_named_curve *server_curve = conn->kex_params.server_ecc_evp_params.negotiated_curve;
391 : 5186 : const struct s2n_kem_group *server_kem_group = conn->kex_params.server_kem_group_params.kem_group;
392 : :
393 : : /* Boolean XOR check. When receiving the supported_groups extension, s2n server
394 : : * should (exclusively) set either server_curve or server_kem_group based on the
395 : : * set of mutually supported groups. If both server_curve and server_kem_group
396 : : * are NULL, it is because client and server do not share any mutually supported
397 : : * groups; key negotiation is not possible and the handshake should be aborted
398 : : * without sending HRR. (The case of both being non-NULL should never occur, and
399 : : * is an error.) */
400 [ # # ][ - + ]: 5186 : POSIX_ENSURE((server_curve == NULL) != (server_kem_group == NULL), S2N_ERR_ECDHE_UNSUPPORTED_CURVE);
401 : :
402 : : /* To avoid extra round trips, we prefer to negotiate a group for which we have already
403 : : * received a key share (even if it is different than the group previously chosen). In
404 : : * general, we prefer to negotiate PQ over ECDHE; however, if both client and server
405 : : * support PQ, but the client sent only EC key shares, then we will negotiate ECHDE. */
406 : :
407 : : /* Option 1: Select the best mutually supported PQ KEM Group that can be negotiated in 1-RTT */
408 [ - + ]: 5186 : if (client_kem_group != NULL) {
409 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(conn->kex_params.client_kem_group_params.kem_params.kem);
410 : :
411 : 0 : conn->kex_params.server_kem_group_params.kem_group = conn->kex_params.client_kem_group_params.kem_group;
412 : 0 : conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve = conn->kex_params.client_kem_group_params.ecc_params.negotiated_curve;
413 : 0 : conn->kex_params.server_kem_group_params.kem_params.kem = conn->kex_params.client_kem_group_params.kem_params.kem;
414 : 0 : conn->kex_params.server_ecc_evp_params.negotiated_curve = NULL;
415 : 0 : return S2N_SUCCESS;
416 : 0 : }
417 : :
418 : : /* Option 2: Otherwise, if any PQ Hybrid Groups can be negotiated in 2-RTT's select that one. This ensures that
419 : : * clients who offer PQ (and presumably therefore have concerns about quantum computing impacting the long term
420 : : * confidentiality of their data), have their choice to offer PQ respected, even if they predict the server-side
421 : : * supports a different PQ KeyShare algorithms. This ensures clients with PQ support are never downgraded to non-PQ
422 : : * algorithms. */
423 [ - + ]: 5186 : if (server_kem_group != NULL) {
424 : : /* Null out any available ECC curves so that they won't be sent in the ClientHelloRetry */
425 : 0 : conn->kex_params.server_ecc_evp_params.negotiated_curve = NULL;
426 [ # # ]: 0 : POSIX_GUARD(s2n_set_hello_retry_required(conn));
427 : 0 : return S2N_SUCCESS;
428 : 0 : }
429 : :
430 : : /* Option 3: Otherwise, if there is a mutually supported classical ECDHE-only group can be negotiated in 1-RTT, select that one */
431 [ + + ]: 5186 : if (client_curve) {
432 : 4521 : conn->kex_params.server_ecc_evp_params.negotiated_curve = conn->kex_params.client_ecc_evp_params.negotiated_curve;
433 : 4521 : conn->kex_params.server_kem_group_params.kem_group = NULL;
434 : 4521 : conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve = NULL;
435 : 4521 : conn->kex_params.server_kem_group_params.kem_params.kem = NULL;
436 : 4521 : return S2N_SUCCESS;
437 : 4521 : }
438 : :
439 : : /* Option 4: Server and client have at least 1 mutually supported group, but the client did not send key shares for
440 : : * any of them. Send a HelloRetryRequest indicating the server's preference. */
441 [ - + ]: 665 : POSIX_GUARD(s2n_set_hello_retry_required(conn));
442 : 665 : return S2N_SUCCESS;
443 : 665 : }
444 : :
445 : : /* Old-style extension functions -- remove after extensions refactor is complete */
446 : :
447 : : /*
448 : : * Calculate the data length for Server Key Share extension
449 : : * based on negotiated_curve selected in server_ecc_evp_params.
450 : : *
451 : : * Retry requests have a different key share format,
452 : : * https://tools.ietf.org/html/rfc8446#section-4.2.8
453 : : *
454 : : * This functions does not error, but s2n_extensions_server_key_share_send() would
455 : : */
456 : : int s2n_extensions_server_key_share_send_size(struct s2n_connection *conn)
457 : 11 : {
458 : 11 : const struct s2n_ecc_named_curve *curve = conn->kex_params.server_ecc_evp_params.negotiated_curve;
459 : 11 : int key_share_size = S2N_SIZE_OF_EXTENSION_TYPE
460 : 11 : + S2N_SIZE_OF_EXTENSION_DATA_SIZE
461 : 11 : + S2N_SIZE_OF_NAMED_GROUP;
462 : :
463 : : /* If this is a KeyShareHelloRetryRequest we don't include the share size */
464 [ + + ]: 11 : if (s2n_is_hello_retry_message(conn)) {
465 : 3 : return key_share_size;
466 : 3 : }
467 : :
468 [ + + ]: 8 : if (curve == NULL) {
469 : 2 : return 0;
470 : 2 : }
471 : :
472 : : /* If this is a full KeyShareEntry, include the share size */
473 : 6 : key_share_size += (S2N_SIZE_OF_KEY_SHARE_SIZE + curve->share_size);
474 : :
475 : 6 : return key_share_size;
476 : 8 : }
477 : :
478 : : /*
479 : : * Sends Key Share extension in Server Hello.
480 : : *
481 : : * Expects negotiated_curve to be set and generates a ephemeral key for key sharing
482 : : */
483 : : int s2n_extensions_server_key_share_send(struct s2n_connection *conn, struct s2n_stuffer *out)
484 : 1 : {
485 : 1 : return s2n_extension_send(&s2n_server_key_share_extension, conn, out);
486 : 1 : }
487 : :
488 : : /*
489 : : * Client receives a Server Hello key share.
490 : : *
491 : : * If the curve is supported, conn->kex_params.server_ecc_evp_params will be set.
492 : : */
493 : : int s2n_extensions_server_key_share_recv(struct s2n_connection *conn, struct s2n_stuffer *extension)
494 : 1 : {
495 : 1 : return s2n_extension_recv(&s2n_server_key_share_extension, conn, extension);
496 : 1 : }
|