v2 / thirdparty / mbedtls / library / psa_crypto_random.c
181 lines · 167 sloc · 6.46 KB · 3d9911f887ecec942f9ae2a5be02d064f233b729
Raw
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
33void 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
58void 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}
63psa_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)
75static 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
137psa_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