diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 023ebf4..e84f457 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -9,6 +9,7 @@ import warnings import sys import signal import subprocess +import sysconfig import time try: import resource @@ -567,6 +568,12 @@ class URandomTests (unittest.TestCase): data2 = self.get_urandom_subprocess(16) self.assertNotEqual(data1, data2) + +HAVE_GETENTROPY = (sysconfig.get_config_var('HAVE_GETENTROPY') == 1) + +@unittest.skipIf(HAVE_GETENTROPY, + "getentropy() does not use a file descriptor") +class URandomFDTests(unittest.TestCase): @unittest.skipUnless(resource, "test requires the resource module") def test_urandom_failure(self): # Check urandom() failing when it is not able to open /dev/random. diff --git a/Misc/NEWS b/Misc/NEWS index 8d44926..dea07e3 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -15,6 +15,9 @@ Core and Builtins Library ------- +- Issue #22585: On OpenBSD 5.6 and newer, os.urandom() now calls getentropy(), + instead of reading /dev/urandom, to get pseudo-random bytes. + - Issue #23093: In the io, module allow more operations to work on detached streams. diff --git a/Python/random.c b/Python/random.c index 1e5776d..d10dda9 100644 --- a/Python/random.c +++ b/Python/random.c @@ -92,8 +92,33 @@ win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise) } return 0; } -#endif /* MS_WINDOWS */ +#elif HAVE_GETENTROPY +/* Fill buffer with size pseudo-random bytes generated by getentropy(). + Return 0 on success, or raise an exception and return -1 on error. + If fatal is nonzero, call Py_FatalError() instead of raising an exception + on error. */ +static int +py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal) +{ + while (size > 0) { + Py_ssize_t len = Py_MIN(size, 256); + int res = getentropy(buffer, len); + if (res < 0) { + if (fatal) { + Py_FatalError("getentropy() failed"); + } + else { + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + } + buffer += len; + size -= len; + } + return 0; +} +#endif #ifdef __VMS /* Use openssl random routine */ @@ -291,6 +316,8 @@ _PyOS_URandom(void *buffer, Py_ssize_t size) #ifdef MS_WINDOWS return win32_urandom((unsigned char *)buffer, size, 1); +#elif HAVE_GETENTROPY + return py_getentropy(buffer, size, 0); #else # ifdef __VMS return vms_urandom((unsigned char *)buffer, size, 1); @@ -350,12 +377,12 @@ _PyRandom_Init(void) else { #ifdef MS_WINDOWS (void)win32_urandom((unsigned char *)secret, secret_size, 0); -#else /* #ifdef MS_WINDOWS */ -# ifdef __VMS +#elif __VMS vms_urandom((unsigned char *)secret, secret_size, 0); -# else - dev_urandom_noraise((unsigned char*)secret, secret_size); -# endif +#elif HAVE_GETENTROPY + (void)py_getentropy(secret, secret_size, 1); +#else + dev_urandom_noraise(secret, secret_size); #endif } } @@ -368,6 +395,8 @@ _PyRandom_Fini(void) CryptReleaseContext(hCryptProv, 0); hCryptProv = 0; } +#elif HAVE_GETENTROPY + /* nothing to clean */ #else dev_urandom_close(); #endif diff --git a/configure b/configure index 09e65eb..133c88b 100755 --- a/configure +++ b/configure @@ -10196,7 +10196,8 @@ $as_echo "MACHDEP_OBJS" >&6; } # checks for library functions for ac_func in alarm setitimer getitimer bind_textdomain_codeset chown \ clock confstr ctermid execv fchmod fchown fork fpathconf ftime ftruncate \ - gai_strerror getgroups getlogin getloadavg getpeername getpgid getpid \ + gai_strerror getentropy getgroups getlogin getloadavg getpeername getpgid \ + getpid \ getpriority getresuid getresgid getpwent getspnam getspent getsid getwd \ initgroups kill killpg lchmod lchown lstat mkfifo mknod mktime mmap \ mremap nice pathconf pause plock poll pthread_init \ diff --git a/configure.ac b/configure.ac index e63edf5..75486f4 100644 --- a/configure.ac +++ b/configure.ac @@ -2913,6 +2913,7 @@ AC_MSG_RESULT(MACHDEP_OBJS) AC_CHECK_FUNCS(alarm setitimer getitimer bind_textdomain_codeset chown \ clock confstr ctermid execv fchmod fchown fork fpathconf ftime ftruncate \ gai_strerror getgroups getlogin getloadavg getpeername getpgid getpid \ + getentropy \ getpriority getresuid getresgid getpwent getspnam getspent getsid getwd \ initgroups kill killpg lchmod lchown lstat mkfifo mknod mktime mmap \ mremap nice pathconf pause plock poll pthread_init \ diff --git a/pyconfig.h.in b/pyconfig.h.in index d6d479a..cb962a4 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -280,6 +280,9 @@ /* Define this if you have flockfile(), getc_unlocked(), and funlockfile() */ #undef HAVE_GETC_UNLOCKED +/* Define to 1 if you have the `getentropy' function. */ +#undef HAVE_GETENTROPY + /* Define to 1 if you have the `getgroups' function. */ #undef HAVE_GETGROUPS