Merge pull request #1497 from gilles-peskine-arm/psa-rng-reseed-3.6

Backport 3.6: API to reseed the PSA RNG
This commit is contained in:
Bence Szépkúti
2026-03-16 13:53:53 +01:00
committed by GitHub
19 changed files with 1132 additions and 316 deletions
+14
View File
@@ -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
@@ -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.
*/
+1 -2
View File
@@ -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,
+150 -1
View File
@@ -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
+1
View File
@@ -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
+1
View File
@@ -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 \
+2 -2
View File
@@ -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;
+2 -2
View File
@@ -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;
}
+82 -46
View File
@@ -37,6 +37,7 @@
* stored keys. */
#include "psa_crypto_storage.h"
#include "psa_crypto_random.h"
#include "psa_crypto_random_impl.h"
#include <stdlib.h>
@@ -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)
{
+82
View File
@@ -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 */
+72
View File
@@ -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 <psa/crypto.h>
#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 */
+57
View File
@@ -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 */
+1 -1
View File
@@ -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!
+3
View File
@@ -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:
+23
View File
@@ -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)
{
@@ -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:
@@ -4,9 +4,151 @@
#include <psa/crypto.h>
/* 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)));
@@ -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:
@@ -2,12 +2,9 @@
#include <stdint.h>
#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 */