Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit dc3883f

Browse files
committed
Patch #934711: Expose platform-specific entropy.
1 parent 6ab78cd commit dc3883f

5 files changed

Lines changed: 126 additions & 1 deletion

File tree

Doc/lib/libos.tex

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1828,3 +1828,22 @@ \subsection{Miscellaneous System Information \label{os-path}}
18281828
Also available via \module{os.path}.
18291829
\versionadded{2.4}
18301830
\end{datadesc}
1831+
1832+
1833+
\subsection{Miscellaneous Functions \label{os-miscfunc}}
1834+
1835+
\begin{funcdesc}{urandom}{n}
1836+
Return a string of \var{n} random bytes suitable for cryptographic use.
1837+
1838+
This function returns random bytes from an OS-specific
1839+
randomness source. The returned data should be unpredictable enough for
1840+
cryptographic applications, though its exact quality depends on the OS
1841+
implementation. On a UNIX-like system this will query /dev/urandom, and
1842+
on Windows it will use CryptGenRandom. If a randomness source is not
1843+
found, \exception{NotImplementedError} will be raised.
1844+
\versionadded{2.4}
1845+
\end{funcdesc}
1846+
1847+
1848+
1849+

Lib/os.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,3 +656,24 @@ def _pickle_statvfs_result(sr):
656656
_make_statvfs_result)
657657
except NameError: # statvfs_result may not exist
658658
pass
659+
660+
if not _exists("urandom"):
661+
_urandomfd = None
662+
def urandom(n):
663+
"""urandom(n) -> str
664+
665+
Return a string of n random bytes suitable for cryptographic use.
666+
667+
"""
668+
global _urandomfd
669+
if not _urandomfd:
670+
try:
671+
_urandomfd = open("/dev/urandom", O_RDONLY)
672+
except:
673+
_urandomfd = NotImplementedError
674+
if _urandomfd is NotImplementedError:
675+
raise NotImplementedError("/dev/urandom (or equivalent) not found")
676+
bytes = ""
677+
while len(bytes) < n:
678+
bytes += read(_urandomfd, n - len(bytes))
679+
return bytes

Lib/test/test_os.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,16 @@ def test_devnull(self):
343343
self.assertEqual(f.read(), '')
344344
f.close()
345345

346+
class URandomTests (unittest.TestCase):
347+
def test_urandom(self):
348+
try:
349+
self.assertEqual(len(os.urandom(1)), 1)
350+
self.assertEqual(len(os.urandom(10)), 10)
351+
self.assertEqual(len(os.urandom(100)), 100)
352+
self.assertEqual(len(os.urandom(1000)), 1000)
353+
except NotImplementedError:
354+
pass
355+
346356
def test_main():
347357
test_support.run_unittest(
348358
TemporaryFileTests,
@@ -351,6 +361,7 @@ def test_main():
351361
WalkTests,
352362
MakedirTests,
353363
DevNullTests,
364+
URandomTests
354365
)
355366

356367
if __name__ == "__main__":

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ Core and builtins
4747
Extension modules
4848
-----------------
4949

50+
- os.urandom has been added for systems that support sources of random
51+
data.
52+
5053
- Patch 1012740: truncate() on a writeable cStringIO now resets the
5154
position to the end of the stream. This is consistent with the original
5255
StringIO module and avoids inadvertently resurrecting data that was

Modules/posixmodule.c

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ extern int lstat(const char *, struct stat *);
228228
#include <io.h>
229229
#include <process.h>
230230
#include "osdefs.h"
231-
#define WIN32_LEAN_AND_MEAN
231+
#define _WIN32_WINNT 0x0400 /* Needed for CryptoAPI on some systems */
232232
#include <windows.h>
233233
#include <shellapi.h> /* for ShellExecute() */
234234
#define popen _popen
@@ -7221,6 +7221,74 @@ posix_getloadavg(PyObject *self, PyObject *noargs)
72217221
}
72227222
#endif
72237223

7224+
#ifdef MS_WINDOWS
7225+
7226+
PyDoc_STRVAR(win32_urandom__doc__,
7227+
"urandom(n) -> str\n\n\
7228+
Return a string of n random bytes suitable for cryptographic use.");
7229+
7230+
typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv,\
7231+
LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType,\
7232+
DWORD dwFlags );
7233+
typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen,\
7234+
BYTE *pbBuffer );
7235+
7236+
static CRYPTGENRANDOM pCryptGenRandom = NULL;
7237+
static HCRYPTPROV hCryptProv = 0;
7238+
7239+
static PyObject* win32_urandom(PyObject *self, PyObject *args)
7240+
{
7241+
int howMany = 0;
7242+
unsigned char* bytes = NULL;
7243+
PyObject* returnVal = NULL;
7244+
7245+
/* Read arguments */
7246+
if (!PyArg_ParseTuple(args, "i", &howMany))
7247+
return(NULL);
7248+
7249+
if (hCryptProv == 0) {
7250+
HINSTANCE hAdvAPI32 = NULL;
7251+
CRYPTACQUIRECONTEXTA pCryptAcquireContext = NULL;
7252+
7253+
/* Obtain handle to the DLL containing CryptoAPI
7254+
This should not fail */
7255+
if( (hAdvAPI32 = GetModuleHandle("advapi32.dll")) == NULL)
7256+
return win32_error("GetModuleHandle", NULL);
7257+
7258+
/* Obtain pointers to the CryptoAPI functions
7259+
This will fail on some early versions of Win95 */
7260+
pCryptAcquireContext=(CRYPTACQUIRECONTEXTA)GetProcAddress(hAdvAPI32,\
7261+
"CryptAcquireContextA");
7262+
pCryptGenRandom=(CRYPTGENRANDOM)GetProcAddress(hAdvAPI32,\
7263+
"CryptGenRandom");
7264+
7265+
if (pCryptAcquireContext == NULL || pCryptGenRandom == NULL)
7266+
return PyErr_Format(PyExc_NotImplementedError,\
7267+
"CryptGenRandom not found");
7268+
7269+
/* Acquire context */
7270+
if(!pCryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
7271+
CRYPT_VERIFYCONTEXT))
7272+
return win32_error("CryptAcquireContext", NULL);
7273+
}
7274+
7275+
/* Allocate bytes */
7276+
if ((bytes = (unsigned char*)PyMem_Malloc(howMany)) == NULL)
7277+
return PyErr_NoMemory();
7278+
7279+
/* Get random data */
7280+
if (!pCryptGenRandom(hCryptProv, howMany, bytes)) {
7281+
PyMem_Free(bytes);
7282+
return win32_error("CryptGenRandom", NULL);
7283+
}
7284+
7285+
/* Build return value */
7286+
returnVal = PyString_FromStringAndSize(bytes, howMany);
7287+
PyMem_Free(bytes);
7288+
7289+
return returnVal;
7290+
}
7291+
#endif
72247292

72257293
static PyMethodDef posix_methods[] = {
72267294
{"access", posix_access, METH_VARARGS, posix_access__doc__},
@@ -7506,6 +7574,9 @@ static PyMethodDef posix_methods[] = {
75067574
#ifdef HAVE_GETLOADAVG
75077575
{"getloadavg", posix_getloadavg, METH_NOARGS, posix_getloadavg__doc__},
75087576
#endif
7577+
#ifdef MS_WINDOWS
7578+
{"urandom", win32_urandom, METH_VARARGS, win32_urandom__doc__},
7579+
#endif
75097580
{NULL, NULL} /* Sentinel */
75107581
};
75117582

0 commit comments

Comments
 (0)