blob: cdace0036b0ae13f90ac9d54208a38cff9dee96a [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>
6#endif
7
Benjamin Peterson69e97272012-02-21 11:08:50 -05008#ifdef Py_DEBUG
9int _Py_HashSecret_Initialized = 0;
10#else
11static int _Py_HashSecret_Initialized = 0;
12#endif
Georg Brandl2daf6ae2012-02-20 19:54:16 +010013
14#ifdef MS_WINDOWS
Georg Brandl2daf6ae2012-02-20 19:54:16 +010015/* This handle is never explicitly released. Instead, the operating
16 system will release it when the process terminates. */
17static HCRYPTPROV hCryptProv = 0;
18
19static int
20win32_urandom_init(int raise)
21{
Georg Brandl2daf6ae2012-02-20 19:54:16 +010022 /* Acquire context */
Martin v. Löwis3f50bf62013-01-25 14:06:18 +010023 if (!CryptAcquireContext(&hCryptProv, NULL, NULL,
24 PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
Georg Brandl2daf6ae2012-02-20 19:54:16 +010025 goto error;
26
27 return 0;
28
29error:
30 if (raise)
31 PyErr_SetFromWindowsErr(0);
32 else
33 Py_FatalError("Failed to initialize Windows random API (CryptoGen)");
34 return -1;
35}
36
37/* Fill buffer with size pseudo-random bytes generated by the Windows CryptoGen
38 API. Return 0 on success, or -1 on error. */
39static int
40win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
41{
42 Py_ssize_t chunk;
43
44 if (hCryptProv == 0)
45 {
46 if (win32_urandom_init(raise) == -1)
47 return -1;
48 }
49
50 while (size > 0)
51 {
52 chunk = size > INT_MAX ? INT_MAX : size;
Victor Stinner0c083462013-11-15 23:26:25 +010053 if (!CryptGenRandom(hCryptProv, (DWORD)chunk, buffer))
Georg Brandl2daf6ae2012-02-20 19:54:16 +010054 {
55 /* CryptGenRandom() failed */
56 if (raise)
57 PyErr_SetFromWindowsErr(0);
58 else
59 Py_FatalError("Failed to initialized the randomized hash "
60 "secret using CryptoGen)");
61 return -1;
62 }
63 buffer += chunk;
64 size -= chunk;
65 }
66 return 0;
67}
68#endif /* MS_WINDOWS */
69
70
Christian Heimesaf01f662013-12-21 16:19:10 +010071#ifndef MS_WINDOWS
Antoine Pitrou4879a962013-08-31 00:26:02 +020072static int urandom_fd = -1;
Georg Brandl2daf6ae2012-02-20 19:54:16 +010073
74/* Read size bytes from /dev/urandom into buffer.
75 Call Py_FatalError() on error. */
76static void
Christian Heimes985ecdc2013-11-20 11:46:18 +010077dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
Georg Brandl2daf6ae2012-02-20 19:54:16 +010078{
79 int fd;
80 Py_ssize_t n;
81
82 assert (0 < size);
83
Victor Stinnerdaf45552013-08-28 00:53:59 +020084 fd = _Py_open("/dev/urandom", O_RDONLY);
Georg Brandl2daf6ae2012-02-20 19:54:16 +010085 if (fd < 0)
86 Py_FatalError("Failed to open /dev/urandom");
87
88 while (0 < size)
89 {
90 do {
91 n = read(fd, buffer, (size_t)size);
92 } while (n < 0 && errno == EINTR);
93 if (n <= 0)
94 {
95 /* stop on error or if read(size) returned 0 */
96 Py_FatalError("Failed to read bytes from /dev/urandom");
97 break;
98 }
99 buffer += n;
100 size -= (Py_ssize_t)n;
101 }
102 close(fd);
103}
104
105/* Read size bytes from /dev/urandom into buffer.
106 Return 0 on success, raise an exception and return -1 on error. */
107static int
108dev_urandom_python(char *buffer, Py_ssize_t size)
109{
110 int fd;
111 Py_ssize_t n;
112
113 if (size <= 0)
114 return 0;
115
Antoine Pitrou4879a962013-08-31 00:26:02 +0200116 if (urandom_fd >= 0)
117 fd = urandom_fd;
118 else {
119 Py_BEGIN_ALLOW_THREADS
120 fd = _Py_open("/dev/urandom", O_RDONLY);
121 Py_END_ALLOW_THREADS
122 if (fd < 0)
123 {
124 if (errno == ENOENT || errno == ENXIO ||
125 errno == ENODEV || errno == EACCES)
126 PyErr_SetString(PyExc_NotImplementedError,
127 "/dev/urandom (or equivalent) not found");
128 else
129 PyErr_SetFromErrno(PyExc_OSError);
130 return -1;
131 }
132 if (urandom_fd >= 0) {
133 /* urandom_fd was initialized by another thread while we were
134 not holding the GIL, keep it. */
135 close(fd);
136 fd = urandom_fd;
137 }
Antoine Pitrouec34ab52013-08-16 20:44:38 +0200138 else
Antoine Pitrou4879a962013-08-31 00:26:02 +0200139 urandom_fd = fd;
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100140 }
141
142 Py_BEGIN_ALLOW_THREADS
143 do {
144 do {
145 n = read(fd, buffer, (size_t)size);
146 } while (n < 0 && errno == EINTR);
147 if (n <= 0)
148 break;
149 buffer += n;
150 size -= (Py_ssize_t)n;
151 } while (0 < size);
152 Py_END_ALLOW_THREADS
153
154 if (n <= 0)
155 {
156 /* stop on error or if read(size) returned 0 */
157 if (n < 0)
158 PyErr_SetFromErrno(PyExc_OSError);
159 else
160 PyErr_Format(PyExc_RuntimeError,
161 "Failed to read %zi bytes from /dev/urandom",
162 size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100163 return -1;
164 }
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100165 return 0;
166}
Antoine Pitrou4879a962013-08-31 00:26:02 +0200167
168static void
169dev_urandom_close(void)
170{
171 if (urandom_fd >= 0) {
172 close(urandom_fd);
173 urandom_fd = -1;
174 }
175}
176
Christian Heimesaf01f662013-12-21 16:19:10 +0100177#endif /* MS_WINDOWS */
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100178
179/* Fill buffer with pseudo-random bytes generated by a linear congruent
180 generator (LCG):
181
182 x(n+1) = (x(n) * 214013 + 2531011) % 2^32
183
184 Use bits 23..16 of x(n) to generate a byte. */
185static void
186lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
187{
188 size_t index;
189 unsigned int x;
190
191 x = x0;
192 for (index=0; index < size; index++) {
193 x *= 214013;
194 x += 2531011;
195 /* modulo 2 ^ (8 * sizeof(int)) */
196 buffer[index] = (x >> 16) & 0xff;
197 }
198}
199
Georg Brandlc6a2c9b2013-10-06 18:43:19 +0200200/* Fill buffer with size pseudo-random bytes from the operating system random
201 number generator (RNG). It is suitable for for most cryptographic purposes
202 except long living private keys for asymmetric encryption.
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100203
204 Return 0 on success, raise an exception and return -1 on error. */
205int
206_PyOS_URandom(void *buffer, Py_ssize_t size)
207{
208 if (size < 0) {
209 PyErr_Format(PyExc_ValueError,
210 "negative argument not allowed");
211 return -1;
212 }
213 if (size == 0)
214 return 0;
215
216#ifdef MS_WINDOWS
217 return win32_urandom((unsigned char *)buffer, size, 1);
218#else
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100219 return dev_urandom_python((char*)buffer, size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100220#endif
221}
222
223void
224_PyRandom_Init(void)
225{
226 char *env;
Christian Heimes985ecdc2013-11-20 11:46:18 +0100227 unsigned char *secret = (unsigned char *)&_Py_HashSecret.uc;
Benjamin Peterson69e97272012-02-21 11:08:50 -0500228 Py_ssize_t secret_size = sizeof(_Py_HashSecret_t);
Christian Heimes985ecdc2013-11-20 11:46:18 +0100229 assert(secret_size == sizeof(_Py_HashSecret.uc));
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100230
Benjamin Peterson69e97272012-02-21 11:08:50 -0500231 if (_Py_HashSecret_Initialized)
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100232 return;
Benjamin Peterson69e97272012-02-21 11:08:50 -0500233 _Py_HashSecret_Initialized = 1;
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100234
235 /*
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100236 Hash randomization is enabled. Generate a per-process secret,
237 using PYTHONHASHSEED if provided.
238 */
239
240 env = Py_GETENV("PYTHONHASHSEED");
Georg Brandl12897d72012-02-20 23:49:29 +0100241 if (env && *env != '\0' && strcmp(env, "random") != 0) {
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100242 char *endptr = env;
243 unsigned long seed;
244 seed = strtoul(env, &endptr, 10);
245 if (*endptr != '\0'
246 || seed > 4294967295UL
247 || (errno == ERANGE && seed == ULONG_MAX))
248 {
249 Py_FatalError("PYTHONHASHSEED must be \"random\" or an integer "
250 "in range [0; 4294967295]");
251 }
252 if (seed == 0) {
253 /* disable the randomized hash */
254 memset(secret, 0, secret_size);
255 }
256 else {
Christian Heimes985ecdc2013-11-20 11:46:18 +0100257 lcg_urandom(seed, secret, secret_size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100258 }
259 }
260 else {
261#ifdef MS_WINDOWS
Christian Heimes985ecdc2013-11-20 11:46:18 +0100262 (void)win32_urandom(secret, secret_size, 0);
Christian Heimesaf01f662013-12-21 16:19:10 +0100263#else
Christian Heimes985ecdc2013-11-20 11:46:18 +0100264 dev_urandom_noraise(secret, secret_size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100265#endif
266 }
267}
Antoine Pitrou4879a962013-08-31 00:26:02 +0200268
269void
270_PyRandom_Fini(void)
271{
Christian Heimesaf01f662013-12-21 16:19:10 +0100272#ifndef MS_WINDOWS
Antoine Pitrou4879a962013-08-31 00:26:02 +0200273 dev_urandom_close();
274#endif
275}