Coverage Report

Created: 2022-06-23 06:44

/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,2022 Jack Lloyd
4
* (C) 2021 Tom Crowley
5
*
6
* Botan is released under the Simplified BSD License (see license.txt)
7
*/
8
9
#include <botan/system_rng.h>
10
11
#if defined(BOTAN_TARGET_OS_HAS_WIN32)
12
  #define NOMINMAX 1
13
  #define _WINSOCKAPI_ // stop windows.h including winsock.h
14
  #include <windows.h>
15
#endif
16
17
#if defined(BOTAN_TARGET_OS_HAS_RTLGENRANDOM)
18
  #include <botan/internal/dyn_load.h>
19
#elif defined(BOTAN_TARGET_OS_HAS_CRYPTO_NG)
20
  #include <bcrypt.h>
21
#elif defined(BOTAN_TARGET_OS_HAS_CCRANDOM)
22
  #include <CommonCrypto/CommonRandom.h>
23
#elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM)
24
  #include <stdlib.h>
25
#elif defined(BOTAN_TARGET_OS_HAS_GETRANDOM)
26
  #include <sys/random.h>
27
  #include <errno.h>
28
#elif defined(BOTAN_TARGET_OS_HAS_DEV_RANDOM)
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
      System_RNG_Impl(const System_RNG_Impl& other) = delete;
50
      System_RNG_Impl(System_RNG_Impl&& other) = delete;
51
      System_RNG_Impl& operator=(const System_RNG_Impl& other) = delete;
52
      System_RNG_Impl& operator=(System_RNG_Impl&& other) = delete;
53
54
      void randomize(uint8_t buf[], size_t len) override
55
         {
56
         const size_t limit = std::numeric_limits<ULONG>::max();
57
58
         uint8_t* pData = buf;
59
         size_t bytesLeft = len;
60
         while (bytesLeft > 0)
61
            {
62
            const ULONG blockSize = static_cast<ULONG>(std::min(bytesLeft, limit));
63
64
            const bool success = m_rtlgenrandom(pData, blockSize) == TRUE;
65
            if (!success)
66
               {
67
               throw System_Error("RtlGenRandom failed");
68
               }
69
70
            BOTAN_ASSERT(bytesLeft >= blockSize, "Block is oversized");
71
            bytesLeft -= blockSize;
72
            pData += blockSize;
73
            }
74
         }
75
76
      void add_entropy(const uint8_t[], size_t) override { /* ignored */ }
77
      bool is_seeded() const override { return true; }
78
      bool accepts_input() const override { return false; }
79
      void clear() override { /* not possible */ }
80
      std::string name() const override { return "RtlGenRandom"; }
81
   private:
82
      using RtlGenRandom_fptr = BOOLEAN (NTAPI *)(PVOID, ULONG);
83
84
      Dynamically_Loaded_Library m_advapi;
85
      RtlGenRandom_fptr m_rtlgenrandom;
86
   };
87
88
#elif defined(BOTAN_TARGET_OS_HAS_CRYPTO_NG)
89
90
class System_RNG_Impl final : public RandomNumberGenerator
91
   {
92
   public:
93
      System_RNG_Impl()
94
         {
95
         auto ret = ::BCryptOpenAlgorithmProvider(&m_prov,
96
                                                  BCRYPT_RNG_ALGORITHM,
97
                                                  MS_PRIMITIVE_PROVIDER, 0);
98
         if(!BCRYPT_SUCCESS(ret))
99
            {
100
            throw System_Error("System_RNG failed to acquire crypto provider", ret);
101
            }
102
         }
103
104
      System_RNG_Impl(const System_RNG_Impl& other) = delete;
105
      System_RNG_Impl(System_RNG_Impl&& other) = delete;
106
      System_RNG_Impl& operator=(const System_RNG_Impl& other) = delete;
107
      System_RNG_Impl& operator=(System_RNG_Impl&& other) = delete;
108
109
      ~System_RNG_Impl() override
110
         {
111
         ::BCryptCloseAlgorithmProvider(m_prov, 0);
112
         }
113
114
      void randomize(uint8_t buf[], size_t len) override
115
         {
116
         const size_t limit = std::numeric_limits<ULONG>::max();
117
118
         uint8_t* pData = buf;
119
         size_t bytesLeft = len;
120
         while (bytesLeft > 0)
121
            {
122
            const ULONG blockSize = static_cast<ULONG>(std::min(bytesLeft, limit));
123
124
            auto ret = BCryptGenRandom(m_prov, static_cast<PUCHAR>(pData), blockSize, 0);
125
            if(!BCRYPT_SUCCESS(ret))
126
               {
127
               throw System_Error("System_RNG call to BCryptGenRandom failed", ret);
128
               }
129
130
            BOTAN_ASSERT(bytesLeft >= blockSize, "Block is oversized");
131
            bytesLeft -= blockSize;
132
            pData += blockSize;
133
            }
134
         }
135
136
      void add_entropy(const uint8_t in[], size_t length) override
137
         {
138
         /*
139
         There is a flag BCRYPT_RNG_USE_ENTROPY_IN_BUFFER to provide
140
         entropy inputs, but it is ignored in Windows 8 and later.
141
         */
142
         }
143
144
      bool is_seeded() const override { return true; }
145
      bool accepts_input() const override { return false; }
146
      void clear() override { /* not possible */ }
147
      std::string name() const override { return "crypto_ng"; }
148
   private:
149
      BCRYPT_ALG_HANDLE m_prov;
150
   };
151
152
#elif defined(BOTAN_TARGET_OS_HAS_CCRANDOM)
153
154
class System_RNG_Impl final : public RandomNumberGenerator
155
   {
156
   public:
157
      void randomize(uint8_t buf[], size_t len) override
158
         {
159
         if (::CCRandomGenerateBytes(buf, len) != kCCSuccess)
160
            {
161
            throw System_Error("System_RNG CCRandomGenerateBytes failed", errno);
162
            }
163
         }
164
      bool accepts_input() const override { return false; }
165
      void add_entropy(const uint8_t[], size_t) override { /* ignored */ }
166
      bool is_seeded() const override { return true; }
167
      void clear() override { /* not possible */ }
168
      std::string name() const override { return "CCRandomGenerateBytes"; }
169
   };
170
171
#elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM)
172
173
class System_RNG_Impl final : public RandomNumberGenerator
174
   {
175
   public:
176
      // No constructor or destructor needed as no userland state maintained
177
178
      void randomize(uint8_t buf[], size_t len) override
179
         {
180
         // macOS 10.15 arc4random crashes if called with buf == nullptr && len == 0
181
   // however it uses ccrng_generate internally which returns a status, ignored
182
   // to respect arc4random "no-fail" interface contract
183
         if(len > 0)
184
            {
185
            ::arc4random_buf(buf, len);
186
            }
187
         }
188
189
      bool accepts_input() const override { return false; }
190
      void add_entropy(const uint8_t[], size_t) override { /* ignored */ }
191
      bool is_seeded() const override { return true; }
192
      void clear() override { /* not possible */ }
193
      std::string name() const override { return "arc4random"; }
194
   };
195
196
#elif defined(BOTAN_TARGET_OS_HAS_GETRANDOM)
197
198
class System_RNG_Impl final : public RandomNumberGenerator
199
   {
200
   public:
201
      // No constructor or destructor needed as no userland state maintained
202
203
      void randomize(uint8_t buf[], size_t len) override
204
         {
205
         const unsigned int flags = 0;
206
207
         while(len > 0)
208
            {
209
            const ssize_t got = ::getrandom(buf, len, flags);
210
211
            if(got < 0)
212
               {
213
               if(errno == EINTR)
214
                  continue;
215
               throw System_Error("System_RNG getrandom failed", errno);
216
               }
217
218
            buf += got;
219
            len -= got;
220
            }
221
         }
222
223
      bool accepts_input() const override { return false; }
224
      void add_entropy(const uint8_t[], size_t) override { /* ignored */ }
225
      bool is_seeded() const override { return true; }
226
      void clear() override { /* not possible */ }
227
      std::string name() const override { return "getrandom"; }
228
   };
