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