ssl_ticket.c 11.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
/*
 *  TLS server tickets callbacks implementation
 *
 *  Copyright (C) 2015, ARM Limited, All Rights Reserved
 *
 *  This file is part of mbed TLS (https://tls.mbed.org)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#if !defined(MBEDTLS_CONFIG_FILE)
#include "mbedtls/config.h"
#else
#include MBEDTLS_CONFIG_FILE
#endif

#if defined(MBEDTLS_SSL_TICKET_C)

#include "mbedtls/ssl_ticket.h"

#if defined(MBEDTLS_PLATFORM_C)
#include "mbedtls/platform.h"
#else
#define mbedtls_malloc     malloc
#define mbedtls_free       free
#endif

#include <string.h>

42 43 44 45 46 47 48 49 50 51 52
/* Implementation that should never be optimized out by the compiler */
static void mbedtls_zeroize( void *v, size_t n ) {
    volatile unsigned char *p = v; while( n-- ) *p++ = 0;
}

/*
 * Initialze context
 */
void mbedtls_ssl_ticket_init( mbedtls_ssl_ticket_context *ctx )
{
    memset( ctx, 0, sizeof( mbedtls_ssl_ticket_context ) );
53 54 55 56

#if defined(MBEDTLS_THREADING_C)
    mbedtls_mutex_init( &ctx->mutex );
#endif
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
}

/*
 * Setup context for actual use
 */
int mbedtls_ssl_ticket_setup( mbedtls_ssl_ticket_context *ctx,
    int (*f_rng)(void *, unsigned char *, size_t), void *p_rng,
    uint32_t lifetime )
{
    int ret;
    unsigned char buf[16];

    ctx->f_rng = f_rng;
    ctx->p_rng = p_rng;

    ctx->ticket_lifetime = lifetime;

    mbedtls_aes_init( &ctx->enc );
    mbedtls_aes_init( &ctx->dec );

    if( ( ret = f_rng( p_rng, ctx->key_name, 16 ) != 0 ) ||
        ( ret = f_rng( p_rng, ctx->mac_key,  16 ) != 0 ) ||
        ( ret = f_rng( p_rng, buf,           16 ) != 0 ) )
    {
        return( ret );
    }

    if( ( ret = mbedtls_aes_setkey_enc( &ctx->enc, buf, 128 ) ) != 0 ||
        ( ret = mbedtls_aes_setkey_dec( &ctx->dec, buf, 128 ) ) != 0 )
    {
        mbedtls_ssl_ticket_free( ctx );
        return( ret );
    }

    mbedtls_zeroize( buf, sizeof( buf ) );

    return( 0 );
}

96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
/*
 * Serialize a session in the following format:
 *  0   .   n-1     session structure, n = sizeof(mbedtls_ssl_session)
 *  n   .   n+2     peer_cert length = m (0 if no certificate)
 *  n+3 .   n+2+m   peer cert ASN.1
 */
static int ssl_save_session( const mbedtls_ssl_session *session,
                             unsigned char *buf, size_t buf_len,
                             size_t *olen )
{
    unsigned char *p = buf;
    size_t left = buf_len;
#if defined(MBEDTLS_X509_CRT_PARSE_C)
    size_t cert_len;
#endif /* MBEDTLS_X509_CRT_PARSE_C */

    if( left < sizeof( mbedtls_ssl_session ) )
113
        return( MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL );
114 115 116 117 118 119 120 121 122 123 124 125

    memcpy( p, session, sizeof( mbedtls_ssl_session ) );
    p += sizeof( mbedtls_ssl_session );
    left -= sizeof( mbedtls_ssl_session );

#if defined(MBEDTLS_X509_CRT_PARSE_C)
    if( session->peer_cert == NULL )
        cert_len = 0;
    else
        cert_len = session->peer_cert->raw.len;

    if( left < 3 + cert_len )
126
        return( MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL );
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216

    *p++ = (unsigned char)( cert_len >> 16 & 0xFF );
    *p++ = (unsigned char)( cert_len >>  8 & 0xFF );
    *p++ = (unsigned char)( cert_len       & 0xFF );

    if( session->peer_cert != NULL )
        memcpy( p, session->peer_cert->raw.p, cert_len );

    p += cert_len;
#endif /* MBEDTLS_X509_CRT_PARSE_C */

    *olen = p - buf;

    return( 0 );
}

/*
 * Unserialise session, see ssl_save_session()
 */
static int ssl_load_session( mbedtls_ssl_session *session,
                             const unsigned char *buf, size_t len )
{
    const unsigned char *p = buf;
    const unsigned char * const end = buf + len;
#if defined(MBEDTLS_X509_CRT_PARSE_C)
    size_t cert_len;
#endif /* MBEDTLS_X509_CRT_PARSE_C */

    if( p + sizeof( mbedtls_ssl_session ) > end )
        return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );

    memcpy( session, p, sizeof( mbedtls_ssl_session ) );
    p += sizeof( mbedtls_ssl_session );

