Use AES-GCM-256 for session ticket protection
diff --git a/library/ssl_ticket.c b/library/ssl_ticket.c
index 18dcdf7..9955060 100644
--- a/library/ssl_ticket.c
+++ b/library/ssl_ticket.c
@@ -64,33 +64,39 @@
uint32_t lifetime )
{
int ret;
- unsigned char buf[16];
+ unsigned char buf[32];
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 ) )
+ if( ( ret = mbedtls_cipher_setup( &ctx->cipher,
+ mbedtls_cipher_info_from_type(
+ MBEDTLS_CIPHER_AES_256_GCM ) ) ) != 0 )
{
- return( ret );
+ goto cleanup;
}
- if( ( ret = mbedtls_aes_setkey_enc( &ctx->enc, buf, 128 ) ) != 0 ||
- ( ret = mbedtls_aes_setkey_dec( &ctx->dec, buf, 128 ) ) != 0 )
+ if( ( ret = f_rng( p_rng, buf, sizeof( buf ) ) != 0 ) )
{
- mbedtls_ssl_ticket_free( ctx );
- return( ret );
+ goto cleanup;
}
+ /* With GCM and CCM, same context can encrypt & decrypt */
+ if( ( ret = mbedtls_cipher_setkey( &ctx->cipher, buf, 256,
+ MBEDTLS_ENCRYPT ) ) != 0 )
+ {
+ goto cleanup;
+ }
+
+cleanup:
mbedtls_zeroize( buf, sizeof( buf ) );
- return( 0 );
+ if( ret != 0 )
+ mbedtls_ssl_ticket_free( ctx );
+
+ return( ret );
}
/*
@@ -203,16 +209,17 @@
}
/*
- * Create session ticket, secured as recommended in RFC 5077 section 4:
+ * Create session ticket, with the following structure:
*
* struct {
- * opaque key_name[16];
- * opaque iv[16];
+ * opaque key_name[4];
+ * opaque iv[12];
* opaque encrypted_state<0..2^16-1>;
- * opaque mac[32];
+ * opaque tag[16];
* } ticket;
*
- * (the internal state structure differs, however).
+ * The key_name, iv, and length of encrypted_state are the additional
+ * authenticated data.
*/
int mbedtls_ssl_ticket_write( void *p_ticket,
const mbedtls_ssl_session *session,
@@ -223,20 +230,21 @@
{
int ret;
mbedtls_ssl_ticket_context *ctx = p_ticket;
- unsigned char *p = start;
- unsigned char *state;
- unsigned char iv[16];
- size_t clear_len, enc_len, pad_len, i;
+ unsigned char *key_name = start;
+ unsigned char *iv = start + 4;
+ unsigned char *state_len_bytes = iv + 12;
+ unsigned char *state = state_len_bytes + 2;
+ unsigned char *tag;
+ size_t clear_len, ciph_len;
*tlen = 0;
- if( ctx == NULL )
+ if( ctx == NULL || ctx->f_rng == NULL )
return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
- /* 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 )
+ /* We need at least 4 bytes for key_name, 12 for IV, 2 for len 16 for tag,
+ * in addition to session itself, that will be checked when writing it. */
+ if( end - start < 4 + 12 + 2 + 16 )
return( MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL );
#if defined(MBEDTLS_THREADING_C)
@@ -246,52 +254,36 @@
*ticket_lifetime = ctx->ticket_lifetime;
- /* Write key name */
- memcpy( p, ctx->key_name, 16 );
- p += 16;
+ memcpy( key_name, ctx->key_name, 4 );
- /* Generate and write IV (with a copy for aes_crypt) */
- if( ( ret = ctx->f_rng( ctx->p_rng, p, 16 ) ) != 0 )
+ if( ( ret = ctx->f_rng( ctx->p_rng, iv, 12 ) ) != 0 )
goto cleanup;
- memcpy( iv, p, 16 );
- p += 16;
/* Dump session state */
- state = p + 2;
if( ( ret = ssl_save_session( session,
- state, end - state, &clear_len ) ) != 0 )
+ state, end - state, &clear_len ) ) != 0 ||
+ (unsigned long) clear_len > 65535 )
{
goto cleanup;
}
+ state_len_bytes[0] = ( clear_len >> 8 ) & 0xff;
+ state_len_bytes[1] = ( clear_len ) & 0xff;
- /* 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 */
- if( ( ret = mbedtls_aes_crypt_cbc( &ctx->enc, MBEDTLS_AES_ENCRYPT,
- enc_len, iv, state, state ) ) != 0 )
+ /* Encrypt and authenticate */
+ tag = state + clear_len;
+ if( ( ret = mbedtls_cipher_auth_encrypt( &ctx->cipher,
+ iv, 12, key_name, 4 + 12 + 2,
+ state, clear_len, state, &ciph_len, tag, 16 ) ) != 0 )
{
goto cleanup;
}
-
- /* 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 ),
- ctx->mac_key, 16,
- start, p - start, p ) ) != 0 )
+ if( ciph_len != clear_len )
{
+ ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR;
goto cleanup;
}
- p += 32;
- *tlen = p - start;
+ *tlen = 4 + 12 + 2 + 16 + ciph_len;
cleanup:
#if defined(MBEDTLS_THREADING_C)
@@ -313,16 +305,17 @@
int ret;
mbedtls_ssl_ticket_context *ctx = p_ticket;
unsigned char *key_name = buf;
- unsigned char *iv = buf + 16;
- unsigned char *enc_len_p = iv + 16;
+ unsigned char *iv = buf + 4;
+ unsigned char *enc_len_p = iv + 12;
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;
+ unsigned char *tag;
+ size_t enc_len, clear_len;
- if( len < 34 || ctx == NULL )
+ if( ctx == NULL || ctx->f_rng == NULL ||
+ len < 4 + 12 + 2 + 16 )
+ {
return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+ }
#if defined(MBEDTLS_THREADING_C)
if( ( ret = mbedtls_mutex_lock( &ctx->mutex ) ) != 0 )
@@ -330,57 +323,34 @@
#endif
enc_len = ( enc_len_p[0] << 8 ) | enc_len_p[1];
- mac = ticket + enc_len;
+ tag = ticket + enc_len;
- if( len != enc_len + 66 )
+ if( len != 4 + 12 + 2 + enc_len + 16 )
{
ret = MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
goto cleanup;
}
- /* Check name, in constant time though it's not a big secret */
- diff = 0;
- for( i = 0; i < 16; i++ )
- diff |= key_name[i] ^ ctx->key_name[i];
- /* 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 ),
- ctx->mac_key, 16,
- buf, len - 32, computed_mac ) ) != 0 )
- {
- goto cleanup;
- }
-
- 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 )
+ /* Check name (public data) */
+ if( memcmp( key_name, ctx->key_name, 4 ) != 0 )
{
ret = MBEDTLS_ERR_SSL_INVALID_MAC;
goto cleanup;
}
- /* Decrypt */
- if( ( ret = mbedtls_aes_crypt_cbc( &ctx->dec, MBEDTLS_AES_DECRYPT,
- enc_len, iv, ticket, ticket ) ) != 0 )
+ /* Decrypt and authenticate */
+ if( ( ret = mbedtls_cipher_auth_decrypt( &ctx->cipher, iv, 12,
+ key_name, 4 + 12 + 2, ticket, enc_len,
+ ticket, &clear_len, tag, 16 ) ) != 0 )
{
+ /* TODO: convert AUTH_FAILED to INVALID_MAC */
goto cleanup;
}
-
- /* 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 )
+ if( clear_len != enc_len )
+ {
+ ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR;
goto cleanup;
-
- clear_len = enc_len - pad_len;
+ }
/* Actually load session */
if( ( ret = ssl_load_session( session, ticket, clear_len ) ) != 0 )
@@ -414,8 +384,7 @@
*/
void mbedtls_ssl_ticket_free( mbedtls_ssl_ticket_context *ctx )
{
- mbedtls_aes_free( &ctx->enc );
- mbedtls_aes_free( &ctx->dec );
+ mbedtls_cipher_free( &ctx->cipher );
#if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_free( &ctx->mutex );