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 : : #include "tls/s2n_resume.h"
16 : :
17 : : #include <math.h>
18 : :
19 : : #include "api/s2n.h"
20 : : #include "error/s2n_errno.h"
21 : : #include "stuffer/s2n_stuffer.h"
22 : : #include "tls/s2n_cipher_suites.h"
23 : : #include "tls/s2n_connection.h"
24 : : #include "tls/s2n_crypto.h"
25 : : #include "tls/s2n_tls.h"
26 : : #include "utils/s2n_blob.h"
27 : : #include "utils/s2n_random.h"
28 : : #include "utils/s2n_safety.h"
29 : :
30 : : int s2n_allowed_to_cache_connection(struct s2n_connection *conn)
31 : 12648 : {
32 : : /* We're unable to cache connections with a Client Cert since we currently don't serialize the Client Cert,
33 : : * which means that callers won't have access to the Client's Cert if the connection is resumed. */
34 [ + + ]: 12648 : if (s2n_connection_is_client_auth_enabled(conn)) {
35 : 4807 : return 0;
36 : 4807 : }
37 : :
38 : 7841 : struct s2n_config *config = conn->config;
39 : :
40 [ - + ][ # # ]: 7841 : POSIX_ENSURE_REF(config);
41 : 7841 : return config->use_session_cache;
42 : 7841 : }
43 : :
44 : : /* If a protocol version is required before the actual_protocol_version
45 : : * is negotiated, we should fall back to resume_protocol_version if available.
46 : : *
47 : : * This covers the case where the application requests a ticket / session state
48 : : * before a NewSessionTicket message has been sent or received. Historically,
49 : : * in that case we return the ticket / session state already set for the connection.
50 : : * resume_protocol_version represents the protocol version of that existing ticket / state.
51 : : */
52 : : static uint8_t s2n_resume_protocol_version(struct s2n_connection *conn)
53 : 976 : {
54 [ + + ][ - + ]: 976 : if (!IS_NEGOTIATED(conn) && conn->resume_protocol_version) {
55 : 0 : return conn->resume_protocol_version;
56 : 976 : } else {
57 : 976 : return conn->actual_protocol_version;
58 : 976 : }
59 : 976 : }
60 : :
61 : : static int s2n_tls12_serialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *to)
62 : 41 : {
63 [ # # ][ - + ]: 41 : POSIX_ENSURE_REF(to);
64 [ - + ][ # # ]: 41 : POSIX_ENSURE_REF(conn);
65 [ # # ][ - + ]: 41 : POSIX_ENSURE_REF(conn->secure);
66 : :
67 : 41 : uint64_t now = 0;
68 : :
69 [ - + ][ # # ]: 41 : S2N_ERROR_IF(s2n_stuffer_space_remaining(to) < S2N_TLS12_STATE_SIZE_IN_BYTES, S2N_ERR_STUFFER_IS_FULL);
70 : :
71 : : /* Get the time */
72 [ - + ]: 41 : POSIX_GUARD_RESULT(s2n_config_wall_clock(conn->config, &now));
73 : :
74 : : /* Write the entry */
75 [ - + ]: 41 : POSIX_GUARD(s2n_stuffer_write_uint8(to, S2N_SERIALIZED_FORMAT_TLS12_V3));
76 [ - + ]: 41 : POSIX_GUARD(s2n_stuffer_write_uint8(to, s2n_resume_protocol_version(conn)));
77 [ - + ]: 41 : POSIX_GUARD(s2n_stuffer_write_bytes(to, conn->secure->cipher_suite->iana_value, S2N_TLS_CIPHER_SUITE_LEN));
78 [ - + ]: 41 : POSIX_GUARD(s2n_stuffer_write_uint64(to, now));
79 [ - + ]: 41 : POSIX_GUARD(s2n_stuffer_write_bytes(to, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN));
80 [ - + ]: 41 : POSIX_GUARD(s2n_stuffer_write_uint8(to, conn->ems_negotiated));
81 : :
82 : 41 : return S2N_SUCCESS;
83 : 41 : }
84 : :
85 : : static S2N_RESULT s2n_tls13_serialize_keying_material_expiration(struct s2n_connection *conn,
86 : : uint64_t now, struct s2n_stuffer *out)
87 : 396 : {
88 [ # # ][ - + ]: 396 : RESULT_ENSURE_REF(conn);
89 [ # # ][ - + ]: 396 : RESULT_ENSURE_REF(out);
90 : :
91 [ + + ]: 396 : if (conn->mode != S2N_SERVER) {
92 : 216 : return S2N_RESULT_OK;
93 : 216 : }
94 : :
95 : 180 : uint64_t expiration_timestamp = now + (conn->server_keying_material_lifetime * (uint64_t) ONE_SEC_IN_NANOS);
96 : :
97 : 180 : struct s2n_psk *chosen_psk = conn->psk_params.chosen_psk;
98 [ + + ][ + - ]: 180 : if (chosen_psk && chosen_psk->type == S2N_PSK_TYPE_RESUMPTION) {
99 [ + - ]: 5 : expiration_timestamp = S2N_MIN(chosen_psk->keying_material_expiration, expiration_timestamp);
100 : 5 : }
101 : :
102 [ - + ]: 180 : RESULT_GUARD_POSIX(s2n_stuffer_write_uint64(out, expiration_timestamp));
103 : 180 : return S2N_RESULT_OK;
104 : 180 : }
105 : :
106 : : static S2N_RESULT s2n_tls13_serialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *out)
107 : 396 : {
108 [ - + ][ # # ]: 396 : RESULT_ENSURE_REF(out);
109 [ # # ][ - + ]: 396 : RESULT_ENSURE_REF(conn);
110 [ - + ][ # # ]: 396 : RESULT_ENSURE_REF(conn->secure);
111 : :
112 : 396 : uint64_t current_time = 0;
113 : 396 : struct s2n_ticket_fields *ticket_fields = &conn->tls13_ticket_fields;
114 : :
115 : : /* Get the time */
116 [ - + ]: 396 : RESULT_GUARD(s2n_config_wall_clock(conn->config, ¤t_time));
117 : :
118 [ - + ]: 396 : RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, S2N_SERIALIZED_FORMAT_TLS13_V1));
119 [ - + ]: 396 : RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, conn->actual_protocol_version));
120 [ - + ]: 396 : RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(out, conn->secure->cipher_suite->iana_value, S2N_TLS_CIPHER_SUITE_LEN));
121 [ - + ]: 396 : RESULT_GUARD_POSIX(s2n_stuffer_write_uint64(out, current_time));
122 [ - + ]: 396 : RESULT_GUARD_POSIX(s2n_stuffer_write_uint32(out, ticket_fields->ticket_age_add));
123 [ # # ][ - + ]: 396 : RESULT_ENSURE_INCLUSIVE_RANGE(1, ticket_fields->session_secret.size, UINT8_MAX);
[ # # ][ - + ]
124 [ - + ]: 396 : RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, ticket_fields->session_secret.size));
125 [ - + ]: 396 : RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(out, ticket_fields->session_secret.data, ticket_fields->session_secret.size));
126 [ - + ]: 396 : RESULT_GUARD(s2n_tls13_serialize_keying_material_expiration(conn, current_time, out));
127 : :
128 : 396 : uint32_t server_max_early_data = 0;
129 [ - + ]: 396 : RESULT_GUARD(s2n_early_data_get_server_max_size(conn, &server_max_early_data));
130 [ - + ]: 396 : RESULT_GUARD_POSIX(s2n_stuffer_write_uint32(out, server_max_early_data));
131 [ + + ]: 396 : if (server_max_early_data > 0) {
132 : 81 : uint8_t application_protocol_len = strlen(conn->application_protocol);
133 [ - + ]: 81 : RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, application_protocol_len));
134 [ - + ]: 81 : RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(out, (uint8_t *) conn->application_protocol, application_protocol_len));
135 [ - + ]: 81 : RESULT_GUARD_POSIX(s2n_stuffer_write_uint16(out, conn->server_early_data_context.size));
136 [ - + ]: 81 : RESULT_GUARD_POSIX(s2n_stuffer_write(out, &conn->server_early_data_context));
137 : 81 : }
138 : :
139 : 396 : return S2N_RESULT_OK;
140 : 396 : }
141 : :
142 : : static S2N_RESULT s2n_serialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *out)
143 : 437 : {
144 [ + + ]: 437 : if (s2n_resume_protocol_version(conn) < S2N_TLS13) {
145 [ - + ]: 41 : RESULT_GUARD_POSIX(s2n_tls12_serialize_resumption_state(conn, out));
146 : 396 : } else {
147 [ - + ]: 396 : RESULT_GUARD(s2n_tls13_serialize_resumption_state(conn, out));
148 : 396 : }
149 : 437 : return S2N_RESULT_OK;
150 : 437 : }
151 : :
152 : : static int s2n_tls12_deserialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *from)
153 : 11 : {
154 [ # # ][ - + ]: 11 : POSIX_ENSURE_REF(conn);
155 [ - + ][ # # ]: 11 : POSIX_ENSURE_REF(conn->secure);
156 : :
157 : 11 : uint8_t protocol_version = 0;
158 : 11 : uint8_t cipher_suite[S2N_TLS_CIPHER_SUITE_LEN] = { 0 };
159 : :
160 [ - + ][ # # ]: 11 : S2N_ERROR_IF(s2n_stuffer_data_available(from) < S2N_TLS12_STATE_SIZE_IN_BYTES - sizeof(uint8_t), S2N_ERR_STUFFER_OUT_OF_DATA);
161 : :
162 [ - + ]: 11 : POSIX_GUARD(s2n_stuffer_read_uint8(from, &protocol_version));
163 [ - + ][ # # ]: 11 : S2N_ERROR_IF(protocol_version != conn->actual_protocol_version, S2N_ERR_INVALID_SERIALIZED_SESSION_STATE);
164 : :
165 [ - + ]: 11 : POSIX_GUARD(s2n_stuffer_read_bytes(from, cipher_suite, S2N_TLS_CIPHER_SUITE_LEN));
166 [ # # ][ - + ]: 11 : POSIX_ENSURE(s2n_constant_time_equals(conn->secure->cipher_suite->iana_value, cipher_suite, S2N_TLS_CIPHER_SUITE_LEN), S2N_ERR_INVALID_SERIALIZED_SESSION_STATE);
167 : :
168 : 11 : uint64_t now = 0;
169 [ - + ]: 11 : POSIX_GUARD_RESULT(s2n_config_wall_clock(conn->config, &now));
170 : :
171 : 11 : uint64_t then = 0;
172 [ - + ]: 11 : POSIX_GUARD(s2n_stuffer_read_uint64(from, &then));
173 [ - + ][ # # ]: 11 : S2N_ERROR_IF(then > now, S2N_ERR_INVALID_SERIALIZED_SESSION_STATE);
174 [ - + ][ # # ]: 11 : S2N_ERROR_IF(now - then > conn->config->session_state_lifetime_in_nanos, S2N_ERR_INVALID_SERIALIZED_SESSION_STATE);
175 : :
176 [ - + ]: 11 : POSIX_GUARD(s2n_stuffer_read_bytes(from, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN));
177 : :
178 [ + - ]: 11 : if (s2n_stuffer_data_available(from)) {
179 : 11 : uint8_t ems_negotiated = 0;
180 [ - + ]: 11 : POSIX_GUARD(s2n_stuffer_read_uint8(from, &ems_negotiated));
181 : :
182 : : /**
183 : : *= https://www.rfc-editor.org/rfc/rfc7627#section-5.3
184 : : *# o If the original session did not use the "extended_master_secret"
185 : : *# extension but the new ClientHello contains the extension, then the
186 : : *# server MUST NOT perform the abbreviated handshake. Instead, it
187 : : *# SHOULD continue with a full handshake (as described in
188 : : *# Section 5.2) to negotiate a new session.
189 : : *#
190 : : *# o If the original session used the "extended_master_secret"
191 : : *# extension but the new ClientHello does not contain it, the server
192 : : *# MUST abort the abbreviated handshake.
193 : : **/
194 [ + + ]: 11 : if (conn->ems_negotiated != ems_negotiated) {
195 : : /* The session ticket needs to have the same EMS state as the current session. If it doesn't
196 : : * have the same state, the current session takes the state of the session ticket and errors.
197 : : * If the deserialization process errors, we will use this state in a few extra checks
198 : : * to determine if we can fallback to a full handshake.
199 : : */
200 : 4 : conn->ems_negotiated = ems_negotiated;
201 [ + - ]: 4 : POSIX_BAIL(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE);
202 : 4 : }
203 : 11 : }
204 : :
205 : 7 : return S2N_SUCCESS;
206 : 11 : }
207 : :
208 : : static int s2n_client_serialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *to)
209 : 227 : {
210 : : /* Serialize session ticket */
211 [ + + ][ + + ]: 227 : if (conn->config->use_tickets && conn->client_ticket.size > 0) {
212 [ - + ]: 222 : POSIX_GUARD(s2n_stuffer_write_uint8(to, S2N_STATE_WITH_SESSION_TICKET));
213 [ - + ]: 222 : POSIX_GUARD(s2n_stuffer_write_uint16(to, conn->client_ticket.size));
214 [ - + ]: 222 : POSIX_GUARD(s2n_stuffer_write(to, &conn->client_ticket));
215 : 222 : } else {
216 : : /* Serialize session id */
217 [ - + ][ # # ]: 5 : POSIX_ENSURE_LT(conn->actual_protocol_version, S2N_TLS13);
218 [ - + ]: 5 : POSIX_GUARD(s2n_stuffer_write_uint8(to, S2N_STATE_WITH_SESSION_ID));
219 [ - + ]: 5 : POSIX_GUARD(s2n_stuffer_write_uint8(to, conn->session_id_len));
220 [ - + ]: 5 : POSIX_GUARD(s2n_stuffer_write_bytes(to, conn->session_id, conn->session_id_len));
221 : 5 : }
222 : :
223 : : /* Serialize session state */
224 [ - + ]: 227 : POSIX_GUARD_RESULT(s2n_serialize_resumption_state(conn, to));
225 : :
226 : 227 : return 0;
227 : 227 : }
228 : :
229 : : static S2N_RESULT s2n_tls12_client_deserialize_session_state(struct s2n_connection *conn,
230 : : struct s2n_blob *ticket, struct s2n_stuffer *from)
231 : 13 : {
232 [ - + ][ # # ]: 13 : RESULT_ENSURE_REF(conn);
233 [ # # ][ - + ]: 13 : RESULT_ENSURE_REF(from);
234 : :
235 : : /* Operate on a copy of the connection to avoid mutating the connection on
236 : : * failure. We have tests in s2n_resume_test.c that prove this level of copy
237 : : * is sufficient.
238 : : */
239 : 13 : struct s2n_crypto_parameters *secure = conn->secure;
240 [ - + ][ # # ]: 13 : RESULT_ENSURE_REF(secure);
241 : 13 : struct s2n_connection temp_conn = *conn;
242 : 13 : struct s2n_crypto_parameters temp_secure = *secure;
243 : 13 : temp_conn.secure = &temp_secure;
244 : :
245 [ - + ]: 13 : RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &temp_conn.resume_protocol_version));
246 : :
247 : 13 : uint8_t *cipher_suite_wire = s2n_stuffer_raw_read(from, S2N_TLS_CIPHER_SUITE_LEN);
248 [ - + ][ # # ]: 13 : RESULT_ENSURE_REF(cipher_suite_wire);
249 [ - + ]: 13 : RESULT_GUARD_POSIX(s2n_set_cipher_as_client(&temp_conn, cipher_suite_wire));
250 : :
251 : 13 : uint64_t then = 0;
252 [ - + ]: 13 : RESULT_GUARD_POSIX(s2n_stuffer_read_uint64(from, &then));
253 : :
254 [ - + ]: 13 : RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(from, temp_conn.secrets.version.tls12.master_secret,
255 : 13 : S2N_TLS_SECRET_LEN));
256 : :
257 [ + - ]: 13 : if (s2n_stuffer_data_available(from)) {
258 : 13 : uint8_t ems_negotiated = 0;
259 [ - + ]: 13 : RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &ems_negotiated));
260 : 13 : temp_conn.ems_negotiated = ems_negotiated;
261 : 13 : }
262 : :
263 : 13 : DEFER_CLEANUP(struct s2n_blob client_ticket = { 0 }, s2n_free);
264 [ + + ]: 13 : if (ticket) {
265 [ - + ]: 8 : RESULT_GUARD_POSIX(s2n_dup(ticket, &client_ticket));
266 : 8 : }
267 : :
268 : : /* Finally, actually update the connection */
269 [ - + ]: 13 : RESULT_GUARD_POSIX(s2n_free(&conn->client_ticket));
270 : 13 : *secure = temp_secure;
271 : 13 : *conn = temp_conn;
272 : 13 : conn->secure = secure;
273 : 13 : conn->client_ticket = client_ticket;
274 : 13 : ZERO_TO_DISABLE_DEFER_CLEANUP(client_ticket);
275 : :
276 : 13 : return S2N_RESULT_OK;
277 : 13 : }
278 : :
279 : : /* `s2n_validate_ticket_age` is a best effort check that the session ticket is
280 : : * less than one week old.
281 : : *
282 : : * Clock skew between hosts or the possibility of a clock jump prevent this from
283 : : * being a precise check.
284 : : */
285 : : static S2N_RESULT s2n_validate_ticket_age(uint64_t current_time, uint64_t ticket_issue_time)
286 : 18 : {
287 : : /* If the `ticket_issue_time` is in the future, then we are observing clock skew.
288 : : * We shouldn't fully reject the ticket, but we assert that the clock skew is
289 : : * less than some MAX_ALLOWED_CLOCK_SKEW_SEC
290 : : */
291 [ - + ]: 18 : if (current_time < ticket_issue_time) {
292 : 0 : uint64_t clock_skew_in_nanos = ticket_issue_time - current_time;
293 : 0 : uint64_t clock_skew_in_seconds = clock_skew_in_nanos / ONE_SEC_IN_NANOS;
294 [ # # ][ # # ]: 0 : RESULT_ENSURE(clock_skew_in_seconds <= MAX_ALLOWED_CLOCK_SKEW_SEC, S2N_ERR_INVALID_SESSION_TICKET);
295 : 18 : } else {
296 : 18 : uint64_t ticket_age_in_nanos = current_time - ticket_issue_time;
297 : 18 : uint64_t ticket_age_in_sec = ticket_age_in_nanos / ONE_SEC_IN_NANOS;
298 [ # # ][ - + ]: 18 : RESULT_ENSURE(ticket_age_in_sec <= ONE_WEEK_IN_SEC, S2N_ERR_INVALID_SESSION_TICKET);
299 : 18 : }
300 : 18 : return S2N_RESULT_OK;
301 : 18 : }
302 : :
303 : : static S2N_RESULT s2n_tls13_deserialize_session_state(struct s2n_connection *conn, struct s2n_blob *psk_identity, struct s2n_stuffer *from)
304 : 18 : {
305 [ - + ][ # # ]: 18 : RESULT_ENSURE_REF(conn);
306 [ # # ][ - + ]: 18 : RESULT_ENSURE_REF(psk_identity);
307 [ - + ][ # # ]: 18 : RESULT_ENSURE_REF(from);
308 : :
309 : 18 : DEFER_CLEANUP(struct s2n_psk psk = { 0 }, s2n_psk_wipe);
310 [ - + ]: 18 : RESULT_GUARD(s2n_psk_init(&psk, S2N_PSK_TYPE_RESUMPTION));
311 [ - + ]: 18 : RESULT_GUARD_POSIX(s2n_psk_set_identity(&psk, psk_identity->data, psk_identity->size));
312 : :
313 : 18 : uint8_t protocol_version = 0;
314 [ - + ]: 18 : RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &protocol_version));
315 [ - + ][ # # ]: 18 : RESULT_ENSURE_GTE(protocol_version, S2N_TLS13);
316 : : /* Clients don't know which protocol version will be negotiated at this stage */
317 [ + + ]: 18 : if (conn->mode == S2N_SERVER) {
318 [ # # ][ - + ]: 11 : RESULT_ENSURE(protocol_version == conn->actual_protocol_version, S2N_ERR_INVALID_SERIALIZED_SESSION_STATE);
319 : 11 : }
320 : :
321 : 18 : uint8_t iana_id[S2N_TLS_CIPHER_SUITE_LEN] = { 0 };
322 [ - + ]: 18 : RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(from, iana_id, S2N_TLS_CIPHER_SUITE_LEN));
323 : 18 : struct s2n_cipher_suite *cipher_suite = NULL;
324 [ - + ]: 18 : RESULT_GUARD(s2n_cipher_suite_from_iana(iana_id, sizeof(iana_id), &cipher_suite));
325 [ # # ][ - + ]: 18 : RESULT_ENSURE_REF(cipher_suite);
326 : 18 : psk.hmac_alg = cipher_suite->prf_alg;
327 : :
328 [ - + ]: 18 : RESULT_GUARD_POSIX(s2n_stuffer_read_uint64(from, &psk.ticket_issue_time));
329 : :
330 : : /**
331 : : *= https://www.rfc-editor.org/rfc/rfc8446#section-4.6.1
332 : : *# Clients MUST NOT cache
333 : : *# tickets for longer than 7 days, regardless of the ticket_lifetime,
334 : : *# and MAY delete tickets earlier based on local policy.
335 : : */
336 : 18 : uint64_t current_time = 0;
337 [ - + ]: 18 : RESULT_GUARD(s2n_config_wall_clock(conn->config, ¤t_time));
338 [ - + ]: 18 : RESULT_GUARD(s2n_validate_ticket_age(current_time, psk.ticket_issue_time));
339 : :
340 [ - + ]: 18 : RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(from, &psk.ticket_age_add));
341 : :
342 : 18 : uint8_t secret_len = 0;
343 [ - + ]: 18 : RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &secret_len));
344 [ - + ][ # # ]: 18 : RESULT_ENSURE_LTE(secret_len, S2N_TLS_SECRET_LEN);
345 : 18 : uint8_t *secret_data = s2n_stuffer_raw_read(from, secret_len);
346 [ # # ][ - + ]: 18 : RESULT_ENSURE_REF(secret_data);
347 [ - + ]: 18 : RESULT_GUARD_POSIX(s2n_psk_set_secret(&psk, secret_data, secret_len));
348 : :
349 [ + + ]: 18 : if (conn->mode == S2N_SERVER) {
350 [ - + ]: 11 : RESULT_GUARD_POSIX(s2n_stuffer_read_uint64(from, &psk.keying_material_expiration));
351 [ # # ][ - + ]: 11 : RESULT_ENSURE(psk.keying_material_expiration > current_time, S2N_ERR_KEYING_MATERIAL_EXPIRED);
352 : 11 : }
353 : :
354 : 18 : uint32_t max_early_data_size = 0;
355 [ - + ]: 18 : RESULT_GUARD_POSIX(s2n_stuffer_read_uint32(from, &max_early_data_size));
356 [ - + ]: 18 : if (max_early_data_size > 0) {
357 [ # # ]: 0 : RESULT_GUARD_POSIX(s2n_psk_configure_early_data(&psk, max_early_data_size,
358 : 0 : iana_id[0], iana_id[1]));
359 : :
360 : 0 : uint8_t app_proto_size = 0;
361 [ # # ]: 0 : RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &app_proto_size));
362 : 0 : uint8_t *app_proto_data = s2n_stuffer_raw_read(from, app_proto_size);
363 [ # # ][ # # ]: 0 : RESULT_ENSURE_REF(app_proto_data);
364 [ # # ]: 0 : RESULT_GUARD_POSIX(s2n_psk_set_application_protocol(&psk, app_proto_data, app_proto_size));
365 : :
366 : 0 : uint16_t early_data_context_size = 0;
367 [ # # ]: 0 : RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(from, &early_data_context_size));
368 : 0 : uint8_t *early_data_context_data = s2n_stuffer_raw_read(from, early_data_context_size);
369 [ # # ][ # # ]: 0 : RESULT_ENSURE_REF(early_data_context_data);
370 [ # # ]: 0 : RESULT_GUARD_POSIX(s2n_psk_set_early_data_context(&psk, early_data_context_data, early_data_context_size));
371 : 0 : }
372 : :
373 : : /* Make sure that this connection is configured for resumption PSKs, not external PSKs */
374 [ - + ]: 18 : RESULT_GUARD(s2n_connection_set_psk_type(conn, S2N_PSK_TYPE_RESUMPTION));
375 : : /* Remove all previously-set PSKs. To keep the session ticket API behavior consistent
376 : : * across protocol versions, we currently only support setting a single resumption PSK. */
377 [ - + ]: 18 : RESULT_GUARD(s2n_psk_parameters_wipe(&conn->psk_params));
378 [ - + ]: 18 : RESULT_GUARD_POSIX(s2n_connection_append_psk(conn, &psk));
379 : :
380 : 18 : return S2N_RESULT_OK;
381 : 18 : }
382 : :
383 : : S2N_RESULT s2n_deserialize_resumption_state(struct s2n_connection *conn,
384 : : struct s2n_blob *ticket, struct s2n_stuffer *from)
385 : 329 : {
386 [ - + ][ # # ]: 329 : RESULT_ENSURE_REF(conn);
387 [ - + ][ # # ]: 329 : RESULT_ENSURE_REF(from);
388 : :
389 : 329 : uint8_t format = 0;
390 [ - + ]: 329 : RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &format));
391 : :
392 [ + + ]: 329 : if (format == S2N_SERIALIZED_FORMAT_TLS12_V3) {
393 [ + + ]: 82 : if (conn->mode == S2N_SERVER) {
394 [ + + ]: 37 : RESULT_GUARD_POSIX(s2n_tls12_deserialize_resumption_state(conn, from));
395 : 45 : } else {
396 [ + + ]: 45 : RESULT_GUARD(s2n_tls12_client_deserialize_session_state(conn, ticket, from));
397 : 45 : }
398 [ + + ]: 247 : } else if (format == S2N_SERIALIZED_FORMAT_TLS13_V1) {
399 [ + + ]: 245 : RESULT_GUARD(s2n_tls13_deserialize_session_state(conn, ticket, from));
400 : 245 : } else {
401 [ + - ]: 2 : RESULT_BAIL(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE);
402 : 2 : }
403 : 316 : conn->set_session = true;
404 : 316 : return S2N_RESULT_OK;
405 : 329 : }
406 : :
407 : : static int s2n_client_deserialize_with_session_id(struct s2n_connection *conn, struct s2n_stuffer *from)
408 : 6 : {
409 : 6 : uint8_t session_id_len = 0;
410 [ - + ]: 6 : POSIX_GUARD(s2n_stuffer_read_uint8(from, &session_id_len));
411 : :
412 [ + + ][ - + ]: 6 : if (session_id_len == 0 || session_id_len > S2N_TLS_SESSION_ID_MAX_LEN
413 [ - + ]: 6 : || session_id_len > s2n_stuffer_data_available(from)) {
414 [ + - ]: 1 : POSIX_BAIL(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE);
415 : 1 : }
416 : :
417 : 5 : conn->session_id_len = session_id_len;
418 [ - + ]: 5 : POSIX_GUARD(s2n_stuffer_read_bytes(from, conn->session_id, session_id_len));
419 : :
420 [ - + ]: 5 : POSIX_GUARD_RESULT(s2n_deserialize_resumption_state(conn, NULL, from));
421 : :
422 : 5 : return 0;
423 : 5 : }
424 : :
425 : : static int s2n_client_deserialize_with_session_ticket(struct s2n_connection *conn, struct s2n_stuffer *from)
426 : 14 : {
427 : 14 : uint16_t session_ticket_len = 0;
428 [ - + ]: 14 : POSIX_GUARD(s2n_stuffer_read_uint16(from, &session_ticket_len));
429 : :
430 [ - + ][ - + ]: 14 : if (session_ticket_len == 0 || session_ticket_len > s2n_stuffer_data_available(from)) {
431 [ # # ]: 0 : POSIX_BAIL(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE);
432 : 0 : }
433 : :
434 : 14 : struct s2n_blob session_ticket = { 0 };
435 : 14 : uint8_t *session_ticket_bytes = s2n_stuffer_raw_read(from, session_ticket_len);
436 [ # # ][ - + ]: 14 : POSIX_ENSURE_REF(session_ticket_bytes);
437 [ - + ]: 14 : POSIX_GUARD(s2n_blob_init(&session_ticket, session_ticket_bytes, session_ticket_len));
438 : :
439 [ - + ]: 14 : POSIX_GUARD_RESULT(s2n_deserialize_resumption_state(conn, &session_ticket, from));
440 : 14 : return 0;
441 : 14 : }
442 : :
443 : : static int s2n_client_deserialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *from)
444 : 21 : {
445 : 21 : uint8_t format = 0;
446 [ - + ]: 21 : POSIX_GUARD(s2n_stuffer_read_uint8(from, &format));
447 : :
448 : 21 : switch (format) {
449 [ + + ]: 6 : case S2N_STATE_WITH_SESSION_ID:
450 [ + + ]: 6 : POSIX_GUARD(s2n_client_deserialize_with_session_id(conn, from));
451 : 5 : break;
452 [ + + ]: 14 : case S2N_STATE_WITH_SESSION_TICKET:
453 [ - + ]: 14 : POSIX_GUARD(s2n_client_deserialize_with_session_ticket(conn, from));
454 : 14 : break;
455 [ + + ]: 14 : default:
456 [ + - ]: 1 : POSIX_BAIL(S2N_ERR_INVALID_SERIALIZED_SESSION_STATE);
457 : 21 : }
458 : :
459 : 19 : return 0;
460 : 21 : }
461 : :
462 : : int s2n_resume_from_cache(struct s2n_connection *conn)
463 : 16 : {
464 [ + + ][ + - ]: 16 : S2N_ERROR_IF(conn->session_id_len == 0, S2N_ERR_SESSION_ID_TOO_SHORT);
465 [ - + ][ # # ]: 13 : S2N_ERROR_IF(conn->session_id_len > S2N_TLS_SESSION_ID_MAX_LEN, S2N_ERR_SESSION_ID_TOO_LONG);
466 : :
467 : 13 : uint8_t data[S2N_TLS12_TICKET_SIZE_IN_BYTES] = { 0 };
468 : 13 : struct s2n_blob entry = { 0 };
469 [ - + ]: 13 : POSIX_GUARD(s2n_blob_init(&entry, data, S2N_TLS12_TICKET_SIZE_IN_BYTES));
470 : 13 : uint64_t size = entry.size;
471 : 13 : int result = conn->config->cache_retrieve(conn, conn->config->cache_retrieve_data, conn->session_id, conn->session_id_len, entry.data, &size);
472 [ + + ]: 13 : if (result == S2N_CALLBACK_BLOCKED) {
473 [ + - ]: 6 : POSIX_BAIL(S2N_ERR_ASYNC_BLOCKED);
474 : 6 : }
475 [ + + ][ + - ]: 7 : POSIX_ENSURE(result >= S2N_SUCCESS, S2N_ERR_CANCELLED);
476 : :
477 [ - + ][ # # ]: 6 : S2N_ERROR_IF(size != entry.size, S2N_ERR_SIZE_MISMATCH);
478 : :
479 : 6 : struct s2n_stuffer from = { 0 };
480 [ - + ]: 6 : POSIX_GUARD(s2n_stuffer_init(&from, &entry));
481 [ - + ]: 6 : POSIX_GUARD(s2n_stuffer_write(&from, &entry));
482 [ + + ]: 6 : POSIX_GUARD_RESULT(s2n_resume_decrypt_session(conn, &from));
483 : :
484 : 3 : return 0;
485 : 6 : }
486 : :
487 : : S2N_RESULT s2n_store_to_cache(struct s2n_connection *conn)
488 : 7 : {
489 : 7 : uint8_t data[S2N_TLS12_TICKET_SIZE_IN_BYTES] = { 0 };
490 : 7 : struct s2n_blob entry = { 0 };
491 [ - + ]: 7 : RESULT_GUARD_POSIX(s2n_blob_init(&entry, data, S2N_TLS12_TICKET_SIZE_IN_BYTES));
492 : 7 : struct s2n_stuffer to = { 0 };
493 : :
494 : : /* session_id_len should always be >0 since either the Client provided a SessionId or the Server generated a new
495 : : * one for the Client */
496 [ # # ][ - + ]: 7 : RESULT_ENSURE(conn->session_id_len > 0, S2N_ERR_SESSION_ID_TOO_SHORT);
497 [ # # ][ - + ]: 7 : RESULT_ENSURE(conn->session_id_len <= S2N_TLS_SESSION_ID_MAX_LEN, S2N_ERR_SESSION_ID_TOO_LONG);
498 : :
499 [ - + ]: 7 : RESULT_GUARD_POSIX(s2n_stuffer_init(&to, &entry));
500 : :
501 : 7 : struct s2n_ticket_key *key = s2n_get_ticket_encrypt_decrypt_key(conn->config);
502 [ + + ]: 7 : RESULT_GUARD(s2n_resume_encrypt_session_ticket(conn, key, &to));
503 : :
504 : : /* Store to the cache */
505 : 5 : conn->config->cache_store(conn, conn->config->cache_store_data, S2N_TLS_SESSION_CACHE_TTL, conn->session_id, conn->session_id_len, entry.data, entry.size);
506 : :
507 : 5 : return S2N_RESULT_OK;
508 : 7 : }
509 : :
510 : : int s2n_connection_set_session(struct s2n_connection *conn, const uint8_t *session, size_t length)
511 : 159 : {
512 [ # # ][ - + ]: 159 : POSIX_ENSURE_REF(conn);
513 [ # # ][ - + ]: 159 : POSIX_ENSURE_REF(session);
514 : :
515 : 159 : DEFER_CLEANUP(struct s2n_blob session_data = { 0 }, s2n_free);
516 : : /* size_t is 64-bit integer on 64-bit system, while s2n_alloc's length parameter is a 32-bit integer */
517 [ + + ][ + - ]: 159 : POSIX_ENSURE(length <= UINT32_MAX, S2N_ERR_INVALID_ARGUMENT);
518 [ - + ]: 158 : POSIX_GUARD(s2n_alloc(&session_data, length));
519 [ - + ][ # # ]: 158 : POSIX_CHECKED_MEMCPY(session_data.data, session, length);
[ + - ]
520 : :
521 : 158 : struct s2n_stuffer from = { 0 };
522 [ - + ]: 158 : POSIX_GUARD(s2n_stuffer_init(&from, &session_data));
523 [ - + ]: 158 : POSIX_GUARD(s2n_stuffer_write(&from, &session_data));
524 [ + + ]: 158 : POSIX_GUARD(s2n_client_deserialize_resumption_state(conn, &from));
525 : 153 : return 0;
526 : 158 : }
527 : :
528 : : int s2n_connection_get_session(struct s2n_connection *conn, uint8_t *session, size_t max_length)
529 : 481 : {
530 [ - + ][ # # ]: 481 : POSIX_ENSURE_REF(conn);
531 [ - + ][ # # ]: 481 : POSIX_ENSURE_REF(session);
532 : :
533 : 481 : const int len = s2n_connection_get_session_length(conn);
534 [ - + ]: 481 : POSIX_GUARD(len);
535 : :
536 [ + + ]: 481 : if (len == 0) {
537 : 2 : return 0;
538 : 2 : }
539 : :
540 [ + + ][ + - ]: 479 : POSIX_ENSURE((size_t) len <= max_length, S2N_ERR_SERIALIZED_SESSION_STATE_TOO_LONG);
541 : :
542 : 478 : struct s2n_blob serialized_data = { 0 };
543 [ - + ]: 478 : POSIX_GUARD(s2n_blob_init(&serialized_data, session, len));
544 [ - + ]: 478 : POSIX_GUARD(s2n_blob_zero(&serialized_data));
545 : :
546 : 478 : struct s2n_stuffer to = { 0 };
547 [ - + ]: 478 : POSIX_GUARD(s2n_stuffer_init(&to, &serialized_data));
548 [ - + ]: 478 : POSIX_GUARD(s2n_client_serialize_resumption_state(conn, &to));
549 : :
550 : 478 : return len;
551 : 478 : }
552 : :
553 : : int s2n_connection_get_session_ticket_lifetime_hint(struct s2n_connection *conn)
554 : 19 : {
555 [ - + ][ # # ]: 19 : POSIX_ENSURE_REF(conn);
556 [ + - ][ + + ]: 19 : S2N_ERROR_IF(!(conn->config->use_tickets && conn->client_ticket.size > 0), S2N_ERR_SESSION_TICKET_NOT_SUPPORTED);
[ + - ]
557 : :
558 : : /* Session resumption using session ticket */
559 : 18 : return conn->ticket_lifetime_hint;
560 : 19 : }
561 : :
562 : : S2N_RESULT s2n_connection_get_session_state_size(struct s2n_connection *conn, size_t *state_size)
563 : 1252 : {
564 [ + + ][ + - ]: 1252 : RESULT_ENSURE_REF(conn);
565 [ - + ][ # # ]: 1251 : RESULT_ENSURE_REF(conn->secure);
566 [ + + ][ + - ]: 1251 : RESULT_ENSURE_REF(state_size);
567 : :
568 [ + + ]: 1250 : if (s2n_resume_protocol_version(conn) < S2N_TLS13) {
569 : 64 : *state_size = S2N_TLS12_STATE_SIZE_IN_BYTES;
570 : 64 : return S2N_RESULT_OK;
571 : 64 : }
572 : :
573 : 1186 : *state_size = S2N_TLS13_FIXED_STATE_SIZE;
574 : :
575 : 1186 : uint8_t secret_size = 0;
576 [ - + ][ # # ]: 1186 : RESULT_ENSURE_REF(conn->secure->cipher_suite);
577 [ - + ]: 1186 : RESULT_GUARD_POSIX(s2n_hmac_digest_size(conn->secure->cipher_suite->prf_alg, &secret_size));
578 : 1186 : *state_size += secret_size;
579 : :
580 : 1186 : uint32_t server_max_early_data = 0;
581 [ - + ]: 1186 : RESULT_GUARD(s2n_early_data_get_server_max_size(conn, &server_max_early_data));
582 [ + + ]: 1186 : if (server_max_early_data > 0) {
583 : 386 : *state_size += S2N_TLS13_FIXED_EARLY_DATA_STATE_SIZE
584 : 386 : + strlen(conn->application_protocol)
585 : 386 : + conn->server_early_data_context.size;
586 : 386 : }
587 : :
588 : 1186 : return S2N_RESULT_OK;
589 : 1186 : }
590 : :
591 : : static S2N_RESULT s2n_connection_get_session_length_impl(struct s2n_connection *conn, size_t *length)
592 : 455 : {
593 [ # # ][ - + ]: 455 : RESULT_ENSURE_REF(conn);
594 [ # # ][ - + ]: 455 : RESULT_ENSURE_REF(conn->config);
595 [ - + ][ # # ]: 455 : RESULT_ENSURE_REF(length);
596 : 455 : *length = 0;
597 : :
598 [ + + ][ + + ]: 455 : if (conn->config->use_tickets && conn->client_ticket.size > 0) {
599 : 441 : size_t session_state_size = 0;
600 [ - + ]: 441 : RESULT_GUARD(s2n_connection_get_session_state_size(conn, &session_state_size));
601 : 441 : *length = S2N_STATE_FORMAT_LEN + S2N_SESSION_TICKET_SIZE_LEN + conn->client_ticket.size + session_state_size;
602 [ + + ][ + + ]: 441 : } else if (conn->session_id_len > 0 && conn->actual_protocol_version < S2N_TLS13) {
603 : 10 : *length = S2N_STATE_FORMAT_LEN + sizeof(conn->session_id_len) + conn->session_id_len + S2N_TLS12_STATE_SIZE_IN_BYTES;
604 : 10 : }
605 : 455 : return S2N_RESULT_OK;
606 : 455 : }
607 : :
608 : : int s2n_connection_get_session_length(struct s2n_connection *conn)
609 : 970 : {
610 : 970 : size_t length = 0;
611 [ + + ]: 970 : if (s2n_result_is_ok(s2n_connection_get_session_length_impl(conn, &length))) {
612 : 969 : return length;
613 : 969 : }
614 : 1 : return 0;
615 : 970 : }
616 : :
617 : : int s2n_connection_is_session_resumed(struct s2n_connection *conn)
618 : 3747 : {
619 [ + + ][ + + ]: 3747 : return conn && IS_RESUMPTION_HANDSHAKE(conn)
[ + - ]
620 [ + + ][ + + ]: 3747 : && (conn->actual_protocol_version < S2N_TLS13 || conn->psk_params.type == S2N_PSK_TYPE_RESUMPTION);
621 : 3747 : }
622 : :
623 : : int s2n_connection_is_ocsp_stapled(struct s2n_connection *conn)
624 : 18 : {
625 [ - + ][ # # ]: 18 : POSIX_ENSURE_REF(conn);
626 : :
627 [ + + ]: 18 : if (conn->actual_protocol_version >= S2N_TLS13) {
628 [ + + ][ + - ]: 10 : return (s2n_server_can_send_ocsp(conn) || s2n_server_sent_ocsp(conn));
[ + - ][ + + ]
[ + + ][ + + ]
629 : 10 : } else {
630 : 8 : return IS_OCSP_STAPLED(conn);
631 : 8 : }
632 : 18 : }
633 : :
634 : : S2N_RESULT s2n_config_is_encrypt_key_available(struct s2n_config *config)
635 : 28 : {
636 [ - + ][ # # ]: 28 : RESULT_ENSURE_REF(config);
637 : :
638 : 28 : uint64_t now = 0;
639 : 28 : struct s2n_ticket_key *ticket_key = NULL;
640 [ - + ]: 28 : RESULT_GUARD(s2n_config_wall_clock(config, &now));
641 [ - + ][ # # ]: 28 : RESULT_ENSURE_REF(config->ticket_keys);
642 : :
643 : 28 : uint32_t ticket_keys_len = 0;
644 [ - + ]: 28 : RESULT_GUARD(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len));
645 : :
646 [ + + ]: 29 : for (uint32_t i = ticket_keys_len; i > 0; i--) {
647 : 23 : uint32_t idx = i - 1;
648 [ - + ]: 23 : RESULT_GUARD(s2n_array_get(config->ticket_keys, idx, (void **) &ticket_key));
649 : 23 : uint64_t key_intro_time = ticket_key->intro_timestamp;
650 : :
651 [ + + ]: 23 : if (key_intro_time <= now
652 [ + - ]: 23 : && now < key_intro_time + config->encrypt_decrypt_key_lifetime_in_nanos) {
653 : 22 : return S2N_RESULT_OK;
654 : 22 : }
655 : 23 : }
656 : :
657 [ + - ]: 6 : RESULT_BAIL(S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY);
658 : 6 : }
659 : :
660 : : /* This function is used in s2n_get_ticket_encrypt_decrypt_key to compute the weight
661 : : * of the keys and to choose a single key from all of the encrypt-decrypt keys.
662 : : * Higher the weight of the key, higher the probability of being picked.
663 : : */
664 : : int s2n_compute_weight_of_encrypt_decrypt_keys(struct s2n_config *config,
665 : : uint8_t *encrypt_decrypt_keys_index,
666 : : uint8_t num_encrypt_decrypt_keys,
667 : : uint64_t now)
668 : 3 : {
669 : 3 : double total_weight = 0;
670 : 3 : struct s2n_ticket_key_weight ticket_keys_weight[S2N_MAX_TICKET_KEYS] = { 0 };
671 : 3 : struct s2n_ticket_key *ticket_key = NULL;
672 : :
673 : : /* Compute weight of encrypt-decrypt keys */
674 [ + + ]: 11 : for (int i = 0; i < num_encrypt_decrypt_keys; i++) {
675 [ - + ]: 8 : POSIX_GUARD_RESULT(s2n_array_get(config->ticket_keys, encrypt_decrypt_keys_index[i], (void **) &ticket_key));
676 : :
677 : 8 : uint64_t key_intro_time = ticket_key->intro_timestamp;
678 : 8 : uint64_t key_encryption_peak_time = key_intro_time + (config->encrypt_decrypt_key_lifetime_in_nanos / 2);
679 : :
680 : : /* The % of encryption using this key is linearly increasing */
681 [ + + ]: 8 : if (now < key_encryption_peak_time) {
682 : 4 : ticket_keys_weight[i].key_weight = now - key_intro_time;
683 : 4 : } else {
684 : : /* The % of encryption using this key is linearly decreasing */
685 : 4 : ticket_keys_weight[i].key_weight = (config->encrypt_decrypt_key_lifetime_in_nanos / 2) - (now - key_encryption_peak_time);
686 : 4 : }
687 : :
688 : 8 : ticket_keys_weight[i].key_index = encrypt_decrypt_keys_index[i];
689 : 8 : total_weight += ticket_keys_weight[i].key_weight;
690 : 8 : }
691 : :
692 : : /* Pick a random number in [0, 1). Using 53 bits (IEEE 754 double-precision floats). */
693 : 3 : uint64_t random_int = 0;
694 [ - + ]: 3 : POSIX_GUARD_RESULT(s2n_public_random(pow(2, 53), &random_int));
695 : 3 : double random = (double) random_int / (double) pow(2, 53);
696 : :
697 : : /* Compute cumulative weight of encrypt-decrypt keys */
698 [ + - ]: 7 : for (int i = 0; i < num_encrypt_decrypt_keys; i++) {
699 : 7 : ticket_keys_weight[i].key_weight = ticket_keys_weight[i].key_weight / total_weight;
700 : :
701 [ + + ]: 7 : if (i > 0) {
702 : 4 : ticket_keys_weight[i].key_weight += ticket_keys_weight[i - 1].key_weight;
703 : 4 : }
704 : :
705 [ + + ]: 7 : if (ticket_keys_weight[i].key_weight > random) {
706 : 3 : return ticket_keys_weight[i].key_index;
707 : 3 : }
708 : 7 : }
709 : :
710 [ # # ]: 0 : POSIX_BAIL(S2N_ERR_ENCRYPT_DECRYPT_KEY_SELECTION_FAILED);
711 : 0 : }
712 : :
713 : : /* This function is used in s2n_resume_encrypt_session_ticket in order for s2n to
714 : : * choose a key in encrypt-decrypt state from all of the keys added to config
715 : : */
716 : : struct s2n_ticket_key *s2n_get_ticket_encrypt_decrypt_key(struct s2n_config *config)
717 : 474 : {
718 : 474 : uint8_t num_encrypt_decrypt_keys = 0;
719 : 474 : uint8_t encrypt_decrypt_keys_index[S2N_MAX_TICKET_KEYS] = { 0 };
720 : 474 : struct s2n_ticket_key *ticket_key = NULL;
721 : :
722 : 474 : uint64_t now = 0;
723 [ - + ]: 474 : PTR_GUARD_RESULT(s2n_config_wall_clock(config, &now));
724 [ + + ][ + - ]: 474 : PTR_ENSURE_REF(config->ticket_keys);
725 : :
726 : 473 : uint32_t ticket_keys_len = 0;
727 [ - + ]: 473 : PTR_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len));
728 : :
729 [ + + ]: 949 : for (uint32_t i = ticket_keys_len; i > 0; i--) {
730 : 476 : uint32_t idx = i - 1;
731 [ - + ]: 476 : PTR_GUARD_RESULT(s2n_array_get(config->ticket_keys, idx, (void **) &ticket_key));
732 : 476 : uint64_t key_intro_time = ticket_key->intro_timestamp;
733 : :
734 : : /* A key can be used at its intro time (<=) and it can be used up to (<)
735 : : * its expiration time.
736 : : */
737 [ + + ]: 476 : if (key_intro_time <= now
738 [ + + ]: 476 : && now < key_intro_time + config->encrypt_decrypt_key_lifetime_in_nanos) {
739 : 471 : encrypt_decrypt_keys_index[num_encrypt_decrypt_keys] = idx;
740 : 471 : num_encrypt_decrypt_keys++;
741 : 471 : }
742 : 476 : }
743 : :
744 [ + + ]: 473 : if (num_encrypt_decrypt_keys == 0) {
745 [ + - ]: 7 : PTR_BAIL(S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY);
746 : 7 : }
747 : :
748 [ + + ]: 466 : if (num_encrypt_decrypt_keys == 1) {
749 [ - + ]: 463 : PTR_GUARD_RESULT(s2n_array_get(config->ticket_keys, encrypt_decrypt_keys_index[0], (void **) &ticket_key));
750 : 463 : return ticket_key;
751 : 463 : }
752 : :
753 : 3 : int8_t idx = 0;
754 [ - + ]: 3 : PTR_GUARD_POSIX(idx = s2n_compute_weight_of_encrypt_decrypt_keys(config, encrypt_decrypt_keys_index, num_encrypt_decrypt_keys, now));
755 : :
756 [ - + ]: 3 : PTR_GUARD_RESULT(s2n_array_get(config->ticket_keys, idx, (void **) &ticket_key));
757 : 3 : return ticket_key;
758 : 3 : }
759 : :
760 : : /* This function is used in s2n_resume_decrypt_session in order for s2n to
761 : : * find the matching key that was used for encryption.
762 : : */
763 : : struct s2n_ticket_key *s2n_find_ticket_key(struct s2n_config *config, const uint8_t name[S2N_TICKET_KEY_NAME_LEN])
764 : 162 : {
765 : 162 : uint64_t now = 0;
766 : 162 : struct s2n_ticket_key *ticket_key = NULL;
767 [ - + ]: 162 : PTR_GUARD_RESULT(s2n_config_wall_clock(config, &now));
768 [ # # ][ - + ]: 162 : PTR_ENSURE_REF(config->ticket_keys);
769 : :
770 : 162 : uint32_t ticket_keys_len = 0;
771 [ - + ]: 162 : PTR_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len));
772 : :
773 [ + + ]: 165 : for (uint32_t i = 0; i < ticket_keys_len; i++) {
774 [ - + ]: 158 : PTR_GUARD_RESULT(s2n_array_get(config->ticket_keys, i, (void **) &ticket_key));
775 : :
776 [ + + ]: 158 : if (s2n_constant_time_equals(ticket_key->key_name, name, S2N_TICKET_KEY_NAME_LEN)) {
777 : : /* Check to see if the key has expired */
778 [ - + ]: 155 : if (now >= ticket_key->intro_timestamp
779 : 155 : + config->encrypt_decrypt_key_lifetime_in_nanos
780 : 155 : + config->decrypt_key_lifetime_in_nanos) {
781 : 0 : return NULL;
782 : 0 : }
783 : :
784 : 155 : return ticket_key;
785 : 155 : }
786 : 158 : }
787 : :
788 : 7 : return NULL;
789 : 162 : }
790 : :
791 : : struct s2n_unique_ticket_key {
792 : : struct s2n_blob initial_key;
793 : : uint8_t info[S2N_AES256_KEY_LEN];
794 : : uint8_t output_key[S2N_AES256_KEY_LEN];
795 : : };
796 : :
797 : : /* Ensures that a session ticket encryption key is used only once per ticket.
798 : : *
799 : : * The AES-GCM encryption scheme breaks if the same nonce is used with the same key more than once.
800 : : * As the number of TLS connections increases per second, it becomes more probable that the same
801 : : * random nonce will be generated twice and used with the same ticket key.
802 : : * To avoid this we generate a unique session ticket encryption key for each ticket.
803 : : **/
804 : : static S2N_RESULT s2n_resume_generate_unique_ticket_key(struct s2n_unique_ticket_key *key)
805 : 235 : {
806 [ # # ][ - + ]: 235 : RESULT_ENSURE_REF(key);
807 : :
808 : 235 : struct s2n_blob out_key_blob = { 0 };
809 [ - + ]: 235 : RESULT_GUARD_POSIX(s2n_blob_init(&out_key_blob, key->output_key, sizeof(key->output_key)));
810 : 235 : struct s2n_blob info_blob = { 0 };
811 [ - + ]: 235 : RESULT_GUARD_POSIX(s2n_blob_init(&info_blob, key->info, sizeof(key->info)));
812 : 235 : struct s2n_blob salt = { 0 };
813 [ - + ]: 235 : RESULT_GUARD_POSIX(s2n_blob_init(&salt, NULL, 0));
814 : :
815 : 235 : DEFER_CLEANUP(struct s2n_hmac_state hmac = { 0 }, s2n_hmac_free);
816 : : /* TODO: There may be an optimization here to reuse existing hmac memory instead of
817 : : * creating an entirely new hmac. See: https://github.com/aws/s2n-tls/issues/3206 */
818 [ - + ]: 235 : RESULT_GUARD_POSIX(s2n_hmac_new(&hmac));
819 [ - + ]: 235 : RESULT_GUARD_POSIX(s2n_hkdf(&hmac, S2N_HMAC_SHA256, &salt, &key->initial_key, &info_blob, &out_key_blob));
820 : :
821 : 235 : return S2N_RESULT_OK;
822 : 235 : }
823 : :
824 : : S2N_RESULT s2n_resume_encrypt_session_ticket(struct s2n_connection *conn,
825 : : struct s2n_ticket_key *key, struct s2n_stuffer *to)
826 : 468 : {
827 [ # # ][ - + ]: 468 : RESULT_ENSURE_REF(conn);
828 [ - + ][ # # ]: 468 : RESULT_ENSURE_REF(to);
829 : :
830 [ + - ][ + + ]: 468 : RESULT_ENSURE(key != NULL, S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY);
831 : :
832 : : /* Generate unique per-ticket encryption key */
833 : 465 : struct s2n_unique_ticket_key ticket_key = { 0 };
834 [ - + ]: 465 : RESULT_GUARD_POSIX(s2n_blob_init(&ticket_key.initial_key, key->aes_key, sizeof(key->aes_key)));
835 : 465 : struct s2n_blob info_blob = { 0 };
836 [ - + ]: 465 : RESULT_GUARD_POSIX(s2n_blob_init(&info_blob, ticket_key.info, sizeof(ticket_key.info)));
837 [ - + ]: 465 : RESULT_GUARD(s2n_get_public_random_data(&info_blob));
838 [ - + ]: 465 : RESULT_GUARD(s2n_resume_generate_unique_ticket_key(&ticket_key));
839 : :
840 : : /* Initialize AES key */
841 : 465 : struct s2n_blob aes_key_blob = { 0 };
842 [ - + ]: 465 : RESULT_GUARD_POSIX(s2n_blob_init(&aes_key_blob, ticket_key.output_key, sizeof(ticket_key.output_key)));
843 : 465 : DEFER_CLEANUP(struct s2n_session_key aes_ticket_key = { 0 }, s2n_session_key_free);
844 [ - + ]: 465 : RESULT_GUARD_POSIX(s2n_session_key_alloc(&aes_ticket_key));
845 [ - + ]: 465 : RESULT_GUARD(s2n_aes256_gcm.init(&aes_ticket_key));
846 [ - + ]: 465 : RESULT_GUARD(s2n_aes256_gcm.set_encryption_key(&aes_ticket_key, &aes_key_blob));
847 : :
848 : : /* Ensure we never encrypt with a zero-filled key */
849 : 465 : uint8_t zero_block[S2N_AES256_KEY_LEN] = { 0 };
850 [ + - ][ + + ]: 465 : RESULT_ENSURE(!s2n_constant_time_equals(key->aes_key, zero_block, S2N_AES256_KEY_LEN),
851 : 464 : S2N_ERR_KEY_CHECK);
852 : :
853 : : /* Initialize Additional Authenticated Data */
854 : 464 : uint8_t aad_data[S2N_TICKET_AAD_LEN] = { 0 };
855 : 464 : struct s2n_blob aad_blob = { 0 };
856 [ - + ]: 464 : RESULT_GUARD_POSIX(s2n_blob_init(&aad_blob, aad_data, sizeof(aad_data)));
857 : 464 : struct s2n_stuffer aad = { 0 };
858 [ - + ]: 464 : RESULT_GUARD_POSIX(s2n_stuffer_init(&aad, &aad_blob));
859 [ - + ]: 464 : RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&aad, key->implicit_aad, sizeof(key->implicit_aad)));
860 [ - + ]: 464 : RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&aad, key->key_name, sizeof(key->key_name)));
861 : :
862 : : /* Write version number */
863 [ + + ]: 464 : RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(to, S2N_PRE_ENCRYPTED_STATE_V1));
864 : :
865 : : /* Write key name */
866 [ - + ]: 463 : RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(to, key->key_name, sizeof(key->key_name)));
867 : :
868 : : /* Write parameter needed to generate unique ticket key */
869 [ - + ]: 463 : RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(to, ticket_key.info, sizeof(ticket_key.info)));
870 : :
871 : : /* Write IV */
872 : 463 : uint8_t iv_data[S2N_TLS_GCM_IV_LEN] = { 0 };
873 : 463 : struct s2n_blob iv = { 0 };
874 [ - + ]: 463 : RESULT_GUARD_POSIX(s2n_blob_init(&iv, iv_data, sizeof(iv_data)));
875 [ - + ]: 463 : RESULT_GUARD(s2n_get_public_random_data(&iv));
876 [ - + ]: 463 : RESULT_GUARD_POSIX(s2n_stuffer_write(to, &iv));
877 : :
878 : : /* Write serialized session state */
879 : 463 : uint32_t plaintext_state_size = s2n_stuffer_data_available(to);
880 [ - + ]: 463 : RESULT_GUARD(s2n_serialize_resumption_state(conn, to));
881 [ - + ]: 463 : RESULT_GUARD_POSIX(s2n_stuffer_skip_write(to, S2N_TLS_GCM_TAG_LEN));
882 : :
883 : : /* Initialize blob to be encrypted */
884 : 463 : struct s2n_blob state_blob = { 0 };
885 : 463 : struct s2n_stuffer copy_for_encryption = *to;
886 [ - + ]: 463 : RESULT_GUARD_POSIX(s2n_stuffer_skip_read(©_for_encryption, plaintext_state_size));
887 : 463 : uint32_t state_blob_size = s2n_stuffer_data_available(©_for_encryption);
888 : 463 : uint8_t *state_blob_data = s2n_stuffer_raw_read(©_for_encryption, state_blob_size);
889 [ - + ][ # # ]: 463 : RESULT_ENSURE_REF(state_blob_data);
890 [ - + ]: 463 : RESULT_GUARD_POSIX(s2n_blob_init(&state_blob, state_blob_data, state_blob_size));
891 : :
892 [ - + ]: 463 : RESULT_GUARD_POSIX(s2n_aes256_gcm.io.aead.encrypt(&aes_ticket_key, &iv, &aad_blob, &state_blob, &state_blob));
893 : :
894 : 463 : return S2N_RESULT_OK;
895 : 463 : }
896 : :
897 : : S2N_RESULT s2n_resume_decrypt_session(struct s2n_connection *conn, struct s2n_stuffer *from)
898 : 171 : {
899 [ - + ][ # # ]: 171 : RESULT_ENSURE_REF(conn);
900 [ # # ][ - + ]: 171 : RESULT_ENSURE_REF(from);
901 [ # # ][ - + ]: 171 : RESULT_ENSURE_REF(conn->config);
902 : :
903 : : /* Read version number */
904 : 171 : uint8_t version = 0;
905 [ - + ]: 171 : RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &version));
906 [ + - ][ + + ]: 171 : RESULT_ENSURE_EQ(version, S2N_PRE_ENCRYPTED_STATE_V1);
907 : :
908 : : /* Read key name */
909 : 162 : uint8_t key_name[S2N_TICKET_KEY_NAME_LEN] = { 0 };
910 [ - + ]: 162 : RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(from, key_name, sizeof(key_name)));
911 : :
912 : 162 : struct s2n_ticket_key *key = s2n_find_ticket_key(conn->config, key_name);
913 : : /* Key has expired; do full handshake */
914 [ + + ][ + - ]: 162 : RESULT_ENSURE(key != NULL, S2N_ERR_KEY_USED_IN_SESSION_TICKET_NOT_FOUND);
915 : :
916 : 155 : struct s2n_unique_ticket_key ticket_key = { 0 };
917 [ - + ]: 155 : RESULT_GUARD_POSIX(s2n_blob_init(&ticket_key.initial_key, key->aes_key, sizeof(key->aes_key)));
918 [ - + ]: 155 : RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(from, ticket_key.info, sizeof(ticket_key.info)));
919 [ - + ]: 155 : RESULT_GUARD(s2n_resume_generate_unique_ticket_key(&ticket_key));
920 : :
921 : : /* Read IV */
922 : 155 : uint8_t iv_data[S2N_TLS_GCM_IV_LEN] = { 0 };
923 : 155 : struct s2n_blob iv = { 0 };
924 [ - + ]: 155 : RESULT_GUARD_POSIX(s2n_blob_init(&iv, iv_data, sizeof(iv_data)));
925 [ - + ]: 155 : RESULT_GUARD_POSIX(s2n_stuffer_read(from, &iv));
926 : :
927 : : /* Initialize AES key */
928 : 155 : struct s2n_blob aes_key_blob = { 0 };
929 [ - + ]: 155 : RESULT_GUARD_POSIX(s2n_blob_init(&aes_key_blob, ticket_key.output_key, sizeof(ticket_key.output_key)));
930 : 155 : DEFER_CLEANUP(struct s2n_session_key aes_ticket_key = { 0 }, s2n_session_key_free);
931 [ - + ]: 155 : RESULT_GUARD_POSIX(s2n_session_key_alloc(&aes_ticket_key));
932 [ - + ]: 155 : RESULT_GUARD(s2n_aes256_gcm.init(&aes_ticket_key));
933 [ - + ]: 155 : RESULT_GUARD(s2n_aes256_gcm.set_decryption_key(&aes_ticket_key, &aes_key_blob));
934 : :
935 : : /* Initialize Additional Authenticated Data */
936 : 155 : uint8_t aad_data[S2N_TICKET_AAD_LEN] = { 0 };
937 : 155 : struct s2n_blob aad_blob = { 0 };
938 [ - + ]: 155 : RESULT_GUARD_POSIX(s2n_blob_init(&aad_blob, aad_data, sizeof(aad_data)));
939 : 155 : struct s2n_stuffer aad = { 0 };
940 [ - + ]: 155 : RESULT_GUARD_POSIX(s2n_stuffer_init(&aad, &aad_blob));
941 [ - + ]: 155 : RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&aad, key->implicit_aad, sizeof(key->implicit_aad)));
942 [ - + ]: 155 : RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&aad, key->key_name, sizeof(key->key_name)));
943 : :
944 : : /* Initialize blob to be decrypted */
945 : 155 : struct s2n_blob en_blob = { 0 };
946 : 155 : uint32_t en_blob_size = s2n_stuffer_data_available(from);
947 : 155 : uint8_t *en_blob_data = s2n_stuffer_raw_read(from, en_blob_size);
948 [ - + ][ # # ]: 155 : RESULT_ENSURE_REF(en_blob_data);
949 [ - + ]: 155 : RESULT_GUARD_POSIX(s2n_blob_init(&en_blob, en_blob_data, en_blob_size));
950 : :
951 [ + + ]: 155 : RESULT_GUARD_POSIX(s2n_aes256_gcm.io.aead.decrypt(&aes_ticket_key, &iv, &aad_blob, &en_blob, &en_blob));
952 : :
953 : : /* Parse decrypted state */
954 : 152 : struct s2n_blob state_blob = { 0 };
955 : 152 : uint32_t state_blob_size = en_blob_size - S2N_TLS_GCM_TAG_LEN;
956 [ - + ]: 152 : RESULT_GUARD_POSIX(s2n_blob_init(&state_blob, en_blob.data, state_blob_size));
957 : 152 : struct s2n_stuffer state_stuffer = { 0 };
958 [ - + ]: 152 : RESULT_GUARD_POSIX(s2n_stuffer_init(&state_stuffer, &state_blob));
959 [ - + ]: 152 : RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&state_stuffer, state_blob_size));
960 [ + + ]: 152 : RESULT_GUARD(s2n_deserialize_resumption_state(conn, &from->blob, &state_stuffer));
961 : :
962 : 148 : return S2N_RESULT_OK;
963 : 152 : }
964 : :
965 : : /* This function is used to remove all or just one expired key from server config */
966 : : int s2n_config_wipe_expired_ticket_crypto_keys(struct s2n_config *config, int8_t expired_key_index)
967 : 145 : {
968 : 145 : int num_of_expired_keys = 0;
969 : 145 : int expired_keys_index[S2N_MAX_TICKET_KEYS] = { 0 };
970 : 145 : struct s2n_ticket_key *ticket_key = NULL;
971 : :
972 [ - + ]: 145 : if (expired_key_index != -1) {
973 : 0 : expired_keys_index[num_of_expired_keys] = expired_key_index;
974 : 0 : num_of_expired_keys++;
975 : :
976 : 0 : goto end;
977 : 0 : }
978 : :
979 : 145 : uint64_t now = 0;
980 [ - + ]: 145 : POSIX_GUARD_RESULT(s2n_config_wall_clock(config, &now));
981 [ # # ][ - + ]: 145 : POSIX_ENSURE_REF(config->ticket_keys);
982 : :
983 : 145 : uint32_t ticket_keys_len = 0;
984 [ - + ]: 145 : POSIX_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len));
985 [ + + ]: 1347 : for (uint32_t i = 0; i < ticket_keys_len; i++) {
986 [ - + ]: 1202 : POSIX_GUARD_RESULT(s2n_array_get(config->ticket_keys, i, (void **) &ticket_key));
987 [ + + ]: 1202 : if (now >= ticket_key->intro_timestamp
988 : 1202 : + config->encrypt_decrypt_key_lifetime_in_nanos
989 : 1202 : + config->decrypt_key_lifetime_in_nanos) {
990 : 3 : expired_keys_index[num_of_expired_keys] = i;
991 : 3 : num_of_expired_keys++;
992 : 3 : }
993 : 1202 : }
994 : :
995 : 145 : end:
996 [ + + ]: 148 : for (int j = 0; j < num_of_expired_keys; j++) {
997 [ - + ]: 3 : POSIX_GUARD_RESULT(s2n_array_remove(config->ticket_keys, expired_keys_index[j] - j));
998 : 3 : }
999 : :
1000 : 145 : return 0;
1001 : 145 : }
1002 : :
1003 : : int s2n_config_store_ticket_key(struct s2n_config *config, struct s2n_ticket_key *key)
1004 : 141 : {
1005 : 141 : uint32_t ticket_keys_len = 0;
1006 [ - + ]: 141 : POSIX_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len));
1007 : :
1008 : : /* The ticket key name and secret must both be unique. */
1009 [ + + ]: 1287 : for (uint32_t i = 0; i < ticket_keys_len; i++) {
1010 : 1150 : struct s2n_ticket_key *other_key = NULL;
1011 [ - + ]: 1150 : POSIX_GUARD_RESULT(s2n_array_get(config->ticket_keys, i, (void **) &other_key));
1012 [ + + ][ + - ]: 1150 : POSIX_ENSURE(!s2n_constant_time_equals(key->key_name, other_key->key_name, s2n_array_len(key->key_name)),
1013 : 1147 : S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH);
1014 [ + + ][ + - ]: 1147 : POSIX_ENSURE(!s2n_constant_time_equals(key->aes_key, other_key->aes_key, s2n_array_len(key->aes_key)),
1015 : 1147 : S2N_ERR_TICKET_KEY_NOT_UNIQUE);
1016 : 1147 : }
1017 : :
1018 [ - + ]: 137 : POSIX_GUARD_RESULT(s2n_array_insert_and_copy(config->ticket_keys, ticket_keys_len, key));
1019 : 137 : return S2N_SUCCESS;
1020 : 137 : }
1021 : :
1022 : : int s2n_config_set_initial_ticket_count(struct s2n_config *config, uint8_t num)
1023 : 4 : {
1024 [ - + ][ # # ]: 4 : POSIX_ENSURE_REF(config);
1025 : :
1026 : 4 : config->initial_tickets_to_send = num;
1027 [ - + ]: 4 : POSIX_GUARD(s2n_config_set_session_tickets_onoff(config, true));
1028 : :
1029 : 4 : return S2N_SUCCESS;
1030 : 4 : }
1031 : :
1032 : : int s2n_connection_add_new_tickets_to_send(struct s2n_connection *conn, uint8_t num)
1033 : 123 : {
1034 [ - + ][ # # ]: 123 : POSIX_ENSURE_REF(conn);
1035 [ + + ]: 123 : POSIX_GUARD_RESULT(s2n_psk_validate_keying_material(conn));
1036 : :
1037 : 121 : uint32_t out = conn->tickets_to_send + num;
1038 [ + + ][ + - ]: 121 : POSIX_ENSURE(out <= UINT16_MAX, S2N_ERR_INTEGER_OVERFLOW);
1039 : 120 : conn->tickets_to_send = out;
1040 : :
1041 : 120 : return S2N_SUCCESS;
1042 : 121 : }
1043 : :
1044 : : int s2n_connection_get_tickets_sent(struct s2n_connection *conn, uint16_t *num)
1045 : 34 : {
1046 [ # # ][ - + ]: 34 : POSIX_ENSURE_REF(conn);
1047 [ - + ][ # # ]: 34 : POSIX_ENSURE_REF(num);
1048 [ + - ][ + + ]: 34 : POSIX_ENSURE(conn->mode == S2N_SERVER, S2N_ERR_CLIENT_MODE);
1049 : 33 : *num = conn->tickets_sent;
1050 : 33 : return S2N_SUCCESS;
1051 : 34 : }
1052 : :
1053 : : int s2n_connection_set_server_keying_material_lifetime(struct s2n_connection *conn, uint32_t lifetime_in_secs)
1054 : 9 : {
1055 [ + + ][ + - ]: 9 : POSIX_ENSURE_REF(conn);
1056 : 8 : conn->server_keying_material_lifetime = lifetime_in_secs;
1057 : 8 : return S2N_SUCCESS;
1058 : 9 : }
1059 : :
1060 : : int s2n_config_set_session_ticket_cb(struct s2n_config *config, s2n_session_ticket_fn callback, void *ctx)
1061 : 39 : {
1062 [ + - ][ + + ]: 39 : POSIX_ENSURE_MUT(config);
1063 : :
1064 : 38 : config->session_ticket_cb = callback;
1065 : 38 : config->session_ticket_ctx = ctx;
1066 : 38 : return S2N_SUCCESS;
1067 : 39 : }
1068 : :
1069 : : int s2n_session_ticket_get_data_len(struct s2n_session_ticket *ticket, size_t *data_len)
1070 : 259 : {
1071 [ + + ][ + - ]: 259 : POSIX_ENSURE_REF(ticket);
1072 [ + - ][ + + ]: 258 : POSIX_ENSURE_MUT(data_len);
1073 : :
1074 : 257 : *data_len = ticket->ticket_data.size;
1075 : 257 : return S2N_SUCCESS;
1076 : 258 : }
1077 : :
1078 : : int s2n_session_ticket_get_data(struct s2n_session_ticket *ticket, size_t max_data_len, uint8_t *data)
1079 : 259 : {
1080 [ + + ][ + - ]: 259 : POSIX_ENSURE_REF(ticket);
1081 [ + + ][ + - ]: 258 : POSIX_ENSURE_MUT(data);
1082 : :
1083 [ + - ][ + + ]: 257 : POSIX_ENSURE(ticket->ticket_data.size <= max_data_len, S2N_ERR_SERIALIZED_SESSION_STATE_TOO_LONG);
1084 [ # # ][ - + ]: 256 : POSIX_CHECKED_MEMCPY(data, ticket->ticket_data.data, ticket->ticket_data.size);
[ + - ]
1085 : :
1086 : 256 : return S2N_SUCCESS;
1087 : 256 : }
1088 : :
1089 : : int s2n_session_ticket_get_lifetime(struct s2n_session_ticket *ticket, uint32_t *session_lifetime)
1090 : 22 : {
1091 [ + - ][ + + ]: 22 : POSIX_ENSURE_REF(ticket);
1092 [ + - ][ + + ]: 21 : POSIX_ENSURE_REF(session_lifetime);
1093 : :
1094 : 20 : *session_lifetime = ticket->session_lifetime;
1095 : :
1096 : 20 : return S2N_SUCCESS;
1097 : 21 : }
|