Botan  2.6.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_ARC4RANDOM)
17  #include <stdlib.h>
18 
19 #elif defined(BOTAN_TARGET_OS_HAS_DEV_RANDOM)
20  #include <sys/types.h>
21  #include <sys/stat.h>
22  #include <fcntl.h>
23  #include <unistd.h>
24  #include <errno.h>
25 #endif
26 
27 namespace Botan {
28 
29 namespace {
30 
31 #if defined(BOTAN_TARGET_OS_HAS_RTLGENRANDOM)
32 
33 class System_RNG_Impl final : public RandomNumberGenerator
34  {
35  public:
36  System_RNG_Impl() : m_advapi("advapi32.dll")
37  {
38  // This throws if the function is not found
39  m_rtlgenrandom = m_advapi.resolve<RtlGenRandom_f>("SystemFunction036");
40  }
41 
42  void randomize(uint8_t buf[], size_t len) override
43  {
44  if(m_rtlgenrandom(buf, len) == false)
45  throw Exception("RtlGenRandom failed");
46  }
47 
48  void add_entropy(const uint8_t[], size_t) override { /* ignored */ }
49  bool is_seeded() const override { return true; }
50  void clear() override { /* not possible */ }
51  std::string name() const override { return "RtlGenRandom"; }
52  private:
53  typedef BOOL (*RtlGenRandom_f)(PVOID, ULONG);
54 
55  Dynamically_Loaded_Library m_advapi;
56  RtlGenRandom_f m_rtlgenrandom;
57  };
58 
59 #elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM)
60 
61 class System_RNG_Impl final : public RandomNumberGenerator
62  {
63  public:
64  // No constructor or destructor needed as no userland state maintained
65 
66  void randomize(uint8_t buf[], size_t len) override
67  {
68  ::arc4random_buf(buf, len);
69  }
70 
71  void add_entropy(const uint8_t[], size_t) override { /* ignored */ }
72  bool is_seeded() const override { return true; }
73  void clear() override { /* not possible */ }
74  std::string name() const override { return "arc4random"; }
75  };
76 
77 #elif defined(BOTAN_TARGET_OS_HAS_DEV_RANDOM)
78 
79 // Read a random device
80 
81 class System_RNG_Impl final : public RandomNumberGenerator
82  {
83  public:
84  System_RNG_Impl()
85  {
86  #ifndef O_NOCTTY
87  #define O_NOCTTY 0
88  #endif
89 
90  m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDWR | O_NOCTTY);
91 
92  /*
93  Cannot open in read-write mode. Fall back to read-only,
94  calls to add_entropy will fail, but randomize will work
95  */
96  if(m_fd < 0)
97  m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDONLY | O_NOCTTY);
98 
99  if(m_fd < 0)
100  throw Exception("System_RNG failed to open RNG device");
101  }
102 
103  ~System_RNG_Impl()
104  {
105  ::close(m_fd);
106  m_fd = -1;
107  }
108 
109  void randomize(uint8_t buf[], size_t len) override;
110  void add_entropy(const uint8_t in[], size_t length) override;
111  bool is_seeded() const override { return true; }
112  void clear() override { /* not possible */ }
113  std::string name() const override { return BOTAN_SYSTEM_RNG_DEVICE; }
114  private:
115  int m_fd;
116  };
117 
118 void System_RNG_Impl::randomize(uint8_t buf[], size_t len)
119  {
120  while(len)
121  {
122  ssize_t got = ::read(m_fd, buf, len);
123 
124  if(got < 0)
125  {
126  if(errno == EINTR)
127  continue;
128  throw Exception("System_RNG read failed error " + std::to_string(errno));
129  }
130  if(got == 0)
131  throw Exception("System_RNG EOF on device"); // ?!?
132 
133  buf += got;
134  len -= got;
135  }
136  }
137 
138 void System_RNG_Impl::add_entropy(const uint8_t input[], size_t len)
139  {
140  while(len)
141  {
142  ssize_t got = ::write(m_fd, input, len);
143 
144  if(got < 0)
145  {
146  if(errno == EINTR)
147  continue;
148 
149  /*
150  * This is seen on OS X CI, despite the fact that the man page
151  * for Darwin urandom explicitly states that writing to it is
152  * supported, and write(2) does not document EPERM at all.
153  * But in any case EPERM seems indicative of a policy decision
154  * by the OS or sysadmin that additional entropy is not wanted
155  * in the system pool, so we accept that and return here,
156  * since there is no corrective action possible.
157  *
158  * In Linux EBADF or EPERM is returned if m_fd is not opened for
159  * writing.
160  */
161  if(errno == EPERM || errno == EBADF)
162  return;
163 
164  // maybe just ignore any failure here and return?
165  throw Exception("System_RNG write failed error " + std::to_string(errno));
166  }
167 
168  input += got;
169  len -= got;
170  }
171  }
172 
173 #endif
174 
175 }
176 
178  {
179  static System_RNG_Impl g_system_rng;
180  return g_system_rng;
181  }
182 
183 }
RandomNumberGenerator & system_rng()
Definition: system_rng.cpp:177
std::string to_string(const BER_Object &obj)
Definition: asn1_obj.cpp:145
#define O_NOCTTY
Definition: alg_id.cpp:13