Author: Amaury Forgeot d'Arc <[email protected]>
Branch: stdlib-2.7.3
Changeset: r55625:d426219a75b2
Date: 2012-06-12 21:16 +0200
http://bitbucket.org/pypy/pypy/changeset/d426219a75b2/
Log: Implement posix.urandom for all platforms
diff --git a/pypy/module/posix/__init__.py b/pypy/module/posix/__init__.py
--- a/pypy/module/posix/__init__.py
+++ b/pypy/module/posix/__init__.py
@@ -91,11 +91,9 @@
'_statfields': 'interp_posix.getstatfields(space)',
'kill' : 'interp_posix.kill',
'abort' : 'interp_posix.abort',
+ 'urandom' : 'interp_posix.urandom',
}
- if os.name == 'nt':
- interpleveldefs['urandom'] = 'interp_posix.win32_urandom'
-
if hasattr(os, 'chown'):
interpleveldefs['chown'] = 'interp_posix.chown'
if hasattr(os, 'lchown'):
diff --git a/pypy/module/posix/interp_posix.py
b/pypy/module/posix/interp_posix.py
--- a/pypy/module/posix/interp_posix.py
+++ b/pypy/module/posix/interp_posix.py
@@ -1,5 +1,5 @@
from pypy.interpreter.gateway import unwrap_spec, NoneNotWrapped
-from pypy.rlib import rposix, objectmodel
+from pypy.rlib import rposix, objectmodel, rurandom
from pypy.rlib.objectmodel import specialize
from pypy.rlib.rarithmetic import r_longlong
from pypy.rlib.unroll import unrolling_iterable
@@ -13,7 +13,6 @@
from pypy.module.sys.interp_encoding import getfilesystemencoding
import os, sys
-_WIN = sys.platform == 'win32'
c_int = "c_int"
@@ -491,17 +490,13 @@
def __init__(self, space):
self.space = space
self.w_environ = space.newdict()
- if _WIN:
- self.cryptProviderPtr = lltype.malloc(
- rffi.CArray(HCRYPTPROV), 1, zero=True,
- flavor='raw', immortal=True)
+ self.random_context = rurandom.init_urandom()
def startup(self, space):
_convertenviron(space, self.w_environ)
def _freeze_(self):
# don't capture the environment in the translated pypy
self.space.call_method(self.w_environ, 'clear')
- if _WIN:
- self.cryptProviderPtr[0] = HCRYPTPROV._default
+ self.random_context = None
return True
def get(space):
@@ -1165,72 +1160,14 @@
raise wrap_oserror(space, e)
return space.wrap(res)
-if _WIN:
- from pypy.rlib import rwin32
+@unwrap_spec(n=int)
+def urandom(space, n):
+ """urandom(n) -> str
- eci = ExternalCompilationInfo(
- includes = ['windows.h', 'wincrypt.h'],
- libraries = ['advapi32'],
- )
-
- class CConfig:
- _compilation_info_ = eci
- PROV_RSA_FULL = rffi_platform.ConstantInteger(
- "PROV_RSA_FULL")
- CRYPT_VERIFYCONTEXT = rffi_platform.ConstantInteger(
- "CRYPT_VERIFYCONTEXT")
-
- globals().update(rffi_platform.configure(CConfig))
-
- HCRYPTPROV = rwin32.ULONG_PTR
-
- CryptAcquireContext = rffi.llexternal(
- 'CryptAcquireContextA',
- [rffi.CArrayPtr(HCRYPTPROV),
- rwin32.LPCSTR, rwin32.LPCSTR, rwin32.DWORD, rwin32.DWORD],
- rwin32.BOOL,
- calling_conv='win',
- compilation_info=eci)
-
- CryptGenRandom = rffi.llexternal(
- 'CryptGenRandom',
- [HCRYPTPROV, rwin32.DWORD, rffi.CArrayPtr(rwin32.BYTE)],
- rwin32.BOOL,
- calling_conv='win',
- compilation_info=eci)
-
- @unwrap_spec(n=int)
- def win32_urandom(space, n):
- """urandom(n) -> str
-
- Return a string of n random bytes suitable for cryptographic use.
- """
-
- if n < 0:
- raise OperationError(space.w_ValueError,
- space.wrap("negative argument not allowed"))
-
- provider = get(space).cryptProviderPtr[0]
- if not provider:
- # Acquire context.
- # This handle is never explicitly released. The operating
- # system will release it when the process terminates.
- if not CryptAcquireContext(
- get(space).cryptProviderPtr, None, None,
- PROV_RSA_FULL, CRYPT_VERIFYCONTEXT):
- raise rwin32.lastWindowsError("CryptAcquireContext")
-
- provider = get(space).cryptProviderPtr[0]
-
- # Get random data
- buf = lltype.malloc(rffi.CArray(rwin32.BYTE), n,
- zero=True, # zero seed
- flavor='raw')
- try:
- if not CryptGenRandom(provider, n, buf):
- raise rwin32.lastWindowsError("CryptGenRandom")
-
- return space.wrap(
- rffi.charpsize2str(rffi.cast(rffi.CCHARP, buf), n))
- finally:
- lltype.free(buf, flavor='raw')
+ Return a string of n random bytes suitable for cryptographic use.
+ """
+ context = get(space).random_context
+ try:
+ return space.wrap(rurandom.urandom(context, n))
+ except OSError, e:
+ raise wrap_oserror(space, e)
diff --git a/pypy/module/posix/test/test_posix2.py
b/pypy/module/posix/test/test_posix2.py
--- a/pypy/module/posix/test/test_posix2.py
+++ b/pypy/module/posix/test/test_posix2.py
@@ -958,6 +958,18 @@
x = f.read(1)
assert x == 'e'
+ def test_urandom(self):
+ os = self.posix
+ s = os.urandom(5)
+ assert isinstance(s, bytes)
+ assert len(s) == 5
+ for x in range(50):
+ if s != os.urandom(5):
+ break
+ else:
+ assert False, "urandom() always returns the same string"
+ # Or very unlucky
+
class AppTestEnvironment(object):
def setup_class(cls):
diff --git a/pypy/rlib/rurandom.py b/pypy/rlib/rurandom.py
new file mode 100644
--- /dev/null
+++ b/pypy/rlib/rurandom.py
@@ -0,0 +1,96 @@
+"""The urandom() function, suitable for cryptographic use.
+"""
+
+import os, sys
+import errno
+
+if sys.platform == 'win32':
+ from pypy.rlib import rwin32
+
+ eci = ExternalCompilationInfo(
+ includes = ['windows.h', 'wincrypt.h'],
+ libraries = ['advapi32'],
+ )
+
+ class CConfig:
+ _compilation_info_ = eci
+ PROV_RSA_FULL = rffi_platform.ConstantInteger(
+ "PROV_RSA_FULL")
+ CRYPT_VERIFYCONTEXT = rffi_platform.ConstantInteger(
+ "CRYPT_VERIFYCONTEXT")
+
+ globals().update(rffi_platform.configure(CConfig))
+
+ HCRYPTPROV = rwin32.ULONG_PTR
+
+ CryptAcquireContext = rffi.llexternal(
+ 'CryptAcquireContextA',
+ [rffi.CArrayPtr(HCRYPTPROV),
+ rwin32.LPCSTR, rwin32.LPCSTR, rwin32.DWORD, rwin32.DWORD],
+ rwin32.BOOL,
+ calling_conv='win',
+ compilation_info=eci)
+
+ CryptGenRandom = rffi.llexternal(
+ 'CryptGenRandom',
+ [HCRYPTPROV, rwin32.DWORD, rffi.CArrayPtr(rwin32.BYTE)],
+ rwin32.BOOL,
+ calling_conv='win',
+ compilation_info=eci)
+
+ def init_urandom():
+ "Acquire context."
+ # This handle is never explicitly released. The operating
+ # system will release it when the process terminates.
+ with lltype.scoped_malloc(rffi.CArray(HCRYPTPROV), 1) as ptr:
+ if not CryptAcquireContext(
+ ptr, None, None,
+ PROV_RSA_FULL, CRYPT_VERIFYCONTEXT):
+ raise rwin32.lastWindowsError("CryptAcquireContext")
+ return ptr[0]
+
+ def urandom(context, n):
+ provider = context
+ # TODO(win64) This is limited to 2**31
+ with lltype.scoped_alloc(rffi.CArray(rwin32.BYTE), n,
+ zero=True, # zero seed
+ ) as buf:
+ if not CryptGenRandom(provider, n, buf):
+ raise rwin32.lastWindowsError("CryptGenRandom")
+
+ return rffi.charpsize2str(rffi.cast(rffi.CCHARP, buf), n)
+
+elif 0: # __VMS
+ from pypy.rlib.ropenssl import libssl_RAND_pseudo_bytes
+ def init_urandom():
+ pass
+
+ def urandom(context, n):
+ with rffi.scoped_alloc_buffer(n) as buf:
+ if libssl_RAND_pseudo_bytes(self.raw, n) < 0:
+ raise ValueError("RAND_pseudo_bytes")
+ return buf.str(n)
+else: # Posix implementation
+ def init_urandom():
+ pass
+
+ def urandom(context, n):
+ "Read n bytes from /dev/urandom."
+ result = ''
+ if n == 0:
+ return result
+ fd = os.open("/dev/urandom", os.O_RDONLY, 0777)
+ try:
+ while n > 0:
+ try:
+ data = os.read(fd, n)
+ except OSError as e:
+ if e.errno != errno.EINTR:
+ raise
+ data = ''
+ result += data
+ n -= len(data)
+ finally:
+ os.close(fd)
+ return result
+
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit