diff --git a/ChangeLog.d/rng-cloning.txt b/ChangeLog.d/rng-cloning.txt new file mode 100644 index 0000000000..fc7d4db7f5 --- /dev/null +++ b/ChangeLog.d/rng-cloning.txt @@ -0,0 +1,14 @@ +Features + * Applications can use the new functions psa_random_reseed() to + request an immediate reseed of the PSA random generator, or + psa_random_deplete() to force a reseed on the next random generator call. + * Applications can call psa_random_set_prediction_resistance() to toggle + prediction resistance in the PSA random generator. + +Security + * Applications running in environments where the application state is + cloned (for example due to resuming a frozen system state multiple + times, or due to cloning a virtual machine image) should arrange to + reseed the random generator using one of the new functions + psa_random_reseed() or psa_random_deplete(). CVE-2026-25835 + diff --git a/include/mbedtls/config_adjust_legacy_crypto.h b/include/mbedtls/config_adjust_legacy_crypto.h index 331ac9b2da..db684ad20d 100644 --- a/include/mbedtls/config_adjust_legacy_crypto.h +++ b/include/mbedtls/config_adjust_legacy_crypto.h @@ -48,6 +48,49 @@ #endif #endif /* _MINGW32__ || (_MSC_VER && (_MSC_VER <= 1900)) */ +/* The number of "true" entropy sources (excluding NV seed). + * This must be consistent with mbedtls_entropy_init() in entropy.c. + */ +/* Define auxiliary macros, because in standard C, defined(xxx) is only + * allowed directly on an #if or #elif line, not in recursive expansion. */ +#if defined(MBEDTLS_NO_PLATFORM_ENTROPY) +#define MBEDTLS_PLATFORM_ENTROPY_ENABLED 0 +#else +#define MBEDTLS_PLATFORM_ENTROPY_ENABLED 1 +#endif +#if defined(MBEDTLS_ENTROPY_HARDWARE_ALT) +#define MBEDTLS_ENTROPY_HARDWARE_ALT_DEFINED 1 +#else +#define MBEDTLS_ENTROPY_HARDWARE_ALT_DEFINED 0 +#endif + +#define MBEDTLS_ENTROPY_TRUE_SOURCES ( \ + MBEDTLS_ENTROPY_HARDWARE_ALT_DEFINED + \ + MBEDTLS_PLATFORM_ENTROPY_ENABLED + \ + 0) + +/* Whether there is at least one entropy source for the entropy module. + * + * Note that when MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG is enabled, the entropy + * module is unused and the configuration will typically not include any + * entropy source, so this macro will typically remain undefined. + */ +#if defined(MBEDTLS_ENTROPY_NV_SEED) +#define MBEDTLS_ENTROPY_HAVE_SOURCES (MBEDTLS_ENTROPY_TRUE_SOURCES + 1) +#elif MBEDTLS_ENTROPY_TRUE_SOURCES != 0 +#define MBEDTLS_ENTROPY_HAVE_SOURCES MBEDTLS_ENTROPY_TRUE_SOURCES +#else +#undef MBEDTLS_ENTROPY_HAVE_SOURCES +#endif + +/* Test function dependencies can only check with defined(), + * not other preprocessor expressions. */ +#if MBEDTLS_ENTROPY_TRUE_SOURCES > 0 +#define MBEDTLS_ENTROPY_HAVE_TRUE_SOURCES +#else +#undef MBEDTLS_ENTROPY_HAVE_TRUE_SOURCES +#endif + /* If MBEDTLS_PSA_CRYPTO_C is defined, make sure MBEDTLS_PSA_CRYPTO_CLIENT * is defined as well to include all PSA code. */ diff --git a/include/mbedtls/ctr_drbg.h b/include/mbedtls/ctr_drbg.h index 0b7cce1923..c8d64830b5 100644 --- a/include/mbedtls/ctr_drbg.h +++ b/include/mbedtls/ctr_drbg.h @@ -186,8 +186,7 @@ typedef struct mbedtls_ctr_drbg_context { unsigned char MBEDTLS_PRIVATE(counter)[16]; /*!< The counter (V). */ int MBEDTLS_PRIVATE(reseed_counter); /*!< The reseed counter. * This is the number of requests that have - * been made since the last (re)seeding, - * minus one. + * been made since the last (re)seeding. * Before the initial seeding, this field * contains the amount of entropy in bytes * to use as a nonce for the initial seeding, diff --git a/include/psa/crypto_extra.h b/include/psa/crypto_extra.h index 89a38a8054..e800f3787d 100644 --- a/include/psa/crypto_extra.h +++ b/include/psa/crypto_extra.h @@ -453,7 +453,7 @@ psa_status_t mbedtls_psa_inject_entropy(const uint8_t *seed, /**@}*/ -/** \defgroup psa_external_rng External random generator +/** \defgroup psa_rng Random generator * @{ */ @@ -502,6 +502,155 @@ psa_status_t mbedtls_psa_external_get_random( uint8_t *output, size_t output_size, size_t *output_length); #endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ +/** Force an immediate reseed of the PSA random generator. + * + * The entropy source(s) are the ones configured at compile time. + * + * The random generator is always seeded automatically before use, and + * it is reseeded as needed based on the configured policy, so most + * applications do not need to call this function. + * + * The main reason to call this function is in scenarios where the process + * state is cloned (i.e. duplicated) while the random generator is active. + * In such scenarios, you must call this function in every clone of + * the original process before performing any cryptographic operation + * that uses randomness. (Note that any operation that uses a private or + * secret key may use randomness internally even if the result is not + * randomized, but hashing and signature verification are ok.) For example: + * + * - If the process is part of a live virtual machine that is cloned, + * call this function after cloning so that the new instance has a + * distinct random generator state. + * - If the process is part of a hibernated image that may be resumed + * multiple times, call this function after resuming so that each + * resumed instance has a distinct random generator state. + * - If the process is cloned through the fork() system call, the + * child process should call this function before using the random + * generator. + * + * An additional consideration applies in configurations where there is no + * actual entropy source, only a nonvolatile seed (i.e. + * #MBEDTLS_ENTROPY_NV_SEED is enabled, #MBEDTLS_NO_PLATFORM_ENTROPY is + * enabled and #MBEDTLS_ENTROPY_HARDWARE_ALT is disabled). + * In such configurations, simply calling psa_random_reseed() in multiple + * cloned processes would result in the same random generator state in + * all the clones. To avoid this, in such configurations, you must pass + * a unique \p perso string in every clone. + * + * \note This function has no effect when the compilation option + * #MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG is enabled. + * + * \note In client-server builds, this function may not be available + * from clients, since the decision to reseed is generally based + * on the server state. + * + * \note If the entropy source fails, the random generator remains usable: + * subsequent calls to generate random data will succeed until + * the random generator itself decides to reseed. If you want to + * force a reseed, either treat the failure as a fatal error, + * or call psa_random_deplete() instead of this function (or in + * addition). + * + * \param[in] perso A personalization string, i.e. a byte string to + * inject into the random generator state in addition + * to entropy obtained from the normal source(s). + * In most cases, it is fine for \c perso to be + * empty. The main use case for a personalization + * string is when the random generator state is cloned, + * as described above, and there is no actual entropy + * source. + * \param perso_size Length of \c perso in bytes. + * + * \retval #PSA_SUCCESS + * The reseed succeeded. + * \retval #PSA_ERROR_BAD_STATE + * The PSA random generator is not active. + * \retval #PSA_ERROR_NOT_SUPPORTED + * PSA uses an external random generator because the compilation + * option #MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG is enabled. This + * configuration does not support explicit reseeding. + * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY + * The entropy source failed. + */ +psa_status_t psa_random_reseed(const uint8_t *perso, size_t perso_size); + +/** Force a reseed of the PSA random generator the next time it is used. + * + * The entropy source(s) are the ones configured at compile time. + * + * The random generator is always seeded automatically before use, and + * it is reseeded as needed based on the configured policy, so most + * applications do not need to call this function. + * + * This function has a similar purpose as psa_random_reseed(), + * but the reseed will happen the next time the random generator is used. + * The advantage of this function is that it does not fail unless the + * system is in an unintended state, so it can be used in contexts where + * propagating errors is difficult. + * + * \note This function has no effect when #MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG + * is enabled. + * + * \note If prediction resistance is enabled (either explicitly, or because + * the reseed interval is set to 1), calling this function is + * unnecessary since the random generator will always reseed anyway. + * + * \retval #PSA_SUCCESS + * The reseed succeeded. + * \retval #PSA_ERROR_BAD_STATE + * The PSA random generator is not active. + * \retval #PSA_ERROR_NOT_SUPPORTED + * PSA uses an external random generator because the compilation + * option #MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG is enabled. This + * configuration does not support explicit reseeding. + */ +psa_status_t psa_random_deplete(void); + +/** Enable or disable prediction resistance in the PSA random generator. + * + * When prediction resistance is enabled, the random generator + * injects extra entropy before each request regardless of its size. + * As a consequence, a temporary compromise of the random generator + * state does not, by itself, compromise future steps. + * Furthermore, duplicating the random generator state (because the + * running application instance is cloned) is safe since it will + * not lead to identical random generator outputs in the clones. + * + * When prediction resistance is disabled, the random generator injects + * extra entropy periodically only as determined by + * #MBEDTLS_CTR_DRBG_RESEED_INTERVAL if #MBEDTLS_CTR_DRBG_C + * is enabled, or #MBEDTLS_HMAC_DRBG_RESEED_INTERVAL otherwise. + * + * Prediction resistance is disabled by default, although setting + * #MBEDTLS_CTR_DRBG_RESEED_INTERVAL or #MBEDTLS_HMAC_DRBG_RESEED_INTERVAL + * to \c 1 satisfies the prediction resistance property even when the + * option is disabled. + * + * \note This function has no effect when #MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG + * is enabled. + * + * \note Prediction resistance cannot be enabled when the only entropy source + * is a nonvolatile seed, since prediction resistance is effectively + * impossible to achieve without actual entropy. + * + * \param enabled \c 1 to enable prediction resistance. + * \c 0 to disable prediction resistance. + * + * \retval #PSA_SUCCESS + * The PSA random generator is active, and prediction resistance + * has been changed to the desired option. + * \retval #PSA_ERROR_BAD_STATE + * The PSA random generator is not active. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p enabled is not valid. + * \retval #PSA_ERROR_NOT_SUPPORTED + * PSA uses an external random generator because the compilation + * option #MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG is enabled. + * Or, the random generator only has a nonvolatile seed but no entropy + * source, and prediction resistance has been requested. + */ +psa_status_t psa_random_set_prediction_resistance(unsigned enabled); + /**@}*/ /** \defgroup psa_builtin_keys Built-in keys diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 479da96008..2ec0381a49 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -76,6 +76,7 @@ set(src_crypto psa_crypto_mac.c psa_crypto_pake.c psa_crypto_rsa.c + psa_crypto_random.c psa_crypto_se.c psa_crypto_slot_management.c psa_crypto_storage.c diff --git a/library/Makefile b/library/Makefile index 6692a81f61..4e368efb86 100644 --- a/library/Makefile +++ b/library/Makefile @@ -167,6 +167,7 @@ OBJS_CRYPTO= \ psa_crypto_hash.o \ psa_crypto_mac.o \ psa_crypto_pake.o \ + psa_crypto_random.o \ psa_crypto_rsa.o \ psa_crypto_se.o \ psa_crypto_slot_management.o \ diff --git a/library/ctr_drbg.c b/library/ctr_drbg.c index b82044eb7d..bbbfdacf3d 100644 --- a/library/ctr_drbg.c +++ b/library/ctr_drbg.c @@ -494,7 +494,7 @@ static int mbedtls_ctr_drbg_reseed_internal(mbedtls_ctr_drbg_context *ctx, if ((ret = ctr_drbg_update_internal(ctx, seed)) != 0) { goto exit; } - ctx->reseed_counter = 1; + ctx->reseed_counter = 0; exit: mbedtls_platform_zeroize(seed, sizeof(seed)); @@ -629,7 +629,7 @@ int mbedtls_ctr_drbg_random_with_add(void *p_rng, memset(locals.add_input, 0, MBEDTLS_CTR_DRBG_SEEDLEN); - if (ctx->reseed_counter > ctx->reseed_interval || + if (ctx->reseed_counter >= ctx->reseed_interval || ctx->prediction_resistance) { if ((ret = mbedtls_ctr_drbg_reseed(ctx, additional, add_len)) != 0) { return ret; diff --git a/library/hmac_drbg.c b/library/hmac_drbg.c index 90174d5d17..d51962832e 100644 --- a/library/hmac_drbg.c +++ b/library/hmac_drbg.c @@ -196,7 +196,7 @@ static int hmac_drbg_reseed_core(mbedtls_hmac_drbg_context *ctx, } /* 3. Reset reseed_counter */ - ctx->reseed_counter = 1; + ctx->reseed_counter = 0; exit: /* 4. Done */ @@ -326,7 +326,7 @@ int mbedtls_hmac_drbg_random_with_add(void *p_rng, /* 1. (aka VII and IX) Check reseed counter and PR */ if (ctx->f_entropy != NULL && /* For no-reseeding instances */ (ctx->prediction_resistance == MBEDTLS_HMAC_DRBG_PR_ON || - ctx->reseed_counter > ctx->reseed_interval)) { + ctx->reseed_counter >= ctx->reseed_interval)) { if ((ret = mbedtls_hmac_drbg_reseed(ctx, additional, add_len)) != 0) { return ret; } diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 811049f194..0ef6c604ed 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -37,6 +37,7 @@ * stored keys. */ #include "psa_crypto_storage.h" +#include "psa_crypto_random.h" #include "psa_crypto_random_impl.h" #include @@ -4412,25 +4413,8 @@ static psa_status_t psa_generate_random_internal(uint8_t *output, return PSA_SUCCESS; #else /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ - - while (output_size > 0) { - int ret = MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED; - size_t request_size = - (output_size > MBEDTLS_PSA_RANDOM_MAX_REQUEST ? - MBEDTLS_PSA_RANDOM_MAX_REQUEST : - output_size); -#if defined(MBEDTLS_CTR_DRBG_C) - ret = mbedtls_ctr_drbg_random(&global_data.rng.drbg, output, request_size); -#elif defined(MBEDTLS_HMAC_DRBG_C) - ret = mbedtls_hmac_drbg_random(&global_data.rng.drbg, output, request_size); -#endif /* !MBEDTLS_CTR_DRBG_C && !MBEDTLS_HMAC_DRBG_C */ - if (ret != 0) { - return mbedtls_to_psa_error(ret); - } - output_size -= request_size; - output += request_size; - } - return PSA_SUCCESS; + return psa_random_internal_generate(&global_data.rng, + output, output_size); #endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ } @@ -7984,28 +7968,7 @@ static void mbedtls_psa_random_init(mbedtls_psa_random_context_t *rng) #if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) memset(rng, 0, sizeof(*rng)); #else /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ - - /* Set default configuration if - * mbedtls_psa_crypto_configure_entropy_sources() hasn't been called. */ - if (rng->entropy_init == NULL) { - rng->entropy_init = mbedtls_entropy_init; - } - if (rng->entropy_free == NULL) { - rng->entropy_free = mbedtls_entropy_free; - } - - rng->entropy_init(&rng->entropy); -#if defined(MBEDTLS_PSA_INJECT_ENTROPY) && \ - defined(MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES) - /* The PSA entropy injection feature depends on using NV seed as an entropy - * source. Add NV seed as an entropy source for PSA entropy injection. */ - mbedtls_entropy_add_source(&rng->entropy, - mbedtls_nv_seed_poll, NULL, - MBEDTLS_ENTROPY_BLOCK_SIZE, - MBEDTLS_ENTROPY_SOURCE_STRONG); -#endif - - mbedtls_psa_drbg_init(&rng->drbg); + psa_random_internal_init(rng); #endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ } @@ -8019,8 +7982,7 @@ static void mbedtls_psa_random_free(mbedtls_psa_random_context_t *rng) #if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) memset(rng, 0, sizeof(*rng)); #else /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ - mbedtls_psa_drbg_free(&rng->drbg); - rng->entropy_free(&rng->entropy); + psa_random_internal_free(rng); #endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ } @@ -8033,13 +7995,87 @@ static psa_status_t mbedtls_psa_random_seed(mbedtls_psa_random_context_t *rng) (void) rng; return PSA_SUCCESS; #else /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ - const unsigned char drbg_seed[] = "PSA"; - int ret = mbedtls_psa_drbg_seed(&rng->drbg, &rng->entropy, - drbg_seed, sizeof(drbg_seed) - 1); + return psa_random_internal_seed(rng); +#endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ +} + +psa_status_t psa_random_reseed(const uint8_t *perso, size_t perso_size) +{ + GUARD_MODULE_INITIALIZED; +#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) + (void) perso; + (void) perso_size; + return PSA_ERROR_NOT_SUPPORTED; +#else /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ +#if defined(MBEDTLS_THREADING_C) + if (mbedtls_mutex_lock(&mbedtls_threading_psa_rngdata_mutex) != 0) { + return PSA_ERROR_SERVICE_FAILURE; + } +#endif /* defined(MBEDTLS_THREADING_C) */ + int ret = mbedtls_psa_drbg_reseed(&global_data.rng.drbg, + perso, perso_size); +#if defined(MBEDTLS_THREADING_C) + mbedtls_mutex_unlock(&mbedtls_threading_psa_rngdata_mutex); +#endif /* defined(MBEDTLS_THREADING_C) */ return mbedtls_to_psa_error(ret); #endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ } +psa_status_t psa_random_deplete(void) +{ + GUARD_MODULE_INITIALIZED; +#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) + return PSA_ERROR_NOT_SUPPORTED; +#else /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ +#if defined(MBEDTLS_THREADING_C) + if (mbedtls_mutex_lock(&mbedtls_threading_psa_rngdata_mutex) != 0) { + return PSA_ERROR_SERVICE_FAILURE; + } +#endif /* defined(MBEDTLS_THREADING_C) */ + mbedtls_psa_drbg_deplete(&global_data.rng.drbg); +#if defined(MBEDTLS_THREADING_C) + mbedtls_mutex_unlock(&mbedtls_threading_psa_rngdata_mutex); +#endif /* defined(MBEDTLS_THREADING_C) */ + return PSA_SUCCESS; +#endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ +} + +psa_status_t psa_random_set_prediction_resistance(unsigned enabled) +{ + GUARD_MODULE_INITIALIZED; + +#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) + (void) enabled; + return PSA_ERROR_NOT_SUPPORTED; +#else /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ + + if (enabled != 0 && enabled != 1) { + return PSA_ERROR_INVALID_ARGUMENT; + } + +#if MBEDTLS_ENTROPY_TRUE_SOURCES > 0 +#if defined(MBEDTLS_THREADING_C) + if (mbedtls_mutex_lock(&mbedtls_threading_psa_rngdata_mutex) != 0) { + return PSA_ERROR_SERVICE_FAILURE; + } +#endif /* defined(MBEDTLS_THREADING_C) */ + mbedtls_psa_drbg_set_prediction_resistance(&global_data.rng.drbg, enabled); +#if defined(MBEDTLS_THREADING_C) + mbedtls_mutex_unlock(&mbedtls_threading_psa_rngdata_mutex); +#endif /* defined(MBEDTLS_THREADING_C) */ + return PSA_SUCCESS; + +#else /* MBEDTLS_ENTROPY_TRUE_SOURCES > 0 */ + if (enabled) { + return PSA_ERROR_NOT_SUPPORTED; + } else { + return PSA_SUCCESS; + } + +#endif /* MBEDTLS_ENTROPY_TRUE_SOURCES > 0 */ +#endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ +} + psa_status_t psa_generate_random(uint8_t *output_external, size_t output_size) { diff --git a/library/psa_crypto_random.c b/library/psa_crypto_random.c new file mode 100644 index 0000000000..f5eb658ab6 --- /dev/null +++ b/library/psa_crypto_random.c @@ -0,0 +1,82 @@ +/* + * PSA crypto random generator. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ + +#include "common.h" + +#if defined(MBEDTLS_PSA_CRYPTO_C) && !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) + +#include "psa_crypto_core.h" +#include "psa_crypto_random.h" +#include "psa_crypto_random_impl.h" + +#if defined(MBEDTLS_PSA_INJECT_ENTROPY) +#include "entropy_poll.h" +#endif + +void psa_random_internal_init(mbedtls_psa_random_context_t *rng) +{ + /* Set default configuration if + * mbedtls_psa_crypto_configure_entropy_sources() hasn't been called. */ + if (rng->entropy_init == NULL) { + rng->entropy_init = mbedtls_entropy_init; + } + if (rng->entropy_free == NULL) { + rng->entropy_free = mbedtls_entropy_free; + } + + rng->entropy_init(&rng->entropy); +#if defined(MBEDTLS_PSA_INJECT_ENTROPY) && \ + defined(MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES) + /* The PSA entropy injection feature depends on using NV seed as an entropy + * source. Add NV seed as an entropy source for PSA entropy injection. */ + mbedtls_entropy_add_source(&rng->entropy, + mbedtls_nv_seed_poll, NULL, + MBEDTLS_ENTROPY_BLOCK_SIZE, + MBEDTLS_ENTROPY_SOURCE_STRONG); +#endif + + mbedtls_psa_drbg_init(&rng->drbg); +} + +void psa_random_internal_free(mbedtls_psa_random_context_t *rng) +{ + mbedtls_psa_drbg_free(&rng->drbg); + rng->entropy_free(&rng->entropy); +} +psa_status_t psa_random_internal_seed(mbedtls_psa_random_context_t *rng) +{ + const unsigned char drbg_seed[] = "PSA"; + int ret = mbedtls_psa_drbg_seed(&rng->drbg, &rng->entropy, + drbg_seed, sizeof(drbg_seed) - 1); + return mbedtls_to_psa_error(ret); +} + +psa_status_t psa_random_internal_generate( + mbedtls_psa_random_context_t *rng, + uint8_t *output, size_t output_size) +{ + while (output_size > 0) { + size_t request_size = + (output_size > MBEDTLS_PSA_RANDOM_MAX_REQUEST ? + MBEDTLS_PSA_RANDOM_MAX_REQUEST : + output_size); +#if defined(MBEDTLS_CTR_DRBG_C) + int ret = mbedtls_ctr_drbg_random(&rng->drbg, output, request_size); +#elif defined(MBEDTLS_HMAC_DRBG_C) + int ret = mbedtls_hmac_drbg_random(&rng->drbg, output, request_size); +#endif /* !MBEDTLS_CTR_DRBG_C && !MBEDTLS_HMAC_DRBG_C */ + if (ret != 0) { + return mbedtls_to_psa_error(ret); + } + output_size -= request_size; + output += request_size; + } + return PSA_SUCCESS; +} + +#endif /* MBEDTLS_PSA_CRYPTO_C && !MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ diff --git a/library/psa_crypto_random.h b/library/psa_crypto_random.h new file mode 100644 index 0000000000..167bb235b3 --- /dev/null +++ b/library/psa_crypto_random.h @@ -0,0 +1,72 @@ +/* + * PSA crypto random generator internal functions. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ + +#ifndef PSA_CRYPTO_RANDOM_H +#define PSA_CRYPTO_RANDOM_H + +#include "common.h" + +#if !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) + +#include +#include "psa_crypto_random_impl.h" + +/** Initialize the PSA random generator. + * + * \param[out] rng The random generator context to initialize. + */ +void psa_random_internal_init(mbedtls_psa_random_context_t *rng); + +/** Deinitialize the PSA random generator. + * + * \param[in,out] rng The random generator context to deinitialize. + */ +void psa_random_internal_free(mbedtls_psa_random_context_t *rng); + +/** Seed the PSA random generator. + * + * \note This function is not thread-safe. + * + * \param[in,out] rng The random generator context to seed. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY + * The entropy source failed. + */ +psa_status_t psa_random_internal_seed(mbedtls_psa_random_context_t *rng); + +/** + * \brief Generate random bytes. Like psa_generate_random(), but for use + * inside the library. + * + * This function is thread-safe. + * + * \warning This function **can** fail! Callers MUST check the return status + * and MUST NOT use the content of the output buffer if the return + * status is not #PSA_SUCCESS. + * + * \param[in,out] rng The random generator context to seed. + * \param[out] output Output buffer for the generated data. + * \param output_size Number of bytes to generate and output. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY + * The random generator needed to reseed, and the entropy + * source failed. + * \retval #PSA_ERROR_HARDWARE_FAILURE + * A hardware accelerator failed. + */ +psa_status_t psa_random_internal_generate( + mbedtls_psa_random_context_t *rng, + uint8_t *output, size_t output_size); + +#endif /* !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) */ + +#endif /* PSA_CRYPTO_RANDOM_H */ diff --git a/library/psa_crypto_random_impl.h b/library/psa_crypto_random_impl.h index 5b5163111b..3ed85e5c4e 100644 --- a/library/psa_crypto_random_impl.h +++ b/library/psa_crypto_random_impl.h @@ -100,6 +100,8 @@ static inline void mbedtls_psa_drbg_free(mbedtls_psa_drbg_context_t *p_rng) /** Seed the PSA DRBG. * + * \param drbg_ctx The DRBG context to seed. + * It must be initialized but not active. * \param entropy An entropy context to read the seed from. * \param custom The personalization string. * This can be \c NULL, in which case the personalization @@ -121,6 +123,61 @@ static inline int mbedtls_psa_drbg_seed(mbedtls_psa_drbg_context_t *drbg_ctx, #endif } +/** Reseed the PSA DRBG. + * + * \param drbg_ctx The DRBG context to reseed. + * It must be active. + * \param additional Additional data to inject. + * \param len The length of \p additional in bytes. + * This can be 0 to simply reseed from the entropy source. + * + * \return \c 0 on success. + * \return An Mbed TLS error code (\c MBEDTLS_ERR_xxx) on failure. + */ +static inline int mbedtls_psa_drbg_reseed(mbedtls_psa_drbg_context_t *drbg_ctx, + const unsigned char *additional, + size_t len) +{ +#if defined(MBEDTLS_CTR_DRBG_C) + return mbedtls_ctr_drbg_reseed(drbg_ctx, additional, len); +#elif defined(MBEDTLS_HMAC_DRBG_C) + return mbedtls_hmac_drbg_reseed(drbg_ctx, additional, len); +#endif +} + +/** Deplete the PSA DRBG, i.e. cause it to reseed the next time it is used. + * + * \note This function is not thread-safe. + * + * \param drbg_ctx The DRBG context to deplete. + * It must be active. + */ +static inline void mbedtls_psa_drbg_deplete(mbedtls_psa_drbg_context_t *drbg_ctx) +{ + drbg_ctx->reseed_counter = drbg_ctx->reseed_interval; +} + +#if MBEDTLS_ENTROPY_TRUE_SOURCES > 0 +/** Set prediction resistance in the PSA DRBG. + * + * \note This function is not thread-safe. + * + * \param drbg_ctx The DRBG context to reconfigure. + * It must be active. + * \param enabled \c 1 to enable, or \c 0 to disable. + */ +static inline void mbedtls_psa_drbg_set_prediction_resistance( + mbedtls_psa_drbg_context_t *drbg_ctx, + unsigned enabled) +{ +#if defined(MBEDTLS_CTR_DRBG_C) + mbedtls_ctr_drbg_set_prediction_resistance(drbg_ctx, enabled); +#elif defined(MBEDTLS_HMAC_DRBG_C) + mbedtls_hmac_drbg_set_prediction_resistance(drbg_ctx, enabled); +#endif +} +#endif /* MBEDTLS_ENTROPY_TRUE_SOURCES > 0 */ + #endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ #endif /* PSA_CRYPTO_RANDOM_IMPL_H */ diff --git a/tests/scripts/components-basic-checks.sh b/tests/scripts/components-basic-checks.sh index f34ec411ca..f1e58755ba 100644 --- a/tests/scripts/components-basic-checks.sh +++ b/tests/scripts/components-basic-checks.sh @@ -91,7 +91,7 @@ component_check_test_dependencies () { grep 'depends_on' \ tests/suites/test_suite_psa*.data tests/suites/test_suite_psa*.function | grep -Eo '!?MBEDTLS_[^: ]*' | - grep -v -e MBEDTLS_PSA_ -e MBEDTLS_TEST_ | + grep -v -e MBEDTLS_ENTROPY_HAVE_ -e MBEDTLS_PLATFORM -e MBEDTLS_PSA_ -e MBEDTLS_TEST_ | sort -u > $found # Expected ones with justification - keep in sorted order by ASCII table! diff --git a/tests/suites/test_suite_entropy.data b/tests/suites/test_suite_entropy.data index 514fced49e..eff261401d 100644 --- a/tests/suites/test_suite_entropy.data +++ b/tests/suites/test_suite_entropy.data @@ -4,6 +4,9 @@ entropy_init_free:0 Entropy init-free-init-free entropy_init_free:1 +Entropy: count default sources +entropy_count_sources: + Create NV seed_file nv_seed_file_create: diff --git a/tests/suites/test_suite_entropy.function b/tests/suites/test_suite_entropy.function index a4f3b1bd7c..0e719f6f06 100644 --- a/tests/suites/test_suite_entropy.function +++ b/tests/suites/test_suite_entropy.function @@ -171,6 +171,29 @@ void entropy_init_free(int reinit) } /* END_CASE */ +/* BEGIN_CASE */ +void entropy_count_sources() +{ + mbedtls_entropy_context ctx; + mbedtls_entropy_init(&ctx); + +#if defined(MBEDTLS_ENTROPY_HAVE_SOURCES) + TEST_EQUAL(MBEDTLS_ENTROPY_HAVE_SOURCES, ctx.source_count); +#else + TEST_EQUAL(0, ctx.source_count); +#endif + +#if defined(MBEDTLS_ENTROPY_NV_SEED) + TEST_EQUAL(MBEDTLS_ENTROPY_TRUE_SOURCES + 1, ctx.source_count); +#else + TEST_EQUAL(MBEDTLS_ENTROPY_TRUE_SOURCES, ctx.source_count); +#endif + +exit: + mbedtls_entropy_free(&ctx); +} +/* END_CASE */ + /* BEGIN_CASE depends_on:MBEDTLS_ENTROPY_NV_SEED:MBEDTLS_FS_IO */ void entropy_seed_file(char *path, int ret) { diff --git a/tests/suites/test_suite_psa_crypto_entropy.data b/tests/suites/test_suite_psa_crypto_entropy.data index 68a7f984e3..03dd6c8222 100644 --- a/tests/suites/test_suite_psa_crypto_entropy.data +++ b/tests/suites/test_suite_psa_crypto_entropy.data @@ -1,3 +1,82 @@ +Create NV seed file +create_nv_seed: + +Custom entropy sources: all standard +custom_entropy_sources:0x0000ffff:PSA_SUCCESS + +# MBEDTLS_PSA_INJECT_ENTROPY means that a source of entropy (the seed file) +# is effectively always available. +Custom entropy sources: none +depends_on:!MBEDTLS_PSA_INJECT_ENTROPY +custom_entropy_sources:0:PSA_ERROR_INSUFFICIENT_ENTROPY + +Fake entropy: never returns anything +fake_entropy_source:MBEDTLS_ENTROPY_BLOCK_SIZE:0:0:0:0:PSA_ERROR_INSUFFICIENT_ENTROPY + +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 + +NV seed only: less than one block +entropy_from_nv_seed:MBEDTLS_ENTROPY_BLOCK_SIZE - 1:PSA_ERROR_INSUFFICIENT_ENTROPY + +NV seed only: just enough +entropy_from_nv_seed:ENTROPY_MIN_NV_SEED_SIZE:PSA_SUCCESS + +Explicit reseed: basic tests +reseed_basic: + +Explicit reseed: entropy consumption +reseed_consumption: + +Deplete: entropy consumption +deplete_consumption: + +Prediction resistance: entropy consumption +prediction_resistance_consumption: + +Prediction resistance: bad state +prediction_resistance_bad_state: + +Prediction resistance: bad arguments +prediction_resistance_bad_arguments: + +Explicit reseed: uniqueness tests (0 = 0) +reseed_uniqueness:"":"" + +Explicit reseed: uniqueness tests (0 != 5) +reseed_uniqueness:"":"706572736f" + +Explicit reseed: uniqueness tests (5 = 5) +reseed_uniqueness:"706572736f":"706572736f" + +Explicit reseed: uniqueness tests (5 != 5) +reseed_uniqueness:"706572736f":"706572736e" + +Explicit reseed: uniqueness tests (5 != 10) +reseed_uniqueness:"706572736f":"706572736f706572736f" + PSA external RNG failure: generate random and key external_rng_failure_generate: @@ -43,3 +122,5 @@ validate_entropy_seed_injection:MBEDTLS_ENTROPY_BLOCK_SIZE-1:PSA_ERROR_INVALID_A PSA validate entropy injection: before and after crypto_init run_entropy_inject_with_crypto_init: +Recreate NV seed file +create_nv_seed: diff --git a/tests/suites/test_suite_psa_crypto_entropy.function b/tests/suites/test_suite_psa_crypto_entropy.function index 4d5eda2baf..413e1ea5d8 100644 --- a/tests/suites/test_suite_psa_crypto_entropy.function +++ b/tests/suites/test_suite_psa_crypto_entropy.function @@ -4,9 +4,151 @@ #include +/* Some tests in this module configure entropy sources. */ +#include "psa_crypto_invasive.h" + #include "mbedtls/entropy.h" #include "entropy_poll.h" +#define ENTROPY_MIN_NV_SEED_SIZE \ + MAX(MBEDTLS_ENTROPY_MIN_PLATFORM, MBEDTLS_ENTROPY_BLOCK_SIZE) + +#include "psa_crypto_random_impl.h" +#if defined(MBEDTLS_PSA_HMAC_DRBG_MD_TYPE) +/* PSA crypto uses the HMAC_DRBG module. It reads from the entropy source twice: + * once for the initial entropy and once for a nonce. The nonce length is + * half the entropy length. For SHA-256, SHA-384 or SHA-512, the + * entropy length is 256 per the documentation of mbedtls_hmac_drbg_seed(), + * and PSA crypto doesn't support other hashes for HMAC_DRBG. */ +#define ENTROPY_NONCE_LEN (256 / 2) +#else +/* 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 +#endif + +#if !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) + +typedef struct { + size_t threshold; /* Minimum bytes to make mbedtls_entropy_func happy */ + size_t max_steps; + size_t *length_sequence; + size_t step; +} fake_entropy_state_t; + +static int fake_entropy_source(void *state_arg, + unsigned char *output, size_t len, + size_t *olen) +{ + fake_entropy_state_t *state = state_arg; + size_t i; + + if (state->step >= state->max_steps) { + return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; + } + + *olen = MIN(len, state->length_sequence[state->step]); + for (i = 0; i < *olen; i++) { + output[i] = i; + } + ++state->step; + return 0; +} + +#define ENTROPY_SOURCE_PLATFORM 0x00000001 +#define ENTROPY_SOURCE_TIMING 0x00000002 +#define ENTROPY_SOURCE_HARDWARE 0x00000004 +#define ENTROPY_SOURCE_NV_SEED 0x00000008 +#define ENTROPY_SOURCE_FAKE 0x40000000 + +static uint32_t custom_entropy_sources_mask; +static fake_entropy_state_t fake_entropy_state; + +/* This is a modified version of mbedtls_entropy_init() from entropy.c + * which chooses entropy sources dynamically. */ +static void custom_entropy_init(mbedtls_entropy_context *ctx) +{ + ctx->source_count = 0; + memset(ctx->source, 0, sizeof(ctx->source)); + +#if defined(MBEDTLS_THREADING_C) + mbedtls_mutex_init(&ctx->mutex); +#endif + + ctx->accumulator_started = 0; + mbedtls_md_init(&ctx->accumulator); + +#if !defined(MBEDTLS_NO_PLATFORM_ENTROPY) + if (custom_entropy_sources_mask & ENTROPY_SOURCE_PLATFORM) { + mbedtls_entropy_add_source(ctx, mbedtls_platform_entropy_poll, NULL, + MBEDTLS_ENTROPY_MIN_PLATFORM, + MBEDTLS_ENTROPY_SOURCE_STRONG); + } +#endif +#if defined(MBEDTLS_ENTROPY_HARDWARE_ALT) + if (custom_entropy_sources_mask & ENTROPY_SOURCE_HARDWARE) { + mbedtls_entropy_add_source(ctx, mbedtls_hardware_poll, NULL, + MBEDTLS_ENTROPY_MIN_HARDWARE, + MBEDTLS_ENTROPY_SOURCE_STRONG); + } +#endif +#if defined(MBEDTLS_ENTROPY_NV_SEED) + if (custom_entropy_sources_mask & ENTROPY_SOURCE_NV_SEED) { + mbedtls_entropy_add_source(ctx, mbedtls_nv_seed_poll, NULL, + MBEDTLS_ENTROPY_BLOCK_SIZE, + MBEDTLS_ENTROPY_SOURCE_STRONG); + ctx->initial_entropy_run = 0; + } else { + /* Skip the NV seed even though it's compiled in. */ + ctx->initial_entropy_run = 1; + } +#endif + + if (custom_entropy_sources_mask & ENTROPY_SOURCE_FAKE) { + mbedtls_entropy_add_source(ctx, + fake_entropy_source, &fake_entropy_state, + fake_entropy_state.threshold, + MBEDTLS_ENTROPY_SOURCE_STRONG); + } +} + +static size_t fake_entropy_lengths[] = { + MBEDTLS_ENTROPY_BLOCK_SIZE, + MBEDTLS_ENTROPY_BLOCK_SIZE, + MBEDTLS_ENTROPY_BLOCK_SIZE, + MBEDTLS_ENTROPY_BLOCK_SIZE, + MBEDTLS_ENTROPY_BLOCK_SIZE, + MBEDTLS_ENTROPY_BLOCK_SIZE, +}; + +/** Initialize PSA with a deterministic RNG seed. + * + * \param max_entropy_queries Maximum number of queries to the entropy source. + * Once this number has been reached, the + * entropy source will fail. + */ +static int psa_init_deterministic(size_t max_entropy_queries) +{ + TEST_LE_U(max_entropy_queries, ARRAY_LENGTH(fake_entropy_lengths)); + + fake_entropy_state.threshold = MBEDTLS_ENTROPY_BLOCK_SIZE; + fake_entropy_state.step = 0; + fake_entropy_state.max_steps = max_entropy_queries; + fake_entropy_state.length_sequence = fake_entropy_lengths; + + custom_entropy_sources_mask = ENTROPY_SOURCE_FAKE; + PSA_ASSERT(mbedtls_psa_crypto_configure_entropy_sources( + custom_entropy_init, mbedtls_entropy_free)); + PSA_INIT(); + return 1; + +exit: + return 0; +} +#endif /* !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) */ + /* Calculating the minimum allowed entropy size in bytes */ #define MBEDTLS_PSA_INJECT_ENTROPY_MIN_SIZE MAX(MBEDTLS_ENTROPY_MIN_PLATFORM, \ MBEDTLS_ENTROPY_BLOCK_SIZE) @@ -68,6 +210,375 @@ psa_status_t remove_seed_file(void) /* END_HEADER */ +/* BEGIN_DEPENDENCIES + * depends_on:MBEDTLS_PSA_CRYPTO_C + * END_DEPENDENCIES + */ + +/* BEGIN_CASE depends_on:MBEDTLS_ENTROPY_NV_SEED:!MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ +void create_nv_seed() +{ + static unsigned char seed[ENTROPY_MIN_NV_SEED_SIZE]; + TEST_ASSERT(mbedtls_nv_seed_write(seed, sizeof(seed)) >= 0); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:!MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ +void custom_entropy_sources(int sources_arg, int expected_init_status_arg) +{ + psa_status_t expected_init_status = expected_init_status_arg; + uint8_t random[10] = { 0 }; + + custom_entropy_sources_mask = sources_arg; + PSA_ASSERT(mbedtls_psa_crypto_configure_entropy_sources( + custom_entropy_init, mbedtls_entropy_free)); + + TEST_EQUAL(psa_crypto_init(), expected_init_status); + if (expected_init_status != PSA_SUCCESS) { + goto exit; + } + + PSA_ASSERT(psa_generate_random(random, sizeof(random))); + +exit: + PSA_DONE(); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:!MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ +void fake_entropy_source(int threshold, + int amount1, + int amount2, + int amount3, + int amount4, + int expected_init_status_arg) +{ + psa_status_t expected_init_status = expected_init_status_arg; + uint8_t random[10] = { 0 }; + size_t lengths[4]; + + fake_entropy_state.threshold = threshold; + fake_entropy_state.step = 0; + fake_entropy_state.max_steps = 0; + if (amount1 >= 0) { + lengths[fake_entropy_state.max_steps++] = amount1; + } + if (amount2 >= 0) { + lengths[fake_entropy_state.max_steps++] = amount2; + } + if (amount3 >= 0) { + lengths[fake_entropy_state.max_steps++] = amount3; + } + if (amount4 >= 0) { + lengths[fake_entropy_state.max_steps++] = amount4; + } + fake_entropy_state.length_sequence = lengths; + + custom_entropy_sources_mask = ENTROPY_SOURCE_FAKE; + PSA_ASSERT(mbedtls_psa_crypto_configure_entropy_sources( + custom_entropy_init, mbedtls_entropy_free)); + + TEST_EQUAL(psa_crypto_init(), expected_init_status); + if (expected_init_status != PSA_SUCCESS) { + goto exit; + } + + PSA_ASSERT(psa_generate_random(random, sizeof(random))); + +exit: + PSA_DONE(); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_ENTROPY_NV_SEED:!MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ +void entropy_from_nv_seed(int seed_size_arg, + int expected_init_status_arg) +{ + psa_status_t expected_init_status = expected_init_status_arg; + uint8_t random[10] = { 0 }; + uint8_t *seed = NULL; + size_t seed_size = seed_size_arg; + + TEST_CALLOC(seed, seed_size); + TEST_ASSERT(mbedtls_nv_seed_write(seed, seed_size) >= 0); + + custom_entropy_sources_mask = ENTROPY_SOURCE_NV_SEED; + PSA_ASSERT(mbedtls_psa_crypto_configure_entropy_sources( + custom_entropy_init, mbedtls_entropy_free)); + + TEST_EQUAL(psa_crypto_init(), expected_init_status); + if (expected_init_status != PSA_SUCCESS) { + goto exit; + } + + PSA_ASSERT(psa_generate_random(random, sizeof(random))); + +exit: + mbedtls_free(seed); + PSA_DONE(); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:!MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ +void reseed_basic() +{ + uint8_t random[10]; + const uint8_t perso[5] = { 'p', 'e', 'r', 's', 'o' }; + + TEST_EQUAL(psa_random_reseed(NULL, 0), PSA_ERROR_BAD_STATE); + TEST_EQUAL(psa_random_deplete(), PSA_ERROR_BAD_STATE); + TEST_EQUAL(psa_generate_random(random, sizeof(random)), PSA_ERROR_BAD_STATE); + + PSA_INIT(); + + PSA_ASSERT(psa_random_reseed(NULL, 0)); + PSA_ASSERT(psa_random_reseed(perso, sizeof(perso))); + PSA_ASSERT(psa_generate_random(random, sizeof(random))); + + PSA_ASSERT(psa_random_deplete()); + PSA_ASSERT(psa_generate_random(random, sizeof(random))); + + mbedtls_psa_crypto_free(); + + TEST_EQUAL(psa_random_reseed(NULL, 0), PSA_ERROR_BAD_STATE); + TEST_EQUAL(psa_random_deplete(), PSA_ERROR_BAD_STATE); + TEST_EQUAL(psa_generate_random(random, sizeof(random)), PSA_ERROR_BAD_STATE); + +exit: + PSA_DONE(); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:!MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ +void prediction_resistance_bad_state() +{ + uint8_t random[10]; + + /* RNG inactive before initialization */ + TEST_EQUAL(psa_random_set_prediction_resistance(0), PSA_ERROR_BAD_STATE); + TEST_EQUAL(psa_random_set_prediction_resistance(1), PSA_ERROR_BAD_STATE); + TEST_EQUAL(psa_generate_random(random, sizeof(random)), PSA_ERROR_BAD_STATE); + + PSA_INIT(); + + /* Good cases, as controls */ + PSA_ASSERT(psa_generate_random(random, sizeof(random))); + PSA_ASSERT(psa_random_set_prediction_resistance(0)); +#if MBEDTLS_ENTROPY_TRUE_SOURCES > 0 + PSA_ASSERT(psa_random_set_prediction_resistance(1)); +#endif + + /* RNG inactive after shutdown */ + mbedtls_psa_crypto_free(); + TEST_EQUAL(psa_random_set_prediction_resistance(0), PSA_ERROR_BAD_STATE); + TEST_EQUAL(psa_random_set_prediction_resistance(1), PSA_ERROR_BAD_STATE); + TEST_EQUAL(psa_generate_random(random, sizeof(random)), PSA_ERROR_BAD_STATE); + +exit: + PSA_DONE(); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:!MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ +void prediction_resistance_bad_arguments() +{ + uint8_t random[10]; + + PSA_INIT(); + + TEST_EQUAL(psa_random_set_prediction_resistance(2), PSA_ERROR_INVALID_ARGUMENT); + TEST_EQUAL(psa_random_set_prediction_resistance(-1), PSA_ERROR_INVALID_ARGUMENT); + + /* Good cases, as controls */ + PSA_ASSERT(psa_generate_random(random, sizeof(random))); + PSA_ASSERT(psa_random_set_prediction_resistance(0)); +#if MBEDTLS_ENTROPY_TRUE_SOURCES > 0 + PSA_ASSERT(psa_random_set_prediction_resistance(1)); +#else + TEST_EQUAL(psa_random_set_prediction_resistance(1), PSA_ERROR_NOT_SUPPORTED); +#endif + +exit: + PSA_DONE(); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:!MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ +/* Check that reseeding consumes entropy. + * + * For simplicity, this test function assumes that the DRBG has prediction + * resistance turned off, so the few RNG queries in this function don't + * trigger a reseed. + */ +void reseed_consumption() +{ + uint8_t random[10] = { 0 }; + const size_t max_get_entropy = 4; + + if (!psa_init_deterministic(max_get_entropy)) { + goto exit; + } + + /* Depending on the DRBG parameters, the initial seeding may + * consume entropy once or twice. Zero would be deeply unsettling + * (how can you initialize the RNG without entropy?). More than 2 would + * be ok, but the test code would need to be adapted. */ + TEST_LE_U(1, fake_entropy_state.step); + TEST_LE_U(fake_entropy_state.step, 2); + /* Arrange to have exactly 2 entropy blocks remaining. */ + fake_entropy_state.step = max_get_entropy - 2; + + /* Explicit reseed, consumes 1 entropy block, 1 remaining */ + PSA_ASSERT(psa_random_reseed(NULL, 0)); + PSA_ASSERT(psa_generate_random(random, sizeof(random))); + + /* Explicit reseed, consumes 1 entropy block, 0 remaining */ + PSA_ASSERT(psa_random_reseed(NULL, 0)); + PSA_ASSERT(psa_generate_random(random, sizeof(random))); + + /* All entropy blocks are now consumed */ + TEST_EQUAL(psa_random_reseed(NULL, 0), PSA_ERROR_INSUFFICIENT_ENTROPY); + + /* The random generator is still fine after failing to reseed + * explicitly. Should it be? */ + PSA_ASSERT(psa_generate_random(random, sizeof(random))); + +exit: + PSA_DONE(); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:!MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ +void deplete_consumption() +{ + uint8_t random[10] = { 0 }; + + if (!psa_init_deterministic(4)) { + goto exit; + } + + /* Depending on the DRBG parameters, the initial seeding may + * consume entropy once or twice. Reset to 1 to keep things simple. */ + fake_entropy_state.step = 1; + + PSA_ASSERT(psa_random_deplete()); + TEST_EQUAL(fake_entropy_state.step, 1); + + PSA_ASSERT(psa_generate_random(random, sizeof(random))); + TEST_LE_U(2, fake_entropy_state.step); + +exit: + PSA_DONE(); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:!MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG:MBEDTLS_ENTROPY_HAVE_TRUE_SOURCES */ +void prediction_resistance_consumption() +{ + uint8_t random[10] = { 0 }; + + if (!psa_init_deterministic(4)) { + goto exit; + } + + /* Depending on the DRBG parameters, the initial seeding may + * consume entropy once or twice. Reset to 1 to keep things simple. */ + fake_entropy_state.step = 1; + + /* Default: no prediction resistance */ + /* (Note, we assume that prediction resistance is not effectively enabled + * at compile time by setting a very low reseed interval.) */ + PSA_ASSERT(psa_generate_random(random, sizeof(random))); + TEST_EQUAL(fake_entropy_state.step, 1); + + /* Explicitly enable prediction resistance */ + PSA_ASSERT(psa_random_set_prediction_resistance(1)); + TEST_EQUAL(fake_entropy_state.step, 1); + PSA_ASSERT(psa_generate_random(random, sizeof(random))); + TEST_EQUAL(fake_entropy_state.step, 2); + PSA_ASSERT(psa_generate_random(random, sizeof(random))); + TEST_EQUAL(fake_entropy_state.step, 3); + + /* Explicitly disable prediction resistance */ + PSA_ASSERT(psa_random_set_prediction_resistance(0)); + TEST_EQUAL(fake_entropy_state.step, 3); + PSA_ASSERT(psa_generate_random(random, sizeof(random))); + TEST_EQUAL(fake_entropy_state.step, 3); + PSA_ASSERT(psa_generate_random(random, sizeof(random))); + TEST_EQUAL(fake_entropy_state.step, 3); + +exit: + PSA_DONE(); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:!MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ +void reseed_uniqueness(data_t *perso1, data_t *perso2) +{ + uint8_t random0[10] = { 0 }; + uint8_t random1[10] = { 0 }; + uint8_t random2[10] = { 0 }; + uint8_t random_again[10] = { 0 }; + /* Enough for 2 initial seeding + 2 reseed + 2 getrandom with + * prediction resistance */ + size_t max_entropy_queries = 6; + + /* Reference: no reseed */ + if (!psa_init_deterministic(max_entropy_queries)) { + goto exit; + } + PSA_ASSERT(psa_generate_random(random0, sizeof(random0))); + mbedtls_psa_crypto_free(); + + /* Reference: no reseed, again */ + if (!psa_init_deterministic(max_entropy_queries)) { + goto exit; + } + PSA_ASSERT(psa_generate_random(random_again, sizeof(random_again))); + mbedtls_psa_crypto_free(); + TEST_MEMORY_COMPARE(random0, sizeof(random0), + random_again, sizeof(random_again)); + + /* Reseed with a personalization string */ + if (!psa_init_deterministic(max_entropy_queries)) { + goto exit; + } + PSA_ASSERT(psa_random_reseed(perso1->x, perso1->len)); + PSA_ASSERT(psa_generate_random(random1, sizeof(random1))); + mbedtls_psa_crypto_free(); + TEST_ASSERT(memcmp(random0, random1, sizeof(random1)) != 0); + + /* Reseed with a personalization string (same or different) */ + if (!psa_init_deterministic(max_entropy_queries)) { + goto exit; + } + PSA_ASSERT(psa_random_reseed(perso2->x, perso2->len)); + PSA_ASSERT(psa_generate_random(random2, sizeof(random2))); + mbedtls_psa_crypto_free(); + if (perso1->len == perso2->len && + memcmp(perso1->x, perso2->x, perso1->len) == 0) { + TEST_MEMORY_COMPARE(random1, sizeof(random1), + random2, sizeof(random2)); + } else { + TEST_ASSERT(memcmp(random1, random2, sizeof(random2)) != 0); + } + + /* Reseed twice */ + if (!psa_init_deterministic(max_entropy_queries)) { + goto exit; + } + PSA_ASSERT(psa_random_reseed(perso1->x, perso1->len)); + PSA_ASSERT(psa_random_reseed(perso1->x, perso1->len)); + PSA_ASSERT(psa_generate_random(random2, sizeof(random2))); + mbedtls_psa_crypto_free(); + TEST_ASSERT(memcmp(random0, random2, sizeof(random2)) != 0); + TEST_ASSERT(memcmp(random1, random2, sizeof(random2)) != 0); + +exit: + PSA_DONE(); +} +/* END_CASE */ + /* BEGIN_CASE depends_on:MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ void external_rng_failure_generate() { @@ -83,6 +594,11 @@ void external_rng_failure_generate() PSA_ASSERT(psa_generate_key(&attributes, &key)); PSA_ASSERT(psa_destroy_key(key)); + TEST_EQUAL(psa_random_reseed(NULL, 0), PSA_ERROR_NOT_SUPPORTED); + TEST_EQUAL(psa_random_deplete(), PSA_ERROR_NOT_SUPPORTED); + TEST_EQUAL(psa_random_set_prediction_resistance(0), PSA_ERROR_NOT_SUPPORTED); + TEST_EQUAL(psa_random_set_prediction_resistance(1), PSA_ERROR_NOT_SUPPORTED); + mbedtls_test_disable_insecure_external_rng(); TEST_EQUAL(PSA_ERROR_INSUFFICIENT_ENTROPY, psa_generate_random(output, sizeof(output))); diff --git a/tests/suites/test_suite_psa_crypto_init.data b/tests/suites/test_suite_psa_crypto_init.data index 147d03fbed..1525ab1e5b 100644 --- a/tests/suites/test_suite_psa_crypto_init.data +++ b/tests/suites/test_suite_psa_crypto_init.data @@ -1,6 +1,3 @@ -Create NV seed file -create_nv_seed: - PSA init/deinit init_deinit:2 @@ -24,49 +21,3 @@ validate_module_init_generate_random:1 No key slot access after deinit validate_module_init_key_based:1 - -Custom entropy sources: all standard -custom_entropy_sources:0x0000ffff:PSA_SUCCESS - -# MBEDTLS_PSA_INJECT_ENTROPY means that a source of entropy (the seed file) -# is effectively always available. -Custom entropy sources: none -depends_on:!MBEDTLS_PSA_INJECT_ENTROPY -custom_entropy_sources:0:PSA_ERROR_INSUFFICIENT_ENTROPY - -Fake entropy: never returns anything -fake_entropy_source:MBEDTLS_ENTROPY_BLOCK_SIZE:0:0:0:0:PSA_ERROR_INSUFFICIENT_ENTROPY - -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 - -NV seed only: less than one block -entropy_from_nv_seed:MBEDTLS_ENTROPY_BLOCK_SIZE - 1:PSA_ERROR_INSUFFICIENT_ENTROPY - -NV seed only: just enough -entropy_from_nv_seed:ENTROPY_MIN_NV_SEED_SIZE:PSA_SUCCESS - -Recreate NV seed file -create_nv_seed: diff --git a/tests/suites/test_suite_psa_crypto_init.function b/tests/suites/test_suite_psa_crypto_init.function index 954560a24e..6b4b18577b 100644 --- a/tests/suites/test_suite_psa_crypto_init.function +++ b/tests/suites/test_suite_psa_crypto_init.function @@ -2,12 +2,9 @@ #include #include "psa_crypto_core.h" -/* Some tests in this module configure entropy sources. */ +/* For mbedtls_psa_crypto_configure_entropy_sources() */ #include "psa_crypto_invasive.h" -#include "mbedtls/entropy.h" -#include "entropy_poll.h" - static int check_stats(void) { mbedtls_psa_stats_t stats; @@ -25,111 +22,6 @@ exit: return 0; } -#define ENTROPY_MIN_NV_SEED_SIZE \ - MAX(MBEDTLS_ENTROPY_MIN_PLATFORM, MBEDTLS_ENTROPY_BLOCK_SIZE) - -#include "psa_crypto_random_impl.h" -#if defined(MBEDTLS_PSA_HMAC_DRBG_MD_TYPE) -/* PSA crypto uses the HMAC_DRBG module. It reads from the entropy source twice: - * once for the initial entropy and once for a nonce. The nonce length is - * half the entropy length. For SHA-256, SHA-384 or SHA-512, the - * entropy length is 256 per the documentation of mbedtls_hmac_drbg_seed(), - * and PSA crypto doesn't support other hashes for HMAC_DRBG. */ -#define ENTROPY_NONCE_LEN (256 / 2) -#else -/* 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 -#endif - -#if !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) - -typedef struct { - size_t threshold; /* Minimum bytes to make mbedtls_entropy_func happy */ - size_t max_steps; - size_t *length_sequence; - size_t step; -} fake_entropy_state_t; -static int fake_entropy_source(void *state_arg, - unsigned char *output, size_t len, - size_t *olen) -{ - fake_entropy_state_t *state = state_arg; - size_t i; - - if (state->step >= state->max_steps) { - return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; - } - - *olen = MIN(len, state->length_sequence[state->step]); - for (i = 0; i < *olen; i++) { - output[i] = i; - } - ++state->step; - return 0; -} - -#define ENTROPY_SOURCE_PLATFORM 0x00000001 -#define ENTROPY_SOURCE_TIMING 0x00000002 -#define ENTROPY_SOURCE_HARDWARE 0x00000004 -#define ENTROPY_SOURCE_NV_SEED 0x00000008 -#define ENTROPY_SOURCE_FAKE 0x40000000 - -static uint32_t custom_entropy_sources_mask; -static fake_entropy_state_t fake_entropy_state; - -/* This is a modified version of mbedtls_entropy_init() from entropy.c - * which chooses entropy sources dynamically. */ -static void custom_entropy_init(mbedtls_entropy_context *ctx) -{ - ctx->source_count = 0; - memset(ctx->source, 0, sizeof(ctx->source)); - -#if defined(MBEDTLS_THREADING_C) - mbedtls_mutex_init(&ctx->mutex); -#endif - - ctx->accumulator_started = 0; - mbedtls_md_init(&ctx->accumulator); - -#if !defined(MBEDTLS_NO_PLATFORM_ENTROPY) - if (custom_entropy_sources_mask & ENTROPY_SOURCE_PLATFORM) { - mbedtls_entropy_add_source(ctx, mbedtls_platform_entropy_poll, NULL, - MBEDTLS_ENTROPY_MIN_PLATFORM, - MBEDTLS_ENTROPY_SOURCE_STRONG); - } -#endif -#if defined(MBEDTLS_ENTROPY_HARDWARE_ALT) - if (custom_entropy_sources_mask & ENTROPY_SOURCE_HARDWARE) { - mbedtls_entropy_add_source(ctx, mbedtls_hardware_poll, NULL, - MBEDTLS_ENTROPY_MIN_HARDWARE, - MBEDTLS_ENTROPY_SOURCE_STRONG); - } -#endif -#if defined(MBEDTLS_ENTROPY_NV_SEED) - if (custom_entropy_sources_mask & ENTROPY_SOURCE_NV_SEED) { - mbedtls_entropy_add_source(ctx, mbedtls_nv_seed_poll, NULL, - MBEDTLS_ENTROPY_BLOCK_SIZE, - MBEDTLS_ENTROPY_SOURCE_STRONG); - ctx->initial_entropy_run = 0; - } else { - /* Skip the NV seed even though it's compiled in. */ - ctx->initial_entropy_run = 1; - } -#endif - - if (custom_entropy_sources_mask & ENTROPY_SOURCE_FAKE) { - mbedtls_entropy_add_source(ctx, - fake_entropy_source, &fake_entropy_state, - fake_entropy_state.threshold, - MBEDTLS_ENTROPY_SOURCE_STRONG); - } -} - -#endif /* !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) */ - #if defined MBEDTLS_THREADING_PTHREAD typedef struct { @@ -190,14 +82,6 @@ exit: * END_DEPENDENCIES */ -/* BEGIN_CASE depends_on:MBEDTLS_ENTROPY_NV_SEED:!MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ -void create_nv_seed() -{ - static unsigned char seed[ENTROPY_MIN_NV_SEED_SIZE]; - TEST_ASSERT(mbedtls_nv_seed_write(seed, sizeof(seed)) >= 0); -} -/* END_CASE */ - /* BEGIN_CASE */ void init_deinit(int count) { @@ -333,99 +217,3 @@ void validate_module_init_key_based(int count) TEST_ASSERT(mbedtls_svc_key_id_is_null(key)); } /* END_CASE */ - -/* BEGIN_CASE depends_on:!MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ -void custom_entropy_sources(int sources_arg, int expected_init_status_arg) -{ - psa_status_t expected_init_status = expected_init_status_arg; - uint8_t random[10] = { 0 }; - - custom_entropy_sources_mask = sources_arg; - PSA_ASSERT(mbedtls_psa_crypto_configure_entropy_sources( - custom_entropy_init, mbedtls_entropy_free)); - - TEST_EQUAL(psa_crypto_init(), expected_init_status); - if (expected_init_status != PSA_SUCCESS) { - goto exit; - } - - PSA_ASSERT(psa_generate_random(random, sizeof(random))); - -exit: - PSA_DONE(); -} -/* END_CASE */ - -/* BEGIN_CASE depends_on:!MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ -void fake_entropy_source(int threshold, - int amount1, - int amount2, - int amount3, - int amount4, - int expected_init_status_arg) -{ - psa_status_t expected_init_status = expected_init_status_arg; - uint8_t random[10] = { 0 }; - size_t lengths[4]; - - fake_entropy_state.threshold = threshold; - fake_entropy_state.step = 0; - fake_entropy_state.max_steps = 0; - if (amount1 >= 0) { - lengths[fake_entropy_state.max_steps++] = amount1; - } - if (amount2 >= 0) { - lengths[fake_entropy_state.max_steps++] = amount2; - } - if (amount3 >= 0) { - lengths[fake_entropy_state.max_steps++] = amount3; - } - if (amount4 >= 0) { - lengths[fake_entropy_state.max_steps++] = amount4; - } - fake_entropy_state.length_sequence = lengths; - - custom_entropy_sources_mask = ENTROPY_SOURCE_FAKE; - PSA_ASSERT(mbedtls_psa_crypto_configure_entropy_sources( - custom_entropy_init, mbedtls_entropy_free)); - - TEST_EQUAL(psa_crypto_init(), expected_init_status); - if (expected_init_status != PSA_SUCCESS) { - goto exit; - } - - PSA_ASSERT(psa_generate_random(random, sizeof(random))); - -exit: - PSA_DONE(); -} -/* END_CASE */ - -/* BEGIN_CASE depends_on:MBEDTLS_ENTROPY_NV_SEED:!MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ -void entropy_from_nv_seed(int seed_size_arg, - int expected_init_status_arg) -{ - psa_status_t expected_init_status = expected_init_status_arg; - uint8_t random[10] = { 0 }; - uint8_t *seed = NULL; - size_t seed_size = seed_size_arg; - - TEST_CALLOC(seed, seed_size); - TEST_ASSERT(mbedtls_nv_seed_write(seed, seed_size) >= 0); - - custom_entropy_sources_mask = ENTROPY_SOURCE_NV_SEED; - PSA_ASSERT(mbedtls_psa_crypto_configure_entropy_sources( - custom_entropy_init, mbedtls_entropy_free)); - - TEST_EQUAL(psa_crypto_init(), expected_init_status); - if (expected_init_status != PSA_SUCCESS) { - goto exit; - } - - PSA_ASSERT(psa_generate_random(random, sizeof(random))); - -exit: - mbedtls_free(seed); - PSA_DONE(); -} -/* END_CASE */