Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
allendowney
GitHub Repository: allendowney/cpython
Path: blob/main/Python/bootstrap_hash.c
12 views
1
#include "Python.h"
2
#include "pycore_initconfig.h"
3
#include "pycore_fileutils.h" // _Py_fstat_noraise()
4
#include "pycore_runtime.h" // _PyRuntime
5
6
#ifdef MS_WINDOWS
7
# include <windows.h>
8
# include <bcrypt.h>
9
#else
10
# include <fcntl.h>
11
# ifdef HAVE_SYS_STAT_H
12
# include <sys/stat.h>
13
# endif
14
# ifdef HAVE_LINUX_RANDOM_H
15
# include <linux/random.h>
16
# endif
17
# if defined(HAVE_SYS_RANDOM_H) && (defined(HAVE_GETRANDOM) || defined(HAVE_GETENTROPY))
18
# include <sys/random.h>
19
# endif
20
# if !defined(HAVE_GETRANDOM) && defined(HAVE_GETRANDOM_SYSCALL)
21
# include <sys/syscall.h>
22
# endif
23
#endif
24
25
#ifdef _Py_MEMORY_SANITIZER
26
# include <sanitizer/msan_interface.h>
27
#endif
28
29
#if defined(__APPLE__) && defined(__has_builtin)
30
# if __has_builtin(__builtin_available)
31
# define HAVE_GETENTRYPY_GETRANDOM_RUNTIME __builtin_available(macOS 10.12, iOS 10.10, tvOS 10.0, watchOS 3.0, *)
32
# endif
33
#endif
34
#ifndef HAVE_GETENTRYPY_GETRANDOM_RUNTIME
35
# define HAVE_GETENTRYPY_GETRANDOM_RUNTIME 1
36
#endif
37
38
39
#ifdef Py_DEBUG
40
int _Py_HashSecret_Initialized = 0;
41
#else
42
static int _Py_HashSecret_Initialized = 0;
43
#endif
44
45
#ifdef MS_WINDOWS
46
47
/* Fill buffer with size pseudo-random bytes generated by the Windows CryptoGen
48
API. Return 0 on success, or raise an exception and return -1 on error. */
49
static int
50
win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
51
{
52
while (size > 0)
53
{
54
DWORD chunk = (DWORD)Py_MIN(size, PY_DWORD_MAX);
55
NTSTATUS status = BCryptGenRandom(NULL, buffer, chunk, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
56
if (!BCRYPT_SUCCESS(status)) {
57
/* BCryptGenRandom() failed */
58
if (raise) {
59
PyErr_SetFromWindowsErr(0);
60
}
61
return -1;
62
}
63
buffer += chunk;
64
size -= chunk;
65
}
66
return 0;
67
}
68
69
#else /* !MS_WINDOWS */
70
71
#if defined(HAVE_GETRANDOM) || defined(HAVE_GETRANDOM_SYSCALL)
72
#define PY_GETRANDOM 1
73
74
/* Call getrandom() to get random bytes:
75
76
- Return 1 on success
77
- Return 0 if getrandom() is not available (failed with ENOSYS or EPERM),
78
or if getrandom(GRND_NONBLOCK) failed with EAGAIN (system urandom not
79
initialized yet) and raise=0.
80
- Raise an exception (if raise is non-zero) and return -1 on error:
81
if getrandom() failed with EINTR, raise is non-zero and the Python signal
82
handler raised an exception, or if getrandom() failed with a different
83
error.
84
85
getrandom() is retried if it failed with EINTR: interrupted by a signal. */
86
static int
87
py_getrandom(void *buffer, Py_ssize_t size, int blocking, int raise)
88
{
89
/* Is getrandom() supported by the running kernel? Set to 0 if getrandom()
90
failed with ENOSYS or EPERM. Need Linux kernel 3.17 or newer, or Solaris
91
11.3 or newer */
92
static int getrandom_works = 1;
93
int flags;
94
char *dest;
95
long n;
96
97
if (!getrandom_works) {
98
return 0;
99
}
100
101
flags = blocking ? 0 : GRND_NONBLOCK;
102
dest = buffer;
103
while (0 < size) {
104
#if defined(__sun) && defined(__SVR4)
105
/* Issue #26735: On Solaris, getrandom() is limited to returning up
106
to 1024 bytes. Call it multiple times if more bytes are
107
requested. */
108
n = Py_MIN(size, 1024);
109
#else
110
n = Py_MIN(size, LONG_MAX);
111
#endif
112
113
errno = 0;
114
#ifdef HAVE_GETRANDOM
115
if (raise) {
116
Py_BEGIN_ALLOW_THREADS
117
n = getrandom(dest, n, flags);
118
Py_END_ALLOW_THREADS
119
}
120
else {
121
n = getrandom(dest, n, flags);
122
}
123
#else
124
/* On Linux, use the syscall() function because the GNU libc doesn't
125
expose the Linux getrandom() syscall yet. See:
126
https://sourceware.org/bugzilla/show_bug.cgi?id=17252 */
127
if (raise) {
128
Py_BEGIN_ALLOW_THREADS
129
n = syscall(SYS_getrandom, dest, n, flags);
130
Py_END_ALLOW_THREADS
131
}
132
else {
133
n = syscall(SYS_getrandom, dest, n, flags);
134
}
135
# ifdef _Py_MEMORY_SANITIZER
136
if (n > 0) {
137
__msan_unpoison(dest, n);
138
}
139
# endif
140
#endif
141
142
if (n < 0) {
143
/* ENOSYS: the syscall is not supported by the kernel.
144
EPERM: the syscall is blocked by a security policy (ex: SECCOMP)
145
or something else. */
146
if (errno == ENOSYS || errno == EPERM) {
147
getrandom_works = 0;
148
return 0;
149
}
150
151
/* getrandom(GRND_NONBLOCK) fails with EAGAIN if the system urandom
152
is not initialized yet. For _PyRandom_Init(), we ignore the
153
error and fall back on reading /dev/urandom which never blocks,
154
even if the system urandom is not initialized yet:
155
see the PEP 524. */
156
if (errno == EAGAIN && !raise && !blocking) {
157
return 0;
158
}
159
160
if (errno == EINTR) {
161
if (raise) {
162
if (PyErr_CheckSignals()) {
163
return -1;
164
}
165
}
166
167
/* retry getrandom() if it was interrupted by a signal */
168
continue;
169
}
170
171
if (raise) {
172
PyErr_SetFromErrno(PyExc_OSError);
173
}
174
return -1;
175
}
176
177
dest += n;
178
size -= n;
179
}
180
return 1;
181
}
182
183
#elif defined(HAVE_GETENTROPY)
184
#define PY_GETENTROPY 1
185
186
/* Fill buffer with size pseudo-random bytes generated by getentropy():
187
188
- Return 1 on success
189
- Return 0 if getentropy() syscall is not available (failed with ENOSYS or
190
EPERM).
191
- Raise an exception (if raise is non-zero) and return -1 on error:
192
if getentropy() failed with EINTR, raise is non-zero and the Python signal
193
handler raised an exception, or if getentropy() failed with a different
194
error.
195
196
getentropy() is retried if it failed with EINTR: interrupted by a signal. */
197
198
#if defined(__APPLE__) && defined(__has_attribute) && __has_attribute(availability)
199
static int
200
py_getentropy(char *buffer, Py_ssize_t size, int raise)
201
__attribute__((availability(macos,introduced=10.12)))
202
__attribute__((availability(ios,introduced=10.0)))
203
__attribute__((availability(tvos,introduced=10.0)))
204
__attribute__((availability(watchos,introduced=3.0)));
205
#endif
206
207
static int
208
py_getentropy(char *buffer, Py_ssize_t size, int raise)
209
{
210
/* Is getentropy() supported by the running kernel? Set to 0 if
211
getentropy() failed with ENOSYS or EPERM. */
212
static int getentropy_works = 1;
213
214
if (!getentropy_works) {
215
return 0;
216
}
217
218
while (size > 0) {
219
/* getentropy() is limited to returning up to 256 bytes. Call it
220
multiple times if more bytes are requested. */
221
Py_ssize_t len = Py_MIN(size, 256);
222
int res;
223
224
if (raise) {
225
Py_BEGIN_ALLOW_THREADS
226
res = getentropy(buffer, len);
227
Py_END_ALLOW_THREADS
228
}
229
else {
230
res = getentropy(buffer, len);
231
}
232
233
if (res < 0) {
234
/* ENOSYS: the syscall is not supported by the running kernel.
235
EPERM: the syscall is blocked by a security policy (ex: SECCOMP)
236
or something else. */
237
if (errno == ENOSYS || errno == EPERM) {
238
getentropy_works = 0;
239
return 0;
240
}
241
242
if (errno == EINTR) {
243
if (raise) {
244
if (PyErr_CheckSignals()) {
245
return -1;
246
}
247
}
248
249
/* retry getentropy() if it was interrupted by a signal */
250
continue;
251
}
252
253
if (raise) {
254
PyErr_SetFromErrno(PyExc_OSError);
255
}
256
return -1;
257
}
258
259
buffer += len;
260
size -= len;
261
}
262
return 1;
263
}
264
#endif /* defined(HAVE_GETENTROPY) && !(defined(__sun) && defined(__SVR4)) */
265
266
267
#define urandom_cache (_PyRuntime.pyhash_state.urandom_cache)
268
269
/* Read random bytes from the /dev/urandom device:
270
271
- Return 0 on success
272
- Raise an exception (if raise is non-zero) and return -1 on error
273
274
Possible causes of errors:
275
276
- open() failed with ENOENT, ENXIO, ENODEV, EACCES: the /dev/urandom device
277
was not found. For example, it was removed manually or not exposed in a
278
chroot or container.
279
- open() failed with a different error
280
- fstat() failed
281
- read() failed or returned 0
282
283
read() is retried if it failed with EINTR: interrupted by a signal.
284
285
The file descriptor of the device is kept open between calls to avoid using
286
many file descriptors when run in parallel from multiple threads:
287
see the issue #18756.
288
289
st_dev and st_ino fields of the file descriptor (from fstat()) are cached to
290
check if the file descriptor was replaced by a different file (which is
291
likely a bug in the application): see the issue #21207.
292
293
If the file descriptor was closed or replaced, open a new file descriptor
294
but don't close the old file descriptor: it probably points to something
295
important for some third-party code. */
296
static int
297
dev_urandom(char *buffer, Py_ssize_t size, int raise)
298
{
299
int fd;
300
Py_ssize_t n;
301
302
if (raise) {
303
struct _Py_stat_struct st;
304
int fstat_result;
305
306
if (urandom_cache.fd >= 0) {
307
Py_BEGIN_ALLOW_THREADS
308
fstat_result = _Py_fstat_noraise(urandom_cache.fd, &st);
309
Py_END_ALLOW_THREADS
310
311
/* Does the fd point to the same thing as before? (issue #21207) */
312
if (fstat_result
313
|| st.st_dev != urandom_cache.st_dev
314
|| st.st_ino != urandom_cache.st_ino) {
315
/* Something changed: forget the cached fd (but don't close it,
316
since it probably points to something important for some
317
third-party code). */
318
urandom_cache.fd = -1;
319
}
320
}
321
if (urandom_cache.fd >= 0)
322
fd = urandom_cache.fd;
323
else {
324
fd = _Py_open("/dev/urandom", O_RDONLY);
325
if (fd < 0) {
326
if (errno == ENOENT || errno == ENXIO ||
327
errno == ENODEV || errno == EACCES) {
328
PyErr_SetString(PyExc_NotImplementedError,
329
"/dev/urandom (or equivalent) not found");
330
}
331
/* otherwise, keep the OSError exception raised by _Py_open() */
332
return -1;
333
}
334
if (urandom_cache.fd >= 0) {
335
/* urandom_fd was initialized by another thread while we were
336
not holding the GIL, keep it. */
337
close(fd);
338
fd = urandom_cache.fd;
339
}
340
else {
341
if (_Py_fstat(fd, &st)) {
342
close(fd);
343
return -1;
344
}
345
else {
346
urandom_cache.fd = fd;
347
urandom_cache.st_dev = st.st_dev;
348
urandom_cache.st_ino = st.st_ino;
349
}
350
}
351
}
352
353
do {
354
n = _Py_read(fd, buffer, (size_t)size);
355
if (n == -1)
356
return -1;
357
if (n == 0) {
358
PyErr_Format(PyExc_RuntimeError,
359
"Failed to read %zi bytes from /dev/urandom",
360
size);
361
return -1;
362
}
363
364
buffer += n;
365
size -= n;
366
} while (0 < size);
367
}
368
else {
369
fd = _Py_open_noraise("/dev/urandom", O_RDONLY);
370
if (fd < 0) {
371
return -1;
372
}
373
374
while (0 < size)
375
{
376
do {
377
n = read(fd, buffer, (size_t)size);
378
} while (n < 0 && errno == EINTR);
379
380
if (n <= 0) {
381
/* stop on error or if read(size) returned 0 */
382
close(fd);
383
return -1;
384
}
385
386
buffer += n;
387
size -= n;
388
}
389
close(fd);
390
}
391
return 0;
392
}
393
394
static void
395
dev_urandom_close(void)
396
{
397
if (urandom_cache.fd >= 0) {
398
close(urandom_cache.fd);
399
urandom_cache.fd = -1;
400
}
401
}
402
403
#undef urandom_cache
404
405
#endif /* !MS_WINDOWS */
406
407
408
/* Fill buffer with pseudo-random bytes generated by a linear congruent
409
generator (LCG):
410
411
x(n+1) = (x(n) * 214013 + 2531011) % 2^32
412
413
Use bits 23..16 of x(n) to generate a byte. */
414
static void
415
lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
416
{
417
size_t index;
418
unsigned int x;
419
420
x = x0;
421
for (index=0; index < size; index++) {
422
x *= 214013;
423
x += 2531011;
424
/* modulo 2 ^ (8 * sizeof(int)) */
425
buffer[index] = (x >> 16) & 0xff;
426
}
427
}
428
429
/* Read random bytes:
430
431
- Return 0 on success
432
- Raise an exception (if raise is non-zero) and return -1 on error
433
434
Used sources of entropy ordered by preference, preferred source first:
435
436
- BCryptGenRandom() on Windows
437
- getrandom() function (ex: Linux and Solaris): call py_getrandom()
438
- getentropy() function (ex: OpenBSD): call py_getentropy()
439
- /dev/urandom device
440
441
Read from the /dev/urandom device if getrandom() or getentropy() function
442
is not available or does not work.
443
444
Prefer getrandom() over getentropy() because getrandom() supports blocking
445
and non-blocking mode: see the PEP 524. Python requires non-blocking RNG at
446
startup to initialize its hash secret, but os.urandom() must block until the
447
system urandom is initialized (at least on Linux 3.17 and newer).
448
449
Prefer getrandom() and getentropy() over reading directly /dev/urandom
450
because these functions don't need file descriptors and so avoid ENFILE or
451
EMFILE errors (too many open files): see the issue #18756.
452
453
Only the getrandom() function supports non-blocking mode.
454
455
Only use RNG running in the kernel. They are more secure because it is
456
harder to get the internal state of a RNG running in the kernel land than a
457
RNG running in the user land. The kernel has a direct access to the hardware
458
and has access to hardware RNG, they are used as entropy sources.
459
460
Note: the OpenSSL RAND_pseudo_bytes() function does not automatically reseed
461
its RNG on fork(), two child processes (with the same pid) generate the same
462
random numbers: see issue #18747. Kernel RNGs don't have this issue,
463
they have access to good quality entropy sources.
464
465
If raise is zero:
466
467
- Don't raise an exception on error
468
- Don't call the Python signal handler (don't call PyErr_CheckSignals()) if
469
a function fails with EINTR: retry directly the interrupted function
470
- Don't release the GIL to call functions.
471
*/
472
static int
473
pyurandom(void *buffer, Py_ssize_t size, int blocking, int raise)
474
{
475
#if defined(PY_GETRANDOM) || defined(PY_GETENTROPY)
476
int res;
477
#endif
478
479
if (size < 0) {
480
if (raise) {
481
PyErr_Format(PyExc_ValueError,
482
"negative argument not allowed");
483
}
484
return -1;
485
}
486
487
if (size == 0) {
488
return 0;
489
}
490
491
#ifdef MS_WINDOWS
492
return win32_urandom((unsigned char *)buffer, size, raise);
493
#else
494
495
#if defined(PY_GETRANDOM) || defined(PY_GETENTROPY)
496
if (HAVE_GETENTRYPY_GETRANDOM_RUNTIME) {
497
#ifdef PY_GETRANDOM
498
res = py_getrandom(buffer, size, blocking, raise);
499
#else
500
res = py_getentropy(buffer, size, raise);
501
#endif
502
if (res < 0) {
503
return -1;
504
}
505
if (res == 1) {
506
return 0;
507
}
508
/* getrandom() or getentropy() function is not available: failed with
509
ENOSYS or EPERM. Fall back on reading from /dev/urandom. */
510
} /* end of availability block */
511
#endif
512
513
return dev_urandom(buffer, size, raise);
514
#endif
515
}
516
517
/* Fill buffer with size pseudo-random bytes from the operating system random
518
number generator (RNG). It is suitable for most cryptographic purposes
519
except long living private keys for asymmetric encryption.
520
521
On Linux 3.17 and newer, the getrandom() syscall is used in blocking mode:
522
block until the system urandom entropy pool is initialized (128 bits are
523
collected by the kernel).
524
525
Return 0 on success. Raise an exception and return -1 on error. */
526
int
527
_PyOS_URandom(void *buffer, Py_ssize_t size)
528
{
529
return pyurandom(buffer, size, 1, 1);
530
}
531
532
/* Fill buffer with size pseudo-random bytes from the operating system random
533
number generator (RNG). It is not suitable for cryptographic purpose.
534
535
On Linux 3.17 and newer (when getrandom() syscall is used), if the system
536
urandom is not initialized yet, the function returns "weak" entropy read
537
from /dev/urandom.
538
539
Return 0 on success. Raise an exception and return -1 on error. */
540
int
541
_PyOS_URandomNonblock(void *buffer, Py_ssize_t size)
542
{
543
return pyurandom(buffer, size, 0, 1);
544
}
545
546
547
PyStatus
548
_Py_HashRandomization_Init(const PyConfig *config)
549
{
550
void *secret = &_Py_HashSecret;
551
Py_ssize_t secret_size = sizeof(_Py_HashSecret_t);
552
553
if (_Py_HashSecret_Initialized) {
554
return _PyStatus_OK();
555
}
556
_Py_HashSecret_Initialized = 1;
557
558
if (config->use_hash_seed) {
559
if (config->hash_seed == 0) {
560
/* disable the randomized hash */
561
memset(secret, 0, secret_size);
562
}
563
else {
564
/* use the specified hash seed */
565
lcg_urandom(config->hash_seed, secret, secret_size);
566
}
567
}
568
else {
569
/* use a random hash seed */
570
int res;
571
572
/* _PyRandom_Init() is called very early in the Python initialization
573
and so exceptions cannot be used (use raise=0).
574
575
_PyRandom_Init() must not block Python initialization: call
576
pyurandom() is non-blocking mode (blocking=0): see the PEP 524. */
577
res = pyurandom(secret, secret_size, 0, 0);
578
if (res < 0) {
579
return _PyStatus_ERR("failed to get random numbers "
580
"to initialize Python");
581
}
582
}
583
return _PyStatus_OK();
584
}
585
586
587
void
588
_Py_HashRandomization_Fini(void)
589
{
590
#ifndef MS_WINDOWS
591
dev_urandom_close();
592
#endif
593
}
594
595