Implement cf_hmac() actually with constant flow
Signed-off-by: Manuel Pégourié-Gonnard <manuel.pegourie-gonnard@arm.com>
This commit is contained in:
parent
9670a59230
commit
7a8b1e6b71
1 changed files with 86 additions and 65 deletions
|
@ -1070,8 +1070,53 @@ int mbedtls_ssl_encrypt_buf( mbedtls_ssl_context *ssl,
|
||||||
( defined(MBEDTLS_SSL_PROTO_TLS1) || \
|
( defined(MBEDTLS_SSL_PROTO_TLS1) || \
|
||||||
defined(MBEDTLS_SSL_PROTO_TLS1_1) || \
|
defined(MBEDTLS_SSL_PROTO_TLS1_1) || \
|
||||||
defined(MBEDTLS_SSL_PROTO_TLS1_2) )
|
defined(MBEDTLS_SSL_PROTO_TLS1_2) )
|
||||||
|
/*
|
||||||
|
* Constant-flow conditional memcpy:
|
||||||
|
* - if c1 == c2, equivalent to memcpy(dst, src, len),
|
||||||
|
* - otherwise, a no-op,
|
||||||
|
* but with execution flow independent of the values of c1 and c2.
|
||||||
|
*
|
||||||
|
* Use only bit operations to avoid branches that could be used by some
|
||||||
|
* compilers on some platforms to translate comparison operators.
|
||||||
|
*/
|
||||||
|
static void mbedtls_ssl_cf_memcpy_if_eq(unsigned char *dst,
|
||||||
|
const unsigned char *src,
|
||||||
|
size_t len,
|
||||||
|
size_t c1, size_t c2 )
|
||||||
|
{
|
||||||
|
/* diff = 0 if c1 == c2, non-zero otherwise */
|
||||||
|
const size_t diff = c1 ^ c2;
|
||||||
|
|
||||||
|
/* MSVC has a warning about unary minus on unsigned integer types,
|
||||||
|
* but this is well-defined and precisely what we want to do here. */
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning( push )
|
||||||
|
#pragma warning( disable : 4146 )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* diff_msb's most significant bit is bit equal to c1 != c2 */
|
||||||
|
const size_t diff_msb = ( diff | -diff );
|
||||||
|
|
||||||
|
/* diff1 = c1 != c2 */
|
||||||
|
const size_t diff1 = diff_msb >> ( sizeof( diff_msb ) * 8 - 1 );
|
||||||
|
|
||||||
|
/* mask = c1 != c2 ? 0xff : 0x00 */
|
||||||
|
unsigned char mask = (unsigned char) -diff1;
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning( pop )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* dst[i] = c1 != c2 ? dst[i] : src[i] */
|
||||||
|
for( size_t i = 0; i < len; i++ )
|
||||||
|
dst[i] = ( dst[i] & mask ) | ( src[i] & ~mask );
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compute HMAC of variable-length data with constant flow.
|
* Compute HMAC of variable-length data with constant flow.
|
||||||
|
*
|
||||||
|
* Only works with MD-5, SHA-1, SHA-256 and SHA-384.
|
||||||
|
* (Otherwise, computation of block_size needs to be adapted.)
|
||||||
*/
|
*/
|
||||||
MBEDTLS_STATIC_TESTABLE int mbedtls_ssl_cf_hmac(
|
MBEDTLS_STATIC_TESTABLE int mbedtls_ssl_cf_hmac(
|
||||||
mbedtls_md_context_t *ctx,
|
mbedtls_md_context_t *ctx,
|
||||||
|
@ -1080,85 +1125,61 @@ MBEDTLS_STATIC_TESTABLE int mbedtls_ssl_cf_hmac(
|
||||||
size_t min_data_len, size_t max_data_len,
|
size_t min_data_len, size_t max_data_len,
|
||||||
unsigned char *output )
|
unsigned char *output )
|
||||||
{
|
{
|
||||||
/* WORK IN PROGRESS - THIS IS ONLY PSEUDO-CONTANT-TIME */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Process MAC and always update for padlen afterwards to make
|
* This function breaks the HMAC abstraction and uses the md_clone()
|
||||||
* total time independent of padlen.
|
* extension to the MD API in order to get constant-flow behaviour.
|
||||||
*
|
*
|
||||||
* Known timing attacks:
|
* HMAC(msg) is defined as HASH(okey + HASH(ikey + msg)) where + means
|
||||||
* - Lucky Thirteen (http://www.isg.rhul.ac.uk/tls/TLStiming.pdf)
|
* concatenation, and okey/ikey is the XOR of the key with some fix bit
|
||||||
|
* patterns (see RFC 2104, sec. 2), which are stored in ctx->hmac_ctx.
|
||||||
*
|
*
|
||||||
* To compensate for different timings for the MAC calculation
|
* We'll first compute inner_hash = HASH(ikey + msg) by hashing up to
|
||||||
* depending on how much padding was removed (which is determined
|
* minlen, then cloning the context, and for each byte up to maxlen
|
||||||
* by padlen), process extra_run more blocks through the hash
|
* finishing up the hash computation, keeping only the correct result.
|
||||||
* function.
|
|
||||||
*
|
*
|
||||||
* The formula in the paper is
|
* Then we only need to compute HASH(okey + inner_hash) and we're done.
|
||||||
* extra_run = ceil( (L1-55) / 64 ) - ceil( (L2-55) / 64 )
|
|
||||||
* where L1 is the size of the header plus the decrypted message
|
|
||||||
* plus CBC padding and L2 is the size of the header plus the
|
|
||||||
* decrypted message. This is for an underlying hash function
|
|
||||||
* with 64-byte blocks.
|
|
||||||
* We use ( (Lx+8) / 64 ) to handle 'negative Lx' values
|
|
||||||
* correctly. We round down instead of up, so -56 is the correct
|
|
||||||
* value for our calculations instead of -55.
|
|
||||||
*
|
|
||||||
* Repeat the formula rather than defining a block_size variable.
|
|
||||||
* This avoids requiring division by a variable at runtime
|
|
||||||
* (which would be marginally less efficient and would require
|
|
||||||
* linking an extra division function in some builds).
|
|
||||||
*/
|
*/
|
||||||
size_t j, extra_run = 0;
|
const mbedtls_md_type_t md_alg = mbedtls_md_get_type( ctx->md_info );
|
||||||
/* This size is enough to server either as input to
|
const size_t block_size = md_alg == MBEDTLS_MD_SHA384 ? 128 : 64;
|
||||||
* md_process() or as output to md_finish() */
|
const unsigned char * const ikey = (unsigned char *) ctx->hmac_ctx;
|
||||||
unsigned char tmp[MBEDTLS_MD_MAX_BLOCK_SIZE];
|
const unsigned char * const okey = ikey + block_size;
|
||||||
|
const size_t hash_size = mbedtls_md_get_size( ctx->md_info );
|
||||||
|
|
||||||
memset( tmp, 0, sizeof( tmp ) );
|
unsigned char aux_out[MBEDTLS_MD_MAX_SIZE];
|
||||||
|
mbedtls_md_context_t aux;
|
||||||
|
size_t offset;
|
||||||
|
|
||||||
switch( mbedtls_md_get_type( ctx->md_info ) )
|
mbedtls_md_init( &aux );
|
||||||
|
mbedtls_md_setup( &aux, ctx->md_info, 0 );
|
||||||
|
|
||||||
|
/* After hmac_start() of hmac_reset(), ikey has already been hashed,
|
||||||
|
* so we can start directly with the message */
|
||||||
|
mbedtls_md_update( ctx, add_data, add_data_len );
|
||||||
|
mbedtls_md_update( ctx, data, min_data_len );
|
||||||
|
|
||||||
|
/* For each possible length, compute the hash up to that point */
|
||||||
|
for( offset = min_data_len; offset <= max_data_len; offset++ )
|
||||||
{
|
{
|
||||||
#if defined(MBEDTLS_MD5_C) || defined(MBEDTLS_SHA1_C) || \
|
mbedtls_md_clone( &aux, ctx );
|
||||||
defined(MBEDTLS_SHA256_C)
|
mbedtls_md_finish( &aux, aux_out );
|
||||||
case MBEDTLS_MD_MD5:
|
/* Keep only the correct inner_hash in the output buffer */
|
||||||
case MBEDTLS_MD_SHA1:
|
mbedtls_ssl_cf_memcpy_if_eq( output, aux_out, hash_size,
|
||||||
case MBEDTLS_MD_SHA256:
|
offset, data_len_secret );
|
||||||
/* 8 bytes of message size, 64-byte compression blocks */
|
|
||||||
extra_run = ( add_data_len + max_data_len + 8 ) / 64 -
|
if( offset < max_data_len )
|
||||||
( add_data_len + data_len_secret + 8 ) / 64;
|
mbedtls_md_update( ctx, data + offset, 1 );
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
#if defined(MBEDTLS_SHA512_C)
|
|
||||||
case MBEDTLS_MD_SHA384:
|
|
||||||
/* 16 bytes of message size, 128-byte compression blocks */
|
|
||||||
extra_run = ( add_data_len + max_data_len + 16 ) / 128 -
|
|
||||||
( add_data_len + data_len_secret + 16 ) / 128;
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
default:
|
|
||||||
return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mbedtls_md_hmac_update( ctx, add_data, add_data_len );
|
/* Now compute HASH(okey + inner_hash) */
|
||||||
mbedtls_md_hmac_update( ctx, data, data_len_secret );
|
|
||||||
/* Make sure we access everything even when padlen > 0. This
|
|
||||||
* makes the synchronisation requirements for just-in-time
|
|
||||||
* Prime+Probe attacks much tighter and hopefully impractical. */
|
|
||||||
ssl_read_memory( data + min_data_len, max_data_len - min_data_len );
|
|
||||||
mbedtls_md_hmac_finish( ctx, output );
|
|
||||||
|
|
||||||
/* Dummy calls to compression function.
|
|
||||||
* Call mbedtls_md_process at least once due to cache attacks
|
|
||||||
* that observe whether md_process() was called of not.
|
|
||||||
* Respect the usual start-(process|update)-finish sequence for
|
|
||||||
* the sake of hardware accelerators that might require it. */
|
|
||||||
mbedtls_md_starts( ctx );
|
mbedtls_md_starts( ctx );
|
||||||
for( j = 0; j < extra_run + 1; j++ )
|
mbedtls_md_update( ctx, okey, block_size );
|
||||||
mbedtls_md_process( ctx, tmp );
|
mbedtls_md_update( ctx, output, hash_size );
|
||||||
mbedtls_md_finish( ctx, tmp );
|
mbedtls_md_finish( ctx, output );
|
||||||
|
|
||||||
|
/* Done, get ready for next time */
|
||||||
mbedtls_md_hmac_reset( ctx );
|
mbedtls_md_hmac_reset( ctx );
|
||||||
|
|
||||||
|
mbedtls_md_free( &aux );
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
#endif /* MBEDTLS_SSL_SOME_SUITES_USE_CBC && TLS 1.0-1.2 */
|
#endif /* MBEDTLS_SSL_SOME_SUITES_USE_CBC && TLS 1.0-1.2 */
|
||||||
|
|
Loading…
Reference in a new issue