Coverage Report

Created: 2024-11-21 07:03

/src/cryptopp/osrng.cpp
Line
Count
Source (jump to first uncovered line)
1
// osrng.cpp - originally written and placed in the public domain by Wei Dai
2
3
// Thanks to Leonard Janke for the suggestion for AutoSeededRandomPool.
4
5
#include "pch.h"
6
#include "config.h"
7
8
#ifndef CRYPTOPP_IMPORTS
9
10
// Win32 has CryptoAPI and <wincrypt.h>. Windows 10 and Windows Store 10 have CNG and <bcrypt.h>.
11
//  There's a hole for Windows Phone 8 and Windows Store 8. There is no userland RNG available.
12
//  Also see http://www.drdobbs.com/windows/using-c-and-com-with-winrt/240168150 and
13
//  http://stackoverflow.com/questions/36974545/random-numbers-for-windows-phone-8-and-windows-store-8 and
14
//  https://social.msdn.microsoft.com/Forums/vstudio/en-US/25b83e13-c85f-4aa1-a057-88a279ea3fd6/what-crypto-random-generator-c-code-could-use-on-wp81
15
#if defined(CRYPTOPP_WIN32_AVAILABLE) && !defined(OS_RNG_AVAILABLE)
16
# pragma message("WARNING: Compiling for Windows but an OS RNG is not available. This is likely a Windows Phone 8 or Windows Store 8 app.")
17
#endif
18
19
#if !defined(NO_OS_DEPENDENCE) && defined(OS_RNG_AVAILABLE)
20
21
#include "osrng.h"
22
#include "rng.h"
23
24
// FreeBSD links /dev/urandom -> /dev/random. It showed up when we added
25
// O_NOFOLLOW to harden the non-blocking generator. Use Arc4Random instead
26
// for a non-blocking generator. Arc4Random is cryptographic quality prng
27
// based on ChaCha20. The ChaCha20 generator is seeded from /dev/random,
28
// so we can't completely avoid the blocking.
29
// https://www.freebsd.org/cgi/man.cgi?query=arc4random_buf.
30
#ifdef __FreeBSD__
31
# define DONT_USE_O_NOFOLLOW 1
32
# define USE_FREEBSD_ARC4RANDOM 1
33
# include <stdlib.h>
34
#endif
35
36
// Solaris links /dev/urandom -> ../devices/pseudo/random@0:urandom
37
// We can't access the device. Avoid O_NOFOLLOW for the platform.
38
#ifdef __sun
39
# define DONT_USE_O_NOFOLLOW 1
40
#endif
41
42
// And other OSes that don't define it
43
#ifndef O_NOFOLLOW
44
# define DONT_USE_O_NOFOLLOW 1
45
#endif
46
47
#ifdef CRYPTOPP_WIN32_AVAILABLE
48
#define WIN32_LEAN_AND_MEAN
49
#include <windows.h>
50
#ifndef ERROR_INCORRECT_SIZE
51
# define ERROR_INCORRECT_SIZE 0x000005B6
52
#endif
53
#if defined(USE_MS_CRYPTOAPI)
54
#include <wincrypt.h>
55
#ifndef CRYPT_NEWKEYSET
56
# define CRYPT_NEWKEYSET 0x00000008
57
#endif
58
#ifndef CRYPT_MACHINE_KEYSET
59
# define CRYPT_MACHINE_KEYSET 0x00000020
60
#endif
61
#elif defined(USE_MS_CNGAPI)
62
#include <bcrypt.h>
63
#ifndef BCRYPT_SUCCESS
64
# define BCRYPT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
65
#endif
66
#ifndef STATUS_INVALID_PARAMETER
67
# define STATUS_INVALID_PARAMETER 0xC000000D
68
#endif
69
#ifndef STATUS_INVALID_HANDLE
70
# define STATUS_INVALID_HANDLE 0xC0000008
71
#endif
72
#endif
73
#endif  // Win32
74
75
#ifdef CRYPTOPP_UNIX_AVAILABLE
76
#include <errno.h>
77
#include <fcntl.h>
78
#include <unistd.h>
79
#endif
80
81
NAMESPACE_BEGIN(CryptoPP)
82
83
#if defined(NONBLOCKING_RNG_AVAILABLE) || defined(BLOCKING_RNG_AVAILABLE)
84
OS_RNG_Err::OS_RNG_Err(const std::string &operation)
85
  : Exception(OTHER_ERROR, "OS_Rng: " + operation + " operation failed with error " +
86
#ifdef CRYPTOPP_WIN32_AVAILABLE
87
    "0x" + IntToString(GetLastError(), 16)
88
#else
89
    IntToString(errno)
90
#endif
91
    )
