cpython: 45fc0c83ed42 (original) (raw)
Mercurial > cpython
changeset 103172:45fc0c83ed42
os.urandom() now blocks on Linux Issue #27776: The os.urandom() function does now block on Linux 3.17 and newer until the system urandom entropy pool is initialized to increase the security. This change is part of the PEP 524. [#27776]
Victor Stinner victor.stinner@gmail.com | |
---|---|
date | Tue, 06 Sep 2016 16:33:52 -0700 |
parents | bb8fe61d78a4 |
children | afa5a16456ed |
files | Doc/library/os.rst Doc/whatsnew/3.6.rst Include/pylifecycle.h Lib/random.py Misc/NEWS Modules/_randommodule.c Modules/posixmodule.c Python/random.c |
diffstat | 8 files changed, 131 insertions(+), 60 deletions(-)[+] [-] Doc/library/os.rst 29 Doc/whatsnew/3.6.rst 12 Include/pylifecycle.h 3 Lib/random.py 9 Misc/NEWS 4 Modules/_randommodule.c 56 Modules/posixmodule.c 3 Python/random.c 75 |
line wrap: on
line diff
--- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -3968,14 +3968,27 @@ Random numbers returned data should be unpredictable enough for cryptographic applications, though its exact quality depends on the OS implementation.
- On Linux, the
getrandom()
syscall is used if available and the urandom - entropy pool is initialized (
getrandom()
does not block). - On a Unix-like system this will query
/dev/urandom
. On Windows, it - will use
CryptGenRandom()
. If a randomness source is not found, - :exc:
NotImplementedError
will be raised. - - For an easy-to-use interface to the random number generator
- provided by your platform, please see :class:
random.SystemRandom
.
- On Linux, if the
getrandom()
syscall is available, it is used in - blocking mode: block until the system urandom entropy pool is initialized
- (128 bits of entropy are collected by the kernel). See the :pep:
524
for - the rationale. On Linux, the :func:
getrandom
function can be used to get - random bytes in non-blocking mode (using the :data:
GRND_NONBLOCK
flag) or - to poll until the system urandom entropy pool is initialized. +
- On a Unix-like system, random bytes are read from the
/dev/urandom
- device. If the
/dev/urandom
device is not available or not readable, the - :exc:
NotImplementedError
exception is raised. + - On Windows, it will use
CryptGenRandom()
. + - .. seealso::
The :mod:`secrets` module provides higher level functions. For an[](#l1.29)
easy-to-use interface to the random number generator provided by your[](#l1.30)
platform, please see :class:`random.SystemRandom`.[](#l1.31)
- .. versionchanged:: 3.6.0
On Linux, ``getrandom()`` is now used in blocking mode to increase the[](#l1.34)
security.[](#l1.35)
.. versionchanged:: 3.5.2
On Linux, if the getrandom()
syscall blocks (the urandom entropy pool
--- a/Doc/whatsnew/3.6.rst +++ b/Doc/whatsnew/3.6.rst @@ -70,6 +70,12 @@ Standard library improvements:
- PEP 519: :ref:
Adding a file system path protocol <pep-519>
+Security improvements: + +* On Linux, :func:os.urandom
now blocks until the system urandom entropy pool
- is initialized to increase the security. See the :pep:
524
for the - rationale. + Windows improvements:
- The
py.exe
launcher, when used interactively, no longer prefers @@ -345,6 +351,9 @@ New Modules Improved Modules ================ +On Linux, :func:os.urandom
now blocks until the system urandom entropy pool +is initialized to increase the security. See the :pep:524
for the rationale. + asyncio ------- @@ -913,6 +922,9 @@ Changes in 'python' Command Behavior Changes in the Python API ------------------------- +* On Linux, :func:os.urandom
now blocks until the system urandom entropy pool
- When :meth:
importlib.abc.Loader.exec_module
is defined, :meth:importlib.abc.Loader.create_module
must also be defined.
--- a/Include/pylifecycle.h +++ b/Include/pylifecycle.h @@ -117,7 +117,8 @@ PyAPI_FUNC(PyOS_sighandler_t) PyOS_getsi PyAPI_FUNC(PyOS_sighandler_t) PyOS_setsig(int, PyOS_sighandler_t); /* Random */ -PyAPI_FUNC(int) _PyOS_URandom (void *buffer, Py_ssize_t size); +PyAPI_FUNC(int) _PyOS_URandom(void *buffer, Py_ssize_t size); +PyAPI_FUNC(int) _PyOS_URandomNonblock(void *buffer, Py_ssize_t size); #ifdef __cplusplus }
--- a/Lib/random.py +++ b/Lib/random.py @@ -105,15 +105,6 @@ class Random(_random.Random): """
if a is None:[](#l4.7)
try:[](#l4.8)
# Seed with enough bytes to span the 19937 bit[](#l4.9)
# state space for the Mersenne Twister[](#l4.10)
a = int.from_bytes(_urandom(2500), 'big')[](#l4.11)
except NotImplementedError:[](#l4.12)
import time[](#l4.13)
a = int(time.time() * 256) # use fractional seconds[](#l4.14)
- if version == 1 and isinstance(a, (str, bytes)): x = ord(a[0]) << 7 if a else 0 for c in a:
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -89,6 +89,10 @@ Core and Builtins
Library
-------
+- Issue #27776: The :func:os.urandom
function does now block on Linux 3.17
- and newer until the system urandom entropy pool is initialized to increase
- the security. This change is part of the :pep:
524
. +
- Issue #27778: Expose the Linux
getrandom()
syscall as a new :func:os.getrandom
function. This change is part of the :pep:524
.
--- a/Modules/_randommodule.c +++ b/Modules/randommodule.c @@ -165,7 +165,7 @@ init_genrand(RandomObject *self, uint32 /* initialize by an array with array-length / / init_key is the array for initializing keys / / key_length is its length */ -static PyObject * +static void init_by_array(RandomObject self, uint32_t init_key[], size_t key_length) { size_t i, j, k; / was signed in the original code. RDH 12/16/2002 */ @@ -190,8 +190,6 @@ init_by_array(RandomObject self, uint32 } mt[0] = 0x80000000U; / MSB is 1; assuring non-zero initial array */
} /* @@ -199,6 +197,37 @@ init_by_array(RandomObject *self, uint32
+static int +random_seed_urandom(RandomObject *self) +{
- if (_PyOS_URandomNonblock(key, sizeof(key)) < 0) {
return -1;[](#l6.31)
- }
- init_by_array(self, key, Py_ARRAY_LENGTH(key));
- return 0;
+} + +static void +random_seed_time_pid(RandomObject *self) +{
- now = _PyTime_GetSystemClock();
- key[0] = (PY_UINT32_T)(now & 0xffffffffU);
- key[1] = (PY_UINT32_T)(now >> 32);
- now = _PyTime_GetMonotonicClock();
- key[3] = (PY_UINT32_T)(now & 0xffffffffU);
- key[4] = (PY_UINT32_T)(now >> 32);
+} + static PyObject * random_seed(RandomObject *self, PyObject *args) { @@ -212,14 +241,17 @@ random_seed(RandomObject *self, PyObject if (!PyArg_UnpackTuple(args, "seed", 0, 1, &arg)) return NULL;
if (arg == NULL || arg == Py_None) {[](#l6.65)
if (random_seed_urandom(self) >= 0) {[](#l6.66)
PyErr_Clear();[](#l6.67)
time(&now);[](#l6.69)
init_genrand(self, (uint32_t)now);[](#l6.70)
Py_INCREF(Py_None);[](#l6.71)
return Py_None;[](#l6.72)
/* Reading system entropy failed, fall back on the worst entropy:[](#l6.73)
use the current time and process identifier. */[](#l6.74)
random_seed_time_pid(self);[](#l6.75)
}[](#l6.76)
} + /* This algorithm relies on the number being unsigned. * So: if the arg is a PyLong, use its absolute value. * Otherwise use its hash value, cast to unsigned.Py_RETURN_NONE;[](#l6.77)
@@ -269,7 +301,11 @@ random_seed(RandomObject *self, PyObject } } #endif
+ Done: Py_XDECREF(n); PyMem_Free(key);
--- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -11168,8 +11168,7 @@ os_urandom_impl(PyObject *module, Py_ssi if (bytes == NULL) return NULL;
- result = _PyOS_URandom(PyBytes_AS_STRING(bytes), PyBytes_GET_SIZE(bytes)); if (result == -1) { Py_DECREF(bytes); return NULL;
--- a/Python/random.c +++ b/Python/random.c @@ -77,7 +77,7 @@ win32_urandom(unsigned char buffer, Py_ } / Issue #25003: Don't use getentropy() on Solaris (available since
#elif defined(HAVE_GETENTROPY) && !defined(sun) #define PY_GETENTROPY 1 @@ -121,24 +121,20 @@ py_getentropy(char buffer, Py_ssize_t s / Call getrandom() - Return 1 on success
or if getrandom(GRND_NONBLOCK) fails with EAGAIN (blocking=0 and system[](#l8.18)
urandom not initialized yet) and raise=0.[](#l8.19)
static int -py_getrandom(void *buffer, Py_ssize_t size, int raise) +py_getrandom(void *buffer, Py_ssize_t size, int blocking, int raise) {
- /* Is getrandom() supported by the running kernel?
Need Linux kernel 3.17 or newer, or Solaris 11.3 or newer */[](#l8.28)
- /* Is getrandom() supported by the running kernel? Set to 0 if getrandom()
fails with ENOSYS. Need Linux kernel 3.17 or newer, or Solaris 11.3[](#l8.30)
static int getrandom_works = 1; -or newer */[](#l8.31)
- /* getrandom() on Linux will block if called before the kernel has
initialized the urandom entropy pool. This will cause Python[](#l8.35)
to hang on startup if called very early in the boot process -[](#l8.36)
see https://bugs.python.org/issue26839. To avoid this, use the[](#l8.37)
GRND_NONBLOCK flag. */[](#l8.38)
- const int flags = GRND_NONBLOCK;
- int flags; char *dest; long n; @@ -146,6 +142,7 @@ py_getrandom(void *buffer, Py_ssize_t si return 0; }
- flags = blocking ? 0 : GRND_NONBLOCK; dest = buffer; while (0 < size) {
#ifdef sun @@ -185,15 +182,12 @@ py_getrandom(void *buffer, Py_ssize_t si getrandom_works = 0; return 0; }
if (errno == EAGAIN) {[](#l8.57)
/* If we failed with EAGAIN, the entropy pool was[](#l8.58)
uninitialized. In this case, we return failure to fall[](#l8.59)
back to reading from /dev/urandom.[](#l8.60)
Note: In this case the data read will not be random so[](#l8.62)
should not be used for cryptographic purposes. Retaining[](#l8.63)
the existing semantics for practical purposes. */[](#l8.64)
getrandom_works = 0;[](#l8.65)
/* getrandom(GRND_NONBLOCK) fails with EAGAIN if the system urandom[](#l8.66)
is not initialiazed yet. For _PyRandom_Init(), we ignore their[](#l8.67)
error and fall back on reading /dev/urandom which never blocks,[](#l8.68)
even if the system urandom is not initialized yet. */[](#l8.69)
if (errno == EAGAIN && !raise && !blocking) {[](#l8.70) return 0;[](#l8.71) }[](#l8.72)
@@ -228,13 +222,13 @@ static struct { } urandom_cache = { -1 }; -/* Read 'size' random bytes from getrandom(). Fall back on reading from +/* Read 'size' random bytes from py_getrandom(). Fall back on reading from /dev/urandom if getrandom() is not available. Return 0 on success. Raise an exception (if raise is non-zero) and return -1 on error. */ static int -dev_urandom(char *buffer, Py_ssize_t size, int raise) +dev_urandom(char *buffer, Py_ssize_t size, int blocking, int raise) { int fd; Py_ssize_t n; @@ -245,7 +239,7 @@ dev_urandom(char *buffer, Py_ssize_t siz assert(size > 0); #ifdef PY_GETRANDOM
- res = py_getrandom(buffer, size, blocking, raise); if (res < 0) { return -1; } @@ -381,7 +375,7 @@ lcg_urandom(unsigned int x0, unsigned ch syscall) - Don't release the GIL to call syscalls. */ static int -pyurandom(void *buffer, Py_ssize_t size, int raise) +pyurandom(void *buffer, Py_ssize_t size, int blocking, int raise) { if (size < 0) { if (raise) {
@@ -400,7 +394,7 @@ pyurandom(void *buffer, Py_ssize_t size, #elif defined(PY_GETENTROPY) return py_getentropy(buffer, size, raise); #else
#endif } @@ -408,11 +402,29 @@ pyurandom(void *buffer, Py_ssize_t size, number generator (RNG). It is suitable for most cryptographic purposes except long living private keys for asymmetric encryption.
- On Linux 3.17 and newer, the getrandom() syscall is used in blocking mode:
- block until the system urandom entropy pool is initialized (128 bits are
- collected by the kernel). +
- Return 0 on success. Raise an exception and return -1 on error. */ int _PyOS_URandom(void *buffer, Py_ssize_t size) {
+} + +/* Fill buffer with size pseudo-random bytes from the operating system random
- number generator (RNG). It is not suitable for cryptographic purpose. +
- On Linux 3.17 and newer (when getrandom() syscall is used), if the system
- urandom is not initialized yet, the function returns "weak" entropy read
- from /dev/urandom. +
- Return 0 on success. Raise an exception and return -1 on error. */ +int +_PyOS_URandomNonblock(void *buffer, Py_ssize_t size) +{
- return pyurandom(buffer, size, 0, 1);
} void @@ -456,8 +468,11 @@ void int res; /* _PyRandom_Init() is called very early in the Python initialization
and so exceptions cannot be used (use raise=0). */[](#l8.153)
res = pyurandom(secret, secret_size, 0);[](#l8.154)
and so exceptions cannot be used (use raise=0).[](#l8.155)
_PyRandom_Init() must not block Python initialization: call[](#l8.157)
pyurandom() is non-blocking mode (blocking=0): see the PEP 524. */[](#l8.158)
res = pyurandom(secret, secret_size, 0, 0);[](#l8.159) if (res < 0) {[](#l8.160) Py_FatalError("failed to get random numbers to initialize Python");[](#l8.161) }[](#l8.162)