Move mbedtls_cf_hmac function to the constant-time module

Signed-off-by: gabor-mezei-arm <gabor.mezei@arm.com>
This commit is contained in:
gabor-mezei-arm 2021-09-27 14:28:31 +02:00
parent 0e7f71e1a9
commit 1349ffde84
No known key found for this signature in database
GPG key ID: 106F5A41ECC305BD
3 changed files with 145 additions and 91 deletions

View file

@ -19,11 +19,16 @@
#include "common.h" #include "common.h"
#include "constant_time.h" #include "constant_time.h"
#include "mbedtls/error.h"
#if defined(MBEDTLS_BIGNUM_C) #if defined(MBEDTLS_BIGNUM_C)
#include "mbedtls/bignum.h" #include "mbedtls/bignum.h"
#endif #endif
#if defined(MBEDTLS_SSL_TLS_C)
#include "ssl_misc.h"
#endif
/* constant-time buffer comparison */ /* constant-time buffer comparison */
int mbedtls_ssl_safer_memcmp( const void *a, const void *b, size_t n ) 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 ); 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 */

View file

@ -23,6 +23,10 @@
#include "mbedtls/bignum.h" #include "mbedtls/bignum.h"
#endif #endif
#if defined(MBEDTLS_SSL_TLS_C)
#include "ssl_misc.h"
#endif
#include <stddef.h> #include <stddef.h>
int mbedtls_ssl_safer_memcmp( const void *a, const void *b, size_t n ); 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_secret,
size_t offset_min, size_t offset_max, size_t offset_min, size_t offset_max,
size_t len ); 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 */

View file

@ -938,97 +938,6 @@ int mbedtls_ssl_encrypt_buf( mbedtls_ssl_context *ssl,
return( 0 ); 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, int mbedtls_ssl_decrypt_buf( mbedtls_ssl_context const *ssl,
mbedtls_ssl_transform *transform, mbedtls_ssl_transform *transform,
mbedtls_record *rec ) mbedtls_record *rec )