#if defined(MBEDTLS_X509_CRT_PARSE_C)
    if( p + 3 > end )
        return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );

    cert_len = ( p[0] << 16 ) | ( p[1] << 8 ) | p[2];
    p += 3;

    if( cert_len == 0 )
    {
        session->peer_cert = NULL;
    }
    else
    {
        int ret;

        if( p + cert_len > end )
            return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );

        session->peer_cert = mbedtls_malloc( sizeof( mbedtls_x509_crt ) );

        if( session->peer_cert == NULL )
            return( MBEDTLS_ERR_SSL_MALLOC_FAILED );

        mbedtls_x509_crt_init( session->peer_cert );

        if( ( ret = mbedtls_x509_crt_parse_der( session->peer_cert,
                                        p, cert_len ) ) != 0 )
        {
            mbedtls_x509_crt_free( session->peer_cert );
            mbedtls_free( session->peer_cert );
            session->peer_cert = NULL;
            return( ret );
        }

        p += cert_len;
    }
#endif /* MBEDTLS_X509_CRT_PARSE_C */

    if( p != end )
        return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );

    return( 0 );
}

/*
 * Create session ticket, secured as recommended in RFC 5077 section 4:
 *
 *    struct {
 *        opaque key_name[16];
 *        opaque iv[16];
 *        opaque encrypted_state<0..2^16-1>;
 *        opaque mac[32];
 *    } ticket;
 *
 * (the internal state structure differs, however).
 */
217
int mbedtls_ssl_ticket_write( void *p_ticket,
218 219 220
                              const mbedtls_ssl_session *session,
                              unsigned char *start,
                              const unsigned char *end,
221 222
                              size_t *tlen,
                              uint32_t *ticket_lifetime )
223 224
{
    int ret;
225
    mbedtls_ssl_ticket_context *ctx = p_ticket;
226 227 228 229 230 231 232
    unsigned char *p = start;
    unsigned char *state;
    unsigned char iv[16];
    size_t clear_len, enc_len, pad_len, i;

    *tlen = 0;

233
    if( ctx == NULL )
234 235
        return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );

236 237 238 239 240 241
    /* We need at least 16 bytes for key_name, 16 for IV, 2 for len
     * 16 for padding, 32 for MAC, in addition to session itself,
     * that will be checked when writing it. */
    if( end - start < 16 + 16 + 2 + 16 + 32 )
        return( MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL );

242 243 244 245 246 247 248
#if defined(MBEDTLS_THREADING_C)
    if( ( ret = mbedtls_mutex_lock( &ctx->mutex ) ) != 0 )
        return( ret );
#endif

    *ticket_lifetime = ctx->ticket_lifetime;

249
    /* Write key name */
250
    memcpy( p, ctx->key_name, 16 );
251 252 253
    p += 16;

    /* Generate and write IV (with a copy for aes_crypt) */
254
    if( ( ret = ctx->f_rng( ctx->p_rng, p, 16 ) ) != 0 )
255
        goto cleanup;
256 257 258
    memcpy( iv, p, 16 );
    p += 16;

259
    /* Dump session state */
260
    state = p + 2;
261 262 263 264 265
    if( ( ret = ssl_save_session( session,
                                  state, end - state, &clear_len ) ) != 0 )
    {
         goto cleanup;
    }
266 267 268 269 270 271 272 273

    /* Apply PKCS padding */
    pad_len = 16 - clear_len % 16;
    enc_len = clear_len + pad_len;
    for( i = clear_len; i < enc_len; i++ )
        state[i] = (unsigned char) pad_len;

    /* Encrypt */
274
    if( ( ret = mbedtls_aes_crypt_cbc( &ctx->enc, MBEDTLS_AES_ENCRYPT,
275 276
                               enc_len, iv, state, state ) ) != 0 )
    {
277
        goto cleanup;
278 279 280 281 282 283 284 285 286
    }

    /* Write length */
    *p++ = (unsigned char)( ( enc_len >> 8 ) & 0xFF );
    *p++ = (unsigned char)( ( enc_len      ) & 0xFF );
    p = state + enc_len;

    /* Compute and write MAC( key_name + iv + enc_state_len + enc_state ) */
    if( ( ret = mbedtls_md_hmac( mbedtls_md_info_from_type( MBEDTLS_MD_SHA256 ),
287
                         ctx->mac_key, 16,
288 289
                         start, p - start, p ) ) != 0 )
    {
290
        goto cleanup;
291 292 293 294 295
    }
    p += 32;

    *tlen = p - start;

