| 1 | /* |
| 2 | * PSA crypto random generator. |
| 3 | */ |
| 4 | /* |
| 5 | * Copyright The Mbed TLS Contributors |
| 6 | * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later |
| 7 | */ |
| 8 | |
| 9 | #include "common.h" |
| 10 | |
| 11 | #if defined(MBEDTLS_PSA_CRYPTO_C) && !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) |
| 12 | |
| 13 | #include "psa_crypto_core.h" |
| 14 | #include "psa_crypto_random.h" |
| 15 | #include "psa_crypto_random_impl.h" |
| 16 | #include "threading_internal.h" |
| 17 | |
| 18 | #if defined(MBEDTLS_PSA_INJECT_ENTROPY) |
| 19 | #include "entropy_poll.h" |
| 20 | #endif |
| 21 | |
| 22 | #if defined(MBEDTLS_PLATFORM_IS_UNIXLIKE) |
| 23 | /* For getpid(), for fork protection */ |
| 24 | #include <unistd.h> |
| 25 | #if defined(MBEDTLS_HAVE_TIME) |
| 26 | #include <mbedtls/platform_time.h> |
| 27 | #else |
| 28 | /* For gettimeofday(), for fork protection without actual entropy */ |
| 29 | #include <sys/time.h> |
| 30 | #endif |
| 31 | #endif |
| 32 | |
| 33 | void psa_random_internal_init(mbedtls_psa_random_context_t *rng) |
| 34 | { |
| 35 | /* Set default configuration if |
| 36 | * mbedtls_psa_crypto_configure_entropy_sources() hasn't been called. */ |
| 37 | if (rng->entropy_init == NULL) { |
| 38 | rng->entropy_init = mbedtls_entropy_init; |
| 39 | } |
| 40 | if (rng->entropy_free == NULL) { |
| 41 | rng->entropy_free = mbedtls_entropy_free; |
| 42 | } |
| 43 | |
| 44 | rng->entropy_init(&rng->entropy); |
| 45 | #if defined(MBEDTLS_PSA_INJECT_ENTROPY) && \ |
| 46 | defined(MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES) |
| 47 | /* The PSA entropy injection feature depends on using NV seed as an entropy |
| 48 | * source. Add NV seed as an entropy source for PSA entropy injection. */ |
| 49 | mbedtls_entropy_add_source(&rng->entropy, |
| 50 | mbedtls_nv_seed_poll, NULL, |
| 51 | MBEDTLS_ENTROPY_BLOCK_SIZE, |
| 52 | MBEDTLS_ENTROPY_SOURCE_STRONG); |
| 53 | #endif |
| 54 | |
| 55 | mbedtls_psa_drbg_init(&rng->drbg); |
| 56 | } |
| 57 | |
| 58 | void psa_random_internal_free(mbedtls_psa_random_context_t *rng) |
| 59 | { |
| 60 | mbedtls_psa_drbg_free(&rng->drbg); |
| 61 | rng->entropy_free(&rng->entropy); |
| 62 | } |
| 63 | psa_status_t psa_random_internal_seed(mbedtls_psa_random_context_t *rng) |
| 64 | { |
| 65 | const unsigned char drbg_seed[] = "PSA"; |
| 66 | int ret = mbedtls_psa_drbg_seed(&rng->drbg, &rng->entropy, |
| 67 | drbg_seed, sizeof(drbg_seed) - 1); |
| 68 | #if defined(MBEDTLS_PLATFORM_IS_UNIXLIKE) |
| 69 | rng->pid = getpid(); |
| 70 | #endif |
| 71 | return mbedtls_to_psa_error(ret); |
| 72 | } |
| 73 | |
| 74 | #if defined(MBEDTLS_PLATFORM_IS_UNIXLIKE) |
| 75 | static psa_status_t psa_random_internal_reseed_child( |
| 76 | mbedtls_psa_random_context_t *rng, |
| 77 | intmax_t pid) |
| 78 | { |
| 79 | /* Reseeding from actual entropy gives the child a unique RNG state |
| 80 | * which the parent process cannot predict, and wipes the |
| 81 | * parent's RNG state from the child. |
| 82 | * |
| 83 | * However, in some library configurations, there is no actual |
| 84 | * entropy source, only a nonvolatile seed (MBEDTLS_ENTROPY_NV_SEED |
| 85 | * enabled and no actual entropy source enabled). In such a |
| 86 | * configuration, the reseed operation is deterministic and |
| 87 | * always injects the same content, so with the DRBG reseed |
| 88 | * process alone, for example, two child processes forked in |
| 89 | * close sequence would end up with the same RNG state. |
| 90 | |
| 91 | * To avoid this, we use a personalization string that has a high |
| 92 | * likelihood of being unique. This way, the child has a unique state. |
| 93 | * The parent can predict the child's RNG state until the next time |
| 94 | * it reseeds or generates some random output, but that's |
| 95 | * unavoidable in the absence of actual entropy. |
| 96 | */ |
| 97 | struct { |
| 98 | /* Using the PID mostly guarantees that each child gets a |
| 99 | * unique state. */ |
| 100 | /* Use intmax_t, not pid_t, because some Unix-like platforms |
| 101 | * don't define pid_t, or more likely nowadays they define |
| 102 | * pid_t but only with certain platform macros which might not |
| 103 | * be the exact ones we use. In practice, this only costs |
| 104 | * a couple of instructions to pass and compare two words |
| 105 | * rather than one. |
| 106 | */ |
| 107 | intmax_t pid; |
| 108 | /* In case an old child had died and its PID is reused for |
| 109 | * a new child of the same process, also include the time. */ |
| 110 | #if defined(MBEDTLS_HAVE_TIME) |
| 111 | mbedtls_ms_time_t now; |
| 112 | #else |
| 113 | struct timeval now; |
| 114 | #endif |
| 115 | } perso; |
| 116 | memset(&perso, 0, sizeof(perso)); |
| 117 | perso.pid = pid; |
| 118 | #if defined(MBEDTLS_HAVE_TIME) |
| 119 | perso.now = mbedtls_ms_time(); |
| 120 | #else |
| 121 | /* We don't have mbedtls_ms_time(), but the platform has getpid(). |
| 122 | * Use gettimeofday(), which is a classic Unix function. Modern POSIX |
| 123 | * has stopped requiring gettimeofday() (in favor of clock_gettime()), |
| 124 | * but this is fallback code for restricted configurations, so it's |
| 125 | * more likely to be used on embedded platforms that only have a subset |
| 126 | * of Unix APIs and are more likely to have the classic gettimeofday(). */ |
| 127 | if (gettimeofday(&perso.now, NULL) == -1) { |
| 128 | return PSA_ERROR_INSUFFICIENT_ENTROPY; |
| 129 | } |
| 130 | #endif |
| 131 | int ret = mbedtls_psa_drbg_reseed(&rng->drbg, |
| 132 | (unsigned char *) &perso, sizeof(perso)); |
| 133 | return mbedtls_to_psa_error(ret); |
| 134 | } |
| 135 | #endif /* MBEDTLS_PLATFORM_IS_UNIXLIKE */ |
| 136 | |
| 137 | psa_status_t psa_random_internal_generate( |
| 138 | mbedtls_psa_random_context_t *rng, |
| 139 | uint8_t *output, size_t output_size) |
| 140 | { |
| 141 | #if defined(MBEDTLS_PLATFORM_IS_UNIXLIKE) |
| 142 | intmax_t pid = getpid(); |
| 143 | if (pid != rng->pid) { |
| 144 | /* This is a (grand...)child of the original process, but |
| 145 | * we inherited the RNG state from our parent. We must reseed! */ |
| 146 | #if defined(MBEDTLS_THREADING_C) |
| 147 | mbedtls_mutex_lock(&mbedtls_threading_psa_rngdata_mutex); |
| 148 | #endif /* defined(MBEDTLS_THREADING_C) */ |
| 149 | psa_status_t status = psa_random_internal_reseed_child(rng, pid); |
| 150 | if (status == PSA_SUCCESS) { |
| 151 | rng->pid = pid; |
| 152 | } |
| 153 | #if defined(MBEDTLS_THREADING_C) |
| 154 | mbedtls_mutex_unlock(&mbedtls_threading_psa_rngdata_mutex); |
| 155 | #endif /* defined(MBEDTLS_THREADING_C) */ |
| 156 | if (status != PSA_SUCCESS) { |
| 157 | return status; |
| 158 | } |
| 159 | } |
| 160 | #endif /* MBEDTLS_PLATFORM_IS_UNIXLIKE */ |
| 161 | |
| 162 | while (output_size > 0) { |
| 163 | size_t request_size = |
| 164 | (output_size > MBEDTLS_PSA_RANDOM_MAX_REQUEST ? |
| 165 | MBEDTLS_PSA_RANDOM_MAX_REQUEST : |
| 166 | output_size); |
| 167 | #if defined(MBEDTLS_CTR_DRBG_C) |
| 168 | int ret = mbedtls_ctr_drbg_random(&rng->drbg, output, request_size); |
| 169 | #elif defined(MBEDTLS_HMAC_DRBG_C) |
| 170 | int ret = mbedtls_hmac_drbg_random(&rng->drbg, output, request_size); |
| 171 | #endif /* !MBEDTLS_CTR_DRBG_C && !MBEDTLS_HMAC_DRBG_C */ |
| 172 | if (ret != 0) { |
| 173 | return mbedtls_to_psa_error(ret); |
| 174 | } |
| 175 | output_size -= request_size; |
| 176 | output += request_size; |
| 177 | } |
| 178 | return PSA_SUCCESS; |
| 179 | } |
| 180 | |
| 181 | #endif /* MBEDTLS_PSA_CRYPTO_C && !MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ |
| 182 | |