1 | /* |
2 | * Platform-specific and custom entropy polling functions |
3 | * |
4 | * Copyright The Mbed TLS Contributors |
5 | * SPDX-License-Identifier: Apache-2.0 |
6 | * |
7 | * Licensed under the Apache License, Version 2.0 (the "License"); you may |
8 | * not use this file except in compliance with the License. |
9 | * You may obtain a copy of the License at |
10 | * |
11 | * http://www.apache.org/licenses/LICENSE-2.0 |
12 | * |
13 | * Unless required by applicable law or agreed to in writing, software |
14 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
15 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | * See the License for the specific language governing permissions and |
17 | * limitations under the License. |
18 | */ |
19 | |
20 | #if defined(__linux__) && !defined(_GNU_SOURCE) |
21 | /* Ensure that syscall() is available even when compiling with -std=c99 */ |
22 | #define _GNU_SOURCE |
23 | #endif |
24 | |
25 | #include "common.h" |
26 | |
27 | #include <string.h> |
28 | |
29 | #if defined(MBEDTLS_ENTROPY_C) |
30 | |
31 | #include "mbedtls/entropy.h" |
32 | #include "mbedtls/entropy_poll.h" |
33 | #include "mbedtls/error.h" |
34 | |
35 | #if defined(MBEDTLS_TIMING_C) |
36 | #include "mbedtls/timing.h" |
37 | #endif |
38 | #if defined(MBEDTLS_HAVEGE_C) |
39 | #include "mbedtls/havege.h" |
40 | #endif |
41 | #include "mbedtls/platform.h" |
42 | |
43 | #if !defined(MBEDTLS_NO_PLATFORM_ENTROPY) |
44 | |
45 | #if !defined(unix) && !defined(__unix__) && !defined(__unix) && \ |
46 | !defined(__APPLE__) && !defined(_WIN32) && !defined(__QNXNTO__) && \ |
47 | !defined(__HAIKU__) && !defined(__midipix__) |
48 | #error \ |
49 | "Platform entropy sources only work on Unix and Windows, see MBEDTLS_NO_PLATFORM_ENTROPY in config.h" |
50 | #endif |
51 | |
52 | #if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) |
53 | |
54 | #if !defined(_WIN32_WINNT) |
55 | #define _WIN32_WINNT 0x0400 |
56 | #endif |
57 | #include <windows.h> |
58 | #include <wincrypt.h> |
59 | |
60 | int mbedtls_platform_entropy_poll(void *data, unsigned char *output, size_t len, |
61 | size_t *olen) |
62 | { |
63 | HCRYPTPROV provider; |
64 | ((void) data); |
65 | *olen = 0; |
66 | |
67 | if (CryptAcquireContext(&provider, NULL, NULL, |
68 | PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) == FALSE) { |
69 | return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; |
70 | } |
71 | |
72 | if (CryptGenRandom(provider, (DWORD) len, output) == FALSE) { |
73 | CryptReleaseContext(provider, 0); |
74 | return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; |
75 | } |
76 | |
77 | CryptReleaseContext(provider, 0); |
78 | *olen = len; |
79 | |
80 | return 0; |
81 | } |
82 | #else /* _WIN32 && !EFIX64 && !EFI32 */ |
83 | |
84 | /* |
85 | * Test for Linux getrandom() support. |
86 | * Since there is no wrapper in the libc yet, use the generic syscall wrapper |
87 | * available in GNU libc and compatible libc's (eg uClibc). |
88 | */ |
89 | #if ((defined(__linux__) && defined(__GLIBC__)) || defined(__midipix__)) |
90 | #include <unistd.h> |
91 | #include <sys/syscall.h> |
92 | #if defined(SYS_getrandom) |
93 | #define HAVE_GETRANDOM |
94 | #include <errno.h> |
95 | |
96 | static int getrandom_wrapper(void *buf, size_t buflen, unsigned int flags) |
97 | { |
98 | /* MemSan cannot understand that the syscall writes to the buffer */ |
99 | #if defined(__has_feature) |
100 | #if __has_feature(memory_sanitizer) |
101 | memset(buf, 0, buflen); |
102 | #endif |
103 | #endif |
104 | return syscall(SYS_getrandom, buf, buflen, flags); |
105 | } |
106 | #endif /* SYS_getrandom */ |
107 | #endif /* __linux__ || __midipix__ */ |
108 | |
109 | #if defined(__FreeBSD__) || defined(__DragonFly__) |
110 | #include <sys/param.h> |
111 | #if (defined(__FreeBSD__) && __FreeBSD_version >= 1200000) || \ |
112 | (defined(__DragonFly__) && __DragonFly_version >= 500700) |
113 | #include <errno.h> |
114 | #include <sys/random.h> |
115 | #define HAVE_GETRANDOM |
116 | static int getrandom_wrapper(void *buf, size_t buflen, unsigned int flags) |
117 | { |
118 | return getrandom(buf, buflen, flags); |
119 | } |
120 | #endif /* (__FreeBSD__ && __FreeBSD_version >= 1200000) || |
121 | (__DragonFly__ && __DragonFly_version >= 500700) */ |
122 | #endif /* __FreeBSD__ || __DragonFly__ */ |
123 | |
124 | /* |
125 | * Some BSD systems provide KERN_ARND. |
126 | * This is equivalent to reading from /dev/urandom, only it doesn't require an |
127 | * open file descriptor, and provides up to 256 bytes per call (basically the |
128 | * same as getentropy(), but with a longer history). |
129 | * |
130 | * Documentation: https://netbsd.gw.com/cgi-bin/man-cgi?sysctl+7 |
131 | */ |
132 | #if (defined(__FreeBSD__) || defined(__NetBSD__)) && !defined(HAVE_GETRANDOM) |
133 | #include <sys/param.h> |
134 | #include <sys/sysctl.h> |
135 | #if defined(KERN_ARND) |
136 | #define HAVE_SYSCTL_ARND |
137 | |
138 | static int sysctl_arnd_wrapper(unsigned char *buf, size_t buflen) |
139 | { |
140 | int name[2]; |
141 | size_t len; |
142 | |
143 | name[0] = CTL_KERN; |
144 | name[1] = KERN_ARND; |
145 | |
146 | while (buflen > 0) { |
147 | len = buflen > 256 ? 256 : buflen; |
148 | if (sysctl(name, 2, buf, &len, NULL, 0) == -1) { |
149 | return -1; |
150 | } |
151 | buflen -= len; |
152 | buf += len; |
153 | } |
154 | return 0; |
155 | } |
156 | #endif /* KERN_ARND */ |
157 | #endif /* __FreeBSD__ || __NetBSD__ */ |
158 | |
159 | #include <stdio.h> |
160 | |
161 | int mbedtls_platform_entropy_poll(void *data, |
162 | unsigned char *output, size_t len, size_t *olen) |
163 | { |
164 | FILE *file; |
165 | size_t read_len; |
166 | int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; |
167 | ((void) data); |
168 | |
169 | #if defined(HAVE_GETRANDOM) |
170 | ret = getrandom_wrapper(output, len, 0); |
171 | if (ret >= 0) { |
172 | *olen = ret; |
173 | return 0; |
174 | } else if (errno != ENOSYS) { |
175 | return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; |
176 | } |
177 | /* Fall through if the system call isn't known. */ |
178 | #else |
179 | ((void) ret); |
180 | #endif /* HAVE_GETRANDOM */ |
181 | |
182 | #if defined(HAVE_SYSCTL_ARND) |
183 | ((void) file); |
184 | ((void) read_len); |
185 | if (sysctl_arnd_wrapper(output, len) == -1) { |
186 | return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; |
187 | } |
188 | *olen = len; |
189 | return 0; |
190 | #else |
191 | |
192 | *olen = 0; |
193 | |
194 | file = fopen("/dev/urandom" , "rb" ); |
195 | if (file == NULL) { |
196 | return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; |
197 | } |
198 | |
199 | read_len = fread(output, 1, len, file); |
200 | if (read_len != len) { |
201 | fclose(file); |
202 | return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; |
203 | } |
204 | |
205 | fclose(file); |
206 | *olen = len; |
207 | |
208 | return 0; |
209 | #endif /* HAVE_SYSCTL_ARND */ |
210 | } |
211 | #endif /* _WIN32 && !EFIX64 && !EFI32 */ |
212 | #endif /* !MBEDTLS_NO_PLATFORM_ENTROPY */ |
213 | |
214 | #if defined(MBEDTLS_TEST_NULL_ENTROPY) |
215 | int mbedtls_null_entropy_poll(void *data, |
216 | unsigned char *output, size_t len, size_t *olen) |
217 | { |
218 | ((void) data); |
219 | ((void) output); |
220 | |
221 | *olen = 0; |
222 | if (len < sizeof(unsigned char)) { |
223 | return 0; |
224 | } |
225 | |
226 | output[0] = 0; |
227 | *olen = sizeof(unsigned char); |
228 | return 0; |
229 | } |
230 | #endif |
231 | |
232 | #if defined(MBEDTLS_TIMING_C) |
233 | int mbedtls_hardclock_poll(void *data, |
234 | unsigned char *output, size_t len, size_t *olen) |
235 | { |
236 | unsigned long timer = mbedtls_timing_hardclock(); |
237 | ((void) data); |
238 | *olen = 0; |
239 | |
240 | if (len < sizeof(unsigned long)) { |
241 | return 0; |
242 | } |
243 | |
244 | memcpy(output, &timer, sizeof(unsigned long)); |
245 | *olen = sizeof(unsigned long); |
246 | |
247 | return 0; |
248 | } |
249 | #endif /* MBEDTLS_TIMING_C */ |
250 | |
251 | #if defined(MBEDTLS_HAVEGE_C) |
252 | int mbedtls_havege_poll(void *data, |
253 | unsigned char *output, size_t len, size_t *olen) |
254 | { |
255 | mbedtls_havege_state *hs = (mbedtls_havege_state *) data; |
256 | *olen = 0; |
257 | |
258 | if (mbedtls_havege_random(hs, output, len) != 0) { |
259 | return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; |
260 | } |
261 | |
262 | *olen = len; |
263 | |
264 | return 0; |
265 | } |
266 | #endif /* MBEDTLS_HAVEGE_C */ |
267 | |
268 | #if defined(MBEDTLS_ENTROPY_NV_SEED) |
269 | int mbedtls_nv_seed_poll(void *data, |
270 | unsigned char *output, size_t len, size_t *olen) |
271 | { |
272 | unsigned char buf[MBEDTLS_ENTROPY_BLOCK_SIZE]; |
273 | size_t use_len = MBEDTLS_ENTROPY_BLOCK_SIZE; |
274 | ((void) data); |
275 | |
276 | memset(buf, 0, MBEDTLS_ENTROPY_BLOCK_SIZE); |
277 | |
278 | if (mbedtls_nv_seed_read(buf, MBEDTLS_ENTROPY_BLOCK_SIZE) < 0) { |
279 | return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; |
280 | } |
281 | |
282 | if (len < use_len) { |
283 | use_len = len; |
284 | } |
285 | |
286 | memcpy(output, buf, use_len); |
287 | *olen = use_len; |
288 | |
289 | return 0; |
290 | } |
291 | #endif /* MBEDTLS_ENTROPY_NV_SEED */ |
292 | |
293 | #endif /* MBEDTLS_ENTROPY_C */ |
294 | |