xref: /reactos/dll/3rdparty/mbedtls/ssl_ticket.c (revision 7eead935)
1 /*
2  *  TLS server tickets callbacks implementation
3  *
4  *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
5  *  SPDX-License-Identifier: GPL-2.0
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License along
18  *  with this program; if not, write to the Free Software Foundation, Inc.,
19  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  *  This file is part of mbed TLS (https://tls.mbed.org)
22  */
23 
24 #if !defined(MBEDTLS_CONFIG_FILE)
25 #include "mbedtls/config.h"
26 #else
27 #include MBEDTLS_CONFIG_FILE
28 #endif
29 
30 #if defined(MBEDTLS_SSL_TICKET_C)
31 
32 #if defined(MBEDTLS_PLATFORM_C)
33 #include "mbedtls/platform.h"
34 #else
35 #include <stdlib.h>
36 #define mbedtls_calloc    calloc
37 #define mbedtls_free      free
38 #endif
39 
40 #include "mbedtls/ssl_ticket.h"
41 
42 #include <string.h>
43 
44 /* Implementation that should never be optimized out by the compiler */
45 static void mbedtls_zeroize( void *v, size_t n ) {
46     volatile unsigned char *p = v; while( n-- ) *p++ = 0;
47 }
48 
49 /*
50  * Initialze context
51  */
52 void mbedtls_ssl_ticket_init( mbedtls_ssl_ticket_context *ctx )
53 {
54     memset( ctx, 0, sizeof( mbedtls_ssl_ticket_context ) );
55 
56 #if defined(MBEDTLS_THREADING_C)
57     mbedtls_mutex_init( &ctx->mutex );
58 #endif
59 }
60 
61 #define MAX_KEY_BYTES 32    /* 256 bits */
62 
63 /*
64  * Generate/update a key
65  */
66 static int ssl_ticket_gen_key( mbedtls_ssl_ticket_context *ctx,
67                                unsigned char index )
68 {
69     int ret;
70     unsigned char buf[MAX_KEY_BYTES];
71     mbedtls_ssl_ticket_key *key = ctx->keys + index;
72 
73 #if defined(MBEDTLS_HAVE_TIME)
74     key->generation_time = (uint32_t) mbedtls_time( NULL );
75 #endif
76 
77     if( ( ret = ctx->f_rng( ctx->p_rng, key->name, sizeof( key->name ) ) ) != 0 )
78         return( ret );
79 
80     if( ( ret = ctx->f_rng( ctx->p_rng, buf, sizeof( buf ) ) ) != 0 )
81         return( ret );
82 
83     /* With GCM and CCM, same context can encrypt & decrypt */
84     ret = mbedtls_cipher_setkey( &key->ctx, buf,
85                                  mbedtls_cipher_get_key_bitlen( &key->ctx ),
86                                  MBEDTLS_ENCRYPT );
87 
88     mbedtls_zeroize( buf, sizeof( buf ) );
89 
90     return( ret );
91 }
92 
93 /*
94  * Rotate/generate keys if necessary
95  */
96 static int ssl_ticket_update_keys( mbedtls_ssl_ticket_context *ctx )
97 {
98 #if !defined(MBEDTLS_HAVE_TIME)
99     ((void) ctx);
100 #else
101     if( ctx->ticket_lifetime != 0 )
102     {
103         uint32_t current_time = (uint32_t) mbedtls_time( NULL );
104         uint32_t key_time = ctx->keys[ctx->active].generation_time;
105 
106         if( current_time >= key_time &&
107             current_time - key_time < ctx->ticket_lifetime )
108         {
109             return( 0 );
110         }
111 
112         ctx->active = 1 - ctx->active;
113 
114         return( ssl_ticket_gen_key( ctx, ctx->active ) );
115     }
116     else
117 #endif /* MBEDTLS_HAVE_TIME */
118         return( 0 );
119 }
120 
121 /*
122  * Setup context for actual use
123  */
124 int mbedtls_ssl_ticket_setup( mbedtls_ssl_ticket_context *ctx,
125     int (*f_rng)(void *, unsigned char *, size_t), void *p_rng,
126     mbedtls_cipher_type_t cipher,
127     uint32_t lifetime )
128 {
129     int ret;
130     const mbedtls_cipher_info_t *cipher_info;
131 
132     ctx->f_rng = f_rng;
133     ctx->p_rng = p_rng;
134 
135     ctx->ticket_lifetime = lifetime;
136 
137     cipher_info = mbedtls_cipher_info_from_type( cipher);
138     if( cipher_info == NULL )
139         return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
140 
141     if( cipher_info->mode != MBEDTLS_MODE_GCM &&
142         cipher_info->mode != MBEDTLS_MODE_CCM )
143     {
144         return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
145     }
146 
147     if( cipher_info->key_bitlen > 8 * MAX_KEY_BYTES )
148         return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
149 
150     if( ( ret = mbedtls_cipher_setup( &ctx->keys[0].ctx, cipher_info ) ) != 0 ||
151         ( ret = mbedtls_cipher_setup( &ctx->keys[1].ctx, cipher_info ) ) != 0 )
152     {
153         return( ret );
154     }
155 
156     if( ( ret = ssl_ticket_gen_key( ctx, 0 ) ) != 0 ||
157         ( ret = ssl_ticket_gen_key( ctx, 1 ) ) != 0 )
158     {
159         return( ret );
160     }
161 
162     return( 0 );
163 }
164 
165 /*
166  * Serialize a session in the following format:
167  *  0   .   n-1     session structure, n = sizeof(mbedtls_ssl_session)
168  *  n   .   n+2     peer_cert length = m (0 if no certificate)
169  *  n+3 .   n+2+m   peer cert ASN.1
170  */
171 static int ssl_save_session( const mbedtls_ssl_session *session,
172                              unsigned char *buf, size_t buf_len,
173                              size_t *olen )
174 {
175     unsigned char *p = buf;
176     size_t left = buf_len;
177 #if defined(MBEDTLS_X509_CRT_PARSE_C)
178     size_t cert_len;
179 #endif /* MBEDTLS_X509_CRT_PARSE_C */
180 
181     if( left < sizeof( mbedtls_ssl_session ) )
182         return( MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL );
183 
184     memcpy( p, session, sizeof( mbedtls_ssl_session ) );
185     p += sizeof( mbedtls_ssl_session );
186     left -= sizeof( mbedtls_ssl_session );
187 
188 #if defined(MBEDTLS_X509_CRT_PARSE_C)
189     if( session->peer_cert == NULL )
190         cert_len = 0;
191     else
192         cert_len = session->peer_cert->raw.len;
193 
194     if( left < 3 + cert_len )
195         return( MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL );
196 
197     *p++ = (unsigned char)( ( cert_len >> 16 ) & 0xFF );
198     *p++ = (unsigned char)( ( cert_len >>  8 ) & 0xFF );
199     *p++ = (unsigned char)( ( cert_len       ) & 0xFF );
200 
201     if( session->peer_cert != NULL )
202         memcpy( p, session->peer_cert->raw.p, cert_len );
203 
204     p += cert_len;
205 #endif /* MBEDTLS_X509_CRT_PARSE_C */
206 
207     *olen = p - buf;
208 
209     return( 0 );
210 }
211 
212 /*
213  * Unserialise session, see ssl_save_session()
214  */
215 static int ssl_load_session( mbedtls_ssl_session *session,
216                              const unsigned char *buf, size_t len )
217 {
218     const unsigned char *p = buf;
219     const unsigned char * const end = buf + len;
220 #if defined(MBEDTLS_X509_CRT_PARSE_C)
221     size_t cert_len;
222 #endif /* MBEDTLS_X509_CRT_PARSE_C */
223 
224     if( sizeof( mbedtls_ssl_session ) > (size_t)( end - p ) )
225         return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
226 
227     memcpy( session, p, sizeof( mbedtls_ssl_session ) );
228     p += sizeof( mbedtls_ssl_session );
229 
230 #if defined(MBEDTLS_X509_CRT_PARSE_C)
231     if( 3 > (size_t)( end - p ) )
232         return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
233 
234     cert_len = ( p[0] << 16 ) | ( p[1] << 8 ) | p[2];
235     p += 3;
236 
237     if( cert_len == 0 )
238     {
239         session->peer_cert = NULL;
240     }
241     else
242     {
243         int ret;
244 
245         if( cert_len > (size_t)( end - p ) )
246             return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
247 
248         session->peer_cert = mbedtls_calloc( 1, sizeof( mbedtls_x509_crt ) );
249 
250         if( session->peer_cert == NULL )
251             return( MBEDTLS_ERR_SSL_ALLOC_FAILED );
252 
253         mbedtls_x509_crt_init( session->peer_cert );
254 
255         if( ( ret = mbedtls_x509_crt_parse_der( session->peer_cert,
256                                                 p, cert_len ) ) != 0 )
257         {
258             mbedtls_x509_crt_free( session->peer_cert );
259             mbedtls_free( session->peer_cert );
260             session->peer_cert = NULL;
261             return( ret );
262         }
263 
264         p += cert_len;
265     }
266 #endif /* MBEDTLS_X509_CRT_PARSE_C */
267 
268     if( p != end )
269         return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
270 
271     return( 0 );
272 }
273 
274 /*
275  * Create session ticket, with the following structure:
276  *
277  *    struct {
278  *        opaque key_name[4];
279  *        opaque iv[12];
280  *        opaque encrypted_state<0..2^16-1>;
281  *        opaque tag[16];
282  *    } ticket;
283  *
284  * The key_name, iv, and length of encrypted_state are the additional
285  * authenticated data.
286  */
287 int mbedtls_ssl_ticket_write( void *p_ticket,
288                               const mbedtls_ssl_session *session,
289                               unsigned char *start,
290                               const unsigned char *end,
291                               size_t *tlen,
292                               uint32_t *ticket_lifetime )
293 {
294     int ret;
295     mbedtls_ssl_ticket_context *ctx = p_ticket;
296     mbedtls_ssl_ticket_key *key;
297     unsigned char *key_name = start;
298     unsigned char *iv = start + 4;
299     unsigned char *state_len_bytes = iv + 12;
300     unsigned char *state = state_len_bytes + 2;
301     unsigned char *tag;
302     size_t clear_len, ciph_len;
303 
304     *tlen = 0;
305 
306     if( ctx == NULL || ctx->f_rng == NULL )
307         return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
308 
309     /* We need at least 4 bytes for key_name, 12 for IV, 2 for len 16 for tag,
310      * in addition to session itself, that will be checked when writing it. */
311     if( end - start < 4 + 12 + 2 + 16 )
312         return( MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL );
313 
314 #if defined(MBEDTLS_THREADING_C)
315     if( ( ret = mbedtls_mutex_lock( &ctx->mutex ) ) != 0 )
316         return( ret );
317 #endif
318 
319     if( ( ret = ssl_ticket_update_keys( ctx ) ) != 0 )
320         goto cleanup;
321 
322     key = &ctx->keys[ctx->active];
323 
324     *ticket_lifetime = ctx->ticket_lifetime;
325 
326     memcpy( key_name, key->name, 4 );
327 
328     if( ( ret = ctx->f_rng( ctx->p_rng, iv, 12 ) ) != 0 )
329         goto cleanup;
330 
331     /* Dump session state */
332     if( ( ret = ssl_save_session( session,
333                                   state, end - state, &clear_len ) ) != 0 ||
334         (unsigned long) clear_len > 65535 )
335     {
336          goto cleanup;
337     }
338     state_len_bytes[0] = ( clear_len >> 8 ) & 0xff;
339     state_len_bytes[1] = ( clear_len      ) & 0xff;
340 
341     /* Encrypt and authenticate */
342     tag = state + clear_len;
343     if( ( ret = mbedtls_cipher_auth_encrypt( &key->ctx,
344                     iv, 12, key_name, 4 + 12 + 2,
345                     state, clear_len, state, &ciph_len, tag, 16 ) ) != 0 )
346     {
347         goto cleanup;
348     }
349     if( ciph_len != clear_len )
350     {
351         ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR;
352         goto cleanup;
353     }
354 
355     *tlen = 4 + 12 + 2 + 16 + ciph_len;
356 
357 cleanup:
358 #if defined(MBEDTLS_THREADING_C)
359     if( mbedtls_mutex_unlock( &ctx->mutex ) != 0 )
360         return( MBEDTLS_ERR_THREADING_MUTEX_ERROR );
361 #endif
362 
363     return( ret );
364 }
365 
366 /*
367  * Select key based on name
368  */
369 static mbedtls_ssl_ticket_key *ssl_ticket_select_key(
370         mbedtls_ssl_ticket_context *ctx,
371         const unsigned char name[4] )
372 {
373     unsigned char i;
374 
375     for( i = 0; i < sizeof( ctx->keys ) / sizeof( *ctx->keys ); i++ )
376         if( memcmp( name, ctx->keys[i].name, 4 ) == 0 )
377             return( &ctx->keys[i] );
378 
379     return( NULL );
380 }
381 
382 /*
383  * Load session ticket (see mbedtls_ssl_ticket_write for structure)
384  */
385 int mbedtls_ssl_ticket_parse( void *p_ticket,
386                               mbedtls_ssl_session *session,
387                               unsigned char *buf,
388                               size_t len )
389 {
390     int ret;
391     mbedtls_ssl_ticket_context *ctx = p_ticket;
392     mbedtls_ssl_ticket_key *key;
393     unsigned char *key_name = buf;
394     unsigned char *iv = buf + 4;
395     unsigned char *enc_len_p = iv + 12;
396     unsigned char *ticket = enc_len_p + 2;
397     unsigned char *tag;
398     size_t enc_len, clear_len;
399 
400     if( ctx == NULL || ctx->f_rng == NULL )
401         return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
402 
403     /* See mbedtls_ssl_ticket_write() */
404     if( len < 4 + 12 + 2 + 16 )
405         return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
406 
407 #if defined(MBEDTLS_THREADING_C)
408     if( ( ret = mbedtls_mutex_lock( &ctx->mutex ) ) != 0 )
409         return( ret );
410 #endif
411 
412     if( ( ret = ssl_ticket_update_keys( ctx ) ) != 0 )
413         goto cleanup;
414 
415     enc_len = ( enc_len_p[0] << 8 ) | enc_len_p[1];
416     tag = ticket + enc_len;
417 
418     if( len != 4 + 12 + 2 + enc_len + 16 )
419     {
420         ret = MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
421         goto cleanup;
422     }
423 
424     /* Select key */
425     if( ( key = ssl_ticket_select_key( ctx, key_name ) ) == NULL )
426     {
427         /* We can't know for sure but this is a likely option unless we're
428          * under attack - this is only informative anyway */
429         ret = MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED;
430         goto cleanup;
431     }
432 
433     /* Decrypt and authenticate */
434     if( ( ret = mbedtls_cipher_auth_decrypt( &key->ctx, iv, 12,
435                     key_name, 4 + 12 + 2, ticket, enc_len,
436                     ticket, &clear_len, tag, 16 ) ) != 0 )
437     {
438         if( ret == MBEDTLS_ERR_CIPHER_AUTH_FAILED )
439             ret = MBEDTLS_ERR_SSL_INVALID_MAC;
440 
441         goto cleanup;
442     }
443     if( clear_len != enc_len )
444     {
445         ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR;
446         goto cleanup;
447     }
448 
449     /* Actually load session */
450     if( ( ret = ssl_load_session( session, ticket, clear_len ) ) != 0 )
451         goto cleanup;
452 
453 #if defined(MBEDTLS_HAVE_TIME)
454     {
455         /* Check for expiration */
456         mbedtls_time_t current_time = mbedtls_time( NULL );
457 
458         if( current_time < session->start ||
459             (uint32_t)( current_time - session->start ) > ctx->ticket_lifetime )
460         {
461             ret = MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED;
462             goto cleanup;
463         }
464     }
465 #endif
466 
467 cleanup:
468 #if defined(MBEDTLS_THREADING_C)
469     if( mbedtls_mutex_unlock( &ctx->mutex ) != 0 )
470         return( MBEDTLS_ERR_THREADING_MUTEX_ERROR );
471 #endif
472 
473     return( ret );
474 }
475 
476 /*
477  * Free context
478  */
479 void mbedtls_ssl_ticket_free( mbedtls_ssl_ticket_context *ctx )
480 {
481     mbedtls_cipher_free( &ctx->keys[0].ctx );
482     mbedtls_cipher_free( &ctx->keys[1].ctx );
483 
484 #if defined(MBEDTLS_THREADING_C)
485     mbedtls_mutex_free( &ctx->mutex );
486 #endif
487 
488     mbedtls_zeroize( ctx, sizeof( mbedtls_ssl_ticket_context ) );
489 }
490 
491 #endif /* MBEDTLS_SSL_TICKET_C */
492