Coverage Report

Created: 2022-01-14 08:07

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