2 * TLS server tickets callbacks implementation
4 * Copyright The Mbed TLS Contributors
5 * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
7 * This file is provided under the Apache License 2.0, or the
8 * GNU General Public License v2.0 or later.
13 * Licensed under the Apache License, Version 2.0 (the "License"); you may
14 * not use this file except in compliance with the License.
15 * You may obtain a copy of the License at
17 * http://www.apache.org/licenses/LICENSE-2.0
19 * Unless required by applicable law or agreed to in writing, software
20 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
21 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 * See the License for the specific language governing permissions and
23 * limitations under the License.
28 * GNU General Public License v2.0 or later:
30 * This program is free software; you can redistribute it and/or modify
31 * it under the terms of the GNU General Public License as published by
32 * the Free Software Foundation; either version 2 of the License, or
33 * (at your option) any later version.
35 * This program is distributed in the hope that it will be useful,
36 * but WITHOUT ANY WARRANTY; without even the implied warranty of
37 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
38 * GNU General Public License for more details.
40 * You should have received a copy of the GNU General Public License along
41 * with this program; if not, write to the Free Software Foundation, Inc.,
42 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
47 #if !defined(MBEDTLS_CONFIG_FILE)
48 #include "mbedtls/config.h"
50 #include MBEDTLS_CONFIG_FILE
53 #if defined(MBEDTLS_SSL_TICKET_C)
55 #if defined(MBEDTLS_PLATFORM_C)
56 #include "mbedtls/platform.h"
59 #define mbedtls_calloc calloc
60 #define mbedtls_free free
63 #include "mbedtls/ssl_internal.h"
64 #include "mbedtls/ssl_ticket.h"
65 #include "mbedtls/platform_util.h"
72 void mbedtls_ssl_ticket_init( mbedtls_ssl_ticket_context
*ctx
)
74 memset( ctx
, 0, sizeof( mbedtls_ssl_ticket_context
) );
76 #if defined(MBEDTLS_THREADING_C)
77 mbedtls_mutex_init( &ctx
->mutex
);
81 #define MAX_KEY_BYTES 32 /* 256 bits */
83 #define TICKET_KEY_NAME_BYTES 4
84 #define TICKET_IV_BYTES 12
85 #define TICKET_CRYPT_LEN_BYTES 2
86 #define TICKET_AUTH_TAG_BYTES 16
88 #define TICKET_MIN_LEN ( TICKET_KEY_NAME_BYTES + \
90 TICKET_CRYPT_LEN_BYTES + \
91 TICKET_AUTH_TAG_BYTES )
92 #define TICKET_ADD_DATA_LEN ( TICKET_KEY_NAME_BYTES + \
94 TICKET_CRYPT_LEN_BYTES )
97 * Generate/update a key
99 static int ssl_ticket_gen_key( mbedtls_ssl_ticket_context
*ctx
,
100 unsigned char index
)
103 unsigned char buf
[MAX_KEY_BYTES
];
104 mbedtls_ssl_ticket_key
*key
= ctx
->keys
+ index
;
106 #if defined(MBEDTLS_HAVE_TIME)
107 key
->generation_time
= (uint32_t) mbedtls_time( NULL
);
110 if( ( ret
= ctx
->f_rng( ctx
->p_rng
, key
->name
, sizeof( key
->name
) ) ) != 0 )
113 if( ( ret
= ctx
->f_rng( ctx
->p_rng
, buf
, sizeof( buf
) ) ) != 0 )
116 /* With GCM and CCM, same context can encrypt & decrypt */
117 ret
= mbedtls_cipher_setkey( &key
->ctx
, buf
,
118 mbedtls_cipher_get_key_bitlen( &key
->ctx
),
121 mbedtls_platform_zeroize( buf
, sizeof( buf
) );
127 * Rotate/generate keys if necessary
129 static int ssl_ticket_update_keys( mbedtls_ssl_ticket_context
*ctx
)
131 #if !defined(MBEDTLS_HAVE_TIME)
134 if( ctx
->ticket_lifetime
!= 0 )
136 uint32_t current_time
= (uint32_t) mbedtls_time( NULL
);
137 uint32_t key_time
= ctx
->keys
[ctx
->active
].generation_time
;
139 if( current_time
>= key_time
&&
140 current_time
- key_time
< ctx
->ticket_lifetime
)
145 ctx
->active
= 1 - ctx
->active
;
147 return( ssl_ticket_gen_key( ctx
, ctx
->active
) );
150 #endif /* MBEDTLS_HAVE_TIME */
155 * Setup context for actual use
157 int mbedtls_ssl_ticket_setup( mbedtls_ssl_ticket_context
*ctx
,
158 int (*f_rng
)(void *, unsigned char *, size_t), void *p_rng
,
159 mbedtls_cipher_type_t cipher
,
163 const mbedtls_cipher_info_t
*cipher_info
;
168 ctx
->ticket_lifetime
= lifetime
;
170 cipher_info
= mbedtls_cipher_info_from_type( cipher
);
171 if( cipher_info
== NULL
)
172 return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA
);
174 if( cipher_info
->mode
!= MBEDTLS_MODE_GCM
&&
175 cipher_info
->mode
!= MBEDTLS_MODE_CCM
)
177 return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA
);
180 if( cipher_info
->key_bitlen
> 8 * MAX_KEY_BYTES
)
181 return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA
);
183 if( ( ret
= mbedtls_cipher_setup( &ctx
->keys
[0].ctx
, cipher_info
) ) != 0 ||
184 ( ret
= mbedtls_cipher_setup( &ctx
->keys
[1].ctx
, cipher_info
) ) != 0 )
189 if( ( ret
= ssl_ticket_gen_key( ctx
, 0 ) ) != 0 ||
190 ( ret
= ssl_ticket_gen_key( ctx
, 1 ) ) != 0 )
199 * Serialize a session in the following format:
200 * 0 . n-1 session structure, n = sizeof(mbedtls_ssl_session)
201 * n . n+2 peer_cert length = m (0 if no certificate)
202 * n+3 . n+2+m peer cert ASN.1
204 static int ssl_save_session( const mbedtls_ssl_session
*session
,
205 unsigned char *buf
, size_t buf_len
,
208 unsigned char *p
= buf
;
209 size_t left
= buf_len
;
210 #if defined(MBEDTLS_X509_CRT_PARSE_C)
212 #endif /* MBEDTLS_X509_CRT_PARSE_C */
214 if( left
< sizeof( mbedtls_ssl_session
) )
215 return( MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL
);
217 memcpy( p
, session
, sizeof( mbedtls_ssl_session
) );
218 p
+= sizeof( mbedtls_ssl_session
);
219 left
-= sizeof( mbedtls_ssl_session
);
221 #if defined(MBEDTLS_X509_CRT_PARSE_C)
222 if( session
->peer_cert
== NULL
)
225 cert_len
= session
->peer_cert
->raw
.len
;
227 if( left
< 3 + cert_len
)
228 return( MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL
);
230 *p
++ = (unsigned char)( ( cert_len
>> 16 ) & 0xFF );
231 *p
++ = (unsigned char)( ( cert_len
>> 8 ) & 0xFF );
232 *p
++ = (unsigned char)( ( cert_len
) & 0xFF );
234 if( session
->peer_cert
!= NULL
)
235 memcpy( p
, session
->peer_cert
->raw
.p
, cert_len
);
238 #endif /* MBEDTLS_X509_CRT_PARSE_C */
246 * Unserialise session, see ssl_save_session()
248 static int ssl_load_session( mbedtls_ssl_session
*session
,
249 const unsigned char *buf
, size_t len
)
251 const unsigned char *p
= buf
;
252 const unsigned char * const end
= buf
+ len
;
253 #if defined(MBEDTLS_X509_CRT_PARSE_C)
255 #endif /* MBEDTLS_X509_CRT_PARSE_C */
257 if( sizeof( mbedtls_ssl_session
) > (size_t)( end
- p
) )
258 return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA
);
260 memcpy( session
, p
, sizeof( mbedtls_ssl_session
) );
261 p
+= sizeof( mbedtls_ssl_session
);
263 #if defined(MBEDTLS_X509_CRT_PARSE_C)
264 if( 3 > (size_t)( end
- p
) )
265 return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA
);
267 cert_len
= ( p
[0] << 16 ) | ( p
[1] << 8 ) | p
[2];
272 session
->peer_cert
= NULL
;
278 if( cert_len
> (size_t)( end
- p
) )
279 return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA
);
281 session
->peer_cert
= mbedtls_calloc( 1, sizeof( mbedtls_x509_crt
) );
283 if( session
->peer_cert
== NULL
)
284 return( MBEDTLS_ERR_SSL_ALLOC_FAILED
);
286 mbedtls_x509_crt_init( session
->peer_cert
);
288 if( ( ret
= mbedtls_x509_crt_parse_der( session
->peer_cert
,
289 p
, cert_len
) ) != 0 )
291 mbedtls_x509_crt_free( session
->peer_cert
);
292 mbedtls_free( session
->peer_cert
);
293 session
->peer_cert
= NULL
;
299 #endif /* MBEDTLS_X509_CRT_PARSE_C */
302 return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA
);
308 * Create session ticket, with the following structure:
311 * opaque key_name[4];
313 * opaque encrypted_state<0..2^16-1>;
317 * The key_name, iv, and length of encrypted_state are the additional
318 * authenticated data.
321 int mbedtls_ssl_ticket_write( void *p_ticket
,
322 const mbedtls_ssl_session
*session
,
323 unsigned char *start
,
324 const unsigned char *end
,
326 uint32_t *ticket_lifetime
)
329 mbedtls_ssl_ticket_context
*ctx
= p_ticket
;
330 mbedtls_ssl_ticket_key
*key
;
331 unsigned char *key_name
= start
;
332 unsigned char *iv
= start
+ TICKET_KEY_NAME_BYTES
;
333 unsigned char *state_len_bytes
= iv
+ TICKET_IV_BYTES
;
334 unsigned char *state
= state_len_bytes
+ TICKET_CRYPT_LEN_BYTES
;
336 size_t clear_len
, ciph_len
;
340 if( ctx
== NULL
|| ctx
->f_rng
== NULL
)
341 return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA
);
343 /* We need at least 4 bytes for key_name, 12 for IV, 2 for len 16 for tag,
344 * in addition to session itself, that will be checked when writing it. */
345 MBEDTLS_SSL_CHK_BUF_PTR( start
, end
, TICKET_MIN_LEN
);
347 #if defined(MBEDTLS_THREADING_C)
348 if( ( ret
= mbedtls_mutex_lock( &ctx
->mutex
) ) != 0 )
352 if( ( ret
= ssl_ticket_update_keys( ctx
) ) != 0 )
355 key
= &ctx
->keys
[ctx
->active
];
357 *ticket_lifetime
= ctx
->ticket_lifetime
;
359 memcpy( key_name
, key
->name
, TICKET_KEY_NAME_BYTES
);
361 if( ( ret
= ctx
->f_rng( ctx
->p_rng
, iv
, TICKET_IV_BYTES
) ) != 0 )
364 /* Dump session state */
365 if( ( ret
= ssl_save_session( session
,
366 state
, end
- state
, &clear_len
) ) != 0 ||
367 (unsigned long) clear_len
> 65535 )
371 state_len_bytes
[0] = ( clear_len
>> 8 ) & 0xff;
372 state_len_bytes
[1] = ( clear_len
) & 0xff;
374 /* Encrypt and authenticate */
375 tag
= state
+ clear_len
;
376 if( ( ret
= mbedtls_cipher_auth_encrypt( &key
->ctx
,
378 /* Additional data: key name, IV and length */
379 key_name
, TICKET_ADD_DATA_LEN
,
380 state
, clear_len
, state
, &ciph_len
,
381 tag
, TICKET_AUTH_TAG_BYTES
) ) != 0 )
385 if( ciph_len
!= clear_len
)
387 ret
= MBEDTLS_ERR_SSL_INTERNAL_ERROR
;
391 *tlen
= TICKET_MIN_LEN
+ ciph_len
;
394 #if defined(MBEDTLS_THREADING_C)
395 if( mbedtls_mutex_unlock( &ctx
->mutex
) != 0 )
396 return( MBEDTLS_ERR_THREADING_MUTEX_ERROR
);
403 * Select key based on name
405 static mbedtls_ssl_ticket_key
*ssl_ticket_select_key(
406 mbedtls_ssl_ticket_context
*ctx
,
407 const unsigned char name
[4] )
411 for( i
= 0; i
< sizeof( ctx
->keys
) / sizeof( *ctx
->keys
); i
++ )
412 if( memcmp( name
, ctx
->keys
[i
].name
, 4 ) == 0 )
413 return( &ctx
->keys
[i
] );
419 * Load session ticket (see mbedtls_ssl_ticket_write for structure)
421 int mbedtls_ssl_ticket_parse( void *p_ticket
,
422 mbedtls_ssl_session
*session
,
427 mbedtls_ssl_ticket_context
*ctx
= p_ticket
;
428 mbedtls_ssl_ticket_key
*key
;
429 unsigned char *key_name
= buf
;
430 unsigned char *iv
= buf
+ TICKET_KEY_NAME_BYTES
;
431 unsigned char *enc_len_p
= iv
+ TICKET_IV_BYTES
;
432 unsigned char *ticket
= enc_len_p
+ TICKET_CRYPT_LEN_BYTES
;
434 size_t enc_len
, clear_len
;
436 if( ctx
== NULL
|| ctx
->f_rng
== NULL
)
437 return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA
);
439 if( len
< TICKET_MIN_LEN
)
440 return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA
);
442 #if defined(MBEDTLS_THREADING_C)
443 if( ( ret
= mbedtls_mutex_lock( &ctx
->mutex
) ) != 0 )
447 if( ( ret
= ssl_ticket_update_keys( ctx
) ) != 0 )
450 enc_len
= ( enc_len_p
[0] << 8 ) | enc_len_p
[1];
451 tag
= ticket
+ enc_len
;
453 if( len
!= TICKET_MIN_LEN
+ enc_len
)
455 ret
= MBEDTLS_ERR_SSL_BAD_INPUT_DATA
;
460 if( ( key
= ssl_ticket_select_key( ctx
, key_name
) ) == NULL
)
462 /* We can't know for sure but this is a likely option unless we're
463 * under attack - this is only informative anyway */
464 ret
= MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED
;
468 /* Decrypt and authenticate */
469 if( ( ret
= mbedtls_cipher_auth_decrypt( &key
->ctx
,
471 /* Additional data: key name, IV and length */
472 key_name
, TICKET_ADD_DATA_LEN
,
475 tag
, TICKET_AUTH_TAG_BYTES
) ) != 0 )
477 if( ret
== MBEDTLS_ERR_CIPHER_AUTH_FAILED
)
478 ret
= MBEDTLS_ERR_SSL_INVALID_MAC
;
482 if( clear_len
!= enc_len
)
484 ret
= MBEDTLS_ERR_SSL_INTERNAL_ERROR
;
488 /* Actually load session */
489 if( ( ret
= ssl_load_session( session
, ticket
, clear_len
) ) != 0 )
492 #if defined(MBEDTLS_HAVE_TIME)
494 /* Check for expiration */
495 mbedtls_time_t current_time
= mbedtls_time( NULL
);
497 if( current_time
< session
->start
||
498 (uint32_t)( current_time
- session
->start
) > ctx
->ticket_lifetime
)
500 ret
= MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED
;
507 #if defined(MBEDTLS_THREADING_C)
508 if( mbedtls_mutex_unlock( &ctx
->mutex
) != 0 )
509 return( MBEDTLS_ERR_THREADING_MUTEX_ERROR
);
518 void mbedtls_ssl_ticket_free( mbedtls_ssl_ticket_context
*ctx
)
520 mbedtls_cipher_free( &ctx
->keys
[0].ctx
);
521 mbedtls_cipher_free( &ctx
->keys
[1].ctx
);
523 #if defined(MBEDTLS_THREADING_C)
524 mbedtls_mutex_free( &ctx
->mutex
);
527 mbedtls_platform_zeroize( ctx
, sizeof( mbedtls_ssl_ticket_context
) );
530 #endif /* MBEDTLS_SSL_TICKET_C */