Botan  2.4.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 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_CRYPTGENRANDOM)
11  #define NOMINMAX 1
12  #define _WINSOCKAPI_ // stop windows.h including winsock.h
13  #include <windows.h>
14  #include <wincrypt.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 #else
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_CRYPTGENRANDOM)
35 
36 class System_RNG_Impl final : public RandomNumberGenerator
37  {
38  public:
39  System_RNG_Impl()
40  {
41  if(!CryptAcquireContext(&m_prov, nullptr, nullptr,
42  BOTAN_SYSTEM_RNG_CRYPTOAPI_PROV_TYPE, CRYPT_VERIFYCONTEXT))
43  throw Exception("System_RNG failed to acquire crypto provider");
44  }
45 
46  ~System_RNG_Impl()
47  {
48  ::CryptReleaseContext(m_prov, 0);
49  }
50 
51  void randomize(uint8_t buf[], size_t len) override
52  {
53  ::CryptGenRandom(m_prov, static_cast<DWORD>(len), buf);
54  }
55 
56  void add_entropy(const uint8_t in[], size_t length) override
57  {
58  /*
59  There is no explicit ConsumeRandom, but all values provided in
60  the call are incorporated into the state.
61  */
62  std::vector<uint8_t> buf(in, in + length);
63  ::CryptGenRandom(m_prov, static_cast<DWORD>(buf.size()), buf.data());
64  }
65 
66  bool is_seeded() const override { return true; }
67  void clear() override { /* not possible */ }
68  std::string name() const override { return "cryptoapi"; }
69  private:
70  HCRYPTPROV m_prov;
71  };
72 
73 #elif defined(BOTAN_TARGET_OS_HAS_CRYPTO_NG)
74 
75 class System_RNG_Impl final : public RandomNumberGenerator
76  {
77  public:
78  System_RNG_Impl()
79  {
80  NTSTATUS ret = ::BCryptOpenAlgorithmProvider(&m_prov,
81  BCRYPT_RNG_ALGORITHM,
82  MS_PRIMITIVE_PROVIDER, 0);
83  if(ret != STATUS_SUCCESS)
84  throw Exception("System_RNG failed to acquire crypto provider");
85  }
86 
87  ~System_RNG_Impl()
88  {
89  ::BCryptCloseAlgorithmProvider(m_prov, 0);
90  }
91 
92  void randomize(uint8_t buf[], size_t len) override
93  {
94  ::BCryptGenRandom(m_prov, static_cast<PUCHAR>(buf), static_cast<ULONG>(len), 0);
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  void clear() override { /* not possible */ }
107  std::string name() const override { return "crypto_ng"; }
108  private:
109  BCRYPT_ALG_HANDLE m_handle;
110  };
111 
112 #elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM)
113 
114 class System_RNG_Impl final : public RandomNumberGenerator
115  {
116  public:
117  // No constructor or destructor needed as no userland state maintained
118 
119  void randomize(uint8_t buf[], size_t len) override
120  {
121  ::arc4random_buf(buf, len);
122  }
123 
124  void add_entropy(const uint8_t[], size_t) override { /* ignored */ }
125  bool is_seeded() const override { return true; }
126  void clear() override { /* not possible */ }
127  std::string name() const override { return "arc4random"; }
128  };
129 
130 #else
131 
132 // Read a random device
133 
134 class System_RNG_Impl final : public RandomNumberGenerator
135  {
136  public:
137  System_RNG_Impl()
138  {
139  #ifndef O_NOCTTY
140  #define O_NOCTTY 0
141  #endif
142 
143  m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDWR | O_NOCTTY);
144 
145  /*
146  Cannot open in read-write mode. Fall back to read-only,
147  calls to add_entropy will fail, but randomize will work
148  */
149  if(m_fd < 0)
150  m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDONLY | O_NOCTTY);
151 
152  if(m_fd < 0)
153  throw Exception("System_RNG failed to open RNG device");
154  }
155 
156  ~System_RNG_Impl()
157  {
158  ::close(m_fd);
159  m_fd = -1;
160  }
161 
162  void randomize(uint8_t buf[], size_t len) override;
163  void add_entropy(const uint8_t in[], size_t length) override;
164  bool is_seeded() const override { return true; }
165  void clear() override { /* not possible */ }
166  std::string name() const override { return BOTAN_SYSTEM_RNG_DEVICE; }
167  private:
168  int m_fd;
169  };
170 
171 void System_RNG_Impl::randomize(uint8_t buf[], size_t len)
172  {
173  while(len)
174  {
175  ssize_t got = ::read(m_fd, buf, len);
176 
177  if(got < 0)
178  {
179  if(errno == EINTR)
180  continue;
181  throw Exception("System_RNG read failed error " + std::to_string(errno));
182  }
183  if(got == 0)
184  throw Exception("System_RNG EOF on device"); // ?!?
185 
186  buf += got;
187  len -= got;
188  }
189  }
190 
191 void System_RNG_Impl::add_entropy(const uint8_t input[], size_t len)
192  {
193  while(len)
194  {
195  ssize_t got = ::write(m_fd, input, len);
196 
197  if(got < 0)
198  {
199  if(errno == EINTR)
200  continue;
201 
202  /*
203  * This is seen on OS X CI, despite the fact that the man page
204  * for Darwin urandom explicitly states that writing to it is
205  * supported, and write(2) does not document EPERM at all.
206  * But in any case EPERM seems indicative of a policy decision
207  * by the OS or sysadmin that additional entropy is not wanted
208  * in the system pool, so we accept that and return here,
209  * since there is no corrective action possible.
210  *
211  * In Linux EBADF or EPERM is returned if m_fd is not opened for
212  * writing.
213  */
214  if(errno == EPERM || errno == EBADF)
215  return;
216 
217  // maybe just ignore any failure here and return?
218  throw Exception("System_RNG write failed error " + std::to_string(errno));
219  }
220 
221  input += got;
222  len -= got;
223  }
224  }
225 
226 #endif
227 
228 }
229 
231  {
232  static System_RNG_Impl g_system_rng;
233  return g_system_rng;
234  }
235 
236 }
RandomNumberGenerator & system_rng()
Definition: system_rng.cpp:230
std::string to_string(const BER_Object &obj)
Definition: asn1_obj.cpp:108
Definition: alg_id.cpp:13
#define O_NOCTTY