From 9d3baea4390247b0ea1acc6040afcbbf57dd5189 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 23 Oct 2019 17:45:59 +0200 Subject: [PATCH 01/15] fixup! HMAC_DRBG: support set_entropy_len() before seed() --- include/mbedtls/hmac_drbg.h | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/include/mbedtls/hmac_drbg.h b/include/mbedtls/hmac_drbg.h index 8ac227caa..00be9df40 100644 --- a/include/mbedtls/hmac_drbg.h +++ b/include/mbedtls/hmac_drbg.h @@ -139,7 +139,7 @@ void mbedtls_hmac_drbg_init( mbedtls_hmac_drbg_context *ctx ); * Note that SHA-256 is just as efficient as SHA-224. * The security strength can be reduced if a smaller * entropy length is set with - * mbedtls_hmac_drbg_set_entropy_len() afterwards. + * mbedtls_hmac_drbg_set_entropy_len(). * * \note The default entropy length is the security strength * (converted from bits to bytes). You can override @@ -222,14 +222,9 @@ void mbedtls_hmac_drbg_set_prediction_resistance( mbedtls_hmac_drbg_context *ctx /** * \brief This function sets the amount of entropy grabbed on each - * reseed. + * seed or reseed. * - * The default value is set by mbedtls_hmac_drbg_seed(). - * - * \note mbedtls_hmac_drbg_seed() always sets the entropy length - * to the default value based on the chosen MD algorithm, - * so this function only has an effect if it is called - * after mbedtls_hmac_drbg_seed(). + * See the documentation of mbedtls_hmac_drbg_seed() for the default value. * * \param ctx The HMAC_DRBG context. * \param len The amount of entropy to grab, in bytes. From 379561feff8d1a2740414cc511ba8c70ba6b1e92 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 18 Oct 2019 16:57:48 +0200 Subject: [PATCH 02/15] fixup! CTR_DRBG: support set_entropy_len() before seed() Update a comment that referred to a now-removed function. --- library/ctr_drbg.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/library/ctr_drbg.c b/library/ctr_drbg.c index ae51d5467..0045b77c6 100644 --- a/library/ctr_drbg.c +++ b/library/ctr_drbg.c @@ -382,14 +382,13 @@ exit: } /* CTR_DRBG_Instantiate with derivation function (SP 800-90A §10.2.1.3.2) - * mbedtls_ctr_drbg_seed_entropy_len(ctx, f_entropy, p_entropy, - * custom, len, entropy_len) + * mbedtls_ctr_drbg_seed(ctx, f_entropy, p_entropy, custom, len) * implements * CTR_DRBG_Instantiate(entropy_input, nonce, personalization_string, * security_strength) -> initial_working_state * with inputs * custom[:len] = nonce || personalization_string - * where entropy_input comes from f_entropy for entropy_len bytes + * where entropy_input comes from f_entropy for ctx->entropy_len bytes * and with outputs * ctx = initial_working_state */ From 460988a472be8dc2d8a43238c75a3196fd9b16a1 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 22 Oct 2019 17:05:10 +0200 Subject: [PATCH 03/15] fixup! CTR_DRBG: support set_entropy_len() before seed() Remove a comment that documented a now-removed restriction. --- include/mbedtls/ctr_drbg.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/include/mbedtls/ctr_drbg.h b/include/mbedtls/ctr_drbg.h index 676b96e49..05b4e95a2 100644 --- a/include/mbedtls/ctr_drbg.h +++ b/include/mbedtls/ctr_drbg.h @@ -301,11 +301,6 @@ void mbedtls_ctr_drbg_set_prediction_resistance( mbedtls_ctr_drbg_context *ctx, * * The default value is #MBEDTLS_CTR_DRBG_ENTROPY_LEN. * - * \note mbedtls_ctr_drbg_seed() always sets the entropy length - * to #MBEDTLS_CTR_DRBG_ENTROPY_LEN, so this function - * only has an effect when it is called after - * mbedtls_ctr_drbg_seed(). - * * \note The security strength of CTR_DRBG is bounded by the * entropy length. Thus: * - When using AES-256 From c0ace355a45dee944889233ecdcfdf008eae699f Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 22 Oct 2019 17:06:31 +0200 Subject: [PATCH 04/15] mbedtls_ctr_drbg_context: minor documentation improvements --- include/mbedtls/ctr_drbg.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/include/mbedtls/ctr_drbg.h b/include/mbedtls/ctr_drbg.h index 05b4e95a2..86d546a10 100644 --- a/include/mbedtls/ctr_drbg.h +++ b/include/mbedtls/ctr_drbg.h @@ -169,14 +169,19 @@ extern "C" { typedef struct mbedtls_ctr_drbg_context { unsigned char counter[16]; /*!< The counter (V). */ - int reseed_counter; /*!< The reseed counter. */ + int reseed_counter; /*!< The reseed counter. + * This is the number of requests that have + * been made since the last (re)seeding, + * minus one. */ int prediction_resistance; /*!< This determines whether prediction resistance is enabled, that is whether to systematically reseed before each random generation. */ size_t entropy_len; /*!< The amount of entropy grabbed on each - seed or reseed operation. */ - int reseed_interval; /*!< The reseed interval. */ + seed or reseed operation, in bytes. */ + int reseed_interval; /*!< The reseed interval. + * This is the maximum number of requests + * that can be made between reseedings. */ mbedtls_aes_context aes_ctx; /*!< The AES context. */ From dbd3f7c68df4ba478a9f8b981458c7a1f76963df Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 22 Oct 2019 17:25:30 +0200 Subject: [PATCH 05/15] mbedtls_ctr_drbg_reseed: Minor readability improvement No semantic change. --- library/ctr_drbg.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/library/ctr_drbg.c b/library/ctr_drbg.c index 0045b77c6..92316dabb 100644 --- a/library/ctr_drbg.c +++ b/library/ctr_drbg.c @@ -337,41 +337,32 @@ int mbedtls_ctr_drbg_reseed( mbedtls_ctr_drbg_context *ctx, size_t seedlen = 0; int ret; - if( ctx->entropy_len > MBEDTLS_CTR_DRBG_MAX_SEED_INPUT || - len > MBEDTLS_CTR_DRBG_MAX_SEED_INPUT - ctx->entropy_len ) + if( ctx->entropy_len > MBEDTLS_CTR_DRBG_MAX_SEED_INPUT ) + return( MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG ); + if( len > MBEDTLS_CTR_DRBG_MAX_SEED_INPUT - ctx->entropy_len ) return( MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG ); memset( seed, 0, MBEDTLS_CTR_DRBG_MAX_SEED_INPUT ); - /* - * Gather entropy_len bytes of entropy to seed state - */ - if( 0 != ctx->f_entropy( ctx->p_entropy, seed, - ctx->entropy_len ) ) + /* Gather entropy_len bytes of entropy to seed state. */ + if( 0 != ctx->f_entropy( ctx->p_entropy, seed, ctx->entropy_len ) ) { return( MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED ); } - seedlen += ctx->entropy_len; - /* - * Add additional data - */ - if( additional && len ) + /* Add additional data if provided. */ + if( additional != NULL && len != 0 ) { memcpy( seed + seedlen, additional, len ); seedlen += len; } - /* - * Reduce to 384 bits - */ + /* Reduce to 384 bits. */ if( ( ret = block_cipher_df( seed, seed, seedlen ) ) != 0 ) goto exit; - /* - * Update state - */ + /* Update state. */ if( ( ret = ctr_drbg_update_internal( ctx, seed ) ) != 0 ) goto exit; ctx->reseed_counter = 1; From 9be5098061b58332187cda4a4413f3abf8749cc2 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 22 Oct 2019 18:42:27 +0200 Subject: [PATCH 06/15] 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. --- include/mbedtls/ctr_drbg.h | 100 +++++++++++++++++++++++++------------ library/ctr_drbg.c | 65 +++++++++++++++++++++--- 2 files changed, 125 insertions(+), 40 deletions(-) diff --git a/include/mbedtls/ctr_drbg.h b/include/mbedtls/ctr_drbg.h index 86d546a10..2b0c61712 100644 --- a/include/mbedtls/ctr_drbg.h +++ b/include/mbedtls/ctr_drbg.h @@ -22,6 +22,9 @@ * - 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 * 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 * between 24 and 47 and the DRBG is not initialized with an explicit * nonce (see mbedtls_ctr_drbg_seed()). @@ -29,6 +32,9 @@ * 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 * 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: * - \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. * This is the number of requests that have * 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 resistance is enabled, that is 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. * 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. + * 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 f_entropy The entropy callback, taking as arguments the * \p p_entropy context, the buffer to fill, and the * length of the buffer. * \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 custom The personalization string. * 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 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, 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. * diff --git a/library/ctr_drbg.c b/library/ctr_drbg.c index 92316dabb..85bd04f2b 100644 --- a/library/ctr_drbg.c +++ b/library/ctr_drbg.c @@ -86,6 +86,32 @@ void mbedtls_ctr_drbg_set_entropy_len( mbedtls_ctr_drbg_context *ctx, 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, int interval ) { @@ -319,7 +345,7 @@ void mbedtls_ctr_drbg_update( mbedtls_ctr_drbg_context *ctx, #endif /* MBEDTLS_DEPRECATED_REMOVED */ /* CTR_DRBG_Reseed with derivation function (SP 800-90A §10.2.1.4.2) - * mbedtls_ctr_drbg_reseed(ctx, additional, len) + * mbedtls_ctr_drbg_reseed(ctx, additional, len, nonce_len) * implements * CTR_DRBG_Reseed(working_state, entropy_input, additional_input) * -> new_working_state @@ -327,11 +353,14 @@ void mbedtls_ctr_drbg_update( mbedtls_ctr_drbg_context *ctx, * ctx contains working_state * additional[:len] = additional_input * and entropy_input comes from calling ctx->f_entropy + * for (ctx->entropy_len + nonce_len) bytes * and with output * ctx contains new_working_state */ -int mbedtls_ctr_drbg_reseed( mbedtls_ctr_drbg_context *ctx, - const unsigned char *additional, size_t len ) +int mbedtls_ctr_drbg_reseed_internal( mbedtls_ctr_drbg_context *ctx, + const unsigned char *additional, + size_t len, + size_t nonce_len ) { unsigned char seed[MBEDTLS_CTR_DRBG_MAX_SEED_INPUT]; 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 ) 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 ); 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; + /* 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. */ if( additional != NULL && len != 0 ) { @@ -372,6 +413,12 @@ exit: 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 §10.2.1.3.2) * mbedtls_ctr_drbg_seed(ctx, f_entropy, p_entropy, custom, len) * implements @@ -403,16 +450,18 @@ int mbedtls_ctr_drbg_seed( mbedtls_ctr_drbg_context *ctx, ctx->entropy_len = MBEDTLS_CTR_DRBG_ENTROPY_LEN; 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, MBEDTLS_CTR_DRBG_KEYBITS ) ) != 0 ) { 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 ); } From 97f59ab527ae02cf34b7271a3d369e92d9039884 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 22 Oct 2019 18:42:27 +0200 Subject: [PATCH 07/15] 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. --- library/ctr_drbg.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/ctr_drbg.c b/library/ctr_drbg.c index 85bd04f2b..8a62f6d46 100644 --- a/library/ctr_drbg.c +++ b/library/ctr_drbg.c @@ -357,10 +357,10 @@ void mbedtls_ctr_drbg_update( mbedtls_ctr_drbg_context *ctx, * and with output * ctx contains new_working_state */ -int mbedtls_ctr_drbg_reseed_internal( mbedtls_ctr_drbg_context *ctx, - const unsigned char *additional, - size_t len, - size_t nonce_len ) +static int mbedtls_ctr_drbg_reseed_internal( mbedtls_ctr_drbg_context *ctx, + const unsigned char *additional, + size_t len, + size_t nonce_len ) { unsigned char seed[MBEDTLS_CTR_DRBG_MAX_SEED_INPUT]; size_t seedlen = 0; From 58b56ce4445af23df4495df8ebb8731b43832849 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 22 Oct 2019 19:10:01 +0200 Subject: [PATCH 08/15] CTR_DRBG entropy usage: test the exact amount of consumed entropy --- tests/suites/test_suite_ctr_drbg.function | 26 +++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/suites/test_suite_ctr_drbg.function b/tests/suites/test_suite_ctr_drbg.function index 01050d92d..2b95062cc 100644 --- a/tests/suites/test_suite_ctr_drbg.function +++ b/tests/suites/test_suite_ctr_drbg.function @@ -197,7 +197,7 @@ void ctr_drbg_entropy_usage( ) unsigned char entropy[1024]; mbedtls_ctr_drbg_context ctx; size_t i, reps = 10; - size_t last_idx; + size_t expected_idx = 0; mbedtls_ctr_drbg_init( &ctx ); test_offset_idx = 0; @@ -207,20 +207,19 @@ void ctr_drbg_entropy_usage( ) memset( add, 0, sizeof( add ) ); /* Init must use entropy */ - last_idx = test_offset_idx; TEST_ASSERT( mbedtls_ctr_drbg_seed( &ctx, mbedtls_test_entropy_func, entropy, NULL, 0 ) == 0 ); - TEST_ASSERT( last_idx < test_offset_idx ); + expected_idx += MBEDTLS_CTR_DRBG_ENTROPY_LEN; + TEST_EQUAL( test_offset_idx, expected_idx ); /* By default, PR is off and reseed_interval is large, * so the next few calls should not use entropy */ - last_idx = test_offset_idx; for( i = 0; i < reps; i++ ) { TEST_ASSERT( mbedtls_ctr_drbg_random( &ctx, out, sizeof( out ) - 4 ) == 0 ); TEST_ASSERT( mbedtls_ctr_drbg_random_with_add( &ctx, out, sizeof( out ) - 4, add, sizeof( add ) ) == 0 ); } - TEST_ASSERT( last_idx == test_offset_idx ); + TEST_EQUAL( test_offset_idx, expected_idx ); /* While at it, make sure we didn't write past the requested length */ TEST_ASSERT( out[sizeof( out ) - 4] == 0 ); @@ -232,17 +231,17 @@ void ctr_drbg_entropy_usage( ) * so the next call should reseed */ mbedtls_ctr_drbg_set_reseed_interval( &ctx, 2 * reps ); TEST_ASSERT( mbedtls_ctr_drbg_random( &ctx, out, sizeof( out ) ) == 0 ); - TEST_ASSERT( last_idx < test_offset_idx ); + expected_idx += MBEDTLS_CTR_DRBG_ENTROPY_LEN; + TEST_EQUAL( test_offset_idx, expected_idx ); /* The new few calls should not reseed */ - last_idx = test_offset_idx; for( i = 0; i < reps / 2; i++ ) { TEST_ASSERT( mbedtls_ctr_drbg_random( &ctx, out, sizeof( out ) ) == 0 ); TEST_ASSERT( mbedtls_ctr_drbg_random_with_add( &ctx, out, sizeof( out ) , add, sizeof( add ) ) == 0 ); } - TEST_ASSERT( last_idx == test_offset_idx ); + TEST_EQUAL( test_offset_idx, expected_idx ); /* Call update with too much data (sizeof entropy > MAX(_SEED)_INPUT). * Make sure it's detected as an error and doesn't cause memory @@ -253,18 +252,19 @@ void ctr_drbg_entropy_usage( ) /* Now enable PR, so the next few calls should all reseed */ mbedtls_ctr_drbg_set_prediction_resistance( &ctx, MBEDTLS_CTR_DRBG_PR_ON ); TEST_ASSERT( mbedtls_ctr_drbg_random( &ctx, out, sizeof( out ) ) == 0 ); - TEST_ASSERT( last_idx < test_offset_idx ); + expected_idx += MBEDTLS_CTR_DRBG_ENTROPY_LEN; + TEST_EQUAL( test_offset_idx, expected_idx ); /* Finally, check setting entropy_len */ mbedtls_ctr_drbg_set_entropy_len( &ctx, 42 ); - last_idx = test_offset_idx; TEST_ASSERT( mbedtls_ctr_drbg_random( &ctx, out, sizeof( out ) ) == 0 ); - TEST_ASSERT( test_offset_idx - last_idx == 42 ); + expected_idx += 42; + TEST_EQUAL( test_offset_idx, expected_idx ); mbedtls_ctr_drbg_set_entropy_len( &ctx, 13 ); - last_idx = test_offset_idx; TEST_ASSERT( mbedtls_ctr_drbg_random( &ctx, out, sizeof( out ) ) == 0 ); - TEST_ASSERT( test_offset_idx - last_idx == 13 ); + expected_idx += 13; + TEST_EQUAL( test_offset_idx, expected_idx ); exit: mbedtls_ctr_drbg_free( &ctx ); From 4d2d4ff9b041a6966db49365ed282a037f995584 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 22 Oct 2019 19:10:33 +0200 Subject: [PATCH 09/15] HMAC_DRBG entropy usage: test the exact amount of consumed entropy --- tests/suites/test_suite_hmac_drbg.function | 35 ++++++++++++++-------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/tests/suites/test_suite_hmac_drbg.function b/tests/suites/test_suite_hmac_drbg.function index 13bc40062..b526f4313 100644 --- a/tests/suites/test_suite_hmac_drbg.function +++ b/tests/suites/test_suite_hmac_drbg.function @@ -37,7 +37,9 @@ void hmac_drbg_entropy_usage( int md_alg ) const mbedtls_md_info_t *md_info; mbedtls_hmac_drbg_context ctx; entropy_ctx entropy; - size_t last_len, i, reps = 10; + size_t i, reps = 10; + size_t default_entropy_len; + size_t expected_consumed_entropy = 0; mbedtls_hmac_drbg_init( &ctx ); memset( buf, 0, sizeof( buf ) ); @@ -48,23 +50,29 @@ void hmac_drbg_entropy_usage( int md_alg ) md_info = mbedtls_md_info_from_type( md_alg ); TEST_ASSERT( md_info != NULL ); + if( mbedtls_md_get_size( md_info ) <= 20 ) + default_entropy_len = 16; + else if( mbedtls_md_get_size( md_info ) <= 28 ) + default_entropy_len = 24; + else + default_entropy_len = 32; /* Init must use entropy */ - last_len = entropy.len; TEST_ASSERT( mbedtls_hmac_drbg_seed( &ctx, md_info, mbedtls_test_entropy_func, &entropy, NULL, 0 ) == 0 ); - TEST_ASSERT( entropy.len < last_len ); + /* default_entropy_len of entropy, plus half as much for the nonce */ + expected_consumed_entropy += default_entropy_len * 3 / 2; + TEST_EQUAL( sizeof( buf ) - entropy.len, expected_consumed_entropy ); /* By default, PR is off and reseed_interval is large, * so the next few calls should not use entropy */ - last_len = entropy.len; for( i = 0; i < reps; i++ ) { TEST_ASSERT( mbedtls_hmac_drbg_random( &ctx, out, sizeof( out ) - 4 ) == 0 ); TEST_ASSERT( mbedtls_hmac_drbg_random_with_add( &ctx, out, sizeof( out ) - 4, buf, 16 ) == 0 ); } - TEST_ASSERT( entropy.len == last_len ); + TEST_EQUAL( sizeof( buf ) - entropy.len, expected_consumed_entropy ); /* While at it, make sure we didn't write past the requested length */ TEST_ASSERT( out[sizeof( out ) - 4] == 0 ); @@ -76,33 +84,34 @@ void hmac_drbg_entropy_usage( int md_alg ) * so the next call should reseed */ mbedtls_hmac_drbg_set_reseed_interval( &ctx, 2 * reps ); TEST_ASSERT( mbedtls_hmac_drbg_random( &ctx, out, sizeof( out ) ) == 0 ); - TEST_ASSERT( entropy.len < last_len ); + expected_consumed_entropy += default_entropy_len; + TEST_EQUAL( sizeof( buf ) - entropy.len, expected_consumed_entropy ); /* The new few calls should not reseed */ - last_len = entropy.len; for( i = 0; i < reps / 2; i++ ) { TEST_ASSERT( mbedtls_hmac_drbg_random( &ctx, out, sizeof( out ) ) == 0 ); TEST_ASSERT( mbedtls_hmac_drbg_random_with_add( &ctx, out, sizeof( out ) , buf, 16 ) == 0 ); } - TEST_ASSERT( entropy.len == last_len ); + TEST_EQUAL( sizeof( buf ) - entropy.len, expected_consumed_entropy ); /* Now enable PR, so the next few calls should all reseed */ mbedtls_hmac_drbg_set_prediction_resistance( &ctx, MBEDTLS_HMAC_DRBG_PR_ON ); TEST_ASSERT( mbedtls_hmac_drbg_random( &ctx, out, sizeof( out ) ) == 0 ); - TEST_ASSERT( entropy.len < last_len ); + expected_consumed_entropy += default_entropy_len; + TEST_EQUAL( sizeof( buf ) - entropy.len, expected_consumed_entropy ); /* Finally, check setting entropy_len */ mbedtls_hmac_drbg_set_entropy_len( &ctx, 42 ); - last_len = entropy.len; TEST_ASSERT( mbedtls_hmac_drbg_random( &ctx, out, sizeof( out ) ) == 0 ); - TEST_ASSERT( (int) last_len - entropy.len == 42 ); + expected_consumed_entropy += 42; + TEST_EQUAL( sizeof( buf ) - entropy.len, expected_consumed_entropy ); mbedtls_hmac_drbg_set_entropy_len( &ctx, 13 ); - last_len = entropy.len; TEST_ASSERT( mbedtls_hmac_drbg_random( &ctx, out, sizeof( out ) ) == 0 ); - TEST_ASSERT( (int) last_len - entropy.len == 13 ); + expected_consumed_entropy += 13; + TEST_EQUAL( sizeof( buf ) - entropy.len, expected_consumed_entropy ); exit: mbedtls_hmac_drbg_free( &ctx ); From c949de06ec87c62437d5f20d417c516600676d81 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 22 Oct 2019 19:14:26 +0200 Subject: [PATCH 10/15] Test mbedtls_ctr_drbg_set_nonce_len Test mbedtls_ctr_drbg_set_nonce_len (good cases only, which is in keeping with the coverage of other functions). --- tests/suites/test_suite_ctr_drbg.data | 10 ++++++++-- tests/suites/test_suite_ctr_drbg.function | 7 ++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/tests/suites/test_suite_ctr_drbg.data b/tests/suites/test_suite_ctr_drbg.data index 312910edd..5f198a4ee 100644 --- a/tests/suites/test_suite_ctr_drbg.data +++ b/tests/suites/test_suite_ctr_drbg.data @@ -1070,8 +1070,14 @@ CTR_DRBG CAVS 14.3 (AES-128 use df,True,128,64,0,0) #0 depends_on:MBEDTLS_CTR_DRBG_USE_128_BIT_KEY ctr_drbg_validate_pr:"d4f1f4ae08bcb3e1":"5d4041942bcf68864a4997d8171f1f9fef55a769b7eaf03fe082029bb32a2b9d8239e865c0a42e14b964b9c09de85a20":"":"":"4155320287eedcf7d484c2c2a1e2eb64b9c9ce77c87202a1ae1616c7a5cfd1c687c7a0bfcc85bda48fdd4629fd330c22d0a76076f88fc7cd04037ee06b7af602" -CTR_DRBG entropy usage -ctr_drbg_entropy_usage: +CTR_DRBG entropy usage (entropy_nonce_len=0 by default) +ctr_drbg_entropy_usage:-1 + +CTR_DRBG entropy usage (entropy_nonce_len=0) +ctr_drbg_entropy_usage:0 + +CTR_DRBG entropy usage (entropy_nonce_len=7) +ctr_drbg_entropy_usage:7 CTR_DRBG write/update seed file [#1] ctr_drbg_seed_file:"data_files/ctr_drbg_seed":0 diff --git a/tests/suites/test_suite_ctr_drbg.function b/tests/suites/test_suite_ctr_drbg.function index 2b95062cc..02f9eca5e 100644 --- a/tests/suites/test_suite_ctr_drbg.function +++ b/tests/suites/test_suite_ctr_drbg.function @@ -190,7 +190,7 @@ void ctr_drbg_validate_reseed_first( data_t * add_init, data_t * entropy, /* BEGIN_CASE */ -void ctr_drbg_entropy_usage( ) +void ctr_drbg_entropy_usage( int entropy_nonce_len ) { unsigned char out[16]; unsigned char add[16]; @@ -206,9 +206,14 @@ void ctr_drbg_entropy_usage( ) memset( out, 0, sizeof( out ) ); memset( add, 0, sizeof( add ) ); + if( entropy_nonce_len >= 0 ) + TEST_ASSERT( mbedtls_ctr_drbg_set_nonce_len( &ctx, entropy_nonce_len ) == 0 ); + /* Init must use entropy */ TEST_ASSERT( mbedtls_ctr_drbg_seed( &ctx, mbedtls_test_entropy_func, entropy, NULL, 0 ) == 0 ); expected_idx += MBEDTLS_CTR_DRBG_ENTROPY_LEN; + if( entropy_nonce_len >= 0 ) + expected_idx += entropy_nonce_len; TEST_EQUAL( test_offset_idx, expected_idx ); /* By default, PR is off and reseed_interval is large, From 0ed378aa02c22fdeba0113d7cd736777c0d4ebf6 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 22 Oct 2019 20:33:56 +0200 Subject: [PATCH 11/15] CTR_DRBG: explicitly set entropy_nonce_len=0 when desired No behavior change. Prepare for a future version that will set the entropy nonce length to a nonzero value by default. --- library/ctr_drbg.c | 2 ++ tests/suites/test_suite_ctr_drbg.function | 1 + 2 files changed, 3 insertions(+) diff --git a/library/ctr_drbg.c b/library/ctr_drbg.c index 8a62f6d46..8c6ee59d5 100644 --- a/library/ctr_drbg.c +++ b/library/ctr_drbg.c @@ -732,6 +732,7 @@ int mbedtls_ctr_drbg_self_test( int verbose ) test_offset = 0; mbedtls_ctr_drbg_set_entropy_len( &ctx, 32 ); + mbedtls_ctr_drbg_set_nonce_len( &ctx, 0 ); CHK( mbedtls_ctr_drbg_seed( &ctx, ctr_drbg_self_test_entropy, (void *) entropy_source_pr, @@ -756,6 +757,7 @@ int mbedtls_ctr_drbg_self_test( int verbose ) test_offset = 0; mbedtls_ctr_drbg_set_entropy_len( &ctx, 32 ); + mbedtls_ctr_drbg_set_nonce_len( &ctx, 0 ); CHK( mbedtls_ctr_drbg_seed( &ctx, ctr_drbg_self_test_entropy, (void *) entropy_source_nopr, diff --git a/tests/suites/test_suite_ctr_drbg.function b/tests/suites/test_suite_ctr_drbg.function index 02f9eca5e..c79b6e2aa 100644 --- a/tests/suites/test_suite_ctr_drbg.function +++ b/tests/suites/test_suite_ctr_drbg.function @@ -45,6 +45,7 @@ static void ctr_drbg_validate_internal( int reseed_mode, data_t * nonce, /* CTR_DRBG_Instantiate(entropy[:entropy->len], nonce, perso, ) * where nonce||perso = nonce[nonce->len] */ mbedtls_ctr_drbg_set_entropy_len( &ctx, entropy_chunk_len ); + mbedtls_ctr_drbg_set_nonce_len( &ctx, 0 ); TEST_ASSERT( mbedtls_ctr_drbg_seed( &ctx, mbedtls_test_entropy_func, entropy->x, From e9a3454e0980887a81f7c61dfdd355769296940c Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 22 Oct 2019 20:43:24 +0200 Subject: [PATCH 12/15] CTR_DRBG: grab a nonce from the entropy source if needed Change the default entropy nonce length to be nonzero in some cases. Specifically, the default nonce length is now set in such a way that the entropy input during the initial seeding always contains enough entropy to achieve the maximum possible security strength per NIST SP 800-90A given the key size and entropy length. If MBEDTLS_CTR_DRBG_ENTROPY_LEN is kept to its default value, mbedtls_ctr_drbg_seed() now grabs extra entropy for a nonce if MBEDTLS_CTR_DRBG_USE_128_BIT_KEY is disabled and either MBEDTLS_ENTROPY_FORCE_SHA256 is enabled or MBEDTLS_SHA512_C is disabled. If MBEDTLS_CTR_DRBG_USE_128_BIT_KEY is enabled, or if the entropy module uses SHA-512, then the default value of MBEDTLS_CTR_DRBG_ENTROPY_LEN does not require a second call to the entropy function to achieve the maximum security strength. This choice of default nonce size guarantees NIST compliance with the maximum security strength while keeping backward compatibility and performance high: in configurations that do not require grabbing more entropy, the code will not grab more entropy than before. --- include/mbedtls/ctr_drbg.h | 79 ++++++++++++----------- library/ctr_drbg.c | 31 +++++++-- tests/suites/test_suite_ctr_drbg.data | 5 ++ tests/suites/test_suite_ctr_drbg.function | 14 ++++ 4 files changed, 88 insertions(+), 41 deletions(-) diff --git a/include/mbedtls/ctr_drbg.h b/include/mbedtls/ctr_drbg.h index 2b0c61712..09f4e620e 100644 --- a/include/mbedtls/ctr_drbg.h +++ b/include/mbedtls/ctr_drbg.h @@ -12,36 +12,14 @@ * The Mbed TLS implementation of CTR_DRBG uses AES-256 (default) or AES-128 * (if \c MBEDTLS_CTR_DRBG_USE_128_BIT_KEY is enabled at compile time) * as the underlying block cipher, with a derivation function. - * The initial seeding grabs #MBEDTLS_CTR_DRBG_ENTROPY_LEN bytes of entropy. - * See the documentation of mbedtls_ctr_drbg_seed() for more details. * - * Based on NIST SP 800-90A §10.2.1 table 3 and NIST SP 800-57 part 1 table 2, - * here are the security strengths achieved in typical configuration: - * - 256 bits under the default configuration of the library, with AES-256 - * and with #MBEDTLS_CTR_DRBG_ENTROPY_LEN set to 48 or more. - * - 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 - * 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 - * between 24 and 47 and the DRBG is not initialized with an explicit - * nonce (see mbedtls_ctr_drbg_seed()). - * - 128 bits if AES-128 is used (\c MBEDTLS_CTR_DRBG_USE_128_BIT_KEY enabled) - * 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 - * 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: - * - \c 48 if the module \c MBEDTLS_SHA512_C is enabled and the symbol - * \c MBEDTLS_ENTROPY_FORCE_SHA256 is disabled at compile time. - * This is the default configuration of the library. - * - \c 32 if the module \c MBEDTLS_SHA512_C is disabled at compile time. - * - \c 32 if \c MBEDTLS_ENTROPY_FORCE_SHA256 is enabled at compile time. + * The security strength as defined in NIST SP 800-90A is + * 128 bits when AES-128 is used (\c MBEDTLS_CTR_DRBG_USE_128_BIT_KEY enabled) + * and 256 bits otherwise, provided that #MBEDTLS_CTR_DRBG_ENTROPY_LEN is + * kept at its default value (and not overridden in config.h) and that the + * DRBG instance is set up with default parameters. + * See the documentation of mbedtls_ctr_drbg_seed() for more + * information. */ /* * Copyright (C) 2006-2019, Arm Limited (or its affiliates), All Rights Reserved @@ -232,6 +210,26 @@ void mbedtls_ctr_drbg_init( mbedtls_ctr_drbg_context *ctx ); * The entropy length is #MBEDTLS_CTR_DRBG_ENTROPY_LEN by default. * You can override it by calling mbedtls_ctr_drbg_set_entropy_len(). * + * The entropy nonce length is: + * - \c 0 if the entropy length is at least 3/2 times the entropy length, + * which guarantees that the security strength is the maximum permitted + * by the key size and entropy length according to NIST SP 800-90A §10.2.1; + * - Half the entropy length otherwise. + * You can override it by calling mbedtls_ctr_drbg_set_nonce_len(). + */ +#if MBEDTLS_CTR_DRBG_ENTROPY_LEN >= MBEDTLS_CTR_DRBG_KEYSIZE * 3 / 2 +/** With the default entropy length, the entropy nonce length is \c 0. + */ +#elif MBEDTLS_CTR_DRBG_ENTROPY_LEN & 1 +/** With the default entropy length, the entropy nonce length is + * (#MBEDTLS_CTR_DRBG_ENTROPY_LEN + 1) / 2. + */ +#else +/** With the default entropy length, the entropy nonce length is + * #MBEDTLS_CTR_DRBG_ENTROPY_LEN / 2. + */ +#endif +/** * You can provide a nonce and personalization string in addition to the * entropy source, to make this instantiation as unique as possible. * See SP 800-90A §8.6.7 for more details about nonces. @@ -241,10 +239,20 @@ void mbedtls_ctr_drbg_init( mbedtls_ctr_drbg_context *ctx ); * 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. + */ +#if MBEDTLS_CTR_DRBG_ENTROPY_LEN >= MBEDTLS_CTR_DRBG_KEYSIZE * 3 / 2 +/** + * - If mbedtls_ctr_drbg_set_nonce_len() has been called, a string + * obtained by calling \p f_entropy function for the specified length. + */ +#else +/** + * - A string obtained by calling \p f_entropy function for the entropy nonce + * length. If the entropy nonce length is \c 0, this function does not + * make a second call to \p f_entropy. + */ +#endif +/** * - The \p custom string. * * \note To achieve the nominal security strength permitted @@ -256,10 +264,7 @@ void mbedtls_ctr_drbg_init( mbedtls_ctr_drbg_context *ctx ); * * 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: + * and the entropy nonce length 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 diff --git a/library/ctr_drbg.c b/library/ctr_drbg.c index 8c6ee59d5..047bb2a3e 100644 --- a/library/ctr_drbg.c +++ b/library/ctr_drbg.c @@ -56,6 +56,9 @@ void mbedtls_ctr_drbg_init( mbedtls_ctr_drbg_context *ctx ) { memset( ctx, 0, sizeof( mbedtls_ctr_drbg_context ) ); + /* Indicate that the entropy nonce length is not set explicitly. + * See mbedtls_ctr_drbg_set_nonce_len(). */ + ctx->reseed_counter = -1; #if defined(MBEDTLS_THREADING_C) mbedtls_mutex_init( &ctx->mutex ); @@ -419,6 +422,19 @@ int mbedtls_ctr_drbg_reseed( mbedtls_ctr_drbg_context *ctx, return( mbedtls_ctr_drbg_reseed_internal( ctx, additional, len, 0 ) ); } +/* Return a "good" nonce length for CTR_DRBG. The chosen nonce length + * is sufficient to achieve the maximum security strength given the key + * size and entropy length. If there is enough entropy in the initial + * call to the entropy function to serve as both the entropy input and + * the nonce, don't make a second call to get a nonce. */ +static size_t good_nonce_len( size_t entropy_len ) +{ + if( entropy_len >= MBEDTLS_CTR_DRBG_KEYSIZE * 3 / 2 ) + return( 0 ); + else + return( ( entropy_len + 1 ) / 2 ); +} + /* CTR_DRBG_Instantiate with derivation function (SP 800-90A §10.2.1.3.2) * mbedtls_ctr_drbg_seed(ctx, f_entropy, p_entropy, custom, len) * implements @@ -438,6 +454,7 @@ int mbedtls_ctr_drbg_seed( mbedtls_ctr_drbg_context *ctx, { int ret; unsigned char key[MBEDTLS_CTR_DRBG_KEYSIZE]; + size_t nonce_len; memset( key, 0, MBEDTLS_CTR_DRBG_KEYSIZE ); @@ -448,6 +465,14 @@ int mbedtls_ctr_drbg_seed( mbedtls_ctr_drbg_context *ctx, if( ctx->entropy_len == 0 ) ctx->entropy_len = MBEDTLS_CTR_DRBG_ENTROPY_LEN; + /* ctx->reseed_counter contains the desired amount of entropy to + * grab for a nonce (see mbedtls_ctr_drbg_set_nonce_len()). + * If it's -1, indicating that the entropy nonce length was not set + * explicitly, use a sufficiently large nonce for security. */ + nonce_len = ( ctx->reseed_counter >= 0 ? + (size_t) ctx->reseed_counter : + good_nonce_len( ctx->entropy_len ) ); + ctx->reseed_interval = MBEDTLS_CTR_DRBG_RESEED_INTERVAL; /* Initialize with an empty key. */ @@ -457,11 +482,9 @@ int mbedtls_ctr_drbg_seed( mbedtls_ctr_drbg_context *ctx, return( ret ); } - /* 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()). */ + /* Do the initial seeding. */ if( ( ret = mbedtls_ctr_drbg_reseed_internal( ctx, custom, len, - ctx->reseed_counter ) ) != 0 ) + nonce_len ) ) != 0 ) { return( ret ); } diff --git a/tests/suites/test_suite_ctr_drbg.data b/tests/suites/test_suite_ctr_drbg.data index 5f198a4ee..461e50255 100644 --- a/tests/suites/test_suite_ctr_drbg.data +++ b/tests/suites/test_suite_ctr_drbg.data @@ -1071,6 +1071,11 @@ depends_on:MBEDTLS_CTR_DRBG_USE_128_BIT_KEY ctr_drbg_validate_pr:"d4f1f4ae08bcb3e1":"5d4041942bcf68864a4997d8171f1f9fef55a769b7eaf03fe082029bb32a2b9d8239e865c0a42e14b964b9c09de85a20":"":"":"4155320287eedcf7d484c2c2a1e2eb64b9c9ce77c87202a1ae1616c7a5cfd1c687c7a0bfcc85bda48fdd4629fd330c22d0a76076f88fc7cd04037ee06b7af602" CTR_DRBG entropy usage (entropy_nonce_len=0 by default) +depends_on:!DEFAULT_ENTROPY_NONCE +ctr_drbg_entropy_usage:-1 + +CTR_DRBG entropy usage (entropy_nonce_len=entropy_len/2 by default) +depends_on:DEFAULT_ENTROPY_NONCE ctr_drbg_entropy_usage:-1 CTR_DRBG entropy usage (entropy_nonce_len=0) diff --git a/tests/suites/test_suite_ctr_drbg.function b/tests/suites/test_suite_ctr_drbg.function index c79b6e2aa..c28438587 100644 --- a/tests/suites/test_suite_ctr_drbg.function +++ b/tests/suites/test_suite_ctr_drbg.function @@ -3,6 +3,14 @@ #include "mbedtls/ctr_drbg.h" #include "string.h" +/* mbedtls_ctr_drbg_seed() grabs a nonce by default if the entropy + * length is smaller than 3/2 times the maximum security strength. */ +#if MBEDTLS_CTR_DRBG_ENTROPY_LEN >= MBEDTLS_CTR_DRBG_KEYSIZE * 3 / 2 +#undef DEFAULT_ENTROPY_NONCE +#else +#define DEFAULT_ENTROPY_NONCE +#endif + /* Modes for ctr_drbg_validate */ enum reseed_mode { @@ -215,6 +223,12 @@ void ctr_drbg_entropy_usage( int entropy_nonce_len ) expected_idx += MBEDTLS_CTR_DRBG_ENTROPY_LEN; if( entropy_nonce_len >= 0 ) expected_idx += entropy_nonce_len; + else + { +#if defined(DEFAULT_ENTROPY_NONCE) + expected_idx += ( MBEDTLS_CTR_DRBG_ENTROPY_LEN + 1 ) / 2; +#endif + } TEST_EQUAL( test_offset_idx, expected_idx ); /* By default, PR is off and reseed_interval is large, From 69971662bf8daf82ab641eac40aa2afe56476779 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 23 Oct 2019 19:39:36 +0200 Subject: [PATCH 13/15] CTR_DRBG: define a constant for the default entropy nonce length The default entropy nonce length is either zero or nonzero depending on the desired security strength and the entropy length. The implementation calculates the actual entropy nonce length from the actual entropy length, and therefore it doesn't need a constant that indicates the default entropy nonce length. A portable application may be interested in this constant, however. And our test code could definitely use it. Define a constant MBEDTLS_CTR_DRBG_ENTROPY_NONCE_LEN and use it in test code. Previously, test_suite_ctr_drbg had knowledge about the default entropy nonce length built in and test_suite_psa_crypto_init failed. Now both use MBEDTLS_CTR_DRBG_ENTROPY_NONCE_LEN. This change means that the test ctr_drbg_entropy_usage no longer validates that the default entropy nonce length is sensible. So add a new test that checks that the default entropy length and the default entropy nonce length are sufficient to ensure the expected security strength. --- include/mbedtls/ctr_drbg.h | 37 +++++++++------- tests/suites/test_suite_ctr_drbg.data | 15 ++++--- tests/suites/test_suite_ctr_drbg.function | 44 +++++++++++++------ tests/suites/test_suite_psa_crypto_init.data | 10 +++++ .../test_suite_psa_crypto_init.function | 6 +++ 5 files changed, 78 insertions(+), 34 deletions(-) diff --git a/include/mbedtls/ctr_drbg.h b/include/mbedtls/ctr_drbg.h index 09f4e620e..a0750e0d4 100644 --- a/include/mbedtls/ctr_drbg.h +++ b/include/mbedtls/ctr_drbg.h @@ -147,6 +147,24 @@ extern "C" { #endif +#if MBEDTLS_CTR_DRBG_ENTROPY_LEN >= MBEDTLS_CTR_DRBG_KEYSIZE * 3 / 2 +/** The default length of the nonce read from the entropy source. + * + * This is \c 0 because a single read from the entropy source is sufficient + * to include a nonce. + * See the documentation of mbedtls_ctr_drbg_seed() for more information. + */ +#define MBEDTLS_CTR_DRBG_ENTROPY_NONCE_LEN 0 +#else +/** The default length of the nonce read from the entropy source. + * + * This is half of the default entropy length because a single read from + * the entropy source does not provide enough material to form a nonce. + * See the documentation of mbedtls_ctr_drbg_seed() for more information. + */ +#define MBEDTLS_CTR_DRBG_ENTROPY_NONCE_LEN ( MBEDTLS_CTR_DRBG_ENTROPY_LEN + 1 ) / 2 +#endif + /** * \brief The CTR_DRBG context structure. */ @@ -216,20 +234,9 @@ void mbedtls_ctr_drbg_init( mbedtls_ctr_drbg_context *ctx ); * by the key size and entropy length according to NIST SP 800-90A §10.2.1; * - Half the entropy length otherwise. * You can override it by calling mbedtls_ctr_drbg_set_nonce_len(). - */ -#if MBEDTLS_CTR_DRBG_ENTROPY_LEN >= MBEDTLS_CTR_DRBG_KEYSIZE * 3 / 2 -/** With the default entropy length, the entropy nonce length is \c 0. - */ -#elif MBEDTLS_CTR_DRBG_ENTROPY_LEN & 1 -/** With the default entropy length, the entropy nonce length is - * (#MBEDTLS_CTR_DRBG_ENTROPY_LEN + 1) / 2. - */ -#else -/** With the default entropy length, the entropy nonce length is - * #MBEDTLS_CTR_DRBG_ENTROPY_LEN / 2. - */ -#endif -/** + * With the default entropy length, the entropy nonce length is + * #MBEDTLS_CTR_DRBG_ENTROPY_NONCE_LEN. + * * You can provide a nonce and personalization string in addition to the * entropy source, to make this instantiation as unique as possible. * See SP 800-90A §8.6.7 for more details about nonces. @@ -240,7 +247,7 @@ void mbedtls_ctr_drbg_init( mbedtls_ctr_drbg_context *ctx ); * - A string obtained by calling \p f_entropy function for the entropy * length. */ -#if MBEDTLS_CTR_DRBG_ENTROPY_LEN >= MBEDTLS_CTR_DRBG_KEYSIZE * 3 / 2 +#if MBEDTLS_CTR_DRBG_ENTROPY_NONCE_LEN == 0 /** * - If mbedtls_ctr_drbg_set_nonce_len() has been called, a string * obtained by calling \p f_entropy function for the specified length. diff --git a/tests/suites/test_suite_ctr_drbg.data b/tests/suites/test_suite_ctr_drbg.data index 461e50255..b50df2ba3 100644 --- a/tests/suites/test_suite_ctr_drbg.data +++ b/tests/suites/test_suite_ctr_drbg.data @@ -1070,12 +1070,7 @@ CTR_DRBG CAVS 14.3 (AES-128 use df,True,128,64,0,0) #0 depends_on:MBEDTLS_CTR_DRBG_USE_128_BIT_KEY ctr_drbg_validate_pr:"d4f1f4ae08bcb3e1":"5d4041942bcf68864a4997d8171f1f9fef55a769b7eaf03fe082029bb32a2b9d8239e865c0a42e14b964b9c09de85a20":"":"":"4155320287eedcf7d484c2c2a1e2eb64b9c9ce77c87202a1ae1616c7a5cfd1c687c7a0bfcc85bda48fdd4629fd330c22d0a76076f88fc7cd04037ee06b7af602" -CTR_DRBG entropy usage (entropy_nonce_len=0 by default) -depends_on:!DEFAULT_ENTROPY_NONCE -ctr_drbg_entropy_usage:-1 - -CTR_DRBG entropy usage (entropy_nonce_len=entropy_len/2 by default) -depends_on:DEFAULT_ENTROPY_NONCE +CTR_DRBG entropy usage (default entropy_nonce_len) ctr_drbg_entropy_usage:-1 CTR_DRBG entropy usage (entropy_nonce_len=0) @@ -1084,6 +1079,14 @@ ctr_drbg_entropy_usage:0 CTR_DRBG entropy usage (entropy_nonce_len=7) ctr_drbg_entropy_usage:7 +CTR_DRBG entropy strength: 128 bits +depends_on:MBEDTLS_CTR_DRBG_USE_128_BIT_KEY +ctr_drbg_entropy_strength:128 + +CTR_DRBG entropy strength: 256 bits +depends_on:!MBEDTLS_CTR_DRBG_USE_128_BIT_KEY +ctr_drbg_entropy_strength:256 + CTR_DRBG write/update seed file [#1] ctr_drbg_seed_file:"data_files/ctr_drbg_seed":0 diff --git a/tests/suites/test_suite_ctr_drbg.function b/tests/suites/test_suite_ctr_drbg.function index c28438587..8317c08c8 100644 --- a/tests/suites/test_suite_ctr_drbg.function +++ b/tests/suites/test_suite_ctr_drbg.function @@ -3,14 +3,6 @@ #include "mbedtls/ctr_drbg.h" #include "string.h" -/* mbedtls_ctr_drbg_seed() grabs a nonce by default if the entropy - * length is smaller than 3/2 times the maximum security strength. */ -#if MBEDTLS_CTR_DRBG_ENTROPY_LEN >= MBEDTLS_CTR_DRBG_KEYSIZE * 3 / 2 -#undef DEFAULT_ENTROPY_NONCE -#else -#define DEFAULT_ENTROPY_NONCE -#endif - /* Modes for ctr_drbg_validate */ enum reseed_mode { @@ -196,7 +188,37 @@ void ctr_drbg_validate_reseed_first( data_t * add_init, data_t * entropy, } /* END_CASE */ +/* BEGIN_CASE */ +void ctr_drbg_entropy_strength( int expected_bit_strength ) +{ + unsigned char entropy[/*initial entropy*/ MBEDTLS_CTR_DRBG_ENTROPY_LEN + + /*nonce*/ MBEDTLS_CTR_DRBG_ENTROPY_NONCE_LEN + + /*reseed*/ MBEDTLS_CTR_DRBG_ENTROPY_LEN]; + mbedtls_ctr_drbg_context ctx; + size_t last_idx; + size_t byte_strength = expected_bit_strength / 8; + mbedtls_ctr_drbg_init( &ctx ); + test_offset_idx = 0; + test_max_idx = sizeof( entropy ); + memset( entropy, 0, sizeof( entropy ) ); + + /* The initial seeding must grab at least byte_strength bytes of entropy + * for the entropy input and byte_strength/2 bytes for a nonce. */ + TEST_ASSERT( mbedtls_ctr_drbg_seed( &ctx, + mbedtls_test_entropy_func, entropy, + NULL, 0 ) == 0 ); + TEST_ASSERT( test_offset_idx >= ( byte_strength * 3 + 1 ) / 2 ); + last_idx = test_offset_idx; + + /* A reseed must grab at least byte_strength bytes of entropy. */ + TEST_ASSERT( mbedtls_ctr_drbg_reseed( &ctx, NULL, 0 ) == 0 ); + TEST_ASSERT( test_offset_idx - last_idx >= byte_strength ); + +exit: + mbedtls_ctr_drbg_free( &ctx ); +} +/* END_CASE */ /* BEGIN_CASE */ void ctr_drbg_entropy_usage( int entropy_nonce_len ) @@ -224,11 +246,7 @@ void ctr_drbg_entropy_usage( int entropy_nonce_len ) if( entropy_nonce_len >= 0 ) expected_idx += entropy_nonce_len; else - { -#if defined(DEFAULT_ENTROPY_NONCE) - expected_idx += ( MBEDTLS_CTR_DRBG_ENTROPY_LEN + 1 ) / 2; -#endif - } + expected_idx += MBEDTLS_CTR_DRBG_ENTROPY_NONCE_LEN; TEST_EQUAL( test_offset_idx, expected_idx ); /* By default, PR is off and reseed_interval is large, diff --git a/tests/suites/test_suite_psa_crypto_init.data b/tests/suites/test_suite_psa_crypto_init.data index c57a764ef..9620a642a 100644 --- a/tests/suites/test_suite_psa_crypto_init.data +++ b/tests/suites/test_suite_psa_crypto_init.data @@ -34,15 +34,25 @@ fake_entropy_source:MBEDTLS_ENTROPY_BLOCK_SIZE:0:0:0:0:PSA_ERROR_INSUFFICIENT_EN Fake entropy: less than the block size fake_entropy_source:MBEDTLS_ENTROPY_BLOCK_SIZE:MBEDTLS_ENTROPY_BLOCK_SIZE - 1:-1:-1:-1:PSA_ERROR_INSUFFICIENT_ENTROPY +Fake entropy: not enough for a nonce +depends_on:ENTROPY_NONCE_LEN != 0 +fake_entropy_source:MBEDTLS_ENTROPY_BLOCK_SIZE:ENTROPY_NONCE_LEN - 1:-1:-1:-1:PSA_ERROR_INSUFFICIENT_ENTROPY + Fake entropy: one block eventually +depends_on:ENTROPY_NONCE_LEN == 0 fake_entropy_source:MBEDTLS_ENTROPY_BLOCK_SIZE:0:0:0:MBEDTLS_ENTROPY_BLOCK_SIZE:PSA_SUCCESS Fake entropy: one block in two steps +depends_on:ENTROPY_NONCE_LEN == 0 fake_entropy_source:MBEDTLS_ENTROPY_BLOCK_SIZE:MBEDTLS_ENTROPY_BLOCK_SIZE - 1:1:-1:-1:PSA_SUCCESS Fake entropy: more than one block in two steps +depends_on:ENTROPY_NONCE_LEN == 0 fake_entropy_source:MBEDTLS_ENTROPY_BLOCK_SIZE:MBEDTLS_ENTROPY_BLOCK_SIZE - 1:MBEDTLS_ENTROPY_BLOCK_SIZE - 1:-1:-1:PSA_SUCCESS +Fake entropy: two blocks eventually +fake_entropy_source:MBEDTLS_ENTROPY_BLOCK_SIZE:0:MBEDTLS_ENTROPY_BLOCK_SIZE:0:MBEDTLS_ENTROPY_BLOCK_SIZE:PSA_SUCCESS + NV seed only: less than minimum entropy_from_nv_seed:MBEDTLS_ENTROPY_MIN_PLATFORM - 1:PSA_ERROR_INSUFFICIENT_ENTROPY diff --git a/tests/suites/test_suite_psa_crypto_init.function b/tests/suites/test_suite_psa_crypto_init.function index 3c4b42e03..3283ac9f6 100644 --- a/tests/suites/test_suite_psa_crypto_init.function +++ b/tests/suites/test_suite_psa_crypto_init.function @@ -11,6 +11,12 @@ #define ENTROPY_MIN_NV_SEED_SIZE \ MAX(MBEDTLS_ENTROPY_MIN_PLATFORM, MBEDTLS_ENTROPY_BLOCK_SIZE) +/* PSA crypto uses the CTR_DRBG module. In some configurations, it needs + * to read from the entropy source twice: once for the initial entropy + * and once for a nonce. */ +#include "mbedtls/ctr_drbg.h" +#define ENTROPY_NONCE_LEN MBEDTLS_CTR_DRBG_ENTROPY_NONCE_LEN + typedef struct { size_t threshold; /* Minimum bytes to make mbedtls_entropy_func happy */ From f0ebbfb3fcd07a31c8329600731c1d6f3d726d87 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Mon, 28 Oct 2019 17:28:46 +0100 Subject: [PATCH 14/15] Fix CTR_DRBG benchmark You can't reuse a CTR_DRBG context without free()ing it and re-init()ing. This generally happened to work, but was never guaranteed. It could have failed with alternative implementations of the AES module because mbedtls_ctr_drbg_seed() calls mbedtls_aes_init() on a context which is already initialized if mbedtls_ctr_drbg_seed() hasn't been called before, plausibly causing a memory leak. Calling free() and seed() with no intervening init fails when MBEDTLS_THREADING_C is enabled and all-bits-zero is not a valid mutex representation. So add the missing free() and init(). --- programs/test/benchmark.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/programs/test/benchmark.c b/programs/test/benchmark.c index b005c203a..8f89c70c6 100644 --- a/programs/test/benchmark.c +++ b/programs/test/benchmark.c @@ -686,12 +686,13 @@ int main( int argc, char *argv[] ) mbedtls_ctr_drbg_context ctr_drbg; mbedtls_ctr_drbg_init( &ctr_drbg ); - if( mbedtls_ctr_drbg_seed( &ctr_drbg, myrand, NULL, NULL, 0 ) != 0 ) mbedtls_exit(1); TIME_AND_TSC( "CTR_DRBG (NOPR)", mbedtls_ctr_drbg_random( &ctr_drbg, buf, BUFSIZE ) ); + mbedtls_ctr_drbg_free( &ctr_drbg ); + mbedtls_ctr_drbg_init( &ctr_drbg ); if( mbedtls_ctr_drbg_seed( &ctr_drbg, myrand, NULL, NULL, 0 ) != 0 ) mbedtls_exit(1); mbedtls_ctr_drbg_set_prediction_resistance( &ctr_drbg, MBEDTLS_CTR_DRBG_PR_ON ); From bd326f93d4297adac26bef6d9e0cc056508ac2ee Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Mon, 28 Oct 2019 17:33:07 +0100 Subject: [PATCH 15/15] Note that mbedtls_ctr_drbg_seed() must not be called twice You can't reuse a CTR_DRBG context without free()ing it and re-init()ing it. This generally happened to work, but was never guaranteed. It could have failed with alternative implementations of the AES module because mbedtls_ctr_drbg_seed() calls mbedtls_aes_init() on a context which is already initialized if mbedtls_ctr_drbg_seed() hasn't been called before, plausibly causing a memory leak. Since the addition of mbedtls_ctr_drbg_set_nonce_len(), the second call to mbedtls_ctr_drbg_seed() uses a nonsensical value as the entropy nonce length. Calling free() and seed() with no intervening init fails when MBEDTLS_THREADING_C is enabled and all-bits-zero is not a valid mutex representation. --- include/mbedtls/ctr_drbg.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/mbedtls/ctr_drbg.h b/include/mbedtls/ctr_drbg.h index a0750e0d4..091f15ac2 100644 --- a/include/mbedtls/ctr_drbg.h +++ b/include/mbedtls/ctr_drbg.h @@ -278,6 +278,13 @@ void mbedtls_ctr_drbg_init( mbedtls_ctr_drbg_context *ctx ); * (maximum achievable strength when using AES-256). * * \param ctx The CTR_DRBG context to seed. + * It must have been initialized with + * mbedtls_ctr_drbg_init(). + * After a successful call to mbedtls_ctr_drbg_seed(), + * you may not call mbedtls_ctr_drbg_seed() again on + * the same context unless you call + * mbedtls_ctr_drbg_free() and mbedtls_ctr_drbg_init() + * again first. * \param f_entropy The entropy callback, taking as arguments the * \p p_entropy context, the buffer to fill, and the * length of the buffer.