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