92
0
{
93
0
}
94
#endif
95
96
#ifdef NONBLOCKING_RNG_AVAILABLE
97
98
#ifdef CRYPTOPP_WIN32_AVAILABLE
99
100
#if defined(USE_MS_CNGAPI)
101
inline DWORD NtStatusToErrorCode(NTSTATUS status)
102
{
103
  if (status == static_cast<NTSTATUS>(STATUS_INVALID_PARAMETER))
104
    return ERROR_INVALID_PARAMETER;
105
  else if (status == static_cast<NTSTATUS>(STATUS_INVALID_HANDLE))
106
    return ERROR_INVALID_HANDLE;
107
  else
108
    return static_cast<DWORD>(status);
109
}
110
#endif
111
112
#if defined(UNICODE) || defined(_UNICODE)
113
# define CRYPTOPP_CONTAINER L"Crypto++ RNG"
114
#else
115
# define CRYPTOPP_CONTAINER "Crypto++ RNG"
116
#endif
117
118
MicrosoftCryptoProvider::MicrosoftCryptoProvider() : m_hProvider(0)
119
{
120
#if defined(USE_MS_CRYPTOAPI)
121
  // See http://support.microsoft.com/en-us/kb/238187 for CRYPT_NEWKEYSET fallback strategy
122
  if (!CryptAcquireContext(&m_hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
123
  {
124
    const DWORD firstErr = GetLastError();
125
    if (!CryptAcquireContext(&m_hProvider, CRYPTOPP_CONTAINER, 0, PROV_RSA_FULL, CRYPT_NEWKEYSET /*user*/) &&
126
        !CryptAcquireContext(&m_hProvider, CRYPTOPP_CONTAINER, 0, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET|CRYPT_NEWKEYSET))
127
    {
128
      // Set original error with original code
129
      SetLastError(firstErr);
130
      throw OS_RNG_Err("CryptAcquireContext");
131
    }
132
  }
133
#elif defined(USE_MS_CNGAPI)
134
  NTSTATUS ret = BCryptOpenAlgorithmProvider(&m_hProvider, BCRYPT_RNG_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0);
135
  if (!(BCRYPT_SUCCESS(ret)))
136
  {
137
    // Hack... OS_RNG_Err calls GetLastError()
138
    SetLastError(NtStatusToErrorCode(ret));
139
    throw OS_RNG_Err("BCryptOpenAlgorithmProvider");
140
  }
141
#endif
142
}
143
144
MicrosoftCryptoProvider::~MicrosoftCryptoProvider()
145
{
146
#if defined(USE_MS_CRYPTOAPI)
147
  if (m_hProvider)
148
    CryptReleaseContext(m_hProvider, 0);
149
#elif defined(USE_MS_CNGAPI)
150
  if (m_hProvider)
151
    BCryptCloseAlgorithmProvider(m_hProvider, 0);
152
#endif
153
}
154
155
#endif  // CRYPTOPP_WIN32_AVAILABLE
156
157
NonblockingRng::NonblockingRng()
158
28
{
159
28
#if !defined(CRYPTOPP_WIN32_AVAILABLE) && !defined(USE_FREEBSD_ARC4RANDOM)
160
# ifndef DONT_USE_O_NOFOLLOW
161
  const int flags = O_RDONLY|O_NOFOLLOW;
162
# else
163
28
  const int flags = O_RDONLY;
164
28
# endif
165
166
28
  m_fd = open("/dev/urandom", flags);
167
28
  if (m_fd == -1)
168
0
    throw OS_RNG_Err("open /dev/urandom");
169
170
28
#endif
171
28
}
172
173
NonblockingRng::~NonblockingRng()
174
28
{
175
28
#if !defined(CRYPTOPP_WIN32_AVAILABLE) && !defined(USE_FREEBSD_ARC4RANDOM)
176
28
  close(m_fd);
177
28
#endif
178
28
}
179
180
void NonblockingRng::GenerateBlock(byte *output, size_t size)
181
28
{
182
#ifdef CRYPTOPP_WIN32_AVAILABLE
183
  // Acquiring a provider is expensive. Do it once and retain the reference.
184
# if defined(CRYPTOPP_CXX11_STATIC_INIT)
185
  static const MicrosoftCryptoProvider hProvider = MicrosoftCryptoProvider();
186
# else
187
  const MicrosoftCryptoProvider &hProvider = Singleton<MicrosoftCryptoProvider>().Ref();
188
# endif
189
# if defined(USE_MS_CRYPTOAPI)
190
  DWORD dwSize;
191
  CRYPTOPP_ASSERT(SafeConvert(size, dwSize));
192
  if (!SafeConvert(size, dwSize))
193
  {
194
    SetLastError(ERROR_INCORRECT_SIZE);
195
    throw OS_RNG_Err("GenerateBlock size");
196
  }
197
  BOOL ret = CryptGenRandom(hProvider.GetProviderHandle(), dwSize, output);
198
  CRYPTOPP_ASSERT(ret != FALSE);
199
  if (ret == FALSE)
200
    throw OS_RNG_Err("CryptGenRandom");
201
# elif defined(USE_MS_CNGAPI)
202
  ULONG ulSize;
203
  CRYPTOPP_ASSERT(SafeConvert(size, ulSize));
204
  if (!SafeConvert(size, ulSize))
205
  {
206
    SetLastError(ERROR_INCORRECT_SIZE);
207
    throw OS_RNG_Err("GenerateBlock size");
208
  }
209
  NTSTATUS ret = BCryptGenRandom(hProvider.GetProviderHandle(), output, ulSize, 0);
210
  CRYPTOPP_ASSERT(BCRYPT_SUCCESS(ret));
211
  if (!(BCRYPT_SUCCESS(ret)))
212
  {
213
    // Hack... OS_RNG_Err calls GetLastError()
214
    SetLastError(NtStatusToErrorCode(ret));
215
    throw OS_RNG_Err("BCryptGenRandom");
216
  }
217
# endif
218
#else
219
220
# if defined(USE_FREEBSD_ARC4RANDOM)
221
  // Cryptographic quality prng based on ChaCha20,
222
  // https://www.freebsd.org/cgi/man.cgi?query=arc4random_buf
223
  arc4random_buf(output, size);
224
# else
225
56
  while (size)
226
28
  {
227
28
    ssize_t len = read(m_fd, output, size);
228
28
    if (len < 0)
229
0
    {
230
      // /dev/urandom reads CAN give EAGAIN errors! (maybe EINTR as well)
231
0
      if (errno != EINTR && errno != EAGAIN)
232
0
        throw OS_RNG_Err("read /dev/urandom");
233
234
0
      continue;
235
0
    }
236
28
    output += len;
237
28
    size -= len;
238
28
  }
239
28
# endif  // USE_FREEBSD_ARC4RANDOM
240
241
28
#endif  // CRYPTOPP_WIN32_AVAILABLE
242
28
}
243
244
#endif  // NONBLOCKING_RNG_AVAILABLE
245
246
// *************************************************************
247
248
#ifdef BLOCKING_RNG_AVAILABLE
249
250
#ifndef CRYPTOPP_BLOCKING_RNG_FILENAME
251
# ifdef __OpenBSD__
252
#  define CRYPTOPP_BLOCKING_RNG_FILENAME "/dev/srandom"
253
# else
254
0
#  define CRYPTOPP_BLOCKING_RNG_FILENAME "/dev/random"
255
# endif
256
#endif
257
258
BlockingRng::BlockingRng()
259
0
{
260
#ifndef DONT_USE_O_NOFOLLOW
261
  const int flags = O_RDONLY|O_NOFOLLOW;
262
#else
263
0
  const int flags = O_RDONLY;
264
0
#endif
265
266
0
  m_fd = open(CRYPTOPP_BLOCKING_RNG_FILENAME, flags);
267
0
  if (m_fd == -1)
268
0
    throw OS_RNG_Err("open " CRYPTOPP_BLOCKING_RNG_FILENAME);
269
0
}
270
271
BlockingRng::~BlockingRng()
272
0
{
273
0
  close(m_fd);
274
0
}
275
276
void BlockingRng::GenerateBlock(byte *output, size_t size)
277
0
{
278
0
  while (size)
279
0
  {
280
    // on some systems /dev/random will block until all bytes
281
    // are available, on others it returns immediately
282
0
    ssize_t len = read(m_fd, output, size);
283
0
    if (len < 0)
284
0
    {
285
      // /dev/random reads CAN give EAGAIN errors! (maybe EINTR as well)
286
0
      if (errno != EINTR && errno != EAGAIN)
287
0
        throw OS_RNG_Err("read " CRYPTOPP_BLOCKING_RNG_FILENAME);
288
289
0
      continue;
290
0
    }
291
292
0
    size -= len;
293
0
    output += len;
294
0
    if (size)
295
0
      sleep(1);
296
0
  }
297
0
}
298
299
#endif  // BLOCKING_RNG_AVAILABLE
300
301
// *************************************************************
302
303
void OS_GenerateRandomBlock(bool blocking, byte *output, size_t size)
304
28
{
305
28
#ifdef NONBLOCKING_RNG_AVAILABLE
306
28
  if (blocking)
307
0
#endif
308
0
  {
309
0
#ifdef BLOCKING_RNG_AVAILABLE
310
0
    BlockingRng rng;
311
0
    rng.GenerateBlock(output, size);
312
0
#endif
313
0
  }
314
315
28
#ifdef BLOCKING_RNG_AVAILABLE
316
28
  if (!blocking)
317
28
#endif
318
28
  {
319
28
#ifdef NONBLOCKING_RNG_AVAILABLE
320
28
    NonblockingRng rng;
321
28
    rng.GenerateBlock(output, size);
322
28
#endif
323
28
  }
324
28
}
325
326
void AutoSeededRandomPool::Reseed(bool blocking, unsigned int seedSize)
327
28
{
328
28
  SecByteBlock seed(seedSize);
329
28
  OS_GenerateRandomBlock(blocking, seed, seedSize);
330
28
  IncorporateEntropy(seed, seedSize);
331
28
}
332
333
NAMESPACE_END
334
335
#endif  // OS_RNG_AVAILABLE
336
337
#endif  // CRYPTOPP_IMPORTS