Botan 2.13.0 Crypto and TLS for C++11

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

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
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 ::arc4random_buf(buf, len);
123 }
124
125 bool accepts_input() const override { return false; }
126 void add_entropy(const uint8_t[], size_t) override { /* ignored */ }
127 bool is_seeded() const override { return true; }
128 void clear() override { /* not possible */ }
129 std::string name() const override { return "arc4random"; }
130 };
131
132 #elif defined(BOTAN_TARGET_OS_HAS_GETRANDOM)
133
134 class System_RNG_Impl final : public RandomNumberGenerator
135 {
136 public:
137 // No constructor or destructor needed as no userland state maintained
138
139 void randomize(uint8_t buf[], size_t len) override
140 {
141 const unsigned int flags = 0;
142
143 while(len > 0)
144 {
145 const ssize_t got = ::getrandom(buf, len, flags);
146
147 if(got < 0)
148 {
149 if(errno == EINTR)
150 continue;
151 throw System_Error("System_RNG getrandom failed", errno);
152 }
153
154 buf += got;
155 len -= got;
156 }
157 }
158
159 bool accepts_input() const override { return false; }
160 void add_entropy(const uint8_t[], size_t) override { /* ignored */ }
161 bool is_seeded() const override { return true; }
162 void clear() override { /* not possible */ }
163 std::string name() const override { return "getrandom"; }
164 };
165
166
167 #elif defined(BOTAN_TARGET_OS_HAS_DEV_RANDOM)
168
169 // Read a random device
170
171 class System_RNG_Impl final : public RandomNumberGenerator
172 {
173 public:
174 System_RNG_Impl()
175 {
176 #ifndef O_NOCTTY
177 #define O_NOCTTY 0
178 #endif
179
180 m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDWR | O_NOCTTY);
181
182 if(m_fd >= 0)
183 {
184 m_writable = true;
185 }
186 else
187 {
188 /*
189 Cannot open in read-write mode. Fall back to read-only,
190 calls to add_entropy will fail, but randomize will work
191 */
192 m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDONLY | O_NOCTTY);
193 m_writable = false;
194 }
195
196 if(m_fd < 0)
197 throw System_Error("System_RNG failed to open RNG device", errno);
198 }
199
200 ~System_RNG_Impl()
201 {
202 ::close(m_fd);
203 m_fd = -1;
204 }
205
206 void randomize(uint8_t buf[], size_t len) override;
207 void add_entropy(const uint8_t in[], size_t length) override;
208 bool is_seeded() const override { return true; }
209 bool accepts_input() const override { return m_writable; }
210 void clear() override { /* not possible */ }
211 std::string name() const override { return BOTAN_SYSTEM_RNG_DEVICE; }
212 private:
213 int m_fd;
214 bool m_writable;
215 };
216
217 void System_RNG_Impl::randomize(uint8_t buf[], size_t len)
218 {
219 while(len)
220 {
221 ssize_t got = ::read(m_fd, buf, len);
222
223 if(got < 0)
224 {
225 if(errno == EINTR)
226 continue;
227 throw System_Error("System_RNG read failed", errno);
228 }
229 if(got == 0)
230 throw System_Error("System_RNG EOF on device"); // ?!?
231
232 buf += got;
233 len -= got;
234 }
235 }
236
237 void System_RNG_Impl::add_entropy(const uint8_t input[], size_t len)
238 {
239 if(!m_writable)
240 return;
241
242 while(len)
243 {
244 ssize_t got = ::write(m_fd, input, len);
245
246 if(got < 0)
247 {
248 if(errno == EINTR)
249 continue;
250
251 /*
252 * This is seen on OS X CI, despite the fact that the man page
253 * for macOS urandom explicitly states that writing to it is
254 * supported, and write(2) does not document EPERM at all.
255 * But in any case EPERM seems indicative of a policy decision
256 * by the OS or sysadmin that additional entropy is not wanted
257 * in the system pool, so we accept that and return here,
258 * since there is no corrective action possible.
259 *
260 * In Linux EBADF or EPERM is returned if m_fd is not opened for
261 * writing.
262 */
263 if(errno == EPERM || errno == EBADF)
264 return;
265
266 // maybe just ignore any failure here and return?
267 throw System_Error("System_RNG write failed", errno);
268 }
269
270 input += got;
271 len -= got;
272 }
273 }
274
275 #endif
276
277 }
278
279 RandomNumberGenerator& system_rng()
280 {
281 static System_RNG_Impl g_system_rng;
282 return g_system_rng;
283 }
284
285 }
Botan::system_rng
RandomNumberGenerator & system_rng()
Definition: system_rng.cpp:279
Botan::RandomNumberGenerator
Definition: rng.h:24
final
int(* final)(unsigned char *, CTX *)
Definition: commoncrypto_hash.cpp:28
Botan::PKCS11::flags
Flags flags(Flag flags)
Definition: p11.h:858
bcrypt.h
name
std::string name
Definition: commoncrypto_hash.cpp:23
O_NOCTTY
#define O_NOCTTY
Botan
Definition: alg_id.cpp:13
TRUE
#define TRUE
Definition: pkcs11t.h:31