/src/botan/src/lib/rng/system_rng/system_rng.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * System RNG |
3 | | * (C) 2014,2015,2017,2018,2022 Jack Lloyd |
4 | | * (C) 2021 Tom Crowley |
5 | | * |
6 | | * Botan is released under the Simplified BSD License (see license.txt) |
7 | | */ |
8 | | |
9 | | #include <botan/system_rng.h> |
10 | | |
11 | | #if defined(BOTAN_TARGET_OS_HAS_WIN32) |
12 | | #define NOMINMAX 1 |
13 | | #define _WINSOCKAPI_ // stop windows.h including winsock.h |
14 | | #include <windows.h> |
15 | | #endif |
16 | | |
17 | | #if defined(BOTAN_TARGET_OS_HAS_RTLGENRANDOM) |
18 | | #include <botan/internal/dyn_load.h> |
19 | | #elif defined(BOTAN_TARGET_OS_HAS_CRYPTO_NG) |
20 | | #include <bcrypt.h> |
21 | | #elif defined(BOTAN_TARGET_OS_HAS_CCRANDOM) |
22 | | #include <CommonCrypto/CommonRandom.h> |
23 | | #elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM) |
24 | | #include <stdlib.h> |
25 | | #elif defined(BOTAN_TARGET_OS_HAS_GETRANDOM) |
26 | | #include <sys/random.h> |
27 | | #include <errno.h> |
28 | | #elif defined(BOTAN_TARGET_OS_HAS_DEV_RANDOM) |
29 | | #include <fcntl.h> |
30 | | #include <unistd.h> |
31 | | #include <errno.h> |
32 | | #endif |
33 | | |
34 | | namespace Botan { |
35 | | |
36 | | namespace { |
37 | | |
38 | | #if defined(BOTAN_TARGET_OS_HAS_RTLGENRANDOM) |
39 | | |
40 | | class System_RNG_Impl final : public RandomNumberGenerator |
41 | | { |
42 | | public: |
43 | | System_RNG_Impl() : m_advapi("advapi32.dll") |
44 | | { |
45 | | // This throws if the function is not found |
46 | | m_rtlgenrandom = m_advapi.resolve<RtlGenRandom_fptr>("SystemFunction036"); |
47 | | } |
48 | | |
49 | | System_RNG_Impl(const System_RNG_Impl& other) = delete; |
50 | | System_RNG_Impl(System_RNG_Impl&& other) = delete; |
51 | | System_RNG_Impl& operator=(const System_RNG_Impl& other) = delete; |
52 | | System_RNG_Impl& operator=(System_RNG_Impl&& other) = delete; |
53 | | |
54 | | void randomize(uint8_t buf[], size_t len) override |
55 | | { |
56 | | const size_t limit = std::numeric_limits<ULONG>::max(); |
57 | | |
58 | | uint8_t* pData = buf; |
59 | | size_t bytesLeft = len; |
60 | | while (bytesLeft > 0) |
61 | | { |
62 | | const ULONG blockSize = static_cast<ULONG>(std::min(bytesLeft, limit)); |
63 | | |
64 | | const bool success = m_rtlgenrandom(pData, blockSize) == TRUE; |
65 | | if (!success) |
66 | | { |
67 | | throw System_Error("RtlGenRandom failed"); |
68 | | } |
69 | | |
70 | | BOTAN_ASSERT(bytesLeft >= blockSize, "Block is oversized"); |
71 | | bytesLeft -= blockSize; |
72 | | pData += blockSize; |
73 | | } |
74 | | } |
75 | | |
76 | | void add_entropy(const uint8_t[], size_t) override { /* ignored */ } |
77 | | bool is_seeded() const override { return true; } |
78 | | bool accepts_input() const override { return false; } |
79 | | void clear() override { /* not possible */ } |
80 | | std::string name() const override { return "RtlGenRandom"; } |
81 | | private: |
82 | | using RtlGenRandom_fptr = BOOLEAN (NTAPI *)(PVOID, ULONG); |
83 | | |
84 | | Dynamically_Loaded_Library m_advapi; |
85 | | RtlGenRandom_fptr m_rtlgenrandom; |
86 | | }; |
87 | | |
88 | | #elif defined(BOTAN_TARGET_OS_HAS_CRYPTO_NG) |
89 | | |
90 | | class System_RNG_Impl final : public RandomNumberGenerator |
91 | | { |
92 | | public: |
93 | | System_RNG_Impl() |
94 | | { |
95 | | auto ret = ::BCryptOpenAlgorithmProvider(&m_prov, |
96 | | BCRYPT_RNG_ALGORITHM, |
97 | | MS_PRIMITIVE_PROVIDER, 0); |
98 | | if(!BCRYPT_SUCCESS(ret)) |
99 | | { |
100 | | throw System_Error("System_RNG failed to acquire crypto provider", ret); |
101 | | } |
102 | | } |
103 | | |
104 | | System_RNG_Impl(const System_RNG_Impl& other) = delete; |
105 | | System_RNG_Impl(System_RNG_Impl&& other) = delete; |
106 | | System_RNG_Impl& operator=(const System_RNG_Impl& other) = delete; |
107 | | System_RNG_Impl& operator=(System_RNG_Impl&& other) = delete; |
108 | | |
109 | | ~System_RNG_Impl() override |
110 | | { |
111 | | ::BCryptCloseAlgorithmProvider(m_prov, 0); |
112 | | } |
113 | | |
114 | | void randomize(uint8_t buf[], size_t len) override |
115 | | { |
116 | | const size_t limit = std::numeric_limits<ULONG>::max(); |
117 | | |
118 | | uint8_t* pData = buf; |
119 | | size_t bytesLeft = len; |
120 | | while (bytesLeft > 0) |
121 | | { |
122 | | const ULONG blockSize = static_cast<ULONG>(std::min(bytesLeft, limit)); |
123 | | |
124 | | auto ret = BCryptGenRandom(m_prov, static_cast<PUCHAR>(pData), blockSize, 0); |
125 | | if(!BCRYPT_SUCCESS(ret)) |
126 | | { |
127 | | throw System_Error("System_RNG call to BCryptGenRandom failed", ret); |
128 | | } |
129 | | |
130 | | BOTAN_ASSERT(bytesLeft >= blockSize, "Block is oversized"); |
131 | | bytesLeft -= blockSize; |
132 | | pData += blockSize; |
133 | | } |
134 | | } |
135 | | |
136 | | void add_entropy(const uint8_t in[], size_t length) override |
137 | | { |
138 | | /* |
139 | | There is a flag BCRYPT_RNG_USE_ENTROPY_IN_BUFFER to provide |
140 | | entropy inputs, but it is ignored in Windows 8 and later. |
141 | | */ |
142 | | } |
143 | | |
144 | | bool is_seeded() const override { return true; } |
145 | | bool accepts_input() const override { return false; } |
146 | | void clear() override { /* not possible */ } |
147 | | std::string name() const override { return "crypto_ng"; } |
148 | | private: |
149 | | BCRYPT_ALG_HANDLE m_prov; |
150 | | }; |
151 | | |
152 | | #elif defined(BOTAN_TARGET_OS_HAS_CCRANDOM) |
153 | | |
154 | | class System_RNG_Impl final : public RandomNumberGenerator |
155 | | { |
156 | | public: |
157 | | void randomize(uint8_t buf[], size_t len) override |
158 | | { |
159 | | if (::CCRandomGenerateBytes(buf, len) != kCCSuccess) |
160 | | { |
161 | | throw System_Error("System_RNG CCRandomGenerateBytes failed", errno); |
162 | | } |
163 | | } |
164 | | bool accepts_input() const override { return false; } |
165 | | void add_entropy(const uint8_t[], size_t) override { /* ignored */ } |
166 | | bool is_seeded() const override { return true; } |
167 | | void clear() override { /* not possible */ } |
168 | | std::string name() const override { return "CCRandomGenerateBytes"; } |
169 | | }; |
170 | | |
171 | | #elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM) |
172 | | |
173 | | class System_RNG_Impl final : public RandomNumberGenerator |
174 | | { |
175 | | public: |
176 | | // No constructor or destructor needed as no userland state maintained |
177 | | |
178 | | void randomize(uint8_t buf[], size_t len) override |
179 | | { |
180 | | // macOS 10.15 arc4random crashes if called with buf == nullptr && len == 0 |
181 | | // however it uses ccrng_generate internally which returns a status, ignored |
182 | | // to respect arc4random "no-fail" interface contract |
183 | | if(len > 0) |
184 | | { |
185 | | ::arc4random_buf(buf, len); |
186 | | } |
187 | | } |
188 | | |
189 | | bool accepts_input() const override { return false; } |
190 | | void add_entropy(const uint8_t[], size_t) override { /* ignored */ } |
191 | | bool is_seeded() const override { return true; } |
192 | | void clear() override { /* not possible */ } |
193 | | std::string name() const override { return "arc4random"; } |
194 | | }; |
195 | | |
196 | | #elif defined(BOTAN_TARGET_OS_HAS_GETRANDOM) |
197 | | |
198 | | class System_RNG_Impl final : public RandomNumberGenerator |
199 | | { |
200 | | public: |
201 | | // No constructor or destructor needed as no userland state maintained |
202 | | |
203 | | void randomize(uint8_t buf[], size_t len) override |
204 | | { |
205 | | const unsigned int flags = 0; |
206 | | |
207 | | while(len > 0) |
208 | | { |
209 | | const ssize_t got = ::getrandom(buf, len, flags); |
210 | | |
211 | | if(got < 0) |
212 | | { |
213 | | if(errno == EINTR) |
214 | | continue; |
215 | | throw System_Error("System_RNG getrandom failed", errno); |
216 | | } |
217 | | |
218 | | buf += got; |
219 | | len -= got; |
220 | | } |
221 | | } |
222 | | |
223 | | bool accepts_input() const override { return false; } |
224 | | void add_entropy(const uint8_t[], size_t) override { /* ignored */ } |
225 | | bool is_seeded() const override { return true; } |
226 | | void clear() override { /* not possible */ } |
227 | | std::string name() const override { return "getrandom"; } |
228 | | }; |
229 | | |
230 | | |
231 | | #elif defined(BOTAN_TARGET_OS_HAS_DEV_RANDOM) |
232 | | |
233 | | // Read a random device |
234 | | |
235 | | class System_RNG_Impl final : public RandomNumberGenerator |
236 | | { |
237 | | public: |
238 | | System_RNG_Impl() |
239 | 0 | { |
240 | | #ifndef O_NOCTTY |
241 | | #define O_NOCTTY 0 |
242 | | #endif |
243 | | |
244 | | /* |
245 | | * First open /dev/random and read one byte. On old Linux kernels |
246 | | * this blocks the RNG until we have been actually seeded. |
247 | | */ |
248 | 0 | m_fd = ::open("/dev/random", O_RDONLY | O_NOCTTY); |
249 | 0 | if(m_fd < 0) |
250 | 0 | throw System_Error("System_RNG failed to open RNG device", errno); |
251 | | |
252 | 0 | uint8_t b; |
253 | 0 | const size_t got = ::read(m_fd, &b, 1); |
254 | 0 | ::close(m_fd); |
255 | |
|
256 | 0 | if(got != 1) |
257 | 0 | throw System_Error("System_RNG failed to read blocking RNG device"); |
258 | | |
259 | 0 | m_fd = ::open("/dev/urandom", O_RDWR | O_NOCTTY); |
260 | |
|
261 | 0 | if(m_fd >= 0) |
262 | 0 | { |
263 | 0 | m_writable = true; |
264 | 0 | } |
265 | 0 | else |
266 | 0 | { |
267 | | /* |
268 | | Cannot open in read-write mode. Fall back to read-only, |
269 | | calls to add_entropy will fail, but randomize will work |
270 | | */ |
271 | 0 | m_fd = ::open("/dev/urandom", O_RDONLY | O_NOCTTY); |
272 | 0 | m_writable = false; |
273 | 0 | } |
274 | |
|
275 | 0 | if(m_fd < 0) |
276 | 0 | throw System_Error("System_RNG failed to open RNG device", errno); |
277 | 0 | } |
278 | | |
279 | | System_RNG_Impl(const System_RNG_Impl& other) = delete; |
280 | | System_RNG_Impl(System_RNG_Impl&& other) = delete; |
281 | | System_RNG_Impl& operator=(const System_RNG_Impl& other) = delete; |
282 | | System_RNG_Impl& operator=(System_RNG_Impl&& other) = delete; |
283 | | |
284 | | ~System_RNG_Impl() override |
285 | 0 | { |
286 | 0 | ::close(m_fd); |
287 | 0 | m_fd = -1; |
288 | 0 | } |
289 | | |
290 | | void randomize(uint8_t buf[], size_t len) override; |
291 | | void add_entropy(const uint8_t in[], size_t length) override; |
292 | 0 | bool is_seeded() const override { return true; } |
293 | 0 | bool accepts_input() const override { return m_writable; } |
294 | 0 | void clear() override { /* not possible */ } |
295 | 0 | std::string name() const override { return "urandom"; } |
296 | | private: |
297 | | int m_fd; |
298 | | bool m_writable; |
299 | | }; |
300 | | |
301 | | void System_RNG_Impl::randomize(uint8_t buf[], size_t len) |
302 | 0 | { |
303 | 0 | while(len) |
304 | 0 | { |
305 | 0 | ssize_t got = ::read(m_fd, buf, len); |
306 | |
|
307 | 0 | if(got < 0) |
308 | 0 | { |
309 | 0 | if(errno == EINTR) |
310 | 0 | continue; |
311 | 0 | throw System_Error("System_RNG read failed", errno); |
312 | 0 | } |
313 | 0 | if(got == 0) |
314 | 0 | throw System_Error("System_RNG EOF on device"); // ?!? |
315 | | |
316 | 0 | buf += got; |
317 | 0 | len -= got; |
318 | 0 | } |
319 | 0 | } |
320 | | |
321 | | void System_RNG_Impl::add_entropy(const uint8_t input[], size_t len) |
322 | 0 | { |
323 | 0 | if(!m_writable) |
324 | 0 | return; |
325 | | |
326 | 0 | while(len) |
327 | 0 | { |
328 | 0 | ssize_t got = ::write(m_fd, input, len); |
329 | |
|
330 | 0 | if(got < 0) |
331 | 0 | { |
332 | 0 | if(errno == EINTR) |
333 | 0 | continue; |
334 | | |
335 | | /* |
336 | | * This is seen on OS X CI, despite the fact that the man page |
337 | | * for macOS urandom explicitly states that writing to it is |
338 | | * supported, and write(2) does not document EPERM at all. |
339 | | * But in any case EPERM seems indicative of a policy decision |
340 | | * by the OS or sysadmin that additional entropy is not wanted |
341 | | * in the system pool, so we accept that and return here, |
342 | | * since there is no corrective action possible. |
343 | | * |
344 | | * In Linux EBADF or EPERM is returned if m_fd is not opened for |
345 | | * writing. |
346 | | */ |
347 | 0 | if(errno == EPERM || errno == EBADF) |
348 | 0 | return; |
349 | | |
350 | | // maybe just ignore any failure here and return? |
351 | 0 | throw System_Error("System_RNG write failed", errno); |
352 | 0 | } |
353 | | |
354 | 0 | input += got; |
355 | 0 | len -= got; |
356 | 0 | } |
357 | 0 | } |
358 | | |
359 | | #endif |
360 | | |
361 | | } |
362 | | |
363 | | RandomNumberGenerator& system_rng() |
364 | 0 | { |
365 | 0 | static System_RNG_Impl g_system_rng; |
366 | 0 | return g_system_rng; |
367 | 0 | } |
368 | | |
369 | | } |