Botan 2.1.0 Crypto and TLS for C++11

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

system_rng.cpp
Go to the documentation of this file.
1 /*
2 * System RNG
3 * (C) 2014,2015 Jack Lloyd
4 *
5 * Botan is released under the Simplified BSD License (see license.txt)
6 */
7
8 #include <botan/system_rng.h>
9 #include <botan/build.h>
10
11 #if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM)
12
13 #include <windows.h>
14 #define NOMINMAX 1
15 #include <wincrypt.h>
16
17 #elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM)
18
19 #include <stdlib.h>
20
21 #else
22
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <errno.h>
28
29 #endif
30
31 namespace Botan {
32
33 namespace {
34
35 class System_RNG_Impl final : public RandomNumberGenerator
36 {
37 public:
38 System_RNG_Impl();
39 ~System_RNG_Impl();
40
41 bool is_seeded() const override { return true; }
42
43 void clear() override {}
44
45 void randomize(uint8_t out[], size_t len) override;
46
47 void add_entropy(const uint8_t in[], size_t length) override;
48
49 std::string name() const override;
50
51 private:
52 #if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM)
53 HCRYPTPROV m_prov;
54 #else
55 int m_fd;
56 #endif
57 };
58
59 std::string System_RNG_Impl::name() const
60 {
61 #if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM)
62 return "cryptoapi";
63 #elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM)
64 return "arc4random";
65 #else
66 return BOTAN_SYSTEM_RNG_DEVICE;
67 #endif
68 }
69
70 System_RNG_Impl::System_RNG_Impl()
71 {
72 #if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM)
73
74 if(!CryptAcquireContext(&m_prov, 0, 0, BOTAN_SYSTEM_RNG_CRYPTOAPI_PROV_TYPE, CRYPT_VERIFYCONTEXT))
75 throw Exception("System_RNG failed to acquire crypto provider");
76
77 #elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM)
78 // Nothing to do, arc4random(3) works from the beginning.
79 #else
80
81 #ifndef O_NOCTTY
82 #define O_NOCTTY 0
83 #endif
84
85 m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDWR | O_NOCTTY);
86
87 // Cannot open in read-write mode. Fall back to read-only
88 // Calls to add_entropy will fail, but randomize will work
89 if(m_fd < 0)
90 m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDONLY | O_NOCTTY);
91
92 if(m_fd < 0)
93 throw Exception("System_RNG failed to open RNG device");
94 #endif
95 }
96
97 System_RNG_Impl::~System_RNG_Impl()
98 {
99 #if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM)
100 ::CryptReleaseContext(m_prov, 0);
101 #elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM)
102 // Nothing to do.
103 #else
104 ::close(m_fd);
105 m_fd = -1;
106 #endif
107 }
108
109 void System_RNG_Impl::add_entropy(const uint8_t input[], size_t len)
110 {
111 #if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM)
112 /*
113 There is no explicit ConsumeRandom, but all values provided in
114 the call are incorporated into the state.
115
116 TODO: figure out a way to avoid this copy. Byte at a time updating
117 seems worse than the allocation.
118
119 for(size_t i = 0; i != len; ++i)
120 {
121 uint8_t b = input[i];
122 ::CryptGenRandom(m_prov, 1, &b);
123 }
124 */
125
126 if(len > 0)
127 {
128 secure_vector<uint8_t> buf(input, input + len);
129 ::CryptGenRandom(m_prov, static_cast<DWORD>(buf.size()), buf.data());
130 }
131 #elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM)
132 // arc4random(3) reseeds itself from the OpenBSD kernel, not from user land.
133 #else
134 while(len)
135 {
136 ssize_t got = ::write(m_fd, input, len);
137
138 if(got < 0)
139 {
140 if(errno == EINTR)
141 continue;
142
143 /*
144 * This is seen on OS X CI, despite the fact that the man page
145 * for Darwin urandom explicitly states that writing to it is
146 * supported, and write(2) does not document EPERM at all.
147 * But in any case EPERM seems indicative of a policy decision
148 * by the OS or sysadmin that additional entropy is not wanted
149 * in the system pool, so we accept that and return here,
150 * since there is no corrective action possible.
151 *
152 * In Linux EBADF or EPERM is returned if m_fd is not opened for
153 * writing.
154 */
155 if(errno == EPERM || errno == EBADF)
156 return;
157
158 // maybe just ignore any failure here and return?
159 throw Exception("System_RNG write failed error " + std::to_string(errno));
160 }
161
162 input += got;
163 len -= got;
164 }
165 #endif
166 }
167
168 void System_RNG_Impl::randomize(uint8_t buf[], size_t len)
169 {
170 #if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM)
171 ::CryptGenRandom(m_prov, static_cast<DWORD>(len), buf);
172 #elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM)
173 ::arc4random_buf(buf, len);
174 #else
175 while(len)
176 {
177 ssize_t got = ::read(m_fd, buf, len);
178
179 if(got < 0)
180 {
181 if(errno == EINTR)
182 continue;
183 throw Exception("System_RNG read failed error " + std::to_string(errno));
184 }
185 if(got == 0)
186 throw Exception("System_RNG EOF on device"); // ?!?
187
188 buf += got;
189 len -= got;
190 }
191 #endif
192 }
193
194 }
195
196 RandomNumberGenerator& system_rng()
197 {
198 static System_RNG_Impl g_system_rng;
199 return g_system_rng;
200 }
201
202 }
Botan::RandomNumberGenerator
Definition: rng.h:25
Botan::ASN1::to_string
std::string to_string(const BER_Object &obj)
Definition: asn1_obj.cpp:47
Botan
Definition: alg_id.cpp:13
O_NOCTTY
#define O_NOCTTY
Botan::system_rng
RandomNumberGenerator & system_rng()
Definition: system_rng.cpp:196
m_fd
int m_fd
Definition: system_rng.cpp:55