229
230
231
#elif defined(BOTAN_TARGET_OS_HAS_DEV_RANDOM)
232
233
// Read a random device
234
235
class System_RNG_Impl final : public RandomNumberGenerator
236
   {
237
   public:
238
      System_RNG_Impl()
239
0
         {
240
#ifndef O_NOCTTY
241
#define O_NOCTTY 0
242
#endif
243
244
         /*
245
         * First open /dev/random and read one byte. On old Linux kernels
246
         * this blocks the RNG until we have been actually seeded.
247
         */
248
0
         m_fd = ::open("/dev/random", O_RDONLY | O_NOCTTY);
249
0
         if(m_fd < 0)
250
0
            throw System_Error("System_RNG failed to open RNG device", errno);
251
252
0
         uint8_t b;
253
0
         const size_t got = ::read(m_fd, &b, 1);
254
0
         ::close(m_fd);
255
256
0
         if(got != 1)
257
0
            throw System_Error("System_RNG failed to read blocking RNG device");
258
259
0
         m_fd = ::open("/dev/urandom", O_RDWR | O_NOCTTY);
260
261
0
         if(m_fd >= 0)
262
0
            {
263
0
            m_writable = true;
264
0
            }
265
0
         else
266
0
            {
267
            /*
268
            Cannot open in read-write mode. Fall back to read-only,
269
            calls to add_entropy will fail, but randomize will work
270
            */
271
0
            m_fd = ::open("/dev/urandom", O_RDONLY | O_NOCTTY);
272
0
            m_writable = false;
273
0
            }
274
275
0
         if(m_fd < 0)
276
0
            throw System_Error("System_RNG failed to open RNG device", errno);
277
0
         }
278
279
      System_RNG_Impl(const System_RNG_Impl& other) = delete;
280
      System_RNG_Impl(System_RNG_Impl&& other) = delete;
281
      System_RNG_Impl& operator=(const System_RNG_Impl& other) = delete;
282
      System_RNG_Impl& operator=(System_RNG_Impl&& other) = delete;
283
284
      ~System_RNG_Impl() override
285
0
         {
286
0
         ::close(m_fd);
287
0
         m_fd = -1;
288
0
         }
289
290
      void randomize(uint8_t buf[], size_t len) override;
291
      void add_entropy(const uint8_t in[], size_t length) override;
292
0
      bool is_seeded() const override { return true; }
293
0
      bool accepts_input() const override { return m_writable; }
294
0
      void clear() override { /* not possible */ }
295
0
      std::string name() const override { return "urandom"; }
296
   private:
297
      int m_fd;
298
      bool m_writable;
299
   };
300
301
void System_RNG_Impl::randomize(uint8_t buf[], size_t len)
302
0
   {
303
0
   while(len)
304
0
      {
305
0
      ssize_t got = ::read(m_fd, buf, len);
306
307
0
      if(got < 0)
308
0
         {
309
0
         if(errno == EINTR)
310
0
            continue;
311
0
         throw System_Error("System_RNG read failed", errno);
312
0
         }
313
0
      if(got == 0)
314
0
         throw System_Error("System_RNG EOF on device"); // ?!?
315
316
0
      buf += got;
317
0
      len -= got;
318
0
      }
319
0
   }
320
321
void System_RNG_Impl::add_entropy(const uint8_t input[], size_t len)
322
0
   {
323
0
   if(!m_writable)
324
0
      return;
325
326
0
   while(len)
327
0
      {
328
0
      ssize_t got = ::write(m_fd, input, len);
329
330
0
      if(got < 0)
331
0
         {
332
0
         if(errno == EINTR)
333
0
            continue;
334
335
         /*
336
         * This is seen on OS X CI, despite the fact that the man page
337
         * for macOS urandom explicitly states that writing to it is
338
         * supported, and write(2) does not document EPERM at all.
339
         * But in any case EPERM seems indicative of a policy decision
340
         * by the OS or sysadmin that additional entropy is not wanted
341
         * in the system pool, so we accept that and return here,
342
         * since there is no corrective action possible.
343
         *
344
         * In Linux EBADF or EPERM is returned if m_fd is not opened for
345
         * writing.
346
         */
347
0
         if(errno == EPERM || errno == EBADF)
348
0
            return;
349
350
         // maybe just ignore any failure here and return?
351
0
         throw System_Error("System_RNG write failed", errno);
352
0
         }
353
354
0
      input += got;
355
0
      len -= got;
356
0
      }
357
0
   }
358
359
#endif
360
361
}
362
363
RandomNumberGenerator& system_rng()
364
0
   {
365
0
   static System_RNG_Impl g_system_rng;
366
0
   return g_system_rng;
367
0
   }
368
369
}