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