blob: 93d300dae45a5cee48ec35c850a8f2d7d1705707 [file] [log] [blame]
Georg Brandl2daf6ae2012-02-20 19:54:16 +01001#include "Python.h"
2#ifdef MS_WINDOWS
3#include <windows.h>
4#else
5#include <fcntl.h>
Antoine Pitroue472aea2014-04-26 14:33:03 +02006#ifdef HAVE_SYS_STAT_H
7#include <sys/stat.h>
8#endif
Georg Brandl2daf6ae2012-02-20 19:54:16 +01009#endif
10
Benjamin Peterson69e97272012-02-21 11:08:50 -050011#ifdef Py_DEBUG
12int _Py_HashSecret_Initialized = 0;
13#else
14static int _Py_HashSecret_Initialized = 0;
15#endif
Georg Brandl2daf6ae2012-02-20 19:54:16 +010016
17#ifdef MS_WINDOWS
Georg Brandl2daf6ae2012-02-20 19:54:16 +010018static HCRYPTPROV hCryptProv = 0;
19
20static int
21win32_urandom_init(int raise)
22{
Georg Brandl2daf6ae2012-02-20 19:54:16 +010023 /* Acquire context */
Martin v. Löwis3f50bf62013-01-25 14:06:18 +010024 if (!CryptAcquireContext(&hCryptProv, NULL, NULL,
25 PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
Georg Brandl2daf6ae2012-02-20 19:54:16 +010026 goto error;
27
28 return 0;
29
30error:
31 if (raise)
32 PyErr_SetFromWindowsErr(0);
33 else
34 Py_FatalError("Failed to initialize Windows random API (CryptoGen)");
35 return -1;
36}
37
38/* Fill buffer with size pseudo-random bytes generated by the Windows CryptoGen
Victor Stinner4d6a3d62014-12-21 01:16:38 +010039 API. Return 0 on success, or raise an exception and return -1 on error. */
Georg Brandl2daf6ae2012-02-20 19:54:16 +010040static int
41win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
42{
43 Py_ssize_t chunk;
44
45 if (hCryptProv == 0)
46 {
47 if (win32_urandom_init(raise) == -1)
48 return -1;
49 }
50
51 while (size > 0)
52 {
53 chunk = size > INT_MAX ? INT_MAX : size;
Victor Stinner0c083462013-11-15 23:26:25 +010054 if (!CryptGenRandom(hCryptProv, (DWORD)chunk, buffer))
Georg Brandl2daf6ae2012-02-20 19:54:16 +010055 {
56 /* CryptGenRandom() failed */
57 if (raise)
58 PyErr_SetFromWindowsErr(0);
59 else
60 Py_FatalError("Failed to initialized the randomized hash "
61 "secret using CryptoGen)");
62 return -1;
63 }
64 buffer += chunk;
65 size -= chunk;
66 }
67 return 0;
68}
Georg Brandl2daf6ae2012-02-20 19:54:16 +010069
Victor Stinner4d6a3d62014-12-21 01:16:38 +010070#elif HAVE_GETENTROPY
71/* Fill buffer with size pseudo-random bytes generated by getentropy().
72 Return 0 on success, or raise an exception and return -1 on error.
Georg Brandl2daf6ae2012-02-20 19:54:16 +010073
Victor Stinner4d6a3d62014-12-21 01:16:38 +010074 If fatal is nonzero, call Py_FatalError() instead of raising an exception
75 on error. */
76static int
77py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal)
78{
79 while (size > 0) {
80 Py_ssize_t len = Py_MIN(size, 256);
81 int res = getentropy(buffer, len);
82 if (res < 0) {
83 if (fatal) {
84 Py_FatalError("getentropy() failed");
85 }
86 else {
87 PyErr_SetFromErrno(PyExc_OSError);
88 return -1;
89 }
90 }
91 buffer += len;
92 size -= len;
93 }
94 return 0;
95}
96
97#else
Antoine Pitroue472aea2014-04-26 14:33:03 +020098static struct {
99 int fd;
100 dev_t st_dev;
101 ino_t st_ino;
102} urandom_cache = { -1 };
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100103
104/* Read size bytes from /dev/urandom into buffer.
105 Call Py_FatalError() on error. */
106static void
Christian Heimes985ecdc2013-11-20 11:46:18 +0100107dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100108{
109 int fd;
110 Py_ssize_t n;
111
112 assert (0 < size);
113
Victor Stinnerdaf45552013-08-28 00:53:59 +0200114 fd = _Py_open("/dev/urandom", O_RDONLY);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100115 if (fd < 0)
116 Py_FatalError("Failed to open /dev/urandom");
117
118 while (0 < size)
119 {
120 do {
121 n = read(fd, buffer, (size_t)size);
122 } while (n < 0 && errno == EINTR);
123 if (n <= 0)
124 {
125 /* stop on error or if read(size) returned 0 */
126 Py_FatalError("Failed to read bytes from /dev/urandom");
127 break;
128 }
129 buffer += n;
130 size -= (Py_ssize_t)n;
131 }
132 close(fd);
133}
134
135/* Read size bytes from /dev/urandom into buffer.
136 Return 0 on success, raise an exception and return -1 on error. */
137static int
138dev_urandom_python(char *buffer, Py_ssize_t size)
139{
140 int fd;
141 Py_ssize_t n;
Antoine Pitroue472aea2014-04-26 14:33:03 +0200142 struct stat st;
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100143
144 if (size <= 0)
145 return 0;
146
Antoine Pitroue472aea2014-04-26 14:33:03 +0200147 if (urandom_cache.fd >= 0) {
148 /* Does the fd point to the same thing as before? (issue #21207) */
149 if (fstat(urandom_cache.fd, &st)
150 || st.st_dev != urandom_cache.st_dev
151 || st.st_ino != urandom_cache.st_ino) {
152 /* Something changed: forget the cached fd (but don't close it,
153 since it probably points to something important for some
154 third-party code). */
155 urandom_cache.fd = -1;
156 }
157 }
158 if (urandom_cache.fd >= 0)
159 fd = urandom_cache.fd;
Antoine Pitrou4879a962013-08-31 00:26:02 +0200160 else {
161 Py_BEGIN_ALLOW_THREADS
162 fd = _Py_open("/dev/urandom", O_RDONLY);
163 Py_END_ALLOW_THREADS
164 if (fd < 0)
165 {
166 if (errno == ENOENT || errno == ENXIO ||
167 errno == ENODEV || errno == EACCES)
168 PyErr_SetString(PyExc_NotImplementedError,
169 "/dev/urandom (or equivalent) not found");
170 else
171 PyErr_SetFromErrno(PyExc_OSError);
172 return -1;
173 }
Antoine Pitroue472aea2014-04-26 14:33:03 +0200174 if (urandom_cache.fd >= 0) {
Antoine Pitrou4879a962013-08-31 00:26:02 +0200175 /* urandom_fd was initialized by another thread while we were
176 not holding the GIL, keep it. */
177 close(fd);
Antoine Pitroue472aea2014-04-26 14:33:03 +0200178 fd = urandom_cache.fd;
Antoine Pitrou4879a962013-08-31 00:26:02 +0200179 }
Antoine Pitroue472aea2014-04-26 14:33:03 +0200180 else {
181 if (fstat(fd, &st)) {
182 PyErr_SetFromErrno(PyExc_OSError);
183 close(fd);
184 return -1;
185 }
186 else {
187 urandom_cache.fd = fd;
188 urandom_cache.st_dev = st.st_dev;
189 urandom_cache.st_ino = st.st_ino;
190 }
191 }
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100192 }
193
194 Py_BEGIN_ALLOW_THREADS
195 do {
196 do {
197 n = read(fd, buffer, (size_t)size);
198 } while (n < 0 && errno == EINTR);
199 if (n <= 0)
200 break;
201 buffer += n;
202 size -= (Py_ssize_t)n;
203 } while (0 < size);
204 Py_END_ALLOW_THREADS
205
206 if (n <= 0)
207 {
208 /* stop on error or if read(size) returned 0 */
209 if (n < 0)
210 PyErr_SetFromErrno(PyExc_OSError);
211 else
212 PyErr_Format(PyExc_RuntimeError,
213 "Failed to read %zi bytes from /dev/urandom",
214 size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100215 return -1;
216 }
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100217 return 0;
218}
Antoine Pitrou4879a962013-08-31 00:26:02 +0200219
220static void
221dev_urandom_close(void)
222{
Antoine Pitroue472aea2014-04-26 14:33:03 +0200223 if (urandom_cache.fd >= 0) {
224 close(urandom_cache.fd);
225 urandom_cache.fd = -1;
Antoine Pitrou4879a962013-08-31 00:26:02 +0200226 }
227}
228
Victor Stinner4d6a3d62014-12-21 01:16:38 +0100229#endif /* HAVE_GETENTROPY */
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100230
231/* Fill buffer with pseudo-random bytes generated by a linear congruent
232 generator (LCG):
233
234 x(n+1) = (x(n) * 214013 + 2531011) % 2^32
235
236 Use bits 23..16 of x(n) to generate a byte. */
237static void
238lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
239{
240 size_t index;
241 unsigned int x;
242
243 x = x0;
244 for (index=0; index < size; index++) {
245 x *= 214013;
246 x += 2531011;
247 /* modulo 2 ^ (8 * sizeof(int)) */
248 buffer[index] = (x >> 16) & 0xff;
249 }
250}
251
Georg Brandlc6a2c9b2013-10-06 18:43:19 +0200252/* Fill buffer with size pseudo-random bytes from the operating system random
Serhiy Storchaka56a6d852014-12-01 18:28:43 +0200253 number generator (RNG). It is suitable for most cryptographic purposes
Georg Brandlc6a2c9b2013-10-06 18:43:19 +0200254 except long living private keys for asymmetric encryption.
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100255
256 Return 0 on success, raise an exception and return -1 on error. */
257int
258_PyOS_URandom(void *buffer, Py_ssize_t size)
259{
260 if (size < 0) {
261 PyErr_Format(PyExc_ValueError,
262 "negative argument not allowed");
263 return -1;
264 }
265 if (size == 0)
266 return 0;
267
268#ifdef MS_WINDOWS
269 return win32_urandom((unsigned char *)buffer, size, 1);
Victor Stinner4d6a3d62014-12-21 01:16:38 +0100270#elif HAVE_GETENTROPY
271 return py_getentropy(buffer, size, 0);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100272#else
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100273 return dev_urandom_python((char*)buffer, size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100274#endif
275}
276
277void
278_PyRandom_Init(void)
279{
280 char *env;
Christian Heimes985ecdc2013-11-20 11:46:18 +0100281 unsigned char *secret = (unsigned char *)&_Py_HashSecret.uc;
Benjamin Peterson69e97272012-02-21 11:08:50 -0500282 Py_ssize_t secret_size = sizeof(_Py_HashSecret_t);
Christian Heimes985ecdc2013-11-20 11:46:18 +0100283 assert(secret_size == sizeof(_Py_HashSecret.uc));
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100284
Benjamin Peterson69e97272012-02-21 11:08:50 -0500285 if (_Py_HashSecret_Initialized)
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100286 return;
Benjamin Peterson69e97272012-02-21 11:08:50 -0500287 _Py_HashSecret_Initialized = 1;
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100288
289 /*
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100290 Hash randomization is enabled. Generate a per-process secret,
291 using PYTHONHASHSEED if provided.
292 */
293
294 env = Py_GETENV("PYTHONHASHSEED");
Georg Brandl12897d72012-02-20 23:49:29 +0100295 if (env && *env != '\0' && strcmp(env, "random") != 0) {
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100296 char *endptr = env;
297 unsigned long seed;
298 seed = strtoul(env, &endptr, 10);
299 if (*endptr != '\0'
300 || seed > 4294967295UL
301 || (errno == ERANGE && seed == ULONG_MAX))
302 {
303 Py_FatalError("PYTHONHASHSEED must be \"random\" or an integer "
304 "in range [0; 4294967295]");
305 }
306 if (seed == 0) {
307 /* disable the randomized hash */
308 memset(secret, 0, secret_size);
309 }
310 else {
Christian Heimes985ecdc2013-11-20 11:46:18 +0100311 lcg_urandom(seed, secret, secret_size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100312 }
313 }
314 else {
315#ifdef MS_WINDOWS
Christian Heimes985ecdc2013-11-20 11:46:18 +0100316 (void)win32_urandom(secret, secret_size, 0);
Victor Stinner4d6a3d62014-12-21 01:16:38 +0100317#elif HAVE_GETENTROPY
318 (void)py_getentropy(secret, secret_size, 1);
Christian Heimesaf01f662013-12-21 16:19:10 +0100319#else
Christian Heimes985ecdc2013-11-20 11:46:18 +0100320 dev_urandom_noraise(secret, secret_size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100321#endif
322 }
323}
Antoine Pitrou4879a962013-08-31 00:26:02 +0200324
325void
326_PyRandom_Fini(void)
327{
Victor Stinnerd50c3f32014-05-02 22:06:44 +0200328#ifdef MS_WINDOWS
329 if (hCryptProv) {
Tim Goldenb8ac3e12014-05-06 13:29:45 +0100330 CryptReleaseContext(hCryptProv, 0);
Victor Stinnerd50c3f32014-05-02 22:06:44 +0200331 hCryptProv = 0;
332 }
Victor Stinner4d6a3d62014-12-21 01:16:38 +0100333#elif HAVE_GETENTROPY
334 /* nothing to clean */
Victor Stinnerd50c3f32014-05-02 22:06:44 +0200335#else
Antoine Pitrou4879a962013-08-31 00:26:02 +0200336 dev_urandom_close();
337#endif
338}