Botan 2.19.2
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 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
34namespace Botan {
35
36namespace {
37
38#if defined(BOTAN_TARGET_OS_HAS_RTLGENRANDOM)
39
40class 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
73class 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
115class 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
138class 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
175class System_RNG_Impl final : public RandomNumberGenerator
176 {
177 public:
178 System_RNG_Impl()
179 {
180#ifndef O_NOCTTY
181#define O_NOCTTY 0
182#endif
183
184 m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDWR | O_NOCTTY);
185
186 if(m_fd >= 0)
187 {
188 m_writable = true;
189 }
190 else
191 {
192 /*
193 Cannot open in read-write mode. Fall back to read-only,
194 calls to add_entropy will fail, but randomize will work
195 */
196 m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDONLY | O_NOCTTY);
197 m_writable = false;
198 }
199
200 if(m_fd < 0)
201 throw System_Error("System_RNG failed to open RNG device", errno);
202 }
203
204 ~System_RNG_Impl()
205 {
206 ::close(m_fd);
207 m_fd = -1;
208 }
209
210 void randomize(uint8_t buf[], size_t len) override;
211 void add_entropy(const uint8_t in[], size_t length) override;
212 bool is_seeded() const override { return true; }
213 bool accepts_input() const override { return m_writable; }
214 void clear() override { /* not possible */ }
215 std::string name() const override { return BOTAN_SYSTEM_RNG_DEVICE; }
216 private:
217 int m_fd;
218 bool m_writable;
219 };
220
221void System_RNG_Impl::randomize(uint8_t buf[], size_t len)
222 {
223 while(len)
224 {
225 ssize_t got = ::read(m_fd, buf, len);
226
227 if(got < 0)
228 {
229 if(errno == EINTR)
230 continue;
231 throw System_Error("System_RNG read failed", errno);
232 }
233 if(got == 0)
234 throw System_Error("System_RNG EOF on device"); // ?!?
235
236 buf += got;
237 len -= got;
238 }
239 }
240
241void System_RNG_Impl::add_entropy(const uint8_t input[], size_t len)
242 {
243 if(!m_writable)
244 return;
245
246 while(len)
247 {
248 ssize_t got = ::write(m_fd, input, len);
249
250 if(got < 0)
251 {
252 if(errno == EINTR)
253 continue;
254
255 /*
256 * This is seen on OS X CI, despite the fact that the man page
257 * for macOS urandom explicitly states that writing to it is
258 * supported, and write(2) does not document EPERM at all.
259 * But in any case EPERM seems indicative of a policy decision
260 * by the OS or sysadmin that additional entropy is not wanted
261 * in the system pool, so we accept that and return here,
262 * since there is no corrective action possible.
263 *
264 * In Linux EBADF or EPERM is returned if m_fd is not opened for
265 * writing.
266 */
267 if(errno == EPERM || errno == EBADF)
268 return;
269
270 // maybe just ignore any failure here and return?
271 throw System_Error("System_RNG write failed", errno);
272 }
273
274 input += got;
275 len -= got;
276 }
277 }
278
279#endif
280
281}
282
284 {
285 static System_RNG_Impl g_system_rng;
286 return g_system_rng;
287 }
288
289}
std::string name
int(* final)(unsigned char *, CTX *)
#define O_NOCTTY
Flags flags(Flag flags)
Definition: p11.h:860
Definition: alg_id.cpp:13
RandomNumberGenerator & system_rng()
Definition: system_rng.cpp:283
#define TRUE
Definition: pkcs11t.h:31