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

Reply via email to