Crypto++
osrng.cpp
1 // osrng.cpp - 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 
7 #ifndef CRYPTOPP_IMPORTS
8 
9 #include "osrng.h"
10 
11 #ifdef OS_RNG_AVAILABLE
12 
13 #include "rng.h"
14 
15 #ifdef CRYPTOPP_WIN32_AVAILABLE
16 #ifndef _WIN32_WINNT
17 #define _WIN32_WINNT 0x0400
18 #endif
19 #include <windows.h>
20 #include <wincrypt.h>
21 #endif
22 
23 #ifdef CRYPTOPP_UNIX_AVAILABLE
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #endif
28 
29 NAMESPACE_BEGIN(CryptoPP)
30 
31 #if defined(NONBLOCKING_RNG_AVAILABLE) || defined(BLOCKING_RNG_AVAILABLE)
32 OS_RNG_Err::OS_RNG_Err(const std::string &operation)
33  : Exception(OTHER_ERROR, "OS_Rng: " + operation + " operation failed with error " +
34 #ifdef CRYPTOPP_WIN32_AVAILABLE
35  "0x" + IntToString(GetLastError(), 16)
36 #else
37  IntToString(errno)
38 #endif
39  )
40 {
41 }
42 #endif
43 
44 #ifdef NONBLOCKING_RNG_AVAILABLE
45 
46 #ifdef CRYPTOPP_WIN32_AVAILABLE
47 
48 MicrosoftCryptoProvider::MicrosoftCryptoProvider()
49 {
50  if(!CryptAcquireContext(&m_hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
51  throw OS_RNG_Err("CryptAcquireContext");
52 }
53 
54 MicrosoftCryptoProvider::~MicrosoftCryptoProvider()
55 {
56  CryptReleaseContext(m_hProvider, 0);
57 }
58 
59 #endif
60 
61 NonblockingRng::NonblockingRng()
62 {
63 #ifndef CRYPTOPP_WIN32_AVAILABLE
64  m_fd = open("/dev/urandom",O_RDONLY);
65  if (m_fd == -1)
66  throw OS_RNG_Err("open /dev/urandom");
67 #endif
68 }
69 
70 NonblockingRng::~NonblockingRng()
71 {
72 #ifndef CRYPTOPP_WIN32_AVAILABLE
73  close(m_fd);
74 #endif
75 }
76 
77 void NonblockingRng::GenerateBlock(byte *output, size_t size)
78 {
79 #ifdef CRYPTOPP_WIN32_AVAILABLE
80 # ifdef WORKAROUND_MS_BUG_Q258000
82 # endif
83  if (!CryptGenRandom(m_Provider.GetProviderHandle(), (DWORD)size, output))
84  throw OS_RNG_Err("CryptGenRandom");
85 #else
86  if (read(m_fd, output, size) != size)
87  throw OS_RNG_Err("read /dev/urandom");
88 #endif
89 }
90 
91 #endif
92 
93 // *************************************************************
94 
95 #ifdef BLOCKING_RNG_AVAILABLE
96 
97 #ifndef CRYPTOPP_BLOCKING_RNG_FILENAME
98 #ifdef __OpenBSD__
99 #define CRYPTOPP_BLOCKING_RNG_FILENAME "/dev/srandom"
100 #else
101 #define CRYPTOPP_BLOCKING_RNG_FILENAME "/dev/random"
102 #endif
103 #endif
104 
105 BlockingRng::BlockingRng()
106 {
107  m_fd = open(CRYPTOPP_BLOCKING_RNG_FILENAME,O_RDONLY);
108  if (m_fd == -1)
109  throw OS_RNG_Err("open " CRYPTOPP_BLOCKING_RNG_FILENAME);
110 }
111 
112 BlockingRng::~BlockingRng()
113 {
114  close(m_fd);
115 }
116 
117 void BlockingRng::GenerateBlock(byte *output, size_t size)
118 {
119  while (size)
120  {
121  // on some systems /dev/random will block until all bytes
122  // are available, on others it will returns immediately
123  ssize_t len = read(m_fd, output, size);
124  if (len < 0)
125  throw OS_RNG_Err("read " CRYPTOPP_BLOCKING_RNG_FILENAME);
126  size -= len;
127  output += len;
128  if (size)
129  sleep(1);
130  }
131 }
132 
133 #endif
134 
135 // *************************************************************
136 
137 void OS_GenerateRandomBlock(bool blocking, byte *output, size_t size)
138 {
139 #ifdef NONBLOCKING_RNG_AVAILABLE
140  if (blocking)
141 #endif
142  {
143 #ifdef BLOCKING_RNG_AVAILABLE
144  BlockingRng rng;
145  rng.GenerateBlock(output, size);
146 #endif
147  }
148 
149 #ifdef BLOCKING_RNG_AVAILABLE
150  if (!blocking)
151 #endif
152  {
153 #ifdef NONBLOCKING_RNG_AVAILABLE
154  NonblockingRng rng;
155  rng.GenerateBlock(output, size);
156 #endif
157  }
158 }
159 
160 void AutoSeededRandomPool::Reseed(bool blocking, unsigned int seedSize)
161 {
162  SecByteBlock seed(seedSize);
163  OS_GenerateRandomBlock(blocking, seed, seedSize);
164  IncorporateEntropy(seed, seedSize);
165 }
166 
167 NAMESPACE_END
168 
169 #endif
170 
171 #endif