HMAC DRBG: Split entropy-gathering requests to reduce request sizes

According to SP800-90A, the DRBG seeding process should use a nonce
of length `security_strength / 2` bits as part of the DRBG seed. It
further notes that this nonce may be drawn from the same source of
entropy that is used for the first `security_strength` bits of the
DRBG seed. The present HMAC DRBG implementation does that, requesting
`security_strength * 3 / 2` bits of entropy from the configured entropy
source in total to form the initial part of the DRBG seed.

However, some entropy sources may have thresholds in terms of how much
entropy they can provide in a single call to their entropy gathering
function which may be exceeded by the present HMAC DRBG implementation
even if the threshold is not smaller than `security_strength` bits.
Specifically, this is the case for our own entropy module implementation
which only allows requesting at most 32 Bytes of entropy at a time
in configurations disabling SHA-512, and this leads to runtime failure
of HMAC DRBG when used with Mbed Crypto' own entropy callbacks in such
configurations.

This commit fixes this by splitting the seed entropy acquisition into
two calls, one requesting `security_strength` bits first, and another
one requesting `security_strength / 2` bits for the nonce.

Fixes #237.
This commit is contained in:
Hanno Becker 2019-08-27 06:47:18 +01:00 committed by Jaeden Amero
parent 0e08fff32f
commit b98e326455

View File

@ -151,20 +151,32 @@ int mbedtls_hmac_drbg_seed_buf( mbedtls_hmac_drbg_context *ctx,
} }
/* /*
* HMAC_DRBG reseeding: 10.1.2.4 (arabic) + 9.2 (Roman) * Internal function used both for seeding and reseeding the DRBG.
* Comments starting with arabic numbers refer to section 10.1.2.4
* of SP800-90A, while roman numbers refer to section 9.2.
*/ */
int mbedtls_hmac_drbg_reseed( mbedtls_hmac_drbg_context *ctx, static int hmac_drbg_reseed_core( mbedtls_hmac_drbg_context *ctx,
const unsigned char *additional, size_t len ) const unsigned char *additional, size_t len,
int use_nonce )
{ {
unsigned char seed[MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT]; unsigned char seed[MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT];
size_t seedlen; size_t seedlen = 0;
int ret; int ret;
/* III. Check input length */
if( len > MBEDTLS_HMAC_DRBG_MAX_INPUT ||
ctx->entropy_len + len > MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT )
{ {
return( MBEDTLS_ERR_HMAC_DRBG_INPUT_TOO_BIG ); size_t total_entropy_len;
if( use_nonce == 0 )
total_entropy_len = ctx->entropy_len;
else
total_entropy_len = ctx->entropy_len * 3 / 2;
/* III. Check input length */
if( len > MBEDTLS_HMAC_DRBG_MAX_INPUT ||
total_entropy_len + len > MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT )
{
return( MBEDTLS_ERR_HMAC_DRBG_INPUT_TOO_BIG );
}
} }
memset( seed, 0, MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT ); memset( seed, 0, MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT );
@ -172,9 +184,32 @@ int mbedtls_hmac_drbg_reseed( mbedtls_hmac_drbg_context *ctx,
/* IV. Gather entropy_len bytes of entropy for the seed */ /* IV. Gather entropy_len bytes of entropy for the seed */
if( ( ret = ctx->f_entropy( ctx->p_entropy, if( ( ret = ctx->f_entropy( ctx->p_entropy,
seed, ctx->entropy_len ) ) != 0 ) seed, ctx->entropy_len ) ) != 0 )
{
return( MBEDTLS_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED ); return( MBEDTLS_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED );
}
seedlen += ctx->entropy_len;
/* For initial seeding, allow adding of nonce generated
* from the entropy source. See Sect 8.6.7 in SP800-90A. */
if( use_nonce )
{
/* Note: We don't merge the two calls to f_entropy() in order
* to avoid requesting too much entropy from f_entropy()
* at once. Specifically, if the underlying digest is not
* SHA-1, 3 / 2 * entropy_len is at least 36 Bytes, which
* is larger than the maximum of 32 Bytes that our own
* entropy source implementation can emit in a single
* call in configurations disabling SHA-512. */
if( ( ret = ctx->f_entropy( ctx->p_entropy,
seed + seedlen,
ctx->entropy_len / 2 ) ) != 0 )
{
return( MBEDTLS_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED );
}
seedlen += ctx->entropy_len / 2;
}
seedlen = ctx->entropy_len;
/* 1. Concatenate entropy and additional data if any */ /* 1. Concatenate entropy and additional data if any */
if( additional != NULL && len != 0 ) if( additional != NULL && len != 0 )
@ -196,8 +231,20 @@ exit:
return( ret ); return( ret );
} }
/*
* HMAC_DRBG reseeding: 10.1.2.4 + 9.2
*/
int mbedtls_hmac_drbg_reseed( mbedtls_hmac_drbg_context *ctx,
const unsigned char *additional, size_t len )
{
return( hmac_drbg_reseed_core( ctx, additional, len, 0 ) );
}
/* /*
* HMAC_DRBG initialisation (10.1.2.3 + 9.1) * HMAC_DRBG initialisation (10.1.2.3 + 9.1)
*
* The nonce is not passed as a separate parameter but extracted
* from the entropy source as suggested in 8.6.7.
*/ */
int mbedtls_hmac_drbg_seed( mbedtls_hmac_drbg_context *ctx, int mbedtls_hmac_drbg_seed( mbedtls_hmac_drbg_context *ctx,
const mbedtls_md_info_t * md_info, const mbedtls_md_info_t * md_info,
@ -207,7 +254,7 @@ int mbedtls_hmac_drbg_seed( mbedtls_hmac_drbg_context *ctx,
size_t len ) size_t len )
{ {
int ret; int ret;
size_t entropy_len, md_size; size_t md_size;
if( ( ret = mbedtls_md_setup( &ctx->md_ctx, md_info, 1 ) ) != 0 ) if( ( ret = mbedtls_md_setup( &ctx->md_ctx, md_info, 1 ) ) != 0 )
return( ret ); return( ret );
@ -235,20 +282,15 @@ int mbedtls_hmac_drbg_seed( mbedtls_hmac_drbg_context *ctx,
* *
* (This also matches the sizes used in the NIST test vectors.) * (This also matches the sizes used in the NIST test vectors.)
*/ */
entropy_len = md_size <= 20 ? 16 : /* 160-bits hash -> 128 bits */ ctx->entropy_len = md_size <= 20 ? 16 : /* 160-bits hash -> 128 bits */
md_size <= 28 ? 24 : /* 224-bits hash -> 192 bits */ md_size <= 28 ? 24 : /* 224-bits hash -> 192 bits */
32; /* better (256+) -> 256 bits */ 32; /* better (256+) -> 256 bits */
/* if( ( ret = hmac_drbg_reseed_core( ctx, custom, len,
* For initialisation, use more entropy to emulate a nonce 1 /* add nonce */ ) ) != 0 )
* (Again, matches test vectors.) {
*/
ctx->entropy_len = entropy_len * 3 / 2;
if( ( ret = mbedtls_hmac_drbg_reseed( ctx, custom, len ) ) != 0 )
return( ret ); return( ret );
}
ctx->entropy_len = entropy_len;
return( 0 ); return( 0 );
} }