2 * TLS server tickets callbacks implementation
4 * Copyright The Mbed TLS Contributors
5 * SPDX-License-Identifier: Apache-2.0
7 * Licensed under the Apache License, Version 2.0 (the "License"); you may
8 * not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
22 #if defined(MBEDTLS_SSL_TICKET_C)
24 #if defined(MBEDTLS_PLATFORM_C)
25 #include "mbedtls/platform.h"
28 #define mbedtls_calloc calloc
29 #define mbedtls_free free
32 #include "mbedtls/ssl_internal.h"
33 #include "mbedtls/ssl_ticket.h"
34 #include "mbedtls/error.h"
35 #include "mbedtls/platform_util.h"
42 void mbedtls_ssl_ticket_init(mbedtls_ssl_ticket_context
*ctx
) {
43 memset(ctx
, 0, sizeof(mbedtls_ssl_ticket_context
));
45 #if defined(MBEDTLS_THREADING_C)
46 mbedtls_mutex_init(&ctx
->mutex
);
50 #define MAX_KEY_BYTES 32 /* 256 bits */
52 #define TICKET_KEY_NAME_BYTES 4
53 #define TICKET_IV_BYTES 12
54 #define TICKET_CRYPT_LEN_BYTES 2
55 #define TICKET_AUTH_TAG_BYTES 16
57 #define TICKET_MIN_LEN ( TICKET_KEY_NAME_BYTES + \
59 TICKET_CRYPT_LEN_BYTES + \
60 TICKET_AUTH_TAG_BYTES )
61 #define TICKET_ADD_DATA_LEN ( TICKET_KEY_NAME_BYTES + \
63 TICKET_CRYPT_LEN_BYTES )
66 * Generate/update a key
68 static int ssl_ticket_gen_key(mbedtls_ssl_ticket_context
*ctx
,
69 unsigned char index
) {
70 int ret
= MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED
;
71 unsigned char buf
[MAX_KEY_BYTES
];
72 mbedtls_ssl_ticket_key
*key
= ctx
->keys
+ index
;
74 #if defined(MBEDTLS_HAVE_TIME)
75 key
->generation_time
= (uint32_t) mbedtls_time(NULL
);
78 if ((ret
= ctx
->f_rng(ctx
->p_rng
, key
->name
, sizeof(key
->name
))) != 0)
81 if ((ret
= ctx
->f_rng(ctx
->p_rng
, buf
, sizeof(buf
))) != 0)
84 /* With GCM and CCM, same context can encrypt & decrypt */
85 ret
= mbedtls_cipher_setkey(&key
->ctx
, buf
,
86 mbedtls_cipher_get_key_bitlen(&key
->ctx
),
89 mbedtls_platform_zeroize(buf
, sizeof(buf
));
95 * Rotate/generate keys if necessary
97 static int ssl_ticket_update_keys(mbedtls_ssl_ticket_context
*ctx
) {
98 #if !defined(MBEDTLS_HAVE_TIME)
101 if (ctx
->ticket_lifetime
!= 0) {
102 uint32_t current_time
= (uint32_t) mbedtls_time(NULL
);
103 uint32_t key_time
= ctx
->keys
[ctx
->active
].generation_time
;
105 if (current_time
>= key_time
&&
106 current_time
- key_time
< ctx
->ticket_lifetime
) {
110 ctx
->active
= 1 - ctx
->active
;
112 return (ssl_ticket_gen_key(ctx
, ctx
->active
));
114 #endif /* MBEDTLS_HAVE_TIME */
119 * Setup context for actual use
121 int mbedtls_ssl_ticket_setup(mbedtls_ssl_ticket_context
*ctx
,
122 int (*f_rng
)(void *, unsigned char *, size_t), void *p_rng
,
123 mbedtls_cipher_type_t cipher
,
125 int ret
= MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED
;
126 const mbedtls_cipher_info_t
*cipher_info
;
131 ctx
->ticket_lifetime
= lifetime
;
133 cipher_info
= mbedtls_cipher_info_from_type(cipher
);
134 if (cipher_info
== NULL
)
135 return (MBEDTLS_ERR_SSL_BAD_INPUT_DATA
);
137 if (cipher_info
->mode
!= MBEDTLS_MODE_GCM
&&
138 cipher_info
->mode
!= MBEDTLS_MODE_CCM
) {
139 return (MBEDTLS_ERR_SSL_BAD_INPUT_DATA
);
142 if (cipher_info
->key_bitlen
> 8 * MAX_KEY_BYTES
)
143 return (MBEDTLS_ERR_SSL_BAD_INPUT_DATA
);
145 #if defined(MBEDTLS_USE_PSA_CRYPTO)
146 ret
= mbedtls_cipher_setup_psa(&ctx
->keys
[0].ctx
,
147 cipher_info
, TICKET_AUTH_TAG_BYTES
);
148 if (ret
!= 0 && ret
!= MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE
)
150 /* We don't yet expect to support all ciphers through PSA,
151 * so allow fallback to ordinary mbedtls_cipher_setup(). */
152 if (ret
== MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE
)
153 #endif /* MBEDTLS_USE_PSA_CRYPTO */
154 if ((ret
= mbedtls_cipher_setup(&ctx
->keys
[0].ctx
, cipher_info
)) != 0)
157 #if defined(MBEDTLS_USE_PSA_CRYPTO)
158 ret
= mbedtls_cipher_setup_psa(&ctx
->keys
[1].ctx
,
159 cipher_info
, TICKET_AUTH_TAG_BYTES
);
160 if (ret
!= 0 && ret
!= MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE
)
162 if (ret
== MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE
)
163 #endif /* MBEDTLS_USE_PSA_CRYPTO */
164 if ((ret
= mbedtls_cipher_setup(&ctx
->keys
[1].ctx
, cipher_info
)) != 0)
167 if ((ret
= ssl_ticket_gen_key(ctx
, 0)) != 0 ||
168 (ret
= ssl_ticket_gen_key(ctx
, 1)) != 0) {
176 * Create session ticket, with the following structure:
179 * opaque key_name[4];
181 * opaque encrypted_state<0..2^16-1>;
185 * The key_name, iv, and length of encrypted_state are the additional
186 * authenticated data.
189 int mbedtls_ssl_ticket_write(void *p_ticket
,
190 const mbedtls_ssl_session
*session
,
191 unsigned char *start
,
192 const unsigned char *end
,
194 uint32_t *ticket_lifetime
) {
195 int ret
= MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED
;
196 mbedtls_ssl_ticket_context
*ctx
= p_ticket
;
197 mbedtls_ssl_ticket_key
*key
;
198 unsigned char *key_name
= start
;
199 unsigned char *iv
= start
+ TICKET_KEY_NAME_BYTES
;
200 unsigned char *state_len_bytes
= iv
+ TICKET_IV_BYTES
;
201 unsigned char *state
= state_len_bytes
+ TICKET_CRYPT_LEN_BYTES
;
202 size_t clear_len
, ciph_len
;
206 if (ctx
== NULL
|| ctx
->f_rng
== NULL
)
207 return (MBEDTLS_ERR_SSL_BAD_INPUT_DATA
);
209 /* We need at least 4 bytes for key_name, 12 for IV, 2 for len 16 for tag,
210 * in addition to session itself, that will be checked when writing it. */
211 MBEDTLS_SSL_CHK_BUF_PTR(start
, end
, TICKET_MIN_LEN
);
213 #if defined(MBEDTLS_THREADING_C)
214 if ((ret
= mbedtls_mutex_lock(&ctx
->mutex
)) != 0)
218 if ((ret
= ssl_ticket_update_keys(ctx
)) != 0)
221 key
= &ctx
->keys
[ctx
->active
];
223 *ticket_lifetime
= ctx
->ticket_lifetime
;
225 memcpy(key_name
, key
->name
, TICKET_KEY_NAME_BYTES
);
227 if ((ret
= ctx
->f_rng(ctx
->p_rng
, iv
, TICKET_IV_BYTES
)) != 0)
230 /* Dump session state */
231 if ((ret
= mbedtls_ssl_session_save(session
,
234 (unsigned long) clear_len
> 65535) {
237 state_len_bytes
[0] = (clear_len
>> 8) & 0xff;
238 state_len_bytes
[1] = (clear_len
) & 0xff;
240 /* Encrypt and authenticate */
241 if ((ret
= mbedtls_cipher_auth_encrypt_ext(&key
->ctx
,
243 /* Additional data: key name, IV and length */
244 key_name
, TICKET_ADD_DATA_LEN
,
246 state
, end
- state
, &ciph_len
,
247 TICKET_AUTH_TAG_BYTES
)) != 0) {
250 if (ciph_len
!= clear_len
+ TICKET_AUTH_TAG_BYTES
) {
251 ret
= MBEDTLS_ERR_SSL_INTERNAL_ERROR
;
255 *tlen
= TICKET_MIN_LEN
+ ciph_len
- TICKET_AUTH_TAG_BYTES
;
258 #if defined(MBEDTLS_THREADING_C)
259 if (mbedtls_mutex_unlock(&ctx
->mutex
) != 0)
260 return (MBEDTLS_ERR_THREADING_MUTEX_ERROR
);
267 * Select key based on name
269 static mbedtls_ssl_ticket_key
*ssl_ticket_select_key(
270 mbedtls_ssl_ticket_context
*ctx
,
271 const unsigned char name
[4]) {
274 for (i
= 0; i
< sizeof(ctx
->keys
) / sizeof(*ctx
->keys
); i
++)
275 if (memcmp(name
, ctx
->keys
[i
].name
, 4) == 0)
276 return (&ctx
->keys
[i
]);
282 * Load session ticket (see mbedtls_ssl_ticket_write for structure)
284 int mbedtls_ssl_ticket_parse(void *p_ticket
,
285 mbedtls_ssl_session
*session
,
288 int ret
= MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED
;
289 mbedtls_ssl_ticket_context
*ctx
= p_ticket
;
290 mbedtls_ssl_ticket_key
*key
;
291 unsigned char *key_name
= buf
;
292 unsigned char *iv
= buf
+ TICKET_KEY_NAME_BYTES
;
293 unsigned char *enc_len_p
= iv
+ TICKET_IV_BYTES
;
294 unsigned char *ticket
= enc_len_p
+ TICKET_CRYPT_LEN_BYTES
;
295 size_t enc_len
, clear_len
;
297 if (ctx
== NULL
|| ctx
->f_rng
== NULL
)
298 return (MBEDTLS_ERR_SSL_BAD_INPUT_DATA
);
300 if (len
< TICKET_MIN_LEN
)
301 return (MBEDTLS_ERR_SSL_BAD_INPUT_DATA
);
303 #if defined(MBEDTLS_THREADING_C)
304 if ((ret
= mbedtls_mutex_lock(&ctx
->mutex
)) != 0)
308 if ((ret
= ssl_ticket_update_keys(ctx
)) != 0)
311 enc_len
= (enc_len_p
[0] << 8) | enc_len_p
[1];
313 if (len
!= TICKET_MIN_LEN
+ enc_len
) {
314 ret
= MBEDTLS_ERR_SSL_BAD_INPUT_DATA
;
319 if ((key
= ssl_ticket_select_key(ctx
, key_name
)) == NULL
) {
320 /* We can't know for sure but this is a likely option unless we're
321 * under attack - this is only informative anyway */
322 ret
= MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED
;
326 /* Decrypt and authenticate */
327 if ((ret
= mbedtls_cipher_auth_decrypt_ext(&key
->ctx
,
329 /* Additional data: key name, IV and length */
330 key_name
, TICKET_ADD_DATA_LEN
,
331 ticket
, enc_len
+ TICKET_AUTH_TAG_BYTES
,
332 ticket
, enc_len
, &clear_len
,
333 TICKET_AUTH_TAG_BYTES
)) != 0) {
334 if (ret
== MBEDTLS_ERR_CIPHER_AUTH_FAILED
)
335 ret
= MBEDTLS_ERR_SSL_INVALID_MAC
;
339 if (clear_len
!= enc_len
) {
340 ret
= MBEDTLS_ERR_SSL_INTERNAL_ERROR
;
344 /* Actually load session */
345 if ((ret
= mbedtls_ssl_session_load(session
, ticket
, clear_len
)) != 0)
348 #if defined(MBEDTLS_HAVE_TIME)
350 /* Check for expiration */
351 mbedtls_time_t current_time
= mbedtls_time(NULL
);
353 if (current_time
< session
->start
||
354 (uint32_t)(current_time
- session
->start
) > ctx
->ticket_lifetime
) {
355 ret
= MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED
;
362 #if defined(MBEDTLS_THREADING_C)
363 if (mbedtls_mutex_unlock(&ctx
->mutex
) != 0)
364 return (MBEDTLS_ERR_THREADING_MUTEX_ERROR
);
373 void mbedtls_ssl_ticket_free(mbedtls_ssl_ticket_context
*ctx
) {
374 mbedtls_cipher_free(&ctx
->keys
[0].ctx
);
375 mbedtls_cipher_free(&ctx
->keys
[1].ctx
);
377 #if defined(MBEDTLS_THREADING_C)
378 mbedtls_mutex_free(&ctx
->mutex
);
381 mbedtls_platform_zeroize(ctx
, sizeof(mbedtls_ssl_ticket_context
));
384 #endif /* MBEDTLS_SSL_TICKET_C */