/src/mbedtls/library/entropy_poll.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Platform-specific and custom entropy polling functions |
3 | | * |
4 | | * Copyright The Mbed TLS Contributors |
5 | | * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later |
6 | | */ |
7 | | |
8 | | #if defined(__linux__) || defined(__midipix__) |
9 | | /* Ensure that syscall() is available even when compiling with -std=c99 */ |
10 | | #if !defined(_GNU_SOURCE) |
11 | | #define _GNU_SOURCE |
12 | | #endif |
13 | | #endif |
14 | | |
15 | | #include "common.h" |
16 | | |
17 | | #include <string.h> |
18 | | |
19 | | #if defined(MBEDTLS_ENTROPY_C) |
20 | | |
21 | | #include "mbedtls/entropy.h" |
22 | | #include "entropy_poll.h" |
23 | | #include "mbedtls/error.h" |
24 | | |
25 | | #if defined(MBEDTLS_TIMING_C) |
26 | | #include "mbedtls/timing.h" |
27 | | #endif |
28 | | #include "mbedtls/platform.h" |
29 | | |
30 | | #if !defined(MBEDTLS_NO_PLATFORM_ENTROPY) |
31 | | |
32 | | #if !defined(unix) && !defined(__unix__) && !defined(__unix) && \ |
33 | | !defined(__APPLE__) && !defined(_WIN32) && !defined(__QNXNTO__) && \ |
34 | | !defined(__HAIKU__) && !defined(__midipix__) && !defined(__MVS__) |
35 | | #error \ |
36 | | "Platform entropy sources only work on Unix and Windows, see MBEDTLS_NO_PLATFORM_ENTROPY in mbedtls_config.h" |
37 | | #endif |
38 | | |
39 | | #if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) |
40 | | |
41 | | #include <windows.h> |
42 | | #include <bcrypt.h> |
43 | | #include <intsafe.h> |
44 | | |
45 | | int mbedtls_platform_entropy_poll(void *data, unsigned char *output, size_t len, |
46 | | size_t *olen) |
47 | | { |
48 | | ((void) data); |
49 | | *olen = 0; |
50 | | |
51 | | /* |
52 | | * BCryptGenRandom takes ULONG for size, which is smaller than size_t on |
53 | | * 64-bit Windows platforms. Extract entropy in chunks of len (dependent |
54 | | * on ULONG_MAX) size. |
55 | | */ |
56 | | while (len != 0) { |
57 | | unsigned long ulong_bytes = |
58 | | (len > ULONG_MAX) ? ULONG_MAX : (unsigned long) len; |
59 | | |
60 | | if (!BCRYPT_SUCCESS(BCryptGenRandom(NULL, output, ulong_bytes, |
61 | | BCRYPT_USE_SYSTEM_PREFERRED_RNG))) { |
62 | | return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; |
63 | | } |
64 | | |
65 | | *olen += ulong_bytes; |
66 | | len -= ulong_bytes; |
67 | | } |
68 | | |
69 | | return 0; |
70 | | } |
71 | | #else /* _WIN32 && !EFIX64 && !EFI32 */ |
72 | | |
73 | | /* |
74 | | * Test for Linux getrandom() support. |
75 | | * Since there is no wrapper in the libc yet, use the generic syscall wrapper |
76 | | * available in GNU libc and compatible libc's (eg uClibc). |
77 | | */ |
78 | | #if ((defined(__linux__) && defined(__GLIBC__)) || defined(__midipix__)) |
79 | | #include <unistd.h> |
80 | | #include <sys/syscall.h> |
81 | | #if defined(SYS_getrandom) |
82 | | #define HAVE_GETRANDOM |
83 | | #include <errno.h> |
84 | | |
85 | | static int getrandom_wrapper(void *buf, size_t buflen, unsigned int flags) |
86 | 10 | { |
87 | | /* MemSan cannot understand that the syscall writes to the buffer */ |
88 | 10 | #if defined(__has_feature) |
89 | | #if __has_feature(memory_sanitizer) |
90 | | memset(buf, 0, buflen); |
91 | | #endif |
92 | 10 | #endif |
93 | 10 | return (int) syscall(SYS_getrandom, buf, buflen, flags); |
94 | 10 | } |
95 | | #endif /* SYS_getrandom */ |
96 | | #endif /* __linux__ || __midipix__ */ |
97 | | |
98 | | #if defined(__FreeBSD__) || defined(__DragonFly__) |
99 | | #include <sys/param.h> |
100 | | #if (defined(__FreeBSD__) && __FreeBSD_version >= 1200000) || \ |
101 | | (defined(__DragonFly__) && __DragonFly_version >= 500700) |
102 | | #include <errno.h> |
103 | | #include <sys/random.h> |
104 | | #define HAVE_GETRANDOM |
105 | | static int getrandom_wrapper(void *buf, size_t buflen, unsigned int flags) |
106 | | { |
107 | | return (int) getrandom(buf, buflen, flags); |
108 | | } |
109 | | #endif /* (__FreeBSD__ && __FreeBSD_version >= 1200000) || |
110 | | (__DragonFly__ && __DragonFly_version >= 500700) */ |
111 | | #endif /* __FreeBSD__ || __DragonFly__ */ |
112 | | |
113 | | /* |
114 | | * Some BSD systems provide KERN_ARND. |
115 | | * This is equivalent to reading from /dev/urandom, only it doesn't require an |
116 | | * open file descriptor, and provides up to 256 bytes per call (basically the |
117 | | * same as getentropy(), but with a longer history). |
118 | | * |
119 | | * Documentation: https://netbsd.gw.com/cgi-bin/man-cgi?sysctl+7 |
120 | | */ |
121 | | #if (defined(__FreeBSD__) || defined(__NetBSD__)) && !defined(HAVE_GETRANDOM) |
122 | | #include <sys/param.h> |
123 | | #include <sys/sysctl.h> |
124 | | #if defined(KERN_ARND) |
125 | | #define HAVE_SYSCTL_ARND |
126 | | |
127 | | static int sysctl_arnd_wrapper(unsigned char *buf, size_t buflen) |
128 | | { |
129 | | int name[2]; |
130 | | size_t len; |
131 | | |
132 | | name[0] = CTL_KERN; |
133 | | name[1] = KERN_ARND; |
134 | | |
135 | | while (buflen > 0) { |
136 | | len = buflen > 256 ? 256 : buflen; |
137 | | if (sysctl(name, 2, buf, &len, NULL, 0) == -1) { |
138 | | return -1; |
139 | | } |
140 | | buflen -= len; |
141 | | buf += len; |
142 | | } |
143 | | return 0; |
144 | | } |
145 | | #endif /* KERN_ARND */ |
146 | | #endif /* __FreeBSD__ || __NetBSD__ */ |
147 | | |
148 | | #include <stdio.h> |
149 | | |
150 | | int mbedtls_platform_entropy_poll(void *data, |
151 | | unsigned char *output, size_t len, size_t *olen) |
152 | 10 | { |
153 | 10 | FILE *file; |
154 | 10 | size_t read_len; |
155 | 10 | int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; |
156 | 10 | ((void) data); |
157 | | |
158 | 10 | #if defined(HAVE_GETRANDOM) |
159 | 10 | ret = getrandom_wrapper(output, len, 0); |
160 | 10 | if (ret >= 0) { |
161 | 10 | *olen = (size_t) ret; |
162 | 10 | return 0; |
163 | 10 | } else if (errno != ENOSYS) { |
164 | 0 | return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; |
165 | 0 | } |
166 | | /* Fall through if the system call isn't known. */ |
167 | | #else |
168 | | ((void) ret); |
169 | | #endif /* HAVE_GETRANDOM */ |
170 | | |
171 | | #if defined(HAVE_SYSCTL_ARND) |
172 | | ((void) file); |
173 | | ((void) read_len); |
174 | | if (sysctl_arnd_wrapper(output, len) == -1) { |
175 | | return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; |
176 | | } |
177 | | *olen = len; |
178 | | return 0; |
179 | | #else |
180 | | |
181 | 0 | *olen = 0; |
182 | |
|
183 | 0 | file = fopen("/dev/urandom", "rb"); |
184 | 0 | if (file == NULL) { |
185 | 0 | return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; |
186 | 0 | } |
187 | | |
188 | | /* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */ |
189 | 0 | mbedtls_setbuf(file, NULL); |
190 | |
|
191 | 0 | read_len = fread(output, 1, len, file); |
192 | 0 | if (read_len != len) { |
193 | 0 | fclose(file); |
194 | 0 | return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; |
195 | 0 | } |
196 | | |
197 | 0 | fclose(file); |
198 | 0 | *olen = len; |
199 | |
|
200 | 0 | return 0; |
201 | 0 | #endif /* HAVE_SYSCTL_ARND */ |
202 | 0 | } |
203 | | #endif /* _WIN32 && !EFIX64 && !EFI32 */ |
204 | | #endif /* !MBEDTLS_NO_PLATFORM_ENTROPY */ |
205 | | |
206 | | #if defined(MBEDTLS_ENTROPY_NV_SEED) |
207 | | int mbedtls_nv_seed_poll(void *data, |
208 | | unsigned char *output, size_t len, size_t *olen) |
209 | | { |
210 | | unsigned char buf[MBEDTLS_ENTROPY_BLOCK_SIZE]; |
211 | | size_t use_len = MBEDTLS_ENTROPY_BLOCK_SIZE; |
212 | | ((void) data); |
213 | | |
214 | | memset(buf, 0, MBEDTLS_ENTROPY_BLOCK_SIZE); |
215 | | |
216 | | if (mbedtls_nv_seed_read(buf, MBEDTLS_ENTROPY_BLOCK_SIZE) < 0) { |
217 | | return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; |
218 | | } |
219 | | |
220 | | if (len < use_len) { |
221 | | use_len = len; |
222 | | } |
223 | | |
224 | | memcpy(output, buf, use_len); |
225 | | *olen = use_len; |
226 | | |
227 | | return 0; |
228 | | } |
229 | | #endif /* MBEDTLS_ENTROPY_NV_SEED */ |
230 | | |
231 | | #endif /* MBEDTLS_ENTROPY_C */ |