[pypy-svn] r77874 - in pypy/branch/fast-forward/pypy: module/_hashlib module/_hashlib/test rlib

afa at codespeak.net afa at codespeak.net
Wed Oct 13 15:53:09 CEST 2010


Author: afa
Date: Wed Oct 13 15:53:07 2010
New Revision: 77874

Modified:
   pypy/branch/fast-forward/pypy/module/_hashlib/interp_hashlib.py
   pypy/branch/fast-forward/pypy/module/_hashlib/test/test_hashlib.py
   pypy/branch/fast-forward/pypy/rlib/ropenssl.py
Log:
Implement the _hashlib module


Modified: pypy/branch/fast-forward/pypy/module/_hashlib/interp_hashlib.py
==============================================================================
--- pypy/branch/fast-forward/pypy/module/_hashlib/interp_hashlib.py	(original)
+++ pypy/branch/fast-forward/pypy/module/_hashlib/interp_hashlib.py	Wed Oct 13 15:53:07 2010
@@ -1,9 +1,10 @@
 from pypy.interpreter.gateway import unwrap_spec, interp2app
-from pypy.interpreter.typedef import TypeDef
+from pypy.interpreter.typedef import TypeDef, GetSetProperty
 from pypy.tool.sourcetools import func_renamer
 from pypy.interpreter.baseobjspace import Wrappable, W_Root, ObjSpace
 from pypy.rpython.lltypesystem import lltype, rffi
 from pypy.rlib import ropenssl
+from pypy.rlib.rstring import StringBuilder
 
 algorithms = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512')
 
@@ -12,37 +13,105 @@
         self.name = name
         self.ctx = lltype.malloc(ropenssl.EVP_MD_CTX.TO, flavor='raw')
 
-    @unwrap_spec('self', ObjSpace, str, str)
-    def descr_init(self, space, name, buffer):
-        digest = ropenssl.EVT_get_digestbyname(name)
+        digest = ropenssl.EVP_get_digestbyname(name)
         if not digest:
             raise OperationError(space.w_ValueError,
                                  space.wrap("unknown hash function"))
         ropenssl.EVP_DigestInit(self.ctx, digest)
 
-        if buffer:
-            self._hash(buffer)
-
     @unwrap_spec('self', ObjSpace)
     def descr_repr(self, space):
         return space.wrap("<%s HASH object @ 0x%x>" % (self.name, id(self)))
 
     @unwrap_spec('self', ObjSpace, str)
     def update(self, space, buffer):
-        self._hash(buffer)
-
-    def _hash(self, buffer):
         buf = rffi.str2charp(buffer)
         try:
             ropenssl.EVP_DigestUpdate(self.ctx, buf, len(buffer))
         finally:
             rffi.free_charp(buf)
 
+    @unwrap_spec('self', ObjSpace)
+    def copy(self, space):
+        "Return a copy of the hash object."
+        w_hash = W_Hash(self.name)
+        ropenssl.EVP_MD_CTX_copy(w_hash.ctx, self.ctx)
+        return w_hash
+
+    @unwrap_spec('self', ObjSpace)
+    def digest(self, space):
+        "Return the digest value as a string of binary data."
+        digest = self._digest(space)
+        return space.wrap(digest)
+
+    @unwrap_spec('self', ObjSpace)
+    def hexdigest(self, space):
+        "Return the digest value as a string of hexadecimal digits."
+        digest = self._digest(space)
+        hexdigits = '0123456789abcdef'
+        result = StringBuilder(self._digest_size() * 2)
+        for c in digest:
+            result.append(hexdigits[(ord(c) >> 4) & 0xf])
+            result.append(hexdigits[ ord(c)       & 0xf])
+        return space.wrap(result.build())
+
+    def get_digest_size(space, self):
+        return space.wrap(self._digest_size())
+
+    def get_block_size(space, self):
+        return space.wrap(self._block_size())
+
+    def _digest(self, space):
+        ctx = self.copy(space).ctx
+        digest_size = self._digest_size()
+        digest = lltype.malloc(rffi.CCHARP.TO, digest_size, flavor='raw')
+
+        try:
+            ropenssl.EVP_DigestFinal(ctx, digest, None)
+            return rffi.charp2strn(digest, digest_size)
+        finally:
+            lltype.free(digest, flavor='raw')
+
+
+    def _digest_size(self):
+        # XXX This isn't the nicest way, but the EVP_MD_size OpenSSL
+        # XXX function is defined as a C macro on OS X and would be
+        # XXX significantly harder to implement in another way.
+        # Values are digest sizes in bytes
+        return {
+            'md5': 16,
+            'sha1': 20,
+            'sha224': 28,
+            'sha256': 32,
+            'sha384': 48,
+            'sha512': 64,
+            }.get(self.name, 0)
+
+    def _block_size(self):
+        # XXX This isn't the nicest way, but the EVP_MD_CTX_block_size
+        # XXX OpenSSL function is defined as a C macro on some systems
+        # XXX and would be significantly harder to implement in
+        # XXX another way.
+        return {
+            'md5':     64,
+            'sha1':    64,
+            'sha224':  64,
+            'sha256':  64,
+            'sha384': 128,
+            'sha512': 128,
+            }.get(self.name, 0)
+
 W_Hash.typedef = TypeDef(
     'HASH',
-    __init__=interp2app(W_Hash.descr_init),
     __repr__=interp2app(W_Hash.descr_repr),
     update=interp2app(W_Hash.update),
+    copy=interp2app(W_Hash.copy),
+    digest=interp2app(W_Hash.digest),
+    hexdigest=interp2app(W_Hash.hexdigest),
+    #
+    digest_size=GetSetProperty(W_Hash.get_digest_size),
+    digestsize=GetSetProperty(W_Hash.get_digest_size),
+    block_size=GetSetProperty(W_Hash.get_block_size),
     )
 
 @unwrap_spec(ObjSpace, str, str)

