Botan  2.7.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_DEV_RANDOM)
23  #include <sys/types.h>
24  #include <sys/stat.h>
25  #include <fcntl.h>
26  #include <unistd.h>
27  #include <errno.h>
28 #endif
29 
30 namespace Botan {
31 
32 namespace {
33 
34 #if defined(BOTAN_TARGET_OS_HAS_RTLGENRANDOM)
35 
36 class System_RNG_Impl final : public RandomNumberGenerator
37  {
38  public:
39  System_RNG_Impl() : m_advapi("advapi32.dll")
40  {
41  // This throws if the function is not found
42  m_rtlgenrandom = m_advapi.resolve<RtlGenRandom_fptr>("SystemFunction036");
43  }
44 
45  void randomize(uint8_t buf[], size_t len) override
46  {
47  bool success = m_rtlgenrandom(buf, len) == TRUE;
48  if(!success)
49  throw Exception("RtlGenRandom failed");
50  }
51 
52  void add_entropy(const uint8_t[], size_t) override { /* ignored */ }
53  bool is_seeded() const override { return true; }
54  void clear() override { /* not possible */ }
55  std::string name() const override { return "RtlGenRandom"; }
56  private:
57  // Use type BYTE instead of BOOLEAN because of a naming conflict
58  // https://msdn.microsoft.com/en-us/library/windows/desktop/aa387694(v=vs.85).aspx
59  // https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx
60  using RtlGenRandom_fptr = BYTE (NTAPI *)(PVOID, ULONG);
61 
62  Dynamically_Loaded_Library m_advapi;
63  RtlGenRandom_fptr m_rtlgenrandom;
64  };
65 
66 #elif defined(BOTAN_TARGET_OS_HAS_CRYPTO_NG)
67 
68 class System_RNG_Impl final : public RandomNumberGenerator
69  {
70  public:
71  System_RNG_Impl()
72  {
73  NTSTATUS ret = ::BCryptOpenAlgorithmProvider(&m_prov,
74  BCRYPT_RNG_ALGORITHM,
75  MS_PRIMITIVE_PROVIDER, 0);
76  if(ret != STATUS_SUCCESS)
77  throw Exception("System_RNG failed to acquire crypto provider");
78  }
79 
80  ~System_RNG_Impl()
81  {
82  ::BCryptCloseAlgorithmProvider(m_prov, 0);
83  }
84 
85  void randomize(uint8_t buf[], size_t len) override
86  {
87  NTSTATUS ret = ::BCryptGenRandom(m_prov, static_cast<PUCHAR>(buf), static_cast<ULONG>(len), 0);
88  if(ret != STATUS_SUCCESS)
89  throw Exception("System_RNG call to BCryptGenRandom failed");
90  }
91 
92  void add_entropy(const uint8_t in[], size_t length) override
93  {
94  /*
95  There is a flag BCRYPT_RNG_USE_ENTROPY_IN_BUFFER to provide
96  entropy inputs, but it is ignored in Windows 8 and later.
97  */
98  }
99 
100  bool is_seeded() const override { return true; }
101  void clear() override { /* not possible */ }
102  std::string name() const override { return "crypto_ng"; }
103  private:
104  BCRYPT_ALG_HANDLE m_handle;
105  };
106 
107 #elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM)
108 
109 class System_RNG_Impl final : public RandomNumberGenerator
110  {
111  public:
112  // No constructor or destructor needed as no userland state maintained
113 
114  void randomize(uint8_t buf[], size_t len) override
115  {
116  ::arc4random_buf(buf, len);
117  }
118 
119  void add_entropy(const uint8_t[], size_t) override { /* ignored */ }
120  bool is_seeded() const override { return true; }
121  void clear() override { /* not possible */ }
122  std::string name() const override { return "arc4random"; }
123  };
124 
125 #elif defined(BOTAN_TARGET_OS_HAS_DEV_RANDOM)
126 
127 // Read a random device
128 
129 class System_RNG_Impl final : public RandomNumberGenerator
130  {
131  public:
132  System_RNG_Impl()
133  {
134  #ifndef O_NOCTTY
135  #define O_NOCTTY 0
136  #endif
137 
138  m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDWR | O_NOCTTY);
139 
140  /*
141  Cannot open in read-write mode. Fall back to read-only,
142  calls to add_entropy will fail, but randomize will work
143  */
144  if(m_fd < 0)
145  m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDONLY | O_NOCTTY);
146 
147  if(m_fd < 0)
148  throw Exception("System_RNG failed to open RNG device");
149  }
150 
151  ~System_RNG_Impl()
152  {
153  ::close(m_fd);
154  m_fd = -1;
155  }
156 
157  void randomize(uint8_t buf[], size_t len) override;
158  void add_entropy(const uint8_t in[], size_t length) override;
159  bool is_seeded() const override { return true; }
160  void clear() override { /* not possible */ }
161  std::string name() const override { return BOTAN_SYSTEM_RNG_DEVICE; }
162  private:
163  int m_fd;
164  };
165 
166 void System_RNG_Impl::randomize(uint8_t buf[], size_t len)
167  {
168  while(len)
169  {
170  ssize_t got = ::read(m_fd, buf, len);
171 
172  if(got < 0)
173  {
174  if(errno == EINTR)
175  continue;
176  throw Exception("System_RNG read failed error " + std::to_string(errno));
177  }
178  if(got == 0)
179  throw Exception("System_RNG EOF on device"); // ?!?
180 
181  buf += got;
182  len -= got;
183  }
184  }
185 
186 void System_RNG_Impl::add_entropy(const uint8_t input[], size_t len)
187  {
188  while(len)
189  {
190  ssize_t got = ::write(m_fd, input, len);
191 
192  if(got < 0)
193  {
194  if(errno == EINTR)
195  continue;
196 
197  /*
198  * This is seen on OS X CI, despite the fact that the man page
199  * for Darwin urandom explicitly states that writing to it is
200  * supported, and write(2) does not document EPERM at all.
201  * But in any case EPERM seems indicative of a policy decision
202  * by the OS or sysadmin that additional entropy is not wanted
203  * in the system pool, so we accept that and return here,
204  * since there is no corrective action possible.
205  *
206  * In Linux EBADF or EPERM is returned if m_fd is not opened for
207  * writing.
208  */
209  if(errno == EPERM || errno == EBADF)
210  return;
211 
212  // maybe just ignore any failure here and return?
213  throw Exception("System_RNG write failed error " + std::to_string(errno));
214  }
215 
216  input += got;
217  len -= got;
218  }
219  }
220 
221 #endif
222 
223 }
224 
226  {
227  static System_RNG_Impl g_system_rng;
228  return g_system_rng;
229  }
230 
231 }
RandomNumberGenerator & system_rng()
Definition: system_rng.cpp:225
std::string to_string(const BER_Object &obj)
Definition: asn1_obj.cpp:210
#define O_NOCTTY
Definition: alg_id.cpp:13
#define TRUE
Definition: pkcs11t.h:31