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