Modified: pypy/branch/fast-forward/pypy/module/_hashlib/test/test_hashlib.py
==============================================================================
--- pypy/branch/fast-forward/pypy/module/_hashlib/test/test_hashlib.py	(original)
+++ pypy/branch/fast-forward/pypy/module/_hashlib/test/test_hashlib.py	Wed Oct 13 15:53:07 2010
@@ -10,7 +10,7 @@
         assert isinstance(_hashlib.new('md5'), _hashlib.HASH)
 
     def test_attributes(self):
-        import _hashlib
+        import hashlib
         for name, expected_size in {'md5': 16,
                                     'sha1': 20,
                                     'sha224': 28,
@@ -29,8 +29,12 @@
             hexdigest = h.hexdigest()
             h2.update('d')
             h2.update('ef')
-            assert digest == h.digest()
-            assert hexdigest == h.hexdigest()
+            assert digest    == h2.digest()
+            assert hexdigest == h2.hexdigest()
+            assert len(digest)    == h.digest_size
+            assert len(hexdigest) == h.digest_size * 2
+            c_digest    = digest
+            c_hexdigest = hexdigest
 
             # also test the pure Python implementation
             h = hashlib.__get_builtin_constructor(name)('')
@@ -44,10 +48,14 @@
             hexdigest = h.hexdigest()
             h2.update('d')
             h2.update('ef')
-            assert digest == h.digest()
-            assert hexdigest == h.hexdigest()
+            assert digest    == h2.digest()
+            assert hexdigest == h2.hexdigest()
+
+            # compare both implementations
+            assert c_digest    == digest
+            assert c_hexdigest == hexdigest
 
     def test_unicode(self):
         import _hashlib
-        assert isinstance(hashlib.new('sha1', u'xxx'), _hashlib.HASH)
+        assert isinstance(_hashlib.new('sha1', u'xxx'), _hashlib.HASH)
 

Modified: pypy/branch/fast-forward/pypy/rlib/ropenssl.py
==============================================================================
--- pypy/branch/fast-forward/pypy/rlib/ropenssl.py	(original)
+++ pypy/branch/fast-forward/pypy/rlib/ropenssl.py	Wed Oct 13 15:53:07 2010
@@ -16,10 +16,12 @@
         # so that openssl/ssl.h can repair this nonsense.
         'wincrypt.h',
         'openssl/ssl.h',
-        'openssl/err.h']
+        'openssl/err.h',
+        'openssl/evp.h']
 else:
     libraries = ['ssl', 'crypto']
-    includes = ['openssl/ssl.h', 'openssl/err.h']
+    includes = ['openssl/ssl.h', 'openssl/err.h',
+                'openssl/evp.h']
 
 eci = ExternalCompilationInfo(
     libraries = libraries,
@@ -176,7 +178,7 @@
 
 ssl_external('SSL_read', [SSL, rffi.CCHARP, rffi.INT], rffi.INT)
 
-EVP_MD_CTX = rffi.COpaquePtr('EVP_MD_CTX')
+EVP_MD_CTX = rffi.COpaquePtr('EVP_MD_CTX', compilation_info=eci)
 EVP_MD     = rffi.COpaquePtr('EVP_MD')
 
 EVP_get_digestbyname = external(
@@ -188,6 +190,13 @@
 EVP_DigestUpdate = external(
     'EVP_DigestUpdate',
     [EVP_MD_CTX, rffi.CCHARP, rffi.SIZE_T], rffi.INT)
+EVP_DigestFinal = external(
+    'EVP_DigestFinal',
+    [EVP_MD_CTX, rffi.CCHARP, rffi.VOIDP], rffi.INT)
+EVP_MD_CTX_copy = external(
+    'EVP_MD_CTX_copy', [EVP_MD_CTX, EVP_MD_CTX], rffi.INT)
+EVP_MD_CTX_cleanup = external(
+    'EVP_MD_CTX_cleanup', [EVP_MD_CTX], rffi.INT)
 
 def _init_ssl():
     libssl_SSL_load_error_strings()



More information about the Pypy-commit mailing list