296 297 298 299 300 301 302
cleanup:
#if defined(MBEDTLS_THREADING_C)
    if( mbedtls_mutex_unlock( &ctx->mutex ) != 0 )
        return( MBEDTLS_ERR_THREADING_MUTEX_ERROR );
#endif

    return( ret );
303 304 305 306 307
}

/*
 * Load session ticket (see mbedtls_ssl_ticket_write for structure)
 */
308
int mbedtls_ssl_ticket_parse( void *p_ticket,
309 310 311
                              mbedtls_ssl_session *session,
                              unsigned char *buf,
                              size_t len )
312 313
{
    int ret;
314
    mbedtls_ssl_ticket_context *ctx = p_ticket;
315 316 317 318 319 320 321 322 323
    unsigned char *key_name = buf;
    unsigned char *iv = buf + 16;
    unsigned char *enc_len_p = iv + 16;
    unsigned char *ticket = enc_len_p + 2;
    unsigned char *mac;
    unsigned char computed_mac[32];
    size_t enc_len, clear_len, i;
    unsigned char pad_len, diff;

324
    if( len < 34 || ctx == NULL )
325 326
        return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );

327 328 329 330 331
#if defined(MBEDTLS_THREADING_C)
    if( ( ret = mbedtls_mutex_lock( &ctx->mutex ) ) != 0 )
        return( ret );
#endif

332 333 334 335
    enc_len = ( enc_len_p[0] << 8 ) | enc_len_p[1];
    mac = ticket + enc_len;

    if( len != enc_len + 66 )
336 337 338 339
    {
        ret = MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
        goto cleanup;
    }
340 341 342 343

    /* Check name, in constant time though it's not a big secret */
    diff = 0;
    for( i = 0; i < 16; i++ )
344
        diff |= key_name[i] ^ ctx->key_name[i];
345 346 347 348
    /* don't return yet, check the MAC anyway */

    /* Check mac, with constant-time buffer comparison */
    if( ( ret = mbedtls_md_hmac( mbedtls_md_info_from_type( MBEDTLS_MD_SHA256 ),
349
                         ctx->mac_key, 16,
350 351
                         buf, len - 32, computed_mac ) ) != 0 )
    {
352
        goto cleanup;
353 354 355 356 357 358 359 360
    }

    for( i = 0; i < 32; i++ )
        diff |= mac[i] ^ computed_mac[i];

    /* Now return if ticket is not authentic, since we want to avoid
     * decrypting arbitrary attacker-chosen data */
    if( diff != 0 )
361 362 363 364
    {
        ret = MBEDTLS_ERR_SSL_INVALID_MAC;
        goto cleanup;
    }
365 366

    /* Decrypt */
367
    if( ( ret = mbedtls_aes_crypt_cbc( &ctx->dec, MBEDTLS_AES_DECRYPT,
368 369
                               enc_len, iv, ticket, ticket ) ) != 0 )
    {
370
        goto cleanup;
371 372 373 374 375 376 377 378 379 380
    }

    /* Check PKCS padding */
    pad_len = ticket[enc_len - 1];

    ret = 0;
    for( i = 2; i < pad_len; i++ )
        if( ticket[enc_len - i] != pad_len )
            ret = MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
    if( ret != 0 )
381
        goto cleanup;
382 383 384 385

    clear_len = enc_len - pad_len;

    /* Actually load session */
386
    if( ( ret = ssl_load_session( session, ticket, clear_len ) ) != 0 )
387
        goto cleanup;
388 389

#if defined(MBEDTLS_HAVE_TIME)
390
    {
391 392 393 394 395 396 397 398 399
        /* Check for expiration */
        time_t current_time = time( NULL );

        if( current_time < session->start ||
            (uint32_t)( current_time - session->start ) > ctx->ticket_lifetime )
        {
            ret = MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED;
            goto cleanup;
        }
400
    }
401 402
#endif

403 404 405 406 407 408 409
cleanup:
#if defined(MBEDTLS_THREADING_C)
    if( mbedtls_mutex_unlock( &ctx->mutex ) != 0 )
        return( MBEDTLS_ERR_THREADING_MUTEX_ERROR );
#endif

    return( ret );
410 411
}

412 413 414 415 416 417 418 419
/*
 * Free context
 */
void mbedtls_ssl_ticket_free( mbedtls_ssl_ticket_context *ctx )
{
    mbedtls_aes_free( &ctx->enc );
    mbedtls_aes_free( &ctx->dec );

420 421 422 423
#if defined(MBEDTLS_THREADING_C)
    mbedtls_mutex_free( &ctx->mutex );
#endif

424 425 426
    mbedtls_zeroize( ctx, sizeof( mbedtls_ssl_ticket_context ) );
}

427
#endif /* MBEDTLS_SSL_TICKET_C */