blob: 3f307cf62a9f81cf19b4ef48d27ca5d9b653f261 [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 Stinnerfe02e392014-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 Stinnerfe02e392014-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 Stinnerfe02e392014-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);
Victor Stinner9aa13312015-03-30 11:18:30 +020081 int res;
82
83 if (!fatal) {
84 Py_BEGIN_ALLOW_THREADS
85 res = getentropy(buffer, len);
86 Py_END_ALLOW_THREADS
87
88 if (res < 0) {
Victor Stinnerfe02e392014-12-21 01:16:38 +010089 PyErr_SetFromErrno(PyExc_OSError);
90 return -1;
91 }
92 }
Victor Stinner9aa13312015-03-30 11:18:30 +020093 else {
94 res = getentropy(buffer, len);
95 if (res < 0)
96 Py_FatalError("getentropy() failed");
97 }
98
Victor Stinnerfe02e392014-12-21 01:16:38 +010099 buffer += len;
100 size -= len;
101 }
102 return 0;
103}
104
105#else
Antoine Pitroue472aea2014-04-26 14:33:03 +0200106static struct {
107 int fd;
108 dev_t st_dev;
109 ino_t st_ino;
110} urandom_cache = { -1 };
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100111
112/* Read size bytes from /dev/urandom into buffer.
113 Call Py_FatalError() on error. */
114static void
Christian Heimes985ecdc2013-11-20 11:46:18 +0100115dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100116{
117 int fd;
118 Py_ssize_t n;
119
120 assert (0 < size);
121
Victor Stinnerdaf45552013-08-28 00:53:59 +0200122 fd = _Py_open("/dev/urandom", O_RDONLY);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100123 if (fd < 0)
124 Py_FatalError("Failed to open /dev/urandom");
125
126 while (0 < size)
127 {
128 do {
129 n = read(fd, buffer, (size_t)size);
130 } while (n < 0 && errno == EINTR);
131 if (n <= 0)
132 {
133 /* stop on error or if read(size) returned 0 */
134 Py_FatalError("Failed to read bytes from /dev/urandom");
135 break;
136 }
137 buffer += n;
138 size -= (Py_ssize_t)n;
139 }
140 close(fd);
141}
142
143/* Read size bytes from /dev/urandom into buffer.
144 Return 0 on success, raise an exception and return -1 on error. */
145static int
146dev_urandom_python(char *buffer, Py_ssize_t size)
147{
148 int fd;
149 Py_ssize_t n;
Antoine Pitroue472aea2014-04-26 14:33:03 +0200150 struct stat st;
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100151
152 if (size <= 0)
153 return 0;
154
Antoine Pitroue472aea2014-04-26 14:33:03 +0200155 if (urandom_cache.fd >= 0) {
156 /* Does the fd point to the same thing as before? (issue #21207) */
157 if (fstat(urandom_cache.fd, &st)
158 || st.st_dev != urandom_cache.st_dev
159 || st.st_ino != urandom_cache.st_ino) {
160 /* Something changed: forget the cached fd (but don't close it,
161 since it probably points to something important for some
162 third-party code). */
163 urandom_cache.fd = -1;
164 }
165 }
166 if (urandom_cache.fd >= 0)
167 fd = urandom_cache.fd;
Antoine Pitrou4879a962013-08-31 00:26:02 +0200168 else {
169 Py_BEGIN_ALLOW_THREADS
170 fd = _Py_open("/dev/urandom", O_RDONLY);
171 Py_END_ALLOW_THREADS
172 if (fd < 0)
173 {
174 if (errno == ENOENT || errno == ENXIO ||
175 errno == ENODEV || errno == EACCES)
176 PyErr_SetString(PyExc_NotImplementedError,
177 "/dev/urandom (or equivalent) not found");
178 else
179 PyErr_SetFromErrno(PyExc_OSError);
180 return -1;
181 }
Antoine Pitroue472aea2014-04-26 14:33:03 +0200182 if (urandom_cache.fd >= 0) {
Antoine Pitrou4879a962013-08-31 00:26:02 +0200183 /* urandom_fd was initialized by another thread while we were
184 not holding the GIL, keep it. */
185 close(fd);
Antoine Pitroue472aea2014-04-26 14:33:03 +0200186 fd = urandom_cache.fd;
Antoine Pitrou4879a962013-08-31 00:26:02 +0200187 }
Antoine Pitroue472aea2014-04-26 14:33:03 +0200188 else {
189 if (fstat(fd, &st)) {
190 PyErr_SetFromErrno(PyExc_OSError);
191 close(fd);
192 return -1;
193 }
194 else {
195 urandom_cache.fd = fd;
196 urandom_cache.st_dev = st.st_dev;
197 urandom_cache.st_ino = st.st_ino;
198 }
199 }
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100200 }
201
202 Py_BEGIN_ALLOW_THREADS
203 do {
204 do {
205 n = read(fd, buffer, (size_t)size);
206 } while (n < 0 && errno == EINTR);
207 if (n <= 0)
208 break;
209 buffer += n;
210 size -= (Py_ssize_t)n;
211 } while (0 < size);
212 Py_END_ALLOW_THREADS
213
214 if (n <= 0)
215 {
216 /* stop on error or if read(size) returned 0 */
217 if (n < 0)
218 PyErr_SetFromErrno(PyExc_OSError);
219 else
220 PyErr_Format(PyExc_RuntimeError,
221 "Failed to read %zi bytes from /dev/urandom",
222 size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100223 return -1;
224 }
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100225 return 0;
226}
Antoine Pitrou4879a962013-08-31 00:26:02 +0200227
228static void
229dev_urandom_close(void)
230{
Antoine Pitroue472aea2014-04-26 14:33:03 +0200231 if (urandom_cache.fd >= 0) {
232 close(urandom_cache.fd);
233 urandom_cache.fd = -1;
Antoine Pitrou4879a962013-08-31 00:26:02 +0200234 }
235}
236
Victor Stinnerfe02e392014-12-21 01:16:38 +0100237#endif /* HAVE_GETENTROPY */
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100238
239/* Fill buffer with pseudo-random bytes generated by a linear congruent
240 generator (LCG):
241
242 x(n+1) = (x(n) * 214013 + 2531011) % 2^32
243
244 Use bits 23..16 of x(n) to generate a byte. */
245static void
246lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
247{
248 size_t index;
249 unsigned int x;
250
251 x = x0;
252 for (index=0; index < size; index++) {
253 x *= 214013;
254 x += 2531011;
255 /* modulo 2 ^ (8 * sizeof(int)) */
256 buffer[index] = (x >> 16) & 0xff;
257 }
258}
259
Georg Brandlc6a2c9b2013-10-06 18:43:19 +0200260/* Fill buffer with size pseudo-random bytes from the operating system random
Serhiy Storchaka56a6d852014-12-01 18:28:43 +0200261 number generator (RNG). It is suitable for most cryptographic purposes
Georg Brandlc6a2c9b2013-10-06 18:43:19 +0200262 except long living private keys for asymmetric encryption.
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100263
264 Return 0 on success, raise an exception and return -1 on error. */
265int
266_PyOS_URandom(void *buffer, Py_ssize_t size)
267{
268 if (size < 0) {
269 PyErr_Format(PyExc_ValueError,
270 "negative argument not allowed");
271 return -1;
272 }
273 if (size == 0)
274 return 0;
275
276#ifdef MS_WINDOWS
277 return win32_urandom((unsigned char *)buffer, size, 1);
Victor Stinnerfe02e392014-12-21 01:16:38 +0100278#elif HAVE_GETENTROPY
279 return py_getentropy(buffer, size, 0);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100280#else
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100281 return dev_urandom_python((char*)buffer, size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100282#endif
283}
284
285void
286_PyRandom_Init(void)
287{
288 char *env;
Christian Heimes985ecdc2013-11-20 11:46:18 +0100289 unsigned char *secret = (unsigned char *)&_Py_HashSecret.uc;
Benjamin Peterson69e97272012-02-21 11:08:50 -0500290 Py_ssize_t secret_size = sizeof(_Py_HashSecret_t);
Christian Heimes985ecdc2013-11-20 11:46:18 +0100291 assert(secret_size == sizeof(_Py_HashSecret.uc));
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100292
Benjamin Peterson69e97272012-02-21 11:08:50 -0500293 if (_Py_HashSecret_Initialized)
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100294 return;
Benjamin Peterson69e97272012-02-21 11:08:50 -0500295 _Py_HashSecret_Initialized = 1;
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100296
297 /*
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100298 Hash randomization is enabled. Generate a per-process secret,
299 using PYTHONHASHSEED if provided.
300 */
301
302 env = Py_GETENV("PYTHONHASHSEED");
Georg Brandl12897d72012-02-20 23:49:29 +0100303 if (env && *env != '\0' && strcmp(env, "random") != 0) {
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100304 char *endptr = env;
305 unsigned long seed;
306 seed = strtoul(env, &endptr, 10);
307 if (*endptr != '\0'
308 || seed > 4294967295UL
309 || (errno == ERANGE && seed == ULONG_MAX))
310 {
311 Py_FatalError("PYTHONHASHSEED must be \"random\" or an integer "
312 "in range [0; 4294967295]");
313 }
314 if (seed == 0) {
315 /* disable the randomized hash */
316 memset(secret, 0, secret_size);
317 }
318 else {
Christian Heimes985ecdc2013-11-20 11:46:18 +0100319 lcg_urandom(seed, secret, secret_size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100320 }
321 }
322 else {
323#ifdef MS_WINDOWS
Christian Heimes985ecdc2013-11-20 11:46:18 +0100324 (void)win32_urandom(secret, secret_size, 0);
Victor Stinnerfe02e392014-12-21 01:16:38 +0100325#elif HAVE_GETENTROPY
326 (void)py_getentropy(secret, secret_size, 1);
Christian Heimesaf01f662013-12-21 16:19:10 +0100327#else
Christian Heimes985ecdc2013-11-20 11:46:18 +0100328 dev_urandom_noraise(secret, secret_size);
Georg Brandl2daf6ae2012-02-20 19:54:16 +0100329#endif
330 }
331}
Antoine Pitrou4879a962013-08-31 00:26:02 +0200332
333void
334_PyRandom_Fini(void)
335{
Victor Stinnerfe02e392014-12-21 01:16:38 +0100336#ifdef MS_WINDOWS
337 if (hCryptProv) {
338 CryptReleaseContext(hCryptProv, 0);
339 hCryptProv = 0;
340 }
341#elif HAVE_GETENTROPY
342 /* nothing to clean */
343#else
Antoine Pitrou4879a962013-08-31 00:26:02 +0200344 dev_urandom_close();
345#endif
346}