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