diff --git a/library/constant_time.c b/library/constant_time.c index d48d646ee..60352ae71 100644 --- a/library/constant_time.c +++ b/library/constant_time.c @@ -19,11 +19,16 @@ #include "common.h" #include "constant_time.h" +#include "mbedtls/error.h" #if defined(MBEDTLS_BIGNUM_C) #include "mbedtls/bignum.h" #endif +#if defined(MBEDTLS_SSL_TLS_C) +#include "ssl_misc.h" +#endif + /* constant-time buffer comparison */ int mbedtls_ssl_safer_memcmp( const void *a, const void *b, size_t n ) @@ -439,3 +444,95 @@ void mbedtls_cf_memcpy_offset( offset, offset_secret ); } } + +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC) + +/* + * 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.) + */ +int mbedtls_cf_hmac( + mbedtls_md_context_t *ctx, + const unsigned char *add_data, size_t add_data_len, + const unsigned char *data, size_t data_len_secret, + size_t min_data_len, size_t max_data_len, + unsigned char *output ) +{ + /* + * This function breaks the HMAC abstraction and uses the md_clone() + * extension to the MD API in order to get constant-flow behaviour. + * + * HMAC(msg) is defined as HASH(okey + HASH(ikey + msg)) where + means + * concatenation, and okey/ikey are the XOR of the key with some fixed bit + * patterns (see RFC 2104, sec. 2), which are stored in ctx->hmac_ctx. + * + * We'll first compute inner_hash = HASH(ikey + msg) by hashing up to + * minlen, then cloning the context, and for each byte up to maxlen + * finishing up the hash computation, keeping only the correct result. + * + * Then we only need to compute HASH(okey + inner_hash) and we're done. + */ + const mbedtls_md_type_t md_alg = mbedtls_md_get_type( ctx->md_info ); + /* TLS 1.2 only supports SHA-384, SHA-256, SHA-1, MD-5, + * all of which have the same block size except SHA-384. */ + const size_t block_size = md_alg == MBEDTLS_MD_SHA384 ? 128 : 64; + const unsigned char * const ikey = ctx->hmac_ctx; + const unsigned char * const okey = ikey + block_size; + const size_t hash_size = mbedtls_md_get_size( ctx->md_info ); + + unsigned char aux_out[MBEDTLS_MD_MAX_SIZE]; + mbedtls_md_context_t aux; + size_t offset; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + mbedtls_md_init( &aux ); + +#define MD_CHK( func_call ) \ + do { \ + ret = (func_call); \ + if( ret != 0 ) \ + goto cleanup; \ + } while( 0 ) + + MD_CHK( 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 */ + MD_CHK( mbedtls_md_update( ctx, add_data, add_data_len ) ); + MD_CHK( 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++ ) + { + MD_CHK( mbedtls_md_clone( &aux, ctx ) ); + MD_CHK( mbedtls_md_finish( &aux, aux_out ) ); + /* Keep only the correct inner_hash in the output buffer */ + mbedtls_cf_memcpy_if_eq( output, aux_out, hash_size, + offset, data_len_secret ); + + if( offset < max_data_len ) + MD_CHK( mbedtls_md_update( ctx, data + offset, 1 ) ); + } + + /* The context needs to finish() before it starts() again */ + MD_CHK( mbedtls_md_finish( ctx, aux_out ) ); + + /* Now compute HASH(okey + inner_hash) */ + MD_CHK( mbedtls_md_starts( ctx ) ); + MD_CHK( mbedtls_md_update( ctx, okey, block_size ) ); + MD_CHK( mbedtls_md_update( ctx, output, hash_size ) ); + MD_CHK( mbedtls_md_finish( ctx, output ) ); + + /* Done, get ready for next time */ + MD_CHK( mbedtls_md_hmac_reset( ctx ) ); + +#undef MD_CHK + +cleanup: + mbedtls_md_free( &aux ); + return( ret ); +} + +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */ diff --git a/library/constant_time.h b/library/constant_time.h index 060aca388..782e89a6a 100644 --- a/library/constant_time.h +++ b/library/constant_time.h @@ -23,6 +23,10 @@ #include "mbedtls/bignum.h" #endif +#if defined(MBEDTLS_SSL_TLS_C) +#include "ssl_misc.h" +#endif + #include int mbedtls_ssl_safer_memcmp( const void *a, const void *b, size_t n ); @@ -98,3 +102,47 @@ void mbedtls_cf_memcpy_offset( unsigned char *dst, size_t offset_secret, size_t offset_min, size_t offset_max, size_t len ); + +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC) + +/** Compute the HMAC of variable-length data with constant flow. + * + * This function computes the HMAC of the concatenation of \p add_data and \p + * data, and does with a code flow and memory access pattern that does not + * depend on \p data_len_secret, but only on \p min_data_len and \p + * max_data_len. In particular, this function always reads exactly \p + * max_data_len bytes from \p data. + * + * \param ctx The HMAC context. It must have keys configured + * with mbedtls_md_hmac_starts() and use one of the + * following hashes: SHA-384, SHA-256, SHA-1 or MD-5. + * It is reset using mbedtls_md_hmac_reset() after + * the computation is complete to prepare for the + * next computation. + * \param add_data The additional data prepended to \p data. This + * must point to a readable buffer of \p add_data_len + * bytes. + * \param add_data_len The length of \p add_data in bytes. + * \param data The data appended to \p add_data. This must point + * to a readable buffer of \p max_data_len bytes. + * \param data_len_secret The length of the data to process in \p data. + * This must be no less than \p min_data_len and no + * greater than \p max_data_len. + * \param min_data_len The minimal length of \p data in bytes. + * \param max_data_len The maximal length of \p data in bytes. + * \param output The HMAC will be written here. This must point to + * a writable buffer of sufficient size to hold the + * HMAC value. + * + * \retval 0 on success. + * \retval MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED + * The hardware accelerator failed. + */ +int mbedtls_ssl_cf_hmac( + mbedtls_md_context_t *ctx, + const unsigned char *add_data, size_t add_data_len, + const unsigned char *data, size_t data_len_secret, + size_t min_data_len, size_t max_data_len, + unsigned char *output ); + +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */ diff --git a/library/ssl_msg.c b/library/ssl_msg.c index fcd63ac86..be9c5f2f8 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -938,97 +938,6 @@ int mbedtls_ssl_encrypt_buf( mbedtls_ssl_context *ssl, return( 0 ); } -#if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC) - -/* - * 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_cf_hmac( - mbedtls_md_context_t *ctx, - const unsigned char *add_data, size_t add_data_len, - const unsigned char *data, size_t data_len_secret, - size_t min_data_len, size_t max_data_len, - unsigned char *output ) -{ - /* - * This function breaks the HMAC abstraction and uses the md_clone() - * extension to the MD API in order to get constant-flow behaviour. - * - * HMAC(msg) is defined as HASH(okey + HASH(ikey + msg)) where + means - * concatenation, and okey/ikey are the XOR of the key with some fixed bit - * patterns (see RFC 2104, sec. 2), which are stored in ctx->hmac_ctx. - * - * We'll first compute inner_hash = HASH(ikey + msg) by hashing up to - * minlen, then cloning the context, and for each byte up to maxlen - * finishing up the hash computation, keeping only the correct result. - * - * Then we only need to compute HASH(okey + inner_hash) and we're done. - */ - const mbedtls_md_type_t md_alg = mbedtls_md_get_type( ctx->md_info ); - /* TLS 1.2 only supports SHA-384, SHA-256, SHA-1, MD-5, - * all of which have the same block size except SHA-384. */ - const size_t block_size = md_alg == MBEDTLS_MD_SHA384 ? 128 : 64; - const unsigned char * const ikey = ctx->hmac_ctx; - const unsigned char * const okey = ikey + block_size; - const size_t hash_size = mbedtls_md_get_size( ctx->md_info ); - - unsigned char aux_out[MBEDTLS_MD_MAX_SIZE]; - mbedtls_md_context_t aux; - size_t offset; - int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; - - mbedtls_md_init( &aux ); - -#define MD_CHK( func_call ) \ - do { \ - ret = (func_call); \ - if( ret != 0 ) \ - goto cleanup; \ - } while( 0 ) - - MD_CHK( 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 */ - MD_CHK( mbedtls_md_update( ctx, add_data, add_data_len ) ); - MD_CHK( 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++ ) - { - MD_CHK( mbedtls_md_clone( &aux, ctx ) ); - MD_CHK( mbedtls_md_finish( &aux, aux_out ) ); - /* Keep only the correct inner_hash in the output buffer */ - mbedtls_cf_memcpy_if_eq( output, aux_out, hash_size, - offset, data_len_secret ); - - if( offset < max_data_len ) - MD_CHK( mbedtls_md_update( ctx, data + offset, 1 ) ); - } - - /* The context needs to finish() before it starts() again */ - MD_CHK( mbedtls_md_finish( ctx, aux_out ) ); - - /* Now compute HASH(okey + inner_hash) */ - MD_CHK( mbedtls_md_starts( ctx ) ); - MD_CHK( mbedtls_md_update( ctx, okey, block_size ) ); - MD_CHK( mbedtls_md_update( ctx, output, hash_size ) ); - MD_CHK( mbedtls_md_finish( ctx, output ) ); - - /* Done, get ready for next time */ - MD_CHK( mbedtls_md_hmac_reset( ctx ) ); - -#undef MD_CHK - -cleanup: - mbedtls_md_free( &aux ); - return( ret ); -} -#endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */ - int mbedtls_ssl_decrypt_buf( mbedtls_ssl_context const *ssl, mbedtls_ssl_transform *transform, mbedtls_record *rec )