blob: 6709c688ced1ef89f7a9f0f829491ca3ccc26a47 [file] [log] [blame]
Manuel Pégourié-Gonnardfd6d8972015-05-15 12:09:00 +02001/*
2 * TLS server tickets callbacks implementation
3 *
4 * Copyright (C) 2015, ARM Limited, All Rights Reserved
5 *
6 * This file is part of mbed TLS (https://tls.mbed.org)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23#if !defined(MBEDTLS_CONFIG_FILE)
24#include "mbedtls/config.h"
25#else
26#include MBEDTLS_CONFIG_FILE
27#endif
28
29#if defined(MBEDTLS_SSL_TICKET_C)
30
31#include "mbedtls/ssl_ticket.h"
32
33#if defined(MBEDTLS_PLATFORM_C)
34#include "mbedtls/platform.h"
35#else
36#define mbedtls_malloc malloc
37#define mbedtls_free free
38#endif
39
40#include <string.h>
41
Manuel Pégourié-Gonnarda4a47352015-05-15 15:14:54 +020042/* Implementation that should never be optimized out by the compiler */
43static void mbedtls_zeroize( void *v, size_t n ) {
44 volatile unsigned char *p = v; while( n-- ) *p++ = 0;
45}
46
47/*
48 * Serialize a session in the following format:
49 * 0 . n-1 session structure, n = sizeof(mbedtls_ssl_session)
50 * n . n+2 peer_cert length = m (0 if no certificate)
51 * n+3 . n+2+m peer cert ASN.1
52 *
53 * Assumes ticket is NULL (always true on server side).
54 */
55static int ssl_save_session( const mbedtls_ssl_session *session,
56 unsigned char *buf, size_t buf_len,
57 size_t *olen )
58{
59 unsigned char *p = buf;
60 size_t left = buf_len;
61#if defined(MBEDTLS_X509_CRT_PARSE_C)
62 size_t cert_len;
63#endif /* MBEDTLS_X509_CRT_PARSE_C */
64
65 if( left < sizeof( mbedtls_ssl_session ) )
66 return( -1 );
67
68 memcpy( p, session, sizeof( mbedtls_ssl_session ) );
69 p += sizeof( mbedtls_ssl_session );
70 left -= sizeof( mbedtls_ssl_session );
71
72#if defined(MBEDTLS_X509_CRT_PARSE_C)
73 if( session->peer_cert == NULL )
74 cert_len = 0;
75 else
76 cert_len = session->peer_cert->raw.len;
77
78 if( left < 3 + cert_len )
79 return( -1 );
80
81 *p++ = (unsigned char)( cert_len >> 16 & 0xFF );
82 *p++ = (unsigned char)( cert_len >> 8 & 0xFF );
83 *p++ = (unsigned char)( cert_len & 0xFF );
84
85 if( session->peer_cert != NULL )
86 memcpy( p, session->peer_cert->raw.p, cert_len );
87
88 p += cert_len;
89#endif /* MBEDTLS_X509_CRT_PARSE_C */
90
91 *olen = p - buf;
92
93 return( 0 );
94}
95
96/*
97 * Unserialise session, see ssl_save_session()
98 */
99static int ssl_load_session( mbedtls_ssl_session *session,
100 const unsigned char *buf, size_t len )
101{
102 const unsigned char *p = buf;
103 const unsigned char * const end = buf + len;
104#if defined(MBEDTLS_X509_CRT_PARSE_C)
105 size_t cert_len;
106#endif /* MBEDTLS_X509_CRT_PARSE_C */
107
108 if( p + sizeof( mbedtls_ssl_session ) > end )
109 return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
110
111 memcpy( session, p, sizeof( mbedtls_ssl_session ) );
112 p += sizeof( mbedtls_ssl_session );
113
114#if defined(MBEDTLS_X509_CRT_PARSE_C)
115 if( p + 3 > end )
116 return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
117
118 cert_len = ( p[0] << 16 ) | ( p[1] << 8 ) | p[2];
119 p += 3;
120
121 if( cert_len == 0 )
122 {
123 session->peer_cert = NULL;
124 }
125 else
126 {
127 int ret;
128
129 if( p + cert_len > end )
130 return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
131
132 session->peer_cert = mbedtls_malloc( sizeof( mbedtls_x509_crt ) );
133
134 if( session->peer_cert == NULL )
135 return( MBEDTLS_ERR_SSL_MALLOC_FAILED );
136
137 mbedtls_x509_crt_init( session->peer_cert );
138
139 if( ( ret = mbedtls_x509_crt_parse_der( session->peer_cert,
140 p, cert_len ) ) != 0 )
141 {
142 mbedtls_x509_crt_free( session->peer_cert );
143 mbedtls_free( session->peer_cert );
144 session->peer_cert = NULL;
145 return( ret );
146 }
147
148 p += cert_len;
149 }
150#endif /* MBEDTLS_X509_CRT_PARSE_C */
151
152 if( p != end )
153 return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
154
155 return( 0 );
156}
157
158/*
159 * Create session ticket, secured as recommended in RFC 5077 section 4:
160 *
161 * struct {
162 * opaque key_name[16];
163 * opaque iv[16];
164 * opaque encrypted_state<0..2^16-1>;
165 * opaque mac[32];
166 * } ticket;
167 *
168 * (the internal state structure differs, however).
169 */
170int mbedtls_ssl_ticket_write( mbedtls_ssl_context *ssl, size_t *tlen )
171{
172 int ret;
173 unsigned char * const start = ssl->out_msg + 10;
174 unsigned char *p = start;
175 unsigned char *state;
176 unsigned char iv[16];
177 size_t clear_len, enc_len, pad_len, i;
178
179 *tlen = 0;
180
181 if( ssl->conf->ticket_keys == NULL )
182 return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
183
184 /* Write key name */
185 memcpy( p, ssl->conf->ticket_keys->key_name, 16 );
186 p += 16;
187
188 /* Generate and write IV (with a copy for aes_crypt) */
189 if( ( ret = ssl->conf->f_rng( ssl->conf->p_rng, p, 16 ) ) != 0 )
190 return( ret );
191 memcpy( iv, p, 16 );
192 p += 16;
193
194 /*
195 * Dump session state
196 *
197 * After the session state itself, we still need room for 16 bytes of
198 * padding and 32 bytes of MAC, so there's only so much room left
199 */
200 state = p + 2;
201 if( ssl_save_session( ssl->session_negotiate, state,
202 MBEDTLS_SSL_MAX_CONTENT_LEN - ( state - ssl->out_msg ) - 48,
203 &clear_len ) != 0 )
204 {
205 return( MBEDTLS_ERR_SSL_CERTIFICATE_TOO_LARGE );
206 }
207
208 /* Apply PKCS padding */
209 pad_len = 16 - clear_len % 16;
210 enc_len = clear_len + pad_len;
211 for( i = clear_len; i < enc_len; i++ )
212 state[i] = (unsigned char) pad_len;
213
214 /* Encrypt */
215 if( ( ret = mbedtls_aes_crypt_cbc( &ssl->conf->ticket_keys->enc, MBEDTLS_AES_ENCRYPT,
216 enc_len, iv, state, state ) ) != 0 )
217 {
218 return( ret );
219 }
220
221 /* Write length */
222 *p++ = (unsigned char)( ( enc_len >> 8 ) & 0xFF );
223 *p++ = (unsigned char)( ( enc_len ) & 0xFF );
224 p = state + enc_len;
225
226 /* Compute and write MAC( key_name + iv + enc_state_len + enc_state ) */
227 if( ( ret = mbedtls_md_hmac( mbedtls_md_info_from_type( MBEDTLS_MD_SHA256 ),
228 ssl->conf->ticket_keys->mac_key, 16,
229 start, p - start, p ) ) != 0 )
230 {
231 return( ret );
232 }
233 p += 32;
234
235 *tlen = p - start;
236
237 return( 0 );
238}
239
240/*
241 * Load session ticket (see mbedtls_ssl_ticket_write for structure)
242 */
243int mbedtls_ssl_ticket_parse( mbedtls_ssl_context *ssl,
244 unsigned char *buf,
245 size_t len )
246{
247 int ret;
248 mbedtls_ssl_session session;
249 unsigned char *key_name = buf;
250 unsigned char *iv = buf + 16;
251 unsigned char *enc_len_p = iv + 16;
252 unsigned char *ticket = enc_len_p + 2;
253 unsigned char *mac;
254 unsigned char computed_mac[32];
255 size_t enc_len, clear_len, i;
256 unsigned char pad_len, diff;
257
258 if( len < 34 || ssl->conf->ticket_keys == NULL )
259 return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
260
261 enc_len = ( enc_len_p[0] << 8 ) | enc_len_p[1];
262 mac = ticket + enc_len;
263
264 if( len != enc_len + 66 )
265 return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
266
267 /* Check name, in constant time though it's not a big secret */
268 diff = 0;
269 for( i = 0; i < 16; i++ )
270 diff |= key_name[i] ^ ssl->conf->ticket_keys->key_name[i];
271 /* don't return yet, check the MAC anyway */
272
273 /* Check mac, with constant-time buffer comparison */
274 if( ( ret = mbedtls_md_hmac( mbedtls_md_info_from_type( MBEDTLS_MD_SHA256 ),
275 ssl->conf->ticket_keys->mac_key, 16,
276 buf, len - 32, computed_mac ) ) != 0 )
277 {
278 return( ret );
279 }
280
281 for( i = 0; i < 32; i++ )
282 diff |= mac[i] ^ computed_mac[i];
283
284 /* Now return if ticket is not authentic, since we want to avoid
285 * decrypting arbitrary attacker-chosen data */
286 if( diff != 0 )
287 return( MBEDTLS_ERR_SSL_INVALID_MAC );
288
289 /* Decrypt */
290 if( ( ret = mbedtls_aes_crypt_cbc( &ssl->conf->ticket_keys->dec, MBEDTLS_AES_DECRYPT,
291 enc_len, iv, ticket, ticket ) ) != 0 )
292 {
293 return( ret );
294 }
295
296 /* Check PKCS padding */
297 pad_len = ticket[enc_len - 1];
298
299 ret = 0;
300 for( i = 2; i < pad_len; i++ )
301 if( ticket[enc_len - i] != pad_len )
302 ret = MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
303 if( ret != 0 )
304 return( ret );
305
306 clear_len = enc_len - pad_len;
307
308 /* Actually load session */
309 if( ( ret = ssl_load_session( &session, ticket, clear_len ) ) != 0 )
310 {
311 mbedtls_ssl_session_free( &session );
312 return( ret );
313 }
314
315#if defined(MBEDTLS_HAVE_TIME)
316 /* Check if still valid */
317 if( (int) ( time( NULL) - session.start ) > ssl->conf->ticket_lifetime )
318 {
319 mbedtls_ssl_session_free( &session );
320 return( MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED );
321 }
322#endif
323
324 /*
325 * Keep the session ID sent by the client, since we MUST send it back to
326 * inform him we're accepting the ticket (RFC 5077 section 3.4)
327 */
328 session.length = ssl->session_negotiate->length;
329 memcpy( &session.id, ssl->session_negotiate->id, session.length );
330
331 mbedtls_ssl_session_free( ssl->session_negotiate );
332 memcpy( ssl->session_negotiate, &session, sizeof( mbedtls_ssl_session ) );
333
334 /* Zeroize instead of free as we copied the content */
335 mbedtls_zeroize( &session, sizeof( mbedtls_ssl_session ) );
336
337 return( 0 );
338}
339
Manuel Pégourié-Gonnardfd6d8972015-05-15 12:09:00 +0200340#endif /* MBEDTLS_SSL_TICKET_C */