diff -r 11ebd14076b4 Doc/library/os.rst --- a/Doc/library/os.rst Tue Jun 07 21:19:55 2016 +0100 +++ b/Doc/library/os.rst Wed Jun 08 11:17:18 2016 +0200 @@ -3727,7 +3727,7 @@ Miscellaneous Functions ----------------------- -.. function:: urandom(n) +.. function:: urandom(n, \*, block=False) Return a string of *n* random bytes suitable for cryptographic use. @@ -3735,18 +3735,21 @@ Miscellaneous Functions returned data should be unpredictable enough for cryptographic applications, though its exact quality depends on the OS implementation. - On Linux, ``getrandom()`` syscall is used if available and the urandom - entropy pool is initialized (``getrandom()`` does not block). + On Linux, ``getrandom()`` syscall is used if available. 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. + The *block* parameter changes the behaviour on Linux when ``getrandom()`` + syscall is used and the urandom entropy pool is not initialized. If *block* + is true, the call waits until the entropy pool is initialized. If *block* is + false, the function falls back on reading ``/dev/urandom``. + For an easy-to-use interface to the random number generator provided by your platform, please see :class:`random.SystemRandom`. .. versionchanged:: 3.5.2 - On Linux, if ``getrandom()`` blocks (the urandom entropy pool is not - initialized yet), fall back on reading ``/dev/urandom``. + Added *block* parameter. .. versionchanged:: 3.5 On Linux 3.17 and newer, the ``getrandom()`` syscall is now used diff -r 11ebd14076b4 Include/pylifecycle.h --- a/Include/pylifecycle.h Tue Jun 07 21:19:55 2016 +0100 +++ b/Include/pylifecycle.h Wed Jun 08 11:17:18 2016 +0200 @@ -116,7 +116,7 @@ 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, int block); #ifdef __cplusplus } diff -r 11ebd14076b4 Lib/random.py --- a/Lib/random.py Tue Jun 07 21:19:55 2016 +0100 +++ b/Lib/random.py Wed Jun 08 11:17:18 2016 +0200 @@ -107,7 +107,7 @@ class Random(_random.Random): try: # Seed with enough bytes to span the 19937 bit # state space for the Mersenne Twister - a = int.from_bytes(_urandom(2500), 'big') + a = int.from_bytes(_urandom(2500, block=False), 'big') except NotImplementedError: import time a = int(time.time() * 256) # use fractional seconds diff -r 11ebd14076b4 Lib/test/test_os.py --- a/Lib/test/test_os.py Tue Jun 07 21:19:55 2016 +0100 +++ b/Lib/test/test_os.py Wed Jun 08 11:17:18 2016 +0200 @@ -1262,6 +1262,11 @@ class URandomTests(unittest.TestCase): data2 = self.get_urandom_subprocess(16) self.assertNotEqual(data1, data2) + def test_urandom_block(self): + data1 = os.urandom(16, block=False) + data2 = os.urandom(16, block=False) + self.assertNotEqual(data1, data2) + # os.urandom() doesn't use a file descriptor when it is implemented with the # getentropy() function, the getrandom() function or the getrandom() syscall diff -r 11ebd14076b4 Modules/clinic/posixmodule.c.h --- a/Modules/clinic/posixmodule.c.h Tue Jun 07 21:19:55 2016 +0100 +++ b/Modules/clinic/posixmodule.c.h Wed Jun 08 11:17:18 2016 +0200 @@ -5149,26 +5149,29 @@ exit: #endif /* defined(USE_XATTRS) */ PyDoc_STRVAR(os_urandom__doc__, -"urandom($module, size, /)\n" +"urandom($module, /, size, *, block=0)\n" "--\n" "\n" "Return a bytes object containing random bytes suitable for cryptographic use."); #define OS_URANDOM_METHODDEF \ - {"urandom", (PyCFunction)os_urandom, METH_O, os_urandom__doc__}, - -static PyObject * -os_urandom_impl(PyModuleDef *module, Py_ssize_t size); - -static PyObject * -os_urandom(PyModuleDef *module, PyObject *arg) + {"urandom", (PyCFunction)os_urandom, METH_VARARGS|METH_KEYWORDS, os_urandom__doc__}, + +static PyObject * +os_urandom_impl(PyModuleDef *module, Py_ssize_t size, int block); + +static PyObject * +os_urandom(PyModuleDef *module, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; + static char *_keywords[] = {"size", "block", NULL}; Py_ssize_t size; - - if (!PyArg_Parse(arg, "n:urandom", &size)) + int block = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "n|$i:urandom", _keywords, + &size, &block)) goto exit; - return_value = os_urandom_impl(module, size); + return_value = os_urandom_impl(module, size, block); exit: return return_value; diff -r 11ebd14076b4 Modules/posixmodule.c --- a/Modules/posixmodule.c Tue Jun 07 21:19:55 2016 +0100 +++ b/Modules/posixmodule.c Wed Jun 08 11:17:18 2016 +0200 @@ -11061,14 +11061,15 @@ exit: os.urandom size: Py_ssize_t - / + * + block: int = 0 Return a bytes object containing random bytes suitable for cryptographic use. [clinic start generated code]*/ static PyObject * -os_urandom_impl(PyModuleDef *module, Py_ssize_t size) -/*[clinic end generated code: output=e0011f021501f03b input=4067cdb1b6776c29]*/ +os_urandom_impl(PyModuleDef *module, Py_ssize_t size, int block) +/*[clinic end generated code: output=3b2eabce5a7bedbb input=3f8dd67b10329a89]*/ { PyObject *bytes; int result; @@ -11081,7 +11082,8 @@ os_urandom_impl(PyModuleDef *module, Py_ return NULL; result = _PyOS_URandom(PyBytes_AS_STRING(bytes), - PyBytes_GET_SIZE(bytes)); + PyBytes_GET_SIZE(bytes), + block); if (result == -1) { Py_DECREF(bytes); return NULL; diff -r 11ebd14076b4 Python/random.c --- a/Python/random.c Tue Jun 07 21:19:55 2016 +0100 +++ b/Python/random.c Wed Jun 08 11:17:18 2016 +0200 @@ -120,23 +120,29 @@ py_getentropy(unsigned char *buffer, Py_ #define PY_GETRANDOM 1 static int -py_getrandom(void *buffer, Py_ssize_t size, int raise) +py_getrandom(void *buffer, Py_ssize_t size, int raise, int block) { /* Is getrandom() supported by the running kernel? * Need Linux kernel 3.17 or newer, or Solaris 11.3 or newer */ static int getrandom_works = 1; - - /* getrandom() on Linux will block if called before the kernel has - * initialized the urandom entropy pool. This will cause Python - * to hang on startup if called very early in the boot process - - * see https://bugs.python.org/issue26839. To avoid this, use the - * GRND_NONBLOCK flag. */ - const int flags = GRND_NONBLOCK; + int flags; int n; if (!getrandom_works) return 0; + if (block) { + flags = 0; + } + else { + /* getrandom() on Linux will block if called before the kernel has + * initialized the urandom entropy pool. This will cause Python + * to hang on startup if called very early in the boot process - + * see https://bugs.python.org/issue26839. To avoid this, use the + * GRND_NONBLOCK flag. */ + flags = GRND_NONBLOCK; + } + while (0 < size) { #ifdef sun /* Issue #26735: On Solaris, getrandom() is limited to returning up @@ -175,6 +181,7 @@ py_getrandom(void *buffer, Py_ssize_t si getrandom_works = 0; return 0; } + if (errno == EAGAIN) { /* If we failed with EAGAIN, the entropy pool was * uninitialized. In this case, we return failure to fall @@ -183,7 +190,7 @@ py_getrandom(void *buffer, Py_ssize_t si * Note: In this case the data read will not be random so * should not be used for cryptographic purposes. Retaining * the existing semantics for practical purposes. */ - getrandom_works = 0; + assert(!block); return 0; } @@ -229,10 +236,11 @@ dev_urandom_noraise(unsigned char *buffe assert (0 < size); #ifdef PY_GETRANDOM - if (py_getrandom(buffer, size, 0) == 1) + if (py_getrandom(buffer, size, 0, 0) == 1) return; - /* getrandom() is not supported by the running kernel, fall back - * on reading /dev/urandom */ + /* getrandom() is not supported by the running kernel or the urandom + entropy pool is not initialized yet, fall back on reading + /dev/urandom */ #endif fd = _Py_open_noraise("/dev/urandom", O_RDONLY); @@ -259,7 +267,7 @@ dev_urandom_noraise(unsigned char *buffe /* Read size bytes from /dev/urandom into buffer. Return 0 on success, raise an exception and return -1 on error. */ static int -dev_urandom_python(char *buffer, Py_ssize_t size) +dev_urandom_python(char *buffer, Py_ssize_t size, int block) { int fd; Py_ssize_t n; @@ -272,13 +280,14 @@ dev_urandom_python(char *buffer, Py_ssiz return 0; #ifdef PY_GETRANDOM - res = py_getrandom(buffer, size, 1); + res = py_getrandom(buffer, size, 1, block); if (res < 0) return -1; if (res == 1) return 0; - /* getrandom() is not supported by the running kernel, fall back - * on reading /dev/urandom */ + /* getrandom() is not supported by the running kernel or the urandom + entropy pool is not initialized yet, fall back on reading + /dev/urandom */ #endif if (urandom_cache.fd >= 0) { @@ -377,9 +386,12 @@ lcg_urandom(unsigned int x0, unsigned ch number generator (RNG). It is suitable for most cryptographic purposes except long living private keys for asymmetric encryption. + If block is non-zero, block until the entropy pool is initialized. If block + is zero, don't block if the entropy pool is not initialized. + Return 0 on success, raise an exception and return -1 on error. */ int -_PyOS_URandom(void *buffer, Py_ssize_t size) +_PyOS_URandom(void *buffer, Py_ssize_t size, int block) { if (size < 0) { PyErr_Format(PyExc_ValueError, @@ -394,7 +406,7 @@ int #elif defined(PY_GETENTROPY) return py_getentropy(buffer, size, 0); #else - return dev_urandom_python((char*)buffer, size); + return dev_urandom_python((char*)buffer, size, block); #endif }