Coverage Report

Created: 2020-06-30 13:58

/src/botan/src/lib/rng/system_rng/system_rng.cpp
Line
Count
Source (jump to first uncovered line)
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
0
         {
180
#ifndef O_NOCTTY
181
#define O_NOCTTY 0
182
#endif
183
184
0
         m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDWR | O_NOCTTY);
185
0
186
0
         if(m_fd >= 0)
187
0
            {
188
0
            m_writable = true;
189
0
            }
190
0
         else
191
0
            {
192
0
            /*
193
0
            Cannot open in read-write mode. Fall back to read-only,
194
0
            calls to add_entropy will fail, but randomize will work
195
0
            */
196
0
            m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDONLY | O_NOCTTY);
197
0
            m_writable = false;
198
0
            }
199
0
200
0
         if(m_fd < 0)
201
0
            throw System_Error("System_RNG failed to open RNG device", errno);
202
0
         }
203
204
      ~System_RNG_Impl()
205
0
         {
206
0
         ::close(m_fd);
207
0
         m_fd = -1;
208
0
         }
209
210
      void randomize(uint8_t buf[], size_t len) override;
211
      void add_entropy(const uint8_t in[], size_t length) override;
212
0
      bool is_seeded() const override { return true; }
213
0
      bool accepts_input() const override { return m_writable; }
214
0
      void clear() override { /* not possible */ }
215
0
      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
0
   {
223
0
   while(len)
224
0
      {
225
0
      ssize_t got = ::read(m_fd, buf, len);
226
0
227
0
      if(got < 0)
228
0
         {
229
0
         if(errno == EINTR)
230
0
            continue;
231
0
         throw System_Error("System_RNG read failed", errno);
232
0
         }
233
0
      if(got == 0)
234
0
         throw System_Error("System_RNG EOF on device"); // ?!?
235
0
236
0
      buf += got;
237
0
      len -= got;
238
0
      }
239
0
   }
240
241
void System_RNG_Impl::add_entropy(const uint8_t input[], size_t len)
242
0
   {
243
0
   if(!m_writable)
244
0
      return;
245
0
246
0
   while(len)
247
0
      {
248
0
      ssize_t got = ::write(m_fd, input, len);
249
0
250
0
      if(got < 0)
251
0
         {
252
0
         if(errno == EINTR)
253
0
            continue;
254
0
255
0
         /*
256
0
         * This is seen on OS X CI, despite the fact that the man page
257
0
         * for macOS urandom explicitly states that writing to it is
258
0
         * supported, and write(2) does not document EPERM at all.
259
0
         * But in any case EPERM seems indicative of a policy decision
260
0
         * by the OS or sysadmin that additional entropy is not wanted
261
0
         * in the system pool, so we accept that and return here,
262
0
         * since there is no corrective action possible.
263
0
         *
264
0
         * In Linux EBADF or EPERM is returned if m_fd is not opened for
265
0
         * writing.
266
0
         */
267
0
         if(errno == EPERM || errno == EBADF)
268
0
            return;
269
0
270
0
         // maybe just ignore any failure here and return?
271
0
         throw System_Error("System_RNG write failed", errno);
272
0
         }
273
0
274
0
      input += got;
275
0
      len -= got;
276
0
      }
277
0
   }
278
279
#endif
280
281
}
282
283
RandomNumberGenerator& system_rng()
284
0
   {
285
0
   static System_RNG_Impl g_system_rng;
286
0
   return g_system_rng;
287
0
   }
288
289
}