3 Copyright (c) 2015, Daryl McDaniel. All rights reserved.<BR>
4 This program and the accompanying materials are licensed and made available under
5 the terms and conditions of the BSD License that accompanies this distribution.
6 The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
21 int _Py_HashSecret_Initialized
= 0;
23 static int _Py_HashSecret_Initialized
= 0;
27 typedef BOOL (WINAPI
*CRYPTACQUIRECONTEXTA
)(HCRYPTPROV
*phProv
,\
28 LPCSTR pszContainer
, LPCSTR pszProvider
, DWORD dwProvType
,\
30 typedef BOOL (WINAPI
*CRYPTGENRANDOM
)(HCRYPTPROV hProv
, DWORD dwLen
,\
33 static CRYPTGENRANDOM pCryptGenRandom
= NULL
;
34 /* This handle is never explicitly released. Instead, the operating
35 system will release it when the process terminates. */
36 static HCRYPTPROV hCryptProv
= 0;
39 win32_urandom_init(int raise
)
41 HINSTANCE hAdvAPI32
= NULL
;
42 CRYPTACQUIRECONTEXTA pCryptAcquireContext
= NULL
;
44 /* Obtain handle to the DLL containing CryptoAPI. This should not fail. */
45 hAdvAPI32
= GetModuleHandle("advapi32.dll");
49 /* Obtain pointers to the CryptoAPI functions. This will fail on some early
51 pCryptAcquireContext
= (CRYPTACQUIRECONTEXTA
)GetProcAddress(
52 hAdvAPI32
, "CryptAcquireContextA");
53 if (pCryptAcquireContext
== NULL
)
56 pCryptGenRandom
= (CRYPTGENRANDOM
)GetProcAddress(hAdvAPI32
,
58 if (pCryptGenRandom
== NULL
)
62 if (! pCryptAcquireContext(&hCryptProv
, NULL
, NULL
,
63 PROV_RSA_FULL
, CRYPT_VERIFYCONTEXT
))
70 PyErr_SetFromWindowsErr(0);
72 Py_FatalError("Failed to initialize Windows random API (CryptoGen)");
76 /* Fill buffer with size pseudo-random bytes generated by the Windows CryptoGen
77 API. Return 0 on success, or -1 on error. */
79 win32_urandom(unsigned char *buffer
, Py_ssize_t size
, int raise
)
85 if (win32_urandom_init(raise
) == -1)
91 chunk
= size
> INT_MAX
? INT_MAX
: size
;
92 if (!pCryptGenRandom(hCryptProv
, chunk
, buffer
))
94 /* CryptGenRandom() failed */
96 PyErr_SetFromWindowsErr(0);
98 Py_FatalError("Failed to initialized the randomized hash "
99 "secret using CryptoGen)");
108 #elif HAVE_GETENTROPY
109 /* Fill buffer with size pseudo-random bytes generated by getentropy().
110 Return 0 on success, or raise an exception and return -1 on error.
111 If fatal is nonzero, call Py_FatalError() instead of raising an exception
114 py_getentropy(unsigned char *buffer
, Py_ssize_t size
, int fatal
)
117 Py_ssize_t len
= size
< 256 ? size
: 256;
121 Py_BEGIN_ALLOW_THREADS
122 res
= getentropy(buffer
, len
);
126 PyErr_SetFromErrno(PyExc_OSError
);
131 res
= getentropy(buffer
, len
);
133 Py_FatalError("getentropy() failed");
144 /* Use openssl random routine */
145 #include <openssl/rand.h>
147 vms_urandom(unsigned char *buffer
, Py_ssize_t size
, int raise
)
149 if (RAND_pseudo_bytes(buffer
, size
) < 0) {
151 PyErr_Format(PyExc_ValueError
,
152 "RAND_pseudo_bytes");
154 Py_FatalError("Failed to initialize the randomized hash "
155 "secret using RAND_pseudo_bytes");
164 #if !defined(MS_WINDOWS) && !defined(__VMS)
168 #ifdef HAVE_STRUCT_STAT_ST_DEV
171 #ifdef HAVE_STRUCT_STAT_ST_INO
174 } urandom_cache
= { -1 };
176 /* Read size bytes from /dev/urandom into buffer.
177 Call Py_FatalError() on error. */
179 dev_urandom_noraise(unsigned char *buffer
, Py_ssize_t size
)
186 fd
= open("/dev/urandom", O_RDONLY
, 0);
188 Py_FatalError("Failed to open /dev/urandom");
193 n
= read(fd
, buffer
, (size_t)size
);
194 } while (n
< 0 && errno
== EINTR
);
197 /* stop on error or if read(size) returned 0 */
198 Py_FatalError("Failed to read bytes from /dev/urandom");
202 size
-= (Py_ssize_t
)n
;
207 /* Read size bytes from /dev/urandom into buffer.
208 Return 0 on success, raise an exception and return -1 on error. */
210 dev_urandom_python(char *buffer
, Py_ssize_t size
)
220 if (urandom_cache
.fd
>= 0) {
221 /* Does the fd point to the same thing as before? (issue #21207) */
222 if (fstat(urandom_cache
.fd
, &st
)
223 #ifdef HAVE_STRUCT_STAT_ST_DEV
224 || st
.st_dev
!= urandom_cache
.st_dev
226 #ifdef HAVE_STRUCT_STAT_ST_INO
227 || st
.st_ino
!= urandom_cache
.st_ino
231 /* Something changed: forget the cached fd (but don't close it,
232 since it probably points to something important for some
233 third-party code). */
234 urandom_cache
.fd
= -1;
237 if (urandom_cache
.fd
>= 0)
238 fd
= urandom_cache
.fd
;
240 Py_BEGIN_ALLOW_THREADS
241 fd
= open("/dev/urandom", O_RDONLY
, 0);
245 if (errno
== ENOENT
|| errno
== ENXIO
||
246 errno
== ENODEV
|| errno
== EACCES
)
247 PyErr_SetString(PyExc_NotImplementedError
,
248 "/dev/urandom (or equivalent) not found");
250 PyErr_SetFromErrno(PyExc_OSError
);
254 /* try to make the file descriptor non-inheritable, ignore errors */
255 attr
= fcntl(fd
, F_GETFD
);
258 (void)fcntl(fd
, F_SETFD
, attr
);
261 if (urandom_cache
.fd
>= 0) {
262 /* urandom_fd was initialized by another thread while we were
263 not holding the GIL, keep it. */
265 fd
= urandom_cache
.fd
;
268 if (fstat(fd
, &st
)) {
269 PyErr_SetFromErrno(PyExc_OSError
);
274 urandom_cache
.fd
= fd
;
275 #ifdef HAVE_STRUCT_STAT_ST_DEV
276 urandom_cache
.st_dev
= st
.st_dev
;
278 #ifdef HAVE_STRUCT_STAT_ST_INO
279 urandom_cache
.st_ino
= st
.st_ino
;
285 Py_BEGIN_ALLOW_THREADS
288 n
= read(fd
, buffer
, (size_t)size
);
289 } while (n
< 0 && errno
== EINTR
);
293 size
-= (Py_ssize_t
)n
;
299 /* stop on error or if read(size) returned 0 */
301 PyErr_SetFromErrno(PyExc_OSError
);
303 PyErr_Format(PyExc_RuntimeError
,
304 "Failed to read %zi bytes from /dev/urandom",
312 dev_urandom_close(void)
314 if (urandom_cache
.fd
>= 0) {
315 close(urandom_cache
.fd
);
316 urandom_cache
.fd
= -1;
321 #endif /* !defined(MS_WINDOWS) && !defined(__VMS) */
323 /* Fill buffer with pseudo-random bytes generated by a linear congruent
326 x(n+1) = (x(n) * 214013 + 2531011) % 2^32
328 Use bits 23..16 of x(n) to generate a byte. */
330 lcg_urandom(unsigned int x0
, unsigned char *buffer
, size_t size
)
336 for (index
=0; index
< size
; index
++) {
339 /* modulo 2 ^ (8 * sizeof(int)) */
340 buffer
[index
] = (x
>> 16) & 0xff;
344 /* Fill buffer with size pseudo-random bytes from the operating system random
345 number generator (RNG). It is suitable for most cryptographic purposes
346 except long living private keys for asymmetric encryption.
348 Return 0 on success, raise an exception and return -1 on error. */
350 _PyOS_URandom(void *buffer
, Py_ssize_t size
)
353 PyErr_Format(PyExc_ValueError
,
354 "negative argument not allowed");
361 return win32_urandom((unsigned char *)buffer
, size
, 1);
362 #elif HAVE_GETENTROPY
363 return py_getentropy(buffer
, size
, 0);
366 return vms_urandom((unsigned char *)buffer
, size
, 1);
368 return dev_urandom_python((char*)buffer
, size
);
377 void *secret
= &_Py_HashSecret
;
378 Py_ssize_t secret_size
= sizeof(_Py_HashSecret_t
);
380 if (_Py_HashSecret_Initialized
)
382 _Py_HashSecret_Initialized
= 1;
385 By default, hash randomization is disabled, and only
386 enabled if PYTHONHASHSEED is set to non-empty or if
387 "-R" is provided at the command line:
389 if (!Py_HashRandomizationFlag
) {
390 /* Disable the randomized hash: */
391 memset(secret
, 0, secret_size
);
396 Hash randomization is enabled. Generate a per-process secret,
397 using PYTHONHASHSEED if provided.
400 env
= Py_GETENV("PYTHONHASHSEED");
401 if (env
&& *env
!= '\0' && strcmp(env
, "random") != 0) {
404 seed
= strtoul(env
, &endptr
, 10);
406 || seed
> 4294967295UL
407 || (errno
== ERANGE
&& seed
== ULONG_MAX
))
409 Py_FatalError("PYTHONHASHSEED must be \"random\" or an integer "
410 "in range [0; 4294967295]");
413 /* disable the randomized hash */
414 memset(secret
, 0, secret_size
);
417 lcg_urandom(seed
, (unsigned char*)secret
, secret_size
);
422 (void)win32_urandom((unsigned char *)secret
, secret_size
, 0);
424 vms_urandom((unsigned char *)secret
, secret_size
, 0);
425 #elif HAVE_GETENTROPY
426 (void)py_getentropy(secret
, secret_size
, 1);
428 dev_urandom_noraise(secret
, secret_size
);
438 CryptReleaseContext(hCryptProv
, 0);
441 #elif HAVE_GETENTROPY
442 /* nothing to clean */