Botan  2.15.0
Crypto and TLS for C++11
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  // 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  {
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 
221 void 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 
241 void 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 }
RandomNumberGenerator & system_rng()
Definition: system_rng.cpp:283
int(* final)(unsigned char *, CTX *)
Flags flags(Flag flags)
Definition: p11.h:858
std::string name
#define O_NOCTTY
Definition: alg_id.cpp:13
#define TRUE
Definition: pkcs11t.h:31