/* * 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" #include "threading_internal.h" #if defined(MBEDTLS_PSA_INJECT_ENTROPY) #include "entropy_poll.h" #endif #if defined(MBEDTLS_PLATFORM_IS_UNIXLIKE) /* For getpid(), for fork protection */ #include #if defined(MBEDTLS_HAVE_TIME) #include #else /* For gettimeofday(), for fork protection without actual entropy */ #include #endif #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); #if defined(MBEDTLS_PLATFORM_IS_UNIXLIKE) rng->pid = getpid(); #endif return mbedtls_to_psa_error(ret); } #if defined(MBEDTLS_PLATFORM_IS_UNIXLIKE) static psa_status_t psa_random_internal_reseed_child( mbedtls_psa_random_context_t *rng, intmax_t pid) { /* Reseeding from actual entropy gives the child a unique RNG state * which the parent process cannot predict, and wipes the * parent's RNG state from the child. * * However, in some library configurations, there is no actual * entropy source, only a nonvolatile seed (MBEDTLS_ENTROPY_NV_SEED * enabled and no actual entropy source enabled). In such a * configuration, the reseed operation is deterministic and * always injects the same content, so with the DRBG reseed * process alone, for example, two child processes forked in * close sequence would end up with the same RNG state. * To avoid this, we use a personalization string that has a high * likelihood of being unique. This way, the child has a unique state. * The parent can predict the child's RNG state until the next time * it reseeds or generates some random output, but that's * unavoidable in the absence of actual entropy. */ struct { /* Using the PID mostly guarantees that each child gets a * unique state. */ /* Use intmax_t, not pid_t, because some Unix-like platforms * don't define pid_t, or more likely nowadays they define * pid_t but only with certain platform macros which might not * be the exact ones we use. In practice, this only costs * a couple of instructions to pass and compare two words * rather than one. */ intmax_t pid; /* In case an old child had died and its PID is reused for * a new child of the same process, also include the time. */ #if defined(MBEDTLS_HAVE_TIME) mbedtls_ms_time_t now; #else struct timeval now; #endif } perso; memset(&perso, 0, sizeof(perso)); perso.pid = pid; #if defined(MBEDTLS_HAVE_TIME) perso.now = mbedtls_ms_time(); #else /* We don't have mbedtls_ms_time(), but the platform has getpid(). * Use gettimeofday(), which is a classic Unix function. Modern POSIX * has stopped requiring gettimeofday() (in favor of clock_gettime()), * but this is fallback code for restricted configurations, so it's * more likely to be used on embedded platforms that only have a subset * of Unix APIs and are more likely to have the classic gettimeofday(). */ if (gettimeofday(&perso.now, NULL) == -1) { return PSA_ERROR_INSUFFICIENT_ENTROPY; } #endif int ret = mbedtls_psa_drbg_reseed(&rng->drbg, (unsigned char *) &perso, sizeof(perso)); return mbedtls_to_psa_error(ret); } #endif /* MBEDTLS_PLATFORM_IS_UNIXLIKE */ psa_status_t psa_random_internal_generate( mbedtls_psa_random_context_t *rng, uint8_t *output, size_t output_size) { #if defined(MBEDTLS_PLATFORM_IS_UNIXLIKE) intmax_t pid = getpid(); if (pid != rng->pid) { /* This is a (grand...)child of the original process, but * we inherited the RNG state from our parent. We must reseed! */ #if defined(MBEDTLS_THREADING_C) mbedtls_mutex_lock(&mbedtls_threading_psa_rngdata_mutex); #endif /* defined(MBEDTLS_THREADING_C) */ psa_status_t status = psa_random_internal_reseed_child(rng, pid); if (status == PSA_SUCCESS) { rng->pid = pid; } #if defined(MBEDTLS_THREADING_C) mbedtls_mutex_unlock(&mbedtls_threading_psa_rngdata_mutex); #endif /* defined(MBEDTLS_THREADING_C) */ if (status != PSA_SUCCESS) { return status; } } #endif /* MBEDTLS_PLATFORM_IS_UNIXLIKE */ 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 */