From 5093f0841576ffb0fb20b7d8b0befd268f1f0ed7 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 29 Jan 2026 13:03:42 +0100 Subject: [PATCH] New API psa_random_deplete(): force a reseed on the next RNG query In some scenarios, application or integration code knows that the random generator should be reseeded, but the reseed cannot or must not happen immediately and there is no way to report errors. In such scenarios, users can call the new function `psa_random_deplete()`, which just marks the DRBG as needing a reseed. This change requires DRBG modules to treat `reseed_counter == reseed_interval` as a condition that requires a reseed. Historically they reseeded when `reseed_counter > reseed_interval`, but that made it impossible to require a reseed when `reseed_interval == MAX_INT`. Note that this edge case is not tested. Signed-off-by: Gilles Peskine --- ChangeLog.d/rng-cloning.txt | 5 ++-- include/psa/crypto_extra.h | 28 +++++++++++++++++ library/psa_crypto.c | 19 ++++++++++++ library/psa_crypto_random_impl.h | 12 ++++++++ .../suites/test_suite_psa_crypto_entropy.data | 3 ++ .../test_suite_psa_crypto_entropy.function | 30 +++++++++++++++++++ 6 files changed, 95 insertions(+), 2 deletions(-) diff --git a/ChangeLog.d/rng-cloning.txt b/ChangeLog.d/rng-cloning.txt index 3c2d63db1a..1539b50889 100644 --- a/ChangeLog.d/rng-cloning.txt +++ b/ChangeLog.d/rng-cloning.txt @@ -1,3 +1,4 @@ Features - * Applications can use the new function psa_random_reseed() to - request an immediate reseed of the PSA random generator. + * 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. diff --git a/include/psa/crypto_extra.h b/include/psa/crypto_extra.h index 7d0b01ca4f..ec8b126d32 100644 --- a/include/psa/crypto_extra.h +++ b/include/psa/crypto_extra.h @@ -581,6 +581,34 @@ psa_status_t mbedtls_psa_external_get_random( */ 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. + * This advantage of this function is that it does not fail unless the + * system is 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. + * + * \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); + /**@}*/ /** \defgroup psa_builtin_keys Built-in keys diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 7951268186..cae7a2bd7d 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -8023,6 +8023,25 @@ psa_status_t psa_random_reseed(const uint8_t *perso, size_t perso_size) #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_generate_random(uint8_t *output_external, size_t output_size) { diff --git a/library/psa_crypto_random_impl.h b/library/psa_crypto_random_impl.h index f342845e46..0078df7453 100644 --- a/library/psa_crypto_random_impl.h +++ b/library/psa_crypto_random_impl.h @@ -145,6 +145,18 @@ static inline int mbedtls_psa_drbg_reseed(mbedtls_psa_drbg_context_t *drbg_ctx, #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; +} + #endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ #endif /* PSA_CRYPTO_RANDOM_IMPL_H */ diff --git a/tests/suites/test_suite_psa_crypto_entropy.data b/tests/suites/test_suite_psa_crypto_entropy.data index e5f7243388..3f71f4490e 100644 --- a/tests/suites/test_suite_psa_crypto_entropy.data +++ b/tests/suites/test_suite_psa_crypto_entropy.data @@ -50,6 +50,9 @@ reseed_basic: Explicit reseed: entropy consumption reseed_consumption: +Deplete: entropy consumption +deplete_consumption: + Explicit reseed: uniqueness tests (0 = 0) reseed_uniqueness:"":"" diff --git a/tests/suites/test_suite_psa_crypto_entropy.function b/tests/suites/test_suite_psa_crypto_entropy.function index 696ee0b7de..71c6e90d2f 100644 --- a/tests/suites/test_suite_psa_crypto_entropy.function +++ b/tests/suites/test_suite_psa_crypto_entropy.function @@ -326,6 +326,7 @@ void reseed_basic() 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(); @@ -334,9 +335,13 @@ void reseed_basic() 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: @@ -389,6 +394,30 @@ exit: } /* 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 */ void reseed_uniqueness(data_t *perso1, data_t *perso2) { @@ -472,6 +501,7 @@ void external_rng_failure_generate() 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); mbedtls_test_disable_insecure_external_rng(); TEST_EQUAL(PSA_ERROR_INSUFFICIENT_ENTROPY,