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 : 12394 : {
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 [ + + ]: 12394 : if (s2n_connection_is_client_auth_enabled(conn)) {
36 : 4752 : return 0;
37 : 4752 : }
38 : :
39 : 7642 : struct s2n_config *config = conn->config;
40 : :
41 [ - + ][ # # ]: 7642 : POSIX_ENSURE_REF(config);
42 : 7642 : return config->use_session_cache;
43 : 7642 : }
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 : 943 : {
55 [ + + ][ - + ]: 943 : if (!IS_NEGOTIATED(conn) && conn->resume_protocol_version) {
56 : 0 : return conn->resume_protocol_version;
57 : 943 : } else {
58 : 943 : return conn->actual_protocol_version;
59 : 943 : }
60 : 943 : }
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 : 386 : {
89 [ # # ][ - + ]: 386 : RESULT_ENSURE_REF(conn);
90 [ # # ][ - + ]: 386 : RESULT_ENSURE_REF(out);
91 : :
92 [ + + ]: 386 : if (conn->mode != S2N_SERVER) {
93 : 207 : return S2N_RESULT_OK;
94 : 207 : }
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 : 386 : {
109 [ - + ][ # # ]: 386 : RESULT_ENSURE_REF(out);
110 [ # # ][ - + ]: 386 : RESULT_ENSURE_REF(conn);
111 [ - + ][ # # ]: 386 : RESULT_ENSURE_REF(conn->secure);
112 : :
113 : 386 : uint64_t current_time = 0;
114 : 386 : struct s2n_ticket_fields *ticket_fields = &conn->tls13_ticket_fields;
115 : :
116 : : /* Get the time */
117 [ - + ]: 386 : RESULT_GUARD(s2n_config_wall_clock(conn->config, ¤t_time));
118 : :
119 [ - + ]: 386 : RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, S2N_SERIALIZED_FORMAT_TLS13_V1));
120 [ - + ]: 386 : RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, conn->actual_protocol_version));
121 [ - + ]: 386 : RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(out, conn->secure->cipher_suite->iana_value, S2N_TLS_CIPHER_SUITE_LEN));
122 [ - + ]: 386 : RESULT_GUARD_POSIX(s2n_stuffer_write_uint64(out, current_time));
123 [ - + ]: 386 : RESULT_GUARD_POSIX(s2n_stuffer_write_uint32(out, ticket_fields->ticket_age_add));
124 [ # # ][ - + ]: 386 : RESULT_ENSURE_INCLUSIVE_RANGE(1, ticket_fields->session_secret.size, UINT8_MAX);
[ - + ][ # # ]
125 [ - + ]: 386 : RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(out, ticket_fields->session_secret.size));
126 [ - + ]: 386 : RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(out, ticket_fields->session_secret.data, ticket_fields->session_secret.size));
127 [ - + ]: 386 : RESULT_GUARD(s2n_tls13_serialize_keying_material_expiration(conn, current_time, out));
128 : :
129 : 386 : uint32_t server_max_early_data = 0;
130 [ - + ]: 386 : RESULT_GUARD(s2n_early_data_get_server_max_size(conn, &server_max_early_data));
131 [ - + ]: 386 : RESULT_GUARD_POSIX(s2n_stuffer_write_uint32(out, server_max_early_data));
132 [ + + ]: 386 : 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 : 386 : return S2N_RESULT_OK;
141 : 386 : }
142 : :
143 : : static S2N_RESULT s2n_serialize_resumption_state(struct s2n_connection *conn, struct s2n_stuffer *out)
144 : 425 : {
145 [ + + ]: 425 : if (s2n_resume_protocol_version(conn) < S2N_TLS13) {
146 [ - + ]: 39 : RESULT_GUARD_POSIX(s2n_tls12_serialize_resumption_state(conn, out));
147 : 386 : } else {
148 [ - + ]: 386 : RESULT_GUARD(s2n_tls13_serialize_resumption_state(conn, out));
149 : 386 : }
150 : 425 : return S2N_RESULT_OK;
151 : 425 : }
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 : 218 : {
211 : : /* Serialize session ticket */
212 [ + + ][ + + ]: 218 : if (conn->config->use_tickets && conn->client_ticket.size > 0) {
213 [ - + ]: 213 : POSIX_GUARD(s2n_stuffer_write_uint8(to, S2N_STATE_WITH_SESSION_TICKET));
214 [ - + ]: 213 : POSIX_GUARD(s2n_stuffer_write_uint16(to, conn->client_ticket.size));
215 [ - + ]: 213 : POSIX_GUARD(s2n_stuffer_write(to, &conn->client_ticket));
216 : 213 : } 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 [ - + ]: 218 : POSIX_GUARD_RESULT(s2n_serialize_resumption_state(conn, to));
226 : :
227 : 218 : return 0;
228 : 218 : }
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 : 157 : {
509 [ # # ][ - + ]: 157 : POSIX_ENSURE_REF(conn);
510 [ # # ][ - + ]: 157 : POSIX_ENSURE_REF(session);
511 : :
512 : 157 : DEFER_CLEANUP(struct s2n_blob session_data = { 0 }, s2n_free);
513 [ - + ]: 157 : POSIX_GUARD(s2n_alloc(&session_data, length));
514 [ # # ][ - + ]: 157 : POSIX_CHECKED_MEMCPY(session_data.data, session, length);
[ + - ]
515 : :
516 : 157 : struct s2n_stuffer from = { 0 };
517 [ - + ]: 157 : POSIX_GUARD(s2n_stuffer_init(&from, &session_data));
518 [ - + ]: 157 : POSIX_GUARD(s2n_stuffer_write(&from, &session_data));
519 [ + + ]: 157 : POSIX_GUARD(s2n_client_deserialize_resumption_state(conn, &from));
520 : 152 : return 0;
521 : 157 : }
522 : :
523 : : int s2n_connection_get_session(struct s2n_connection *conn, uint8_t *session, size_t max_length)
524 : 470 : {
525 [ - + ][ # # ]: 470 : POSIX_ENSURE_REF(conn);
526 [ - + ][ # # ]: 470 : POSIX_ENSURE_REF(session);
527 : :
528 : 470 : const int len = s2n_connection_get_session_length(conn);
529 [ - + ]: 470 : POSIX_GUARD(len);
530 : :
531 [ + + ]: 470 : if (len == 0) {
532 : 2 : return 0;
533 : 2 : }
534 : :
535 [ + - ][ + + ]: 468 : POSIX_ENSURE((size_t) len <= max_length, S2N_ERR_SERIALIZED_SESSION_STATE_TOO_LONG);
536 : :
537 : 467 : struct s2n_blob serialized_data = { 0 };
538 [ - + ]: 467 : POSIX_GUARD(s2n_blob_init(&serialized_data, session, len));
539 [ - + ]: 467 : POSIX_GUARD(s2n_blob_zero(&serialized_data));
540 : :
541 : 467 : struct s2n_stuffer to = { 0 };
542 [ - + ]: 467 : POSIX_GUARD(s2n_stuffer_init(&to, &serialized_data));
543 [ - + ]: 467 : POSIX_GUARD(s2n_client_serialize_resumption_state(conn, &to));
544 : :
545 : 467 : return len;
546 : 467 : }
547 : :
548 : : int s2n_connection_get_session_ticket_lifetime_hint(struct s2n_connection *conn)
549 : 19 : {
550 [ # # ][ - + ]: 19 : POSIX_ENSURE_REF(conn);
551 [ + + ][ + - ]: 19 : S2N_ERROR_IF(!(conn->config->use_tickets && conn->client_ticket.size > 0), S2N_ERR_SESSION_TICKET_NOT_SUPPORTED);
[ + - ]
552 : :
553 : : /* Session resumption using session ticket */
554 : 18 : return conn->ticket_lifetime_hint;
555 : 19 : }
556 : :
557 : : S2N_RESULT s2n_connection_get_session_state_size(struct s2n_connection *conn, size_t *state_size)
558 : 1227 : {
559 [ + + ][ + - ]: 1227 : RESULT_ENSURE_REF(conn);
560 [ - + ][ # # ]: 1226 : RESULT_ENSURE_REF(conn->secure);
561 [ + + ][ + - ]: 1226 : RESULT_ENSURE_REF(state_size);
562 : :
563 [ + + ]: 1225 : if (s2n_resume_protocol_version(conn) < S2N_TLS13) {
564 : 64 : *state_size = S2N_TLS12_STATE_SIZE_IN_BYTES;
565 : 64 : return S2N_RESULT_OK;
566 : 64 : }
567 : :
568 : 1161 : *state_size = S2N_TLS13_FIXED_STATE_SIZE;
569 : :
570 : 1161 : uint8_t secret_size = 0;
571 [ # # ][ - + ]: 1161 : RESULT_ENSURE_REF(conn->secure->cipher_suite);
572 [ - + ]: 1161 : RESULT_GUARD_POSIX(s2n_hmac_digest_size(conn->secure->cipher_suite->prf_alg, &secret_size));
573 : 1161 : *state_size += secret_size;
574 : :
575 : 1161 : uint32_t server_max_early_data = 0;
576 [ - + ]: 1161 : RESULT_GUARD(s2n_early_data_get_server_max_size(conn, &server_max_early_data));
577 [ + + ]: 1161 : if (server_max_early_data > 0) {
578 : 386 : *state_size += S2N_TLS13_FIXED_EARLY_DATA_STATE_SIZE
579 : 386 : + strlen(conn->application_protocol)
580 : 386 : + conn->server_early_data_context.size;
581 : 386 : }
582 : :
583 : 1161 : return S2N_RESULT_OK;
584 : 1161 : }
585 : :
586 : : static S2N_RESULT s2n_connection_get_session_length_impl(struct s2n_connection *conn, size_t *length)
587 : 437 : {
588 [ # # ][ - + ]: 437 : RESULT_ENSURE_REF(conn);
589 [ - + ][ # # ]: 437 : RESULT_ENSURE_REF(conn->config);
590 [ # # ][ - + ]: 437 : RESULT_ENSURE_REF(length);
591 : 437 : *length = 0;
592 : :
593 [ + + ][ + + ]: 437 : if (conn->config->use_tickets && conn->client_ticket.size > 0) {
594 : 423 : size_t session_state_size = 0;
595 [ - + ]: 423 : RESULT_GUARD(s2n_connection_get_session_state_size(conn, &session_state_size));
596 : 423 : *length = S2N_STATE_FORMAT_LEN + S2N_SESSION_TICKET_SIZE_LEN + conn->client_ticket.size + session_state_size;
597 [ + + ][ + + ]: 423 : } else if (conn->session_id_len > 0 && conn->actual_protocol_version < S2N_TLS13) {
598 : 10 : *length = S2N_STATE_FORMAT_LEN + sizeof(conn->session_id_len) + conn->session_id_len + S2N_TLS12_STATE_SIZE_IN_BYTES;
599 : 10 : }
600 : 437 : return S2N_RESULT_OK;
601 : 437 : }
602 : :
603 : : int s2n_connection_get_session_length(struct s2n_connection *conn)
604 : 948 : {
605 : 948 : size_t length = 0;
606 [ + + ]: 948 : if (s2n_result_is_ok(s2n_connection_get_session_length_impl(conn, &length))) {
607 : 947 : return length;
608 : 947 : }
609 : 1 : return 0;
610 : 948 : }
611 : :
612 : : int s2n_connection_is_session_resumed(struct s2n_connection *conn)
613 : 3685 : {
614 [ + + ][ + + ]: 3685 : return conn && IS_RESUMPTION_HANDSHAKE(conn)
[ + - ]
615 [ + + ][ + + ]: 3685 : && (conn->actual_protocol_version < S2N_TLS13 || conn->psk_params.type == S2N_PSK_TYPE_RESUMPTION);
616 : 3685 : }
617 : :
618 : : int s2n_connection_is_ocsp_stapled(struct s2n_connection *conn)
619 : 18 : {
620 [ - + ][ # # ]: 18 : POSIX_ENSURE_REF(conn);
621 : :
622 [ + + ]: 18 : if (conn->actual_protocol_version >= S2N_TLS13) {
623 [ + + ][ + - ]: 10 : return (s2n_server_can_send_ocsp(conn) || s2n_server_sent_ocsp(conn));
[ + - ][ + + ]
[ + + ][ + + ]
624 : 10 : } else {
625 : 8 : return IS_OCSP_STAPLED(conn);
626 : 8 : }
627 : 18 : }
628 : :
629 : : S2N_RESULT s2n_config_is_encrypt_key_available(struct s2n_config *config)
630 : 26 : {
631 [ - + ][ # # ]: 26 : RESULT_ENSURE_REF(config);
632 : :
633 : 26 : uint64_t now = 0;
634 : 26 : struct s2n_ticket_key *ticket_key = NULL;
635 [ - + ]: 26 : RESULT_GUARD(s2n_config_wall_clock(config, &now));
636 [ - + ][ # # ]: 26 : RESULT_ENSURE_REF(config->ticket_keys);
637 : :
638 : 26 : uint32_t ticket_keys_len = 0;
639 [ - + ]: 26 : RESULT_GUARD(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len));
640 : :
641 [ + + ]: 27 : for (uint32_t i = ticket_keys_len; i > 0; i--) {
642 : 21 : uint32_t idx = i - 1;
643 [ - + ]: 21 : RESULT_GUARD(s2n_array_get(config->ticket_keys, idx, (void **) &ticket_key));
644 : 21 : uint64_t key_intro_time = ticket_key->intro_timestamp;
645 : :
646 [ + + ]: 21 : if (key_intro_time <= now
647 [ + - ]: 21 : && now < key_intro_time + config->encrypt_decrypt_key_lifetime_in_nanos) {
648 : 20 : return S2N_RESULT_OK;
649 : 20 : }
650 : 21 : }
651 : :
652 [ + - ]: 6 : RESULT_BAIL(S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY);
653 : 6 : }
654 : :
655 : : /* This function is used in s2n_get_ticket_encrypt_decrypt_key to compute the weight
656 : : * of the keys and to choose a single key from all of the encrypt-decrypt keys.
657 : : * Higher the weight of the key, higher the probability of being picked.
658 : : */
659 : : int s2n_compute_weight_of_encrypt_decrypt_keys(struct s2n_config *config,
660 : : uint8_t *encrypt_decrypt_keys_index,
661 : : uint8_t num_encrypt_decrypt_keys,
662 : : uint64_t now)
663 : 3 : {
664 : 3 : double total_weight = 0;
665 : 3 : struct s2n_ticket_key_weight ticket_keys_weight[S2N_MAX_TICKET_KEYS];
666 : 3 : struct s2n_ticket_key *ticket_key = NULL;
667 : :
668 : : /* Compute weight of encrypt-decrypt keys */
669 [ + + ]: 11 : for (int i = 0; i < num_encrypt_decrypt_keys; i++) {
670 [ - + ]: 8 : POSIX_GUARD_RESULT(s2n_array_get(config->ticket_keys, encrypt_decrypt_keys_index[i], (void **) &ticket_key));
671 : :
672 : 8 : uint64_t key_intro_time = ticket_key->intro_timestamp;
673 : 8 : uint64_t key_encryption_peak_time = key_intro_time + (config->encrypt_decrypt_key_lifetime_in_nanos / 2);
674 : :
675 : : /* The % of encryption using this key is linearly increasing */
676 [ + + ]: 8 : if (now < key_encryption_peak_time) {
677 : 4 : ticket_keys_weight[i].key_weight = now - key_intro_time;
678 : 4 : } else {
679 : : /* The % of encryption using this key is linearly decreasing */
680 : 4 : ticket_keys_weight[i].key_weight = (config->encrypt_decrypt_key_lifetime_in_nanos / 2) - (now - key_encryption_peak_time);
681 : 4 : }
682 : :
683 : 8 : ticket_keys_weight[i].key_index = encrypt_decrypt_keys_index[i];
684 : 8 : total_weight += ticket_keys_weight[i].key_weight;
685 : 8 : }
686 : :
687 : : /* Pick a random number in [0, 1). Using 53 bits (IEEE 754 double-precision floats). */
688 : 3 : uint64_t random_int = 0;
689 [ - + ]: 3 : POSIX_GUARD_RESULT(s2n_public_random(pow(2, 53), &random_int));
690 : 3 : double random = (double) random_int / (double) pow(2, 53);
691 : :
692 : : /* Compute cumulative weight of encrypt-decrypt keys */
693 [ + - ]: 7 : for (int i = 0; i < num_encrypt_decrypt_keys; i++) {
694 : 7 : ticket_keys_weight[i].key_weight = ticket_keys_weight[i].key_weight / total_weight;
695 : :
696 [ + + ]: 7 : if (i > 0) {
697 : 4 : ticket_keys_weight[i].key_weight += ticket_keys_weight[i - 1].key_weight;
698 : 4 : }
699 : :
700 [ + + ]: 7 : if (ticket_keys_weight[i].key_weight > random) {
701 : 3 : return ticket_keys_weight[i].key_index;
702 : 3 : }
703 : 7 : }
704 : :
705 [ # # ]: 0 : POSIX_BAIL(S2N_ERR_ENCRYPT_DECRYPT_KEY_SELECTION_FAILED);
706 : 0 : }
707 : :
708 : : /* This function is used in s2n_resume_encrypt_session_ticket in order for s2n to
709 : : * choose a key in encrypt-decrypt state from all of the keys added to config
710 : : */
711 : : struct s2n_ticket_key *s2n_get_ticket_encrypt_decrypt_key(struct s2n_config *config)
712 : 469 : {
713 : 469 : uint8_t num_encrypt_decrypt_keys = 0;
714 : 469 : uint8_t encrypt_decrypt_keys_index[S2N_MAX_TICKET_KEYS] = { 0 };
715 : 469 : struct s2n_ticket_key *ticket_key = NULL;
716 : :
717 : 469 : uint64_t now = 0;
718 [ - + ]: 469 : PTR_GUARD_RESULT(s2n_config_wall_clock(config, &now));
719 [ + - ][ + + ]: 469 : PTR_ENSURE_REF(config->ticket_keys);
720 : :
721 : 468 : uint32_t ticket_keys_len = 0;
722 [ - + ]: 468 : PTR_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len));
723 : :
724 [ + + ]: 939 : for (uint32_t i = ticket_keys_len; i > 0; i--) {
725 : 471 : uint32_t idx = i - 1;
726 [ - + ]: 471 : PTR_GUARD_RESULT(s2n_array_get(config->ticket_keys, idx, (void **) &ticket_key));
727 : 471 : uint64_t key_intro_time = ticket_key->intro_timestamp;
728 : :
729 : : /* A key can be used at its intro time (<=) and it can be used up to (<)
730 : : * its expiration time.
731 : : */
732 [ + + ]: 471 : if (key_intro_time <= now
733 [ + + ]: 471 : && now < key_intro_time + config->encrypt_decrypt_key_lifetime_in_nanos) {
734 : 466 : encrypt_decrypt_keys_index[num_encrypt_decrypt_keys] = idx;
735 : 466 : num_encrypt_decrypt_keys++;
736 : 466 : }
737 : 471 : }
738 : :
739 [ + + ]: 468 : if (num_encrypt_decrypt_keys == 0) {
740 [ + - ]: 7 : PTR_BAIL(S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY);
741 : 7 : }
742 : :
743 [ + + ]: 461 : if (num_encrypt_decrypt_keys == 1) {
744 [ - + ]: 458 : PTR_GUARD_RESULT(s2n_array_get(config->ticket_keys, encrypt_decrypt_keys_index[0], (void **) &ticket_key));
745 : 458 : return ticket_key;
746 : 458 : }
747 : :
748 : 3 : int8_t idx = 0;
749 [ - + ]: 3 : PTR_GUARD_POSIX(idx = s2n_compute_weight_of_encrypt_decrypt_keys(config, encrypt_decrypt_keys_index, num_encrypt_decrypt_keys, now));
750 : :
751 [ - + ]: 3 : PTR_GUARD_RESULT(s2n_array_get(config->ticket_keys, idx, (void **) &ticket_key));
752 : 3 : return ticket_key;
753 : 3 : }
754 : :
755 : : /* This function is used in s2n_resume_decrypt_session in order for s2n to
756 : : * find the matching key that was used for encryption.
757 : : */
758 : : struct s2n_ticket_key *s2n_find_ticket_key(struct s2n_config *config, const uint8_t name[S2N_TICKET_KEY_NAME_LEN])
759 : 162 : {
760 : 162 : uint64_t now = 0;
761 : 162 : struct s2n_ticket_key *ticket_key = NULL;
762 [ - + ]: 162 : PTR_GUARD_RESULT(s2n_config_wall_clock(config, &now));
763 [ - + ][ # # ]: 162 : PTR_ENSURE_REF(config->ticket_keys);
764 : :
765 : 162 : uint32_t ticket_keys_len = 0;
766 [ - + ]: 162 : PTR_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len));
767 : :
768 [ + + ]: 165 : for (uint32_t i = 0; i < ticket_keys_len; i++) {
769 [ - + ]: 158 : PTR_GUARD_RESULT(s2n_array_get(config->ticket_keys, i, (void **) &ticket_key));
770 : :
771 [ + + ]: 158 : if (s2n_constant_time_equals(ticket_key->key_name, name, S2N_TICKET_KEY_NAME_LEN)) {
772 : : /* Check to see if the key has expired */
773 [ - + ]: 155 : if (now >= ticket_key->intro_timestamp
774 : 155 : + config->encrypt_decrypt_key_lifetime_in_nanos
775 : 155 : + config->decrypt_key_lifetime_in_nanos) {
776 : 0 : return NULL;
777 : 0 : }
778 : :
779 : 155 : return ticket_key;
780 : 155 : }
781 : 158 : }
782 : :
783 : 7 : return NULL;
784 : 162 : }
785 : :
786 : : struct s2n_unique_ticket_key {
787 : : struct s2n_blob initial_key;
788 : : uint8_t info[S2N_AES256_KEY_LEN];
789 : : uint8_t output_key[S2N_AES256_KEY_LEN];
790 : : };
791 : :
792 : : /* Ensures that a session ticket encryption key is used only once per ticket.
793 : : *
794 : : * The AES-GCM encryption scheme breaks if the same nonce is used with the same key more than once.
795 : : * As the number of TLS connections increases per second, it becomes more probable that the same
796 : : * random nonce will be generated twice and used with the same ticket key.
797 : : * To avoid this we generate a unique session ticket encryption key for each ticket.
798 : : **/
799 : : static S2N_RESULT s2n_resume_generate_unique_ticket_key(struct s2n_unique_ticket_key *key)
800 : 232 : {
801 [ - + ][ # # ]: 232 : RESULT_ENSURE_REF(key);
802 : :
803 : 232 : struct s2n_blob out_key_blob = { 0 };
804 [ - + ]: 232 : RESULT_GUARD_POSIX(s2n_blob_init(&out_key_blob, key->output_key, sizeof(key->output_key)));
805 : 232 : struct s2n_blob info_blob = { 0 };
806 [ - + ]: 232 : RESULT_GUARD_POSIX(s2n_blob_init(&info_blob, key->info, sizeof(key->info)));
807 : 232 : struct s2n_blob salt = { 0 };
808 [ - + ]: 232 : RESULT_GUARD_POSIX(s2n_blob_init(&salt, NULL, 0));
809 : :
810 : 232 : DEFER_CLEANUP(struct s2n_hmac_state hmac = { 0 }, s2n_hmac_free);
811 : : /* TODO: There may be an optimization here to reuse existing hmac memory instead of
812 : : * creating an entirely new hmac. See: https://github.com/aws/s2n-tls/issues/3206 */
813 [ - + ]: 232 : RESULT_GUARD_POSIX(s2n_hmac_new(&hmac));
814 [ - + ]: 232 : RESULT_GUARD_POSIX(s2n_hkdf(&hmac, S2N_HMAC_SHA256, &salt, &key->initial_key, &info_blob, &out_key_blob));
815 : :
816 : 232 : return S2N_RESULT_OK;
817 : 232 : }
818 : :
819 : : S2N_RESULT s2n_resume_encrypt_session_ticket(struct s2n_connection *conn,
820 : : struct s2n_ticket_key *key, struct s2n_stuffer *to)
821 : 463 : {
822 [ - + ][ # # ]: 463 : RESULT_ENSURE_REF(conn);
823 [ - + ][ # # ]: 463 : RESULT_ENSURE_REF(to);
824 : :
825 [ + + ][ + - ]: 463 : RESULT_ENSURE(key != NULL, S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY);
826 : :
827 : : /* Generate unique per-ticket encryption key */
828 : 460 : struct s2n_unique_ticket_key ticket_key = { 0 };
829 [ - + ]: 460 : RESULT_GUARD_POSIX(s2n_blob_init(&ticket_key.initial_key, key->aes_key, sizeof(key->aes_key)));
830 : 460 : struct s2n_blob info_blob = { 0 };
831 [ - + ]: 460 : RESULT_GUARD_POSIX(s2n_blob_init(&info_blob, ticket_key.info, sizeof(ticket_key.info)));
832 [ - + ]: 460 : RESULT_GUARD(s2n_get_public_random_data(&info_blob));
833 [ - + ]: 460 : RESULT_GUARD(s2n_resume_generate_unique_ticket_key(&ticket_key));
834 : :
835 : : /* Initialize AES key */
836 : 460 : struct s2n_blob aes_key_blob = { 0 };
837 [ - + ]: 460 : RESULT_GUARD_POSIX(s2n_blob_init(&aes_key_blob, ticket_key.output_key, sizeof(ticket_key.output_key)));
838 : 460 : DEFER_CLEANUP(struct s2n_session_key aes_ticket_key = { 0 }, s2n_session_key_free);
839 [ - + ]: 460 : RESULT_GUARD_POSIX(s2n_session_key_alloc(&aes_ticket_key));
840 [ - + ]: 460 : RESULT_GUARD(s2n_aes256_gcm.init(&aes_ticket_key));
841 [ - + ]: 460 : RESULT_GUARD(s2n_aes256_gcm.set_encryption_key(&aes_ticket_key, &aes_key_blob));
842 : :
843 : : /* Ensure we never encrypt with a zero-filled key */
844 : 460 : uint8_t zero_block[S2N_AES256_KEY_LEN] = { 0 };
845 [ + - ][ + + ]: 460 : RESULT_ENSURE(!s2n_constant_time_equals(key->aes_key, zero_block, S2N_AES256_KEY_LEN),
846 : 459 : S2N_ERR_KEY_CHECK);
847 : :
848 : : /* Initialize Additional Authenticated Data */
849 : 459 : uint8_t aad_data[S2N_TICKET_AAD_LEN] = { 0 };
850 : 459 : struct s2n_blob aad_blob = { 0 };
851 [ - + ]: 459 : RESULT_GUARD_POSIX(s2n_blob_init(&aad_blob, aad_data, sizeof(aad_data)));
852 : 459 : struct s2n_stuffer aad = { 0 };
853 [ - + ]: 459 : RESULT_GUARD_POSIX(s2n_stuffer_init(&aad, &aad_blob));
854 [ - + ]: 459 : RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&aad, key->implicit_aad, sizeof(key->implicit_aad)));
855 [ - + ]: 459 : RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&aad, key->key_name, sizeof(key->key_name)));
856 : :
857 : : /* Write version number */
858 [ + + ]: 459 : RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(to, S2N_PRE_ENCRYPTED_STATE_V1));
859 : :
860 : : /* Write key name */
861 [ - + ]: 458 : RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(to, key->key_name, sizeof(key->key_name)));
862 : :
863 : : /* Write parameter needed to generate unique ticket key */
864 [ - + ]: 458 : RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(to, ticket_key.info, sizeof(ticket_key.info)));
865 : :
866 : : /* Write IV */
867 : 458 : uint8_t iv_data[S2N_TLS_GCM_IV_LEN] = { 0 };
868 : 458 : struct s2n_blob iv = { 0 };
869 [ - + ]: 458 : RESULT_GUARD_POSIX(s2n_blob_init(&iv, iv_data, sizeof(iv_data)));
870 [ - + ]: 458 : RESULT_GUARD(s2n_get_public_random_data(&iv));
871 [ - + ]: 458 : RESULT_GUARD_POSIX(s2n_stuffer_write(to, &iv));
872 : :
873 : : /* Write serialized session state */
874 : 458 : uint32_t plaintext_state_size = s2n_stuffer_data_available(to);
875 [ - + ]: 458 : RESULT_GUARD(s2n_serialize_resumption_state(conn, to));
876 [ - + ]: 458 : RESULT_GUARD_POSIX(s2n_stuffer_skip_write(to, S2N_TLS_GCM_TAG_LEN));
877 : :
878 : : /* Initialize blob to be encrypted */
879 : 458 : struct s2n_blob state_blob = { 0 };
880 : 458 : struct s2n_stuffer copy_for_encryption = *to;
881 [ - + ]: 458 : RESULT_GUARD_POSIX(s2n_stuffer_skip_read(©_for_encryption, plaintext_state_size));
882 : 458 : uint32_t state_blob_size = s2n_stuffer_data_available(©_for_encryption);
883 : 458 : uint8_t *state_blob_data = s2n_stuffer_raw_read(©_for_encryption, state_blob_size);
884 [ # # ][ - + ]: 458 : RESULT_ENSURE_REF(state_blob_data);
885 [ - + ]: 458 : RESULT_GUARD_POSIX(s2n_blob_init(&state_blob, state_blob_data, state_blob_size));
886 : :
887 [ - + ]: 458 : RESULT_GUARD_POSIX(s2n_aes256_gcm.io.aead.encrypt(&aes_ticket_key, &iv, &aad_blob, &state_blob, &state_blob));
888 : :
889 : 458 : return S2N_RESULT_OK;
890 : 458 : }
891 : :
892 : : S2N_RESULT s2n_resume_decrypt_session(struct s2n_connection *conn, struct s2n_stuffer *from)
893 : 171 : {
894 [ - + ][ # # ]: 171 : RESULT_ENSURE_REF(conn);
895 [ # # ][ - + ]: 171 : RESULT_ENSURE_REF(from);
896 [ - + ][ # # ]: 171 : RESULT_ENSURE_REF(conn->config);
897 : :
898 : : /* Read version number */
899 : 171 : uint8_t version = 0;
900 [ - + ]: 171 : RESULT_GUARD_POSIX(s2n_stuffer_read_uint8(from, &version));
901 [ + + ][ + - ]: 171 : RESULT_ENSURE_EQ(version, S2N_PRE_ENCRYPTED_STATE_V1);
902 : :
903 : : /* Read key name */
904 : 162 : uint8_t key_name[S2N_TICKET_KEY_NAME_LEN] = { 0 };
905 [ - + ]: 162 : RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(from, key_name, sizeof(key_name)));
906 : :
907 : 162 : struct s2n_ticket_key *key = s2n_find_ticket_key(conn->config, key_name);
908 : : /* Key has expired; do full handshake */
909 [ + + ][ + - ]: 162 : RESULT_ENSURE(key != NULL, S2N_ERR_KEY_USED_IN_SESSION_TICKET_NOT_FOUND);
910 : :
911 : 155 : struct s2n_unique_ticket_key ticket_key = { 0 };
912 [ - + ]: 155 : RESULT_GUARD_POSIX(s2n_blob_init(&ticket_key.initial_key, key->aes_key, sizeof(key->aes_key)));
913 [ - + ]: 155 : RESULT_GUARD_POSIX(s2n_stuffer_read_bytes(from, ticket_key.info, sizeof(ticket_key.info)));
914 [ - + ]: 155 : RESULT_GUARD(s2n_resume_generate_unique_ticket_key(&ticket_key));
915 : :
916 : : /* Read IV */
917 : 155 : uint8_t iv_data[S2N_TLS_GCM_IV_LEN] = { 0 };
918 : 155 : struct s2n_blob iv = { 0 };
919 [ - + ]: 155 : RESULT_GUARD_POSIX(s2n_blob_init(&iv, iv_data, sizeof(iv_data)));
920 [ - + ]: 155 : RESULT_GUARD_POSIX(s2n_stuffer_read(from, &iv));
921 : :
922 : : /* Initialize AES key */
923 : 155 : struct s2n_blob aes_key_blob = { 0 };
924 [ - + ]: 155 : RESULT_GUARD_POSIX(s2n_blob_init(&aes_key_blob, ticket_key.output_key, sizeof(ticket_key.output_key)));
925 : 155 : DEFER_CLEANUP(struct s2n_session_key aes_ticket_key = { 0 }, s2n_session_key_free);
926 [ - + ]: 155 : RESULT_GUARD_POSIX(s2n_session_key_alloc(&aes_ticket_key));
927 [ - + ]: 155 : RESULT_GUARD(s2n_aes256_gcm.init(&aes_ticket_key));
928 [ - + ]: 155 : RESULT_GUARD(s2n_aes256_gcm.set_decryption_key(&aes_ticket_key, &aes_key_blob));
929 : :
930 : : /* Initialize Additional Authenticated Data */
931 : 155 : uint8_t aad_data[S2N_TICKET_AAD_LEN] = { 0 };
932 : 155 : struct s2n_blob aad_blob = { 0 };
933 [ - + ]: 155 : RESULT_GUARD_POSIX(s2n_blob_init(&aad_blob, aad_data, sizeof(aad_data)));
934 : 155 : struct s2n_stuffer aad = { 0 };
935 [ - + ]: 155 : RESULT_GUARD_POSIX(s2n_stuffer_init(&aad, &aad_blob));
936 [ - + ]: 155 : RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&aad, key->implicit_aad, sizeof(key->implicit_aad)));
937 [ - + ]: 155 : RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&aad, key->key_name, sizeof(key->key_name)));
938 : :
939 : : /* Initialize blob to be decrypted */
940 : 155 : struct s2n_blob en_blob = { 0 };
941 : 155 : uint32_t en_blob_size = s2n_stuffer_data_available(from);
942 : 155 : uint8_t *en_blob_data = s2n_stuffer_raw_read(from, en_blob_size);
943 [ # # ][ - + ]: 155 : RESULT_ENSURE_REF(en_blob_data);
944 [ - + ]: 155 : RESULT_GUARD_POSIX(s2n_blob_init(&en_blob, en_blob_data, en_blob_size));
945 : :
946 [ + + ]: 155 : RESULT_GUARD_POSIX(s2n_aes256_gcm.io.aead.decrypt(&aes_ticket_key, &iv, &aad_blob, &en_blob, &en_blob));
947 : :
948 : : /* Parse decrypted state */
949 : 152 : struct s2n_blob state_blob = { 0 };
950 : 152 : uint32_t state_blob_size = en_blob_size - S2N_TLS_GCM_TAG_LEN;
951 [ - + ]: 152 : RESULT_GUARD_POSIX(s2n_blob_init(&state_blob, en_blob.data, state_blob_size));
952 : 152 : struct s2n_stuffer state_stuffer = { 0 };
953 [ - + ]: 152 : RESULT_GUARD_POSIX(s2n_stuffer_init(&state_stuffer, &state_blob));
954 [ - + ]: 152 : RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&state_stuffer, state_blob_size));
955 [ + + ]: 152 : RESULT_GUARD(s2n_deserialize_resumption_state(conn, &from->blob, &state_stuffer));
956 : :
957 : 148 : return S2N_RESULT_OK;
958 : 152 : }
959 : :
960 : : /* This function is used to remove all or just one expired key from server config */
961 : : int s2n_config_wipe_expired_ticket_crypto_keys(struct s2n_config *config, int8_t expired_key_index)
962 : 140 : {
963 : 140 : int num_of_expired_keys = 0;
964 : 140 : int expired_keys_index[S2N_MAX_TICKET_KEYS];
965 : 140 : struct s2n_ticket_key *ticket_key = NULL;
966 : :
967 [ - + ]: 140 : if (expired_key_index != -1) {
968 : 0 : expired_keys_index[num_of_expired_keys] = expired_key_index;
969 : 0 : num_of_expired_keys++;
970 : :
971 : 0 : goto end;
972 : 0 : }
973 : :
974 : 140 : uint64_t now = 0;
975 [ - + ]: 140 : POSIX_GUARD_RESULT(s2n_config_wall_clock(config, &now));
976 [ - + ][ # # ]: 140 : POSIX_ENSURE_REF(config->ticket_keys);
977 : :
978 : 140 : uint32_t ticket_keys_len = 0;
979 [ - + ]: 140 : POSIX_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len));
980 [ + + ]: 1342 : for (uint32_t i = 0; i < ticket_keys_len; i++) {
981 [ - + ]: 1202 : POSIX_GUARD_RESULT(s2n_array_get(config->ticket_keys, i, (void **) &ticket_key));
982 [ + + ]: 1202 : if (now >= ticket_key->intro_timestamp
983 : 1202 : + config->encrypt_decrypt_key_lifetime_in_nanos
984 : 1202 : + config->decrypt_key_lifetime_in_nanos) {
985 : 3 : expired_keys_index[num_of_expired_keys] = i;
986 : 3 : num_of_expired_keys++;
987 : 3 : }
988 : 1202 : }
989 : :
990 : 140 : end:
991 [ + + ]: 143 : for (int j = 0; j < num_of_expired_keys; j++) {
992 [ - + ]: 3 : POSIX_GUARD_RESULT(s2n_array_remove(config->ticket_keys, expired_keys_index[j] - j));
993 : 3 : }
994 : :
995 : 140 : return 0;
996 : 140 : }
997 : :
998 : : int s2n_config_store_ticket_key(struct s2n_config *config, struct s2n_ticket_key *key)
999 : 136 : {
1000 : 136 : uint32_t ticket_keys_len = 0;
1001 [ - + ]: 136 : POSIX_GUARD_RESULT(s2n_array_num_elements(config->ticket_keys, &ticket_keys_len));
1002 : :
1003 : : /* The ticket key name and secret must both be unique. */
1004 [ + + ]: 1282 : for (uint32_t i = 0; i < ticket_keys_len; i++) {
1005 : 1150 : struct s2n_ticket_key *other_key = NULL;
1006 [ - + ]: 1150 : POSIX_GUARD_RESULT(s2n_array_get(config->ticket_keys, i, (void **) &other_key));
1007 [ + + ][ + - ]: 1150 : POSIX_ENSURE(!s2n_constant_time_equals(key->key_name, other_key->key_name, s2n_array_len(key->key_name)),
1008 : 1147 : S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH);
1009 [ + + ][ + - ]: 1147 : POSIX_ENSURE(!s2n_constant_time_equals(key->aes_key, other_key->aes_key, s2n_array_len(key->aes_key)),
1010 : 1147 : S2N_ERR_TICKET_KEY_NOT_UNIQUE);
1011 : 1147 : }
1012 : :
1013 [ - + ]: 132 : POSIX_GUARD_RESULT(s2n_array_insert_and_copy(config->ticket_keys, ticket_keys_len, key));
1014 : 132 : return S2N_SUCCESS;
1015 : 132 : }
1016 : :
1017 : : int s2n_config_set_initial_ticket_count(struct s2n_config *config, uint8_t num)
1018 : 4 : {
1019 [ - + ][ # # ]: 4 : POSIX_ENSURE_REF(config);
1020 : :
1021 : 4 : config->initial_tickets_to_send = num;
1022 [ - + ]: 4 : POSIX_GUARD(s2n_config_set_session_tickets_onoff(config, true));
1023 : :
1024 : 4 : return S2N_SUCCESS;
1025 : 4 : }
1026 : :
1027 : : int s2n_connection_add_new_tickets_to_send(struct s2n_connection *conn, uint8_t num)
1028 : 122 : {
1029 [ - + ][ # # ]: 122 : POSIX_ENSURE_REF(conn);
1030 [ + + ]: 122 : POSIX_GUARD_RESULT(s2n_psk_validate_keying_material(conn));
1031 : :
1032 : 120 : uint32_t out = conn->tickets_to_send + num;
1033 [ + - ][ + + ]: 120 : POSIX_ENSURE(out <= UINT16_MAX, S2N_ERR_INTEGER_OVERFLOW);
1034 : 119 : conn->tickets_to_send = out;
1035 : :
1036 : 119 : return S2N_SUCCESS;
1037 : 120 : }
1038 : :
1039 : : int s2n_connection_get_tickets_sent(struct s2n_connection *conn, uint16_t *num)
1040 : 33 : {
1041 [ - + ][ # # ]: 33 : POSIX_ENSURE_REF(conn);
1042 [ # # ][ - + ]: 33 : POSIX_ENSURE_REF(num);
1043 [ + + ][ + - ]: 33 : POSIX_ENSURE(conn->mode == S2N_SERVER, S2N_ERR_CLIENT_MODE);
1044 : 32 : *num = conn->tickets_sent;
1045 : 32 : return S2N_SUCCESS;
1046 : 33 : }
1047 : :
1048 : : int s2n_connection_set_server_keying_material_lifetime(struct s2n_connection *conn, uint32_t lifetime_in_secs)
1049 : 9 : {
1050 [ + + ][ + - ]: 9 : POSIX_ENSURE_REF(conn);
1051 : 8 : conn->server_keying_material_lifetime = lifetime_in_secs;
1052 : 8 : return S2N_SUCCESS;
1053 : 9 : }
1054 : :
1055 : : int s2n_config_set_session_ticket_cb(struct s2n_config *config, s2n_session_ticket_fn callback, void *ctx)
1056 : 37 : {
1057 [ + + ][ + - ]: 37 : POSIX_ENSURE_MUT(config);
1058 : :
1059 : 36 : config->session_ticket_cb = callback;
1060 : 36 : config->session_ticket_ctx = ctx;
1061 : 36 : return S2N_SUCCESS;
1062 : 37 : }
1063 : :
1064 : : int s2n_session_ticket_get_data_len(struct s2n_session_ticket *ticket, size_t *data_len)
1065 : 257 : {
1066 [ + + ][ + - ]: 257 : POSIX_ENSURE_REF(ticket);
1067 [ + + ][ + - ]: 256 : POSIX_ENSURE_MUT(data_len);
1068 : :
1069 : 255 : *data_len = ticket->ticket_data.size;
1070 : 255 : return S2N_SUCCESS;
1071 : 256 : }
1072 : :
1073 : : int s2n_session_ticket_get_data(struct s2n_session_ticket *ticket, size_t max_data_len, uint8_t *data)
1074 : 257 : {
1075 [ + - ][ + + ]: 257 : POSIX_ENSURE_REF(ticket);
1076 [ + + ][ + - ]: 256 : POSIX_ENSURE_MUT(data);
1077 : :
1078 [ + + ][ + - ]: 255 : POSIX_ENSURE(ticket->ticket_data.size <= max_data_len, S2N_ERR_SERIALIZED_SESSION_STATE_TOO_LONG);
1079 [ - + ][ # # ]: 254 : POSIX_CHECKED_MEMCPY(data, ticket->ticket_data.data, ticket->ticket_data.size);
[ + - ]
1080 : :
1081 : 254 : return S2N_SUCCESS;
1082 : 254 : }
1083 : :
1084 : : int s2n_session_ticket_get_lifetime(struct s2n_session_ticket *ticket, uint32_t *session_lifetime)
1085 : 22 : {
1086 [ + + ][ + - ]: 22 : POSIX_ENSURE_REF(ticket);
1087 [ + - ][ + + ]: 21 : POSIX_ENSURE_REF(session_lifetime);
1088 : :
1089 : 20 : *session_lifetime = ticket->session_lifetime;
1090 : :
1091 : 20 : return S2N_SUCCESS;
1092 : 21 : }
|