New API psa_random_set_prediction_resistance()

Let applications configure prediction resistance at runtime.

Prediction resistance is always considered disabled when there is no actual
entropy source, only a nonvolatile seed.

Signed-off-by: Gilles Peskine <[email protected]>
This commit is contained in:
Gilles Peskine
2026-03-04 17:54:56 +01:00
parent 5093f08415
commit a863415205
6 changed files with 209 additions and 0 deletions
+2
View File
@@ -2,3 +2,5 @@ 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.
+45
View File
@@ -609,6 +609,51 @@ psa_status_t psa_random_reseed(const uint8_t *perso, size_t perso_size);
*/
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
+36
View File
@@ -8042,6 +8042,42 @@ psa_status_t psa_random_deplete(void)
#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)
{
+21
View File
@@ -157,6 +157,27 @@ 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 */
@@ -53,6 +53,15 @@ 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:"":""
@@ -349,6 +349,60 @@ exit:
}
/* 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.
*
@@ -418,6 +472,46 @@ exit:
}
/* 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)
{
@@ -502,6 +596,8 @@ void external_rng_failure_generate()
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,