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