9 int _Py_HashSecret_Initialized
= 0;
11 static int _Py_HashSecret_Initialized
= 0;
15 typedef BOOL (WINAPI
*CRYPTACQUIRECONTEXTA
)(HCRYPTPROV
*phProv
,\
16 LPCSTR pszContainer
, LPCSTR pszProvider
, DWORD dwProvType
,\
18 typedef BOOL (WINAPI
*CRYPTGENRANDOM
)(HCRYPTPROV hProv
, DWORD dwLen
,\
21 static CRYPTGENRANDOM pCryptGenRandom
= NULL
;
22 /* This handle is never explicitly released. Instead, the operating
23 system will release it when the process terminates. */
24 static HCRYPTPROV hCryptProv
= 0;
27 win32_urandom_init(int raise
)
29 HINSTANCE hAdvAPI32
= NULL
;
30 CRYPTACQUIRECONTEXTA pCryptAcquireContext
= NULL
;
32 /* Obtain handle to the DLL containing CryptoAPI. This should not fail. */
33 hAdvAPI32
= GetModuleHandle("advapi32.dll");
37 /* Obtain pointers to the CryptoAPI functions. This will fail on some early
39 pCryptAcquireContext
= (CRYPTACQUIRECONTEXTA
)GetProcAddress(
40 hAdvAPI32
, "CryptAcquireContextA");
41 if (pCryptAcquireContext
== NULL
)
44 pCryptGenRandom
= (CRYPTGENRANDOM
)GetProcAddress(hAdvAPI32
,
46 if (pCryptGenRandom
== NULL
)
50 if (! pCryptAcquireContext(&hCryptProv
, NULL
, NULL
,
51 PROV_RSA_FULL
, CRYPT_VERIFYCONTEXT
))
58 PyErr_SetFromWindowsErr(0);
60 Py_FatalError("Failed to initialize Windows random API (CryptoGen)");
64 /* Fill buffer with size pseudo-random bytes generated by the Windows CryptoGen
65 API. Return 0 on success, or -1 on error. */
67 win32_urandom(unsigned char *buffer
, Py_ssize_t size
, int raise
)
73 if (win32_urandom_init(raise
) == -1)
79 chunk
= size
> INT_MAX
? INT_MAX
: size
;
80 if (!pCryptGenRandom(hCryptProv
, chunk
, buffer
))
82 /* CryptGenRandom() failed */
84 PyErr_SetFromWindowsErr(0);
86 Py_FatalError("Failed to initialized the randomized hash "
87 "secret using CryptoGen)");
97 /* Fill buffer with size pseudo-random bytes generated by getentropy().
98 Return 0 on success, or raise an exception and return -1 on error.
99 If fatal is nonzero, call Py_FatalError() instead of raising an exception
102 py_getentropy(unsigned char *buffer
, Py_ssize_t size
, int fatal
)
105 Py_ssize_t len
= size
< 256 ? size
: 256;
109 Py_BEGIN_ALLOW_THREADS
110 res
= getentropy(buffer
, len
);
114 PyErr_SetFromErrno(PyExc_OSError
);
119 res
= getentropy(buffer
, len
);
121 Py_FatalError("getentropy() failed");
132 /* Use openssl random routine */
133 #include <openssl/rand.h>
135 vms_urandom(unsigned char *buffer
, Py_ssize_t size
, int raise
)
137 if (RAND_pseudo_bytes(buffer
, size
) < 0) {
139 PyErr_Format(PyExc_ValueError
,
140 "RAND_pseudo_bytes");
142 Py_FatalError("Failed to initialize the randomized hash "
143 "secret using RAND_pseudo_bytes");
152 #if !defined(MS_WINDOWS) && !defined(__VMS)
158 } urandom_cache
= { -1 };
160 /* Read size bytes from /dev/urandom into buffer.
161 Call Py_FatalError() on error. */
163 dev_urandom_noraise(unsigned char *buffer
, Py_ssize_t size
)
170 fd
= open("/dev/urandom", O_RDONLY
);
172 Py_FatalError("Failed to open /dev/urandom");
177 n
= read(fd
, buffer
, (size_t)size
);
178 } while (n
< 0 && errno
== EINTR
);
181 /* stop on error or if read(size) returned 0 */
182 Py_FatalError("Failed to read bytes from /dev/urandom");
186 size
-= (Py_ssize_t
)n
;
191 /* Read size bytes from /dev/urandom into buffer.
192 Return 0 on success, raise an exception and return -1 on error. */
194 dev_urandom_python(char *buffer
, Py_ssize_t size
)
204 if (urandom_cache
.fd
>= 0) {
205 /* Does the fd point to the same thing as before? (issue #21207) */
206 if (fstat(urandom_cache
.fd
, &st
)
207 || st
.st_dev
!= urandom_cache
.st_dev
208 || st
.st_ino
!= urandom_cache
.st_ino
) {
209 /* Something changed: forget the cached fd (but don't close it,
210 since it probably points to something important for some
211 third-party code). */
212 urandom_cache
.fd
= -1;
215 if (urandom_cache
.fd
>= 0)
216 fd
= urandom_cache
.fd
;
218 Py_BEGIN_ALLOW_THREADS
219 fd
= open("/dev/urandom", O_RDONLY
);
223 if (errno
== ENOENT
|| errno
== ENXIO
||
224 errno
== ENODEV
|| errno
== EACCES
)
225 PyErr_SetString(PyExc_NotImplementedError
,
226 "/dev/urandom (or equivalent) not found");
228 PyErr_SetFromErrno(PyExc_OSError
);
232 /* try to make the file descriptor non-inheritable, ignore errors */
233 attr
= fcntl(fd
, F_GETFD
);
236 (void)fcntl(fd
, F_SETFD
, attr
);
239 if (urandom_cache
.fd
>= 0) {
240 /* urandom_fd was initialized by another thread while we were
241 not holding the GIL, keep it. */
243 fd
= urandom_cache
.fd
;
246 if (fstat(fd
, &st
)) {
247 PyErr_SetFromErrno(PyExc_OSError
);
252 urandom_cache
.fd
= fd
;
253 urandom_cache
.st_dev
= st
.st_dev
;
254 urandom_cache
.st_ino
= st
.st_ino
;
259 Py_BEGIN_ALLOW_THREADS
262 n
= read(fd
, buffer
, (size_t)size
);
263 } while (n
< 0 && errno
== EINTR
);
267 size
-= (Py_ssize_t
)n
;
273 /* stop on error or if read(size) returned 0 */
275 PyErr_SetFromErrno(PyExc_OSError
);
277 PyErr_Format(PyExc_RuntimeError
,
278 "Failed to read %zi bytes from /dev/urandom",
286 dev_urandom_close(void)
288 if (urandom_cache
.fd
>= 0) {
289 close(urandom_cache
.fd
);
290 urandom_cache
.fd
= -1;
295 #endif /* !defined(MS_WINDOWS) && !defined(__VMS) */
297 /* Fill buffer with pseudo-random bytes generated by a linear congruent
300 x(n+1) = (x(n) * 214013 + 2531011) % 2^32
302 Use bits 23..16 of x(n) to generate a byte. */
304 lcg_urandom(unsigned int x0
, unsigned char *buffer
, size_t size
)
310 for (index
=0; index
< size
; index
++) {
313 /* modulo 2 ^ (8 * sizeof(int)) */
314 buffer
[index
] = (x
>> 16) & 0xff;
318 /* Fill buffer with size pseudo-random bytes from the operating system random
319 number generator (RNG). It is suitable for most cryptographic purposes
320 except long living private keys for asymmetric encryption.
322 Return 0 on success, raise an exception and return -1 on error. */
324 _PyOS_URandom(void *buffer
, Py_ssize_t size
)
327 PyErr_Format(PyExc_ValueError
,
328 "negative argument not allowed");
335 return win32_urandom((unsigned char *)buffer
, size
, 1);
336 #elif HAVE_GETENTROPY
337 return py_getentropy(buffer
, size
, 0);
340 return vms_urandom((unsigned char *)buffer
, size
, 1);
342 return dev_urandom_python((char*)buffer
, size
);
351 void *secret
= &_Py_HashSecret
;
352 Py_ssize_t secret_size
= sizeof(_Py_HashSecret_t
);
354 if (_Py_HashSecret_Initialized
)
356 _Py_HashSecret_Initialized
= 1;
359 By default, hash randomization is disabled, and only
360 enabled if PYTHONHASHSEED is set to non-empty or if
361 "-R" is provided at the command line:
363 if (!Py_HashRandomizationFlag
) {
364 /* Disable the randomized hash: */
365 memset(secret
, 0, secret_size
);
370 Hash randomization is enabled. Generate a per-process secret,
371 using PYTHONHASHSEED if provided.
374 env
= Py_GETENV("PYTHONHASHSEED");
375 if (env
&& *env
!= '\0' && strcmp(env
, "random") != 0) {
378 seed
= strtoul(env
, &endptr
, 10);
380 || seed
> 4294967295UL
381 || (errno
== ERANGE
&& seed
== ULONG_MAX
))
383 Py_FatalError("PYTHONHASHSEED must be \"random\" or an integer "
384 "in range [0; 4294967295]");
387 /* disable the randomized hash */
388 memset(secret
, 0, secret_size
);
391 lcg_urandom(seed
, (unsigned char*)secret
, secret_size
);
396 (void)win32_urandom((unsigned char *)secret
, secret_size
, 0);
398 vms_urandom((unsigned char *)secret
, secret_size
, 0);
399 #elif HAVE_GETENTROPY
400 (void)py_getentropy(secret
, secret_size
, 1);
402 dev_urandom_noraise(secret
, secret_size
);
412 CryptReleaseContext(hCryptProv
, 0);
415 #elif HAVE_GETENTROPY
416 /* nothing to clean */