mirror of
https://github.com/Mbed-TLS/mbedtls.git
synced 2026-04-03 02:56:55 +02:00
New function psa_random_reseed()
Explicit reseed of the PSA random generator. Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
This commit is contained in:
3
ChangeLog.d/rng-cloning.txt
Normal file
3
ChangeLog.d/rng-cloning.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
Features
|
||||
* Applications can use the new function psa_random_reseed() to
|
||||
request an immediate reseed of the PSA random generator.
|
||||
@@ -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,85 @@ 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 a 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
|
||||
* other than ones that do not use randomness (e.g. hash calculation,
|
||||
* signature verification). 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
|
||||
* library will detect it in most circumstances, so you generally do
|
||||
* not need to call this function. This detection is based on a
|
||||
* process ID (PID) change. You need to call this function in at least
|
||||
* the parent or the child process in cases where the library might not
|
||||
* observe a process ID change, such as:
|
||||
* - If the child forks another process before invoking the random
|
||||
* generator, but after the original process has died. In this case,
|
||||
* it is rare but possible for the grandchild to have the same PID
|
||||
* as the original process.
|
||||
* - When using the Linux clone() system call with the `CLONE_NEWPID`
|
||||
* flag to put the child process in its own PID namespace, and the
|
||||
* original process has PID 1.
|
||||
* - When the child is moved to a new or existing PID namespace before
|
||||
* any call to the PSA random generator, and the PID in the child's
|
||||
* namespace might match the PID of the original process.
|
||||
* - When using the Linux clone3() system call with a `set_tid` array
|
||||
* to force the PID of the new process.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* \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);
|
||||
|
||||
/**@}*/
|
||||
|
||||
/** \defgroup psa_builtin_keys Built-in keys
|
||||
|
||||
@@ -8001,6 +8001,28 @@ static psa_status_t mbedtls_psa_random_seed(mbedtls_psa_random_context_t *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_generate_random(uint8_t *output_external,
|
||||
size_t output_size)
|
||||
{
|
||||
|
||||
@@ -44,6 +44,27 @@ entropy_from_nv_seed:MBEDTLS_ENTROPY_BLOCK_SIZE - 1:PSA_ERROR_INSUFFICIENT_ENTRO
|
||||
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:
|
||||
|
||||
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:
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ typedef struct {
|
||||
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)
|
||||
@@ -113,6 +114,39 @@ static void custom_entropy_init(mbedtls_entropy_context *ctx)
|
||||
}
|
||||
}
|
||||
|
||||
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 */
|
||||
@@ -285,6 +319,130 @@ exit:
|
||||
}
|
||||
/* 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_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)));
|
||||
|
||||
mbedtls_psa_crypto_free();
|
||||
|
||||
TEST_EQUAL(psa_random_reseed(NULL, 0), 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 */
|
||||
/* 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 };
|
||||
|
||||
if (!psa_init_deterministic(3)) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* 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 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 };
|
||||
|
||||
/* Reference: no reseed */
|
||||
if (!psa_init_deterministic(3)) {
|
||||
goto exit;
|
||||
}
|
||||
PSA_ASSERT(psa_generate_random(random0, sizeof(random0)));
|
||||
mbedtls_psa_crypto_free();
|
||||
|
||||
/* Reference: no reseed, again */
|
||||
if (!psa_init_deterministic(3)) {
|
||||
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(3)) {
|
||||
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(3)) {
|
||||
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(3)) {
|
||||
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()
|
||||
{
|
||||
@@ -300,6 +458,8 @@ 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);
|
||||
|
||||
mbedtls_test_disable_insecure_external_rng();
|
||||
TEST_EQUAL(PSA_ERROR_INSUFFICIENT_ENTROPY,
|
||||
psa_generate_random(output, sizeof(output)));
|
||||
|
||||
Reference in New Issue
Block a user