CTR_DRBG: add the possibility of grabbing entropy for a nonce

Add a new function mbedtls_ctr_drbg_set_nonce_len() which configures
the DRBG instance to call f_entropy a second time during the initial
seeding to grab a nonce.

The default nonce length is 0, so there is no behavior change unless
the user calls the new function.
This commit is contained in:
Gilles Peskine 2019-10-22 18:42:27 +02:00
parent dbd3f7c68d
commit 9be5098061
2 changed files with 125 additions and 40 deletions

View file

@ -22,6 +22,9 @@
* - 256 bits if AES-256 is used, #MBEDTLS_CTR_DRBG_ENTROPY_LEN is set * - 256 bits if AES-256 is used, #MBEDTLS_CTR_DRBG_ENTROPY_LEN is set
* to 32 or more, and the DRBG is initialized with an explicit * to 32 or more, and the DRBG is initialized with an explicit
* nonce in the \c custom parameter to mbedtls_ctr_drbg_seed(). * nonce in the \c custom parameter to mbedtls_ctr_drbg_seed().
* - 256 bits if AES-256 is used, #MBEDTLS_CTR_DRBG_ENTROPY_LEN is set
* to 32 or more, and mbedtls_ctr_drbg_set_nonce_len() is called to set
* an entropy nonce length of 16 bytes or more.
* - 128 bits if AES-256 is used but #MBEDTLS_CTR_DRBG_ENTROPY_LEN is * - 128 bits if AES-256 is used but #MBEDTLS_CTR_DRBG_ENTROPY_LEN is
* between 24 and 47 and the DRBG is not initialized with an explicit * between 24 and 47 and the DRBG is not initialized with an explicit
* nonce (see mbedtls_ctr_drbg_seed()). * nonce (see mbedtls_ctr_drbg_seed()).
@ -29,6 +32,9 @@
* and #MBEDTLS_CTR_DRBG_ENTROPY_LEN is set to 24 or more (which is * and #MBEDTLS_CTR_DRBG_ENTROPY_LEN is set to 24 or more (which is
* always the case unless it is explicitly set to a different value * always the case unless it is explicitly set to a different value
* in config.h). * in config.h).
* - 128 bits if AES-128 is used (\c MBEDTLS_CTR_DRBG_USE_128_BIT_KEY enabled)
* to 16 or more, and mbedtls_ctr_drbg_set_nonce_len() is called to set
* an entropy nonce length of 8 bytes or more.
* *
* Note that the value of #MBEDTLS_CTR_DRBG_ENTROPY_LEN defaults to: * Note that the value of #MBEDTLS_CTR_DRBG_ENTROPY_LEN defaults to:
* - \c 48 if the module \c MBEDTLS_SHA512_C is enabled and the symbol * - \c 48 if the module \c MBEDTLS_SHA512_C is enabled and the symbol
@ -172,7 +178,11 @@ typedef struct mbedtls_ctr_drbg_context
int reseed_counter; /*!< The reseed counter. int reseed_counter; /*!< The reseed counter.
* This is the number of requests that have * This is the number of requests that have
* been made since the last (re)seeding, * been made since the last (re)seeding,
* minus one. */ * minus one.
* Before the initial seeding, this field
* contains the amount of entropy in bytes
* to use as a nonce for the initial seeding.
*/
int prediction_resistance; /*!< This determines whether prediction int prediction_resistance; /*!< This determines whether prediction
resistance is enabled, that is resistance is enabled, that is
whether to systematically reseed before whether to systematically reseed before
@ -222,43 +232,45 @@ void mbedtls_ctr_drbg_init( mbedtls_ctr_drbg_context *ctx );
* The entropy length is #MBEDTLS_CTR_DRBG_ENTROPY_LEN by default. * The entropy length is #MBEDTLS_CTR_DRBG_ENTROPY_LEN by default.
* You can override it by calling mbedtls_ctr_drbg_set_entropy_len(). * You can override it by calling mbedtls_ctr_drbg_set_entropy_len().
* *
* You can provide a personalization string in addition to the * You can provide a nonce and personalization string in addition to the
* entropy source, to make this instantiation as unique as possible. * entropy source, to make this instantiation as unique as possible.
* See SP 800-90A §8.6.7 for more details about nonces.
*
* The _seed_material_ value passed to the derivation function in
* the CTR_DRBG Instantiate Process described in NIST SP 800-90A §10.2.1.3.2
* is the concatenation of the following strings:
* - A string obtained by calling \p f_entropy function for the entropy
* length.
* - A string obtained by calling \p f_entropy function for the nonce
* length set with mbedtls_ctr_drbg_set_nonce_len(). If the entropy
* nonce length is \c 0, this function does not make a second call
* to \p f_entropy.
* - The \p custom string.
*
* \note To achieve the nominal security strength permitted
* by CTR_DRBG, the entropy length must be:
* - at least 16 bytes for a 128-bit strength
* (maximum achievable strength when using AES-128);
* - at least 32 bytes for a 256-bit strength
* (maximum achievable strength when using AES-256).
*
* In addition, if you do not pass a nonce in \p custom,
* the sum of the entropy length
* (#MBEDTLS_CTR_DRBG_ENTROPY_LEN unless overridden with
* mbedtls_ctr_drbg_set_entropy_len())
* and the entropy nonce length (\c 0 unless overridden
* with mbedtls_ctr_drbg_set_nonce_len()) must be:
* - at least 24 bytes for a 128-bit strength
* (maximum achievable strength when using AES-128);
* - at least 48 bytes for a 256-bit strength
* (maximum achievable strength when using AES-256).
* *
* \note The _seed_material_ value passed to the derivation
* function in the CTR_DRBG Instantiate Process
* described in NIST SP 800-90A §10.2.1.3.2
* is the concatenation of the string obtained from
* calling \p f_entropy and the \p custom string.
* The origin of the nonce depends on the value of
* the entropy length relative to the security strength.
* - If the entropy length is at least 1.5 times the
* security strength then the nonce is taken from the
* string obtained with \p f_entropy.
* - If the entropy length is less than the security
* strength, then the nonce is taken from \p custom.
* In this case, for compliance with SP 800-90A,
* you must pass a unique value of \p custom at
* each invocation. See SP 800-90A §8.6.7 for more
* details.
*/
#if MBEDTLS_CTR_DRBG_ENTROPY_LEN < MBEDTLS_CTR_DRBG_KEYSIZE * 3 / 2
/** \warning When #MBEDTLS_CTR_DRBG_ENTROPY_LEN is less than
* #MBEDTLS_CTR_DRBG_KEYSIZE * 3 / 2, to achieve the
* maximum security strength permitted by CTR_DRBG,
* you must pass a value of \p custom that is a nonce:
* this value must never be repeated in subsequent
* runs of the same application or on a different
* device.
*/
#endif
/**
* \param ctx The CTR_DRBG context to seed. * \param ctx The CTR_DRBG context to seed.
* \param f_entropy The entropy callback, taking as arguments the * \param f_entropy The entropy callback, taking as arguments the
* \p p_entropy context, the buffer to fill, and the * \p p_entropy context, the buffer to fill, and the
* length of the buffer. * length of the buffer.
* \p f_entropy is always called with a buffer size * \p f_entropy is always called with a buffer size
* equal to the entropy length. * less than or equal to the entropy length.
* \param p_entropy The entropy context to pass to \p f_entropy. * \param p_entropy The entropy context to pass to \p f_entropy.
* \param custom The personalization string. * \param custom The personalization string.
* This can be \c NULL, in which case the personalization * This can be \c NULL, in which case the personalization
@ -320,11 +332,35 @@ void mbedtls_ctr_drbg_set_prediction_resistance( mbedtls_ctr_drbg_context *ctx,
* *
* \param ctx The CTR_DRBG context. * \param ctx The CTR_DRBG context.
* \param len The amount of entropy to grab, in bytes. * \param len The amount of entropy to grab, in bytes.
* This must be at most #MBEDTLS_CTR_DRBG_MAX_SEED_INPUT. * This must be at most #MBEDTLS_CTR_DRBG_MAX_SEED_INPUT
* and at most the maximum length accepted by the
* entropy function that is set in the context.
*/ */
void mbedtls_ctr_drbg_set_entropy_len( mbedtls_ctr_drbg_context *ctx, void mbedtls_ctr_drbg_set_entropy_len( mbedtls_ctr_drbg_context *ctx,
size_t len ); size_t len );
/**
* \brief This function sets the amount of entropy grabbed
* as a nonce for the initial seeding.
*
* Call this function before calling mbedtls_ctr_drbg_seed() to read
* a nonce from the entropy source during the initial seeding.
*
* \param ctx The CTR_DRBG context.
* \param len The amount of entropy to grab for the nonce, in bytes.
* This must be at most #MBEDTLS_CTR_DRBG_MAX_SEED_INPUT
* and at most the maximum length accepted by the
* entropy function that is set in the context.
*
* \return \c 0 on success.
* \return #MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG if \p len is
* more than #MBEDTLS_CTR_DRBG_MAX_SEED_INPUT.
* \return #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED
* if the initial seeding has already taken place.
*/
int mbedtls_ctr_drbg_set_nonce_len( mbedtls_ctr_drbg_context *ctx,
size_t len );
/** /**
* \brief This function sets the reseed interval. * \brief This function sets the reseed interval.
* *

View file

@ -86,6 +86,32 @@ void mbedtls_ctr_drbg_set_entropy_len( mbedtls_ctr_drbg_context *ctx,
ctx->entropy_len = len; ctx->entropy_len = len;
} }
int mbedtls_ctr_drbg_set_nonce_len( mbedtls_ctr_drbg_context *ctx,
size_t len )
{
/* If mbedtls_ctr_drbg_seed() has already been called, it's
* too late. Return the error code that's closest to making sense. */
if( ctx->f_entropy != NULL )
return( MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED );
if( len > MBEDTLS_CTR_DRBG_MAX_SEED_INPUT )
return( MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG );
#if SIZE_MAX > INT_MAX
/* This shouldn't be an issue because
* MBEDTLS_CTR_DRBG_MAX_SEED_INPUT < INT_MAX in any sensible
* configuration, but make sure anyway. */
if( len > INT_MAX )
return( MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG );
#endif
/* For backward compatibility with Mbed TLS <= 2.19, store the
* entropy nonce length in a field that already exists, but isn't
* used until after the initial seeding. */
/* Due to the capping of len above, the value fits in an int. */
ctx->reseed_counter = (int) len;
return( 0 );
}
void mbedtls_ctr_drbg_set_reseed_interval( mbedtls_ctr_drbg_context *ctx, void mbedtls_ctr_drbg_set_reseed_interval( mbedtls_ctr_drbg_context *ctx,
int interval ) int interval )
{ {
@ -319,7 +345,7 @@ void mbedtls_ctr_drbg_update( mbedtls_ctr_drbg_context *ctx,
#endif /* MBEDTLS_DEPRECATED_REMOVED */ #endif /* MBEDTLS_DEPRECATED_REMOVED */
/* CTR_DRBG_Reseed with derivation function (SP 800-90A &sect;10.2.1.4.2) /* CTR_DRBG_Reseed with derivation function (SP 800-90A &sect;10.2.1.4.2)
* mbedtls_ctr_drbg_reseed(ctx, additional, len) * mbedtls_ctr_drbg_reseed(ctx, additional, len, nonce_len)
* implements * implements
* CTR_DRBG_Reseed(working_state, entropy_input, additional_input) * CTR_DRBG_Reseed(working_state, entropy_input, additional_input)
* -> new_working_state * -> new_working_state
@ -327,11 +353,14 @@ void mbedtls_ctr_drbg_update( mbedtls_ctr_drbg_context *ctx,
* ctx contains working_state * ctx contains working_state
* additional[:len] = additional_input * additional[:len] = additional_input
* and entropy_input comes from calling ctx->f_entropy * and entropy_input comes from calling ctx->f_entropy
* for (ctx->entropy_len + nonce_len) bytes
* and with output * and with output
* ctx contains new_working_state * ctx contains new_working_state
*/ */
int mbedtls_ctr_drbg_reseed( mbedtls_ctr_drbg_context *ctx, int mbedtls_ctr_drbg_reseed_internal( mbedtls_ctr_drbg_context *ctx,
const unsigned char *additional, size_t len ) const unsigned char *additional,
size_t len,
size_t nonce_len )
{ {
unsigned char seed[MBEDTLS_CTR_DRBG_MAX_SEED_INPUT]; unsigned char seed[MBEDTLS_CTR_DRBG_MAX_SEED_INPUT];
size_t seedlen = 0; size_t seedlen = 0;
@ -339,7 +368,9 @@ int mbedtls_ctr_drbg_reseed( mbedtls_ctr_drbg_context *ctx,
if( ctx->entropy_len > MBEDTLS_CTR_DRBG_MAX_SEED_INPUT ) if( ctx->entropy_len > MBEDTLS_CTR_DRBG_MAX_SEED_INPUT )
return( MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG ); return( MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG );
if( len > MBEDTLS_CTR_DRBG_MAX_SEED_INPUT - ctx->entropy_len ) if( nonce_len > MBEDTLS_CTR_DRBG_MAX_SEED_INPUT - ctx->entropy_len )
return( MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG );
if( len > MBEDTLS_CTR_DRBG_MAX_SEED_INPUT - ctx->entropy_len - nonce_len )
return( MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG ); return( MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG );
memset( seed, 0, MBEDTLS_CTR_DRBG_MAX_SEED_INPUT ); memset( seed, 0, MBEDTLS_CTR_DRBG_MAX_SEED_INPUT );
@ -351,6 +382,16 @@ int mbedtls_ctr_drbg_reseed( mbedtls_ctr_drbg_context *ctx,
} }
seedlen += ctx->entropy_len; seedlen += ctx->entropy_len;
/* Gather entropy for a nonce if requested. */
if( nonce_len != 0 )
{
if( 0 != ctx->f_entropy( ctx->p_entropy, seed, nonce_len ) )
{
return( MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED );
}
seedlen += nonce_len;
}
/* Add additional data if provided. */ /* Add additional data if provided. */
if( additional != NULL && len != 0 ) if( additional != NULL && len != 0 )
{ {
@ -372,6 +413,12 @@ exit:
return( ret ); return( ret );
} }
int mbedtls_ctr_drbg_reseed( mbedtls_ctr_drbg_context *ctx,
const unsigned char *additional, size_t len )
{
return( mbedtls_ctr_drbg_reseed_internal( ctx, additional, len, 0 ) );
}
/* CTR_DRBG_Instantiate with derivation function (SP 800-90A &sect;10.2.1.3.2) /* CTR_DRBG_Instantiate with derivation function (SP 800-90A &sect;10.2.1.3.2)
* mbedtls_ctr_drbg_seed(ctx, f_entropy, p_entropy, custom, len) * mbedtls_ctr_drbg_seed(ctx, f_entropy, p_entropy, custom, len)
* implements * implements
@ -403,16 +450,18 @@ int mbedtls_ctr_drbg_seed( mbedtls_ctr_drbg_context *ctx,
ctx->entropy_len = MBEDTLS_CTR_DRBG_ENTROPY_LEN; ctx->entropy_len = MBEDTLS_CTR_DRBG_ENTROPY_LEN;
ctx->reseed_interval = MBEDTLS_CTR_DRBG_RESEED_INTERVAL; ctx->reseed_interval = MBEDTLS_CTR_DRBG_RESEED_INTERVAL;
/* /* Initialize with an empty key. */
* Initialize with an empty key
*/
if( ( ret = mbedtls_aes_setkey_enc( &ctx->aes_ctx, key, if( ( ret = mbedtls_aes_setkey_enc( &ctx->aes_ctx, key,
MBEDTLS_CTR_DRBG_KEYBITS ) ) != 0 ) MBEDTLS_CTR_DRBG_KEYBITS ) ) != 0 )
{ {
return( ret ); return( ret );
} }
if( ( ret = mbedtls_ctr_drbg_reseed( ctx, custom, len ) ) != 0 ) /* Do the initial seeding.
* ctx->reseed_counter contains the desired amount of entropy to
* grab for a nonce (see mbedtls_ctr_drbg_set_nonce_len()). */
if( ( ret = mbedtls_ctr_drbg_reseed_internal( ctx, custom, len,
ctx->reseed_counter ) ) != 0 )
{ {
return( ret ); return( ret );
} }