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