[pypy-svn] r7405 - in pypy/trunk/src/pypy/appspace: . test

jacob at codespeak.net jacob at codespeak.net
Thu Nov 18 19:47:09 CET 2004


Author: jacob
Date: Thu Nov 18 19:47:08 2004
New Revision: 7405

Added:
   pypy/trunk/src/pypy/appspace/pymd5.py
   pypy/trunk/src/pypy/appspace/pysha.py
   pypy/trunk/src/pypy/appspace/test/test_md5.py
   pypy/trunk/src/pypy/appspace/test/test_sha.py
Log:
Python implementations of MD5 and SHA.

Added: pypy/trunk/src/pypy/appspace/pymd5.py
==============================================================================
--- (empty file)
+++ pypy/trunk/src/pypy/appspace/pymd5.py	Thu Nov 18 19:47:08 2004
@@ -0,0 +1,423 @@
+#!/usr/bin/env python
+# -*- coding: iso-8859-1
+
+"""A sample implementation of MD5 in pure Python.
+
+This is an implementation of the MD5 hash function, as specified by
+RFC 1321, in pure Python. It was implemented using Bruce Schneier's
+excellent book "Applied Cryptography", 2nd ed., 1996.
+
+Surely this is not meant to compete with the existing implementation
+of the Python standard library (written in C). Rather, it should be
+seen as a Python complement that is more readable than C and can be
+used more conveniently for learning and experimenting purposes in
+the field of cryptography.
+
+This module tries very hard to follow the API of the existing Python
+standard library's "md5" module, but although it seems to work fine,
+it has not been extensively tested! (But note that there is a test
+module, test_md5py.py, that compares this Python implementation with
+the C one of the Python standard library.
+
+BEWARE: this comes with no guarantee whatsoever about fitness and/or
+other properties! Specifically, do not use this in any production
+code! License is Python License!
+
+Special thanks to Aurelian Coman who fixed some nasty bugs!
+
+Dinu C. Gherman
+"""
+
+
+__date__    = '2004-11-17'
+__version__ = 0.91 # Modernised by J. Hallén and L. Creighton for Pypy
+
+
+import struct, copy
+
+
+# ======================================================================
+# Bit-Manipulation helpers
+#
+#   _long2bytes() was contributed by Barry Warsaw
+#   and is reused here with tiny modifications.
+# ======================================================================
+
+def _long2bytes(n, blocksize=0):
+    """Convert a long integer to a byte string.
+
+    If optional blocksize is given and greater than zero, pad the front
+    of the byte string with binary zeros so that the length is a multiple
+    of blocksize.
+    """
+
+    # After much testing, this algorithm was deemed to be the fastest.
+    s = ''
+    pack = struct.pack
+    while n > 0:
+        ### CHANGED FROM '>I' TO '<I'. (DCG)
+        s = pack('<I', n & 0xffffffffL) + s
+        ### --------------------------
+        n = n >> 32
+
+    # Strip off leading zeros.
+    for i in range(len(s)):
+        if s[i] <> '\000':
+            break
+    else:
+        # Only happens when n == 0.
+        s = '\000'
+        i = 0
+
+    s = s[i:]
+
+    # Add back some pad bytes. This could be done more efficiently
+    # w.r.t. the de-padding being done above, but sigh...
+    if blocksize > 0 and len(s) % blocksize:
+        s = (blocksize - len(s) % blocksize) * '\000' + s
+
+    return s
+
+
+def _bytelist2long(list):
+    "Transform a list of characters into a list of longs."
+
+    imax = len(list)/4
+    hl = [0L] * imax
+
+    j = 0
+    i = 0
+    while i < imax:
+        b0 = long(ord(list[j]))
+        b1 = (long(ord(list[j+1]))) << 8
+        b2 = (long(ord(list[j+2]))) << 16
+        b3 = (long(ord(list[j+3]))) << 24
+        hl[i] = b0 | b1 |b2 | b3
+        i = i+1
+        j = j+4
+
+    return hl
+
+
+def _rotateLeft(x, n):
+    "Rotate x (32 bit) left n bits circularly."
+
+    return (x << n) | (x >> (32-n))
+
+
+# ======================================================================
+# The real MD5 meat...
+#
+#   Implemented after "Applied Cryptography", 2nd ed., 1996,
+#   pp. 436-441 by Bruce Schneier.
+# ======================================================================
+
+# F, G, H and I are basic MD5 functions.
+
+def F(x, y, z):
+    return (x & y) | ((~x) & z)
+
+def G(x, y, z):
+    return (x & z) | (y & (~z))
+
+def H(x, y, z):
+    return x ^ y ^ z
+
+def I(x, y, z):
+    return y ^ (x | (~z))
+
+
+def XX(func, a, b, c, d, x, s, ac):
+    """Wrapper for call distribution to functions F, G, H and I.
+
+    This replaces functions FF, GG, HH and II from "Appl. Crypto."
+    Rotation is separate from addition to prevent recomputation
+    (now summed-up in one function).
+    """
+
+    res = 0L
+    res = res + a + func(b, c, d)
+    res = res + x 
+    res = res + ac
+    res = res & 0xffffffffL
+    res = _rotateLeft(res, s)
+    res = res & 0xffffffffL
+    res = res + b
+
+    return res & 0xffffffffL
+
+
+class MD5:
+    "An implementation of the MD5 hash function in pure Python."
+
+    def __init__(self):
+        "Initialisation."
+        
+        # Initial message length in bits(!).
+        self.length = 0L
+        self.count = [0, 0]
+
+        # Initial empty message as a sequence of bytes (8 bit characters).
+        self.input = []
+
+        # Call a separate init function, that can be used repeatedly
+        # to start from scratch on the same object.
+        self.init()
+
+
+    def init(self):
+        "Initialize the message-digest and set all fields to zero."
+
+        self.length = 0L
+        self.input = []
+
+        # Load magic initialization constants.
+        self.A = 0x67452301L
+        self.B = 0xefcdab89L
+        self.C = 0x98badcfeL
+        self.D = 0x10325476L
+
+
+    def _transform(self, inp):
+        """Basic MD5 step transforming the digest based on the input.
+
+        Note that if the Mysterious Constants are arranged backwards
+        in little-endian order and decrypted with the DES they produce
+        OCCULT MESSAGES!
+        """
+
+        a, b, c, d = A, B, C, D = self.A, self.B, self.C, self.D
+
+        # Round 1.
+
+        S11, S12, S13, S14 = 7, 12, 17, 22
+
+        a = XX(F, a, b, c, d, inp[ 0], S11, 0xD76AA478L) # 1 
+        d = XX(F, d, a, b, c, inp[ 1], S12, 0xE8C7B756L) # 2 
+        c = XX(F, c, d, a, b, inp[ 2], S13, 0x242070DBL) # 3 
+        b = XX(F, b, c, d, a, inp[ 3], S14, 0xC1BDCEEEL) # 4 
+        a = XX(F, a, b, c, d, inp[ 4], S11, 0xF57C0FAFL) # 5 
+        d = XX(F, d, a, b, c, inp[ 5], S12, 0x4787C62AL) # 6 
+        c = XX(F, c, d, a, b, inp[ 6], S13, 0xA8304613L) # 7 
+        b = XX(F, b, c, d, a, inp[ 7], S14, 0xFD469501L) # 8 
+        a = XX(F, a, b, c, d, inp[ 8], S11, 0x698098D8L) # 9 
+        d = XX(F, d, a, b, c, inp[ 9], S12, 0x8B44F7AFL) # 10 
+        c = XX(F, c, d, a, b, inp[10], S13, 0xFFFF5BB1L) # 11 
+        b = XX(F, b, c, d, a, inp[11], S14, 0x895CD7BEL) # 12 
+        a = XX(F, a, b, c, d, inp[12], S11, 0x6B901122L) # 13 
+        d = XX(F, d, a, b, c, inp[13], S12, 0xFD987193L) # 14 
+        c = XX(F, c, d, a, b, inp[14], S13, 0xA679438EL) # 15 
+        b = XX(F, b, c, d, a, inp[15], S14, 0x49B40821L) # 16 
+
+        # Round 2.
+
+        S21, S22, S23, S24 = 5, 9, 14, 20
+
+        a = XX(G, a, b, c, d, inp[ 1], S21, 0xF61E2562L) # 17 
+        d = XX(G, d, a, b, c, inp[ 6], S22, 0xC040B340L) # 18 
+        c = XX(G, c, d, a, b, inp[11], S23, 0x265E5A51L) # 19 
+        b = XX(G, b, c, d, a, inp[ 0], S24, 0xE9B6C7AAL) # 20 
+        a = XX(G, a, b, c, d, inp[ 5], S21, 0xD62F105DL) # 21 
+        d = XX(G, d, a, b, c, inp[10], S22, 0x02441453L) # 22 
+        c = XX(G, c, d, a, b, inp[15], S23, 0xD8A1E681L) # 23 
+        b = XX(G, b, c, d, a, inp[ 4], S24, 0xE7D3FBC8L) # 24 
+        a = XX(G, a, b, c, d, inp[ 9], S21, 0x21E1CDE6L) # 25 
+        d = XX(G, d, a, b, c, inp[14], S22, 0xC33707D6L) # 26 
+        c = XX(G, c, d, a, b, inp[ 3], S23, 0xF4D50D87L) # 27 
+        b = XX(G, b, c, d, a, inp[ 8], S24, 0x455A14EDL) # 28 
+        a = XX(G, a, b, c, d, inp[13], S21, 0xA9E3E905L) # 29 
+        d = XX(G, d, a, b, c, inp[ 2], S22, 0xFCEFA3F8L) # 30 
+        c = XX(G, c, d, a, b, inp[ 7], S23, 0x676F02D9L) # 31 
+        b = XX(G, b, c, d, a, inp[12], S24, 0x8D2A4C8AL) # 32 
+
+        # Round 3.
+
+        S31, S32, S33, S34 = 4, 11, 16, 23
+
+        a = XX(H, a, b, c, d, inp[ 5], S31, 0xFFFA3942L) # 33 
+        d = XX(H, d, a, b, c, inp[ 8], S32, 0x8771F681L) # 34 
+        c = XX(H, c, d, a, b, inp[11], S33, 0x6D9D6122L) # 35 
+        b = XX(H, b, c, d, a, inp[14], S34, 0xFDE5380CL) # 36 
+        a = XX(H, a, b, c, d, inp[ 1], S31, 0xA4BEEA44L) # 37 
+        d = XX(H, d, a, b, c, inp[ 4], S32, 0x4BDECFA9L) # 38 
+        c = XX(H, c, d, a, b, inp[ 7], S33, 0xF6BB4B60L) # 39 
+        b = XX(H, b, c, d, a, inp[10], S34, 0xBEBFBC70L) # 40 
+        a = XX(H, a, b, c, d, inp[13], S31, 0x289B7EC6L) # 41 
+        d = XX(H, d, a, b, c, inp[ 0], S32, 0xEAA127FAL) # 42 
+        c = XX(H, c, d, a, b, inp[ 3], S33, 0xD4EF3085L) # 43 
+        b = XX(H, b, c, d, a, inp[ 6], S34, 0x04881D05L) # 44 
+        a = XX(H, a, b, c, d, inp[ 9], S31, 0xD9D4D039L) # 45 
+        d = XX(H, d, a, b, c, inp[12], S32, 0xE6DB99E5L) # 46 
+        c = XX(H, c, d, a, b, inp[15], S33, 0x1FA27CF8L) # 47 
+        b = XX(H, b, c, d, a, inp[ 2], S34, 0xC4AC5665L) # 48 
+
+        # Round 4.
+
+        S41, S42, S43, S44 = 6, 10, 15, 21
+
+        a = XX(I, a, b, c, d, inp[ 0], S41, 0xF4292244L) # 49 
+        d = XX(I, d, a, b, c, inp[ 7], S42, 0x432AFF97L) # 50 
+        c = XX(I, c, d, a, b, inp[14], S43, 0xAB9423A7L) # 51 
+        b = XX(I, b, c, d, a, inp[ 5], S44, 0xFC93A039L) # 52 
+        a = XX(I, a, b, c, d, inp[12], S41, 0x655B59C3L) # 53 
+        d = XX(I, d, a, b, c, inp[ 3], S42, 0x8F0CCC92L) # 54 
+        c = XX(I, c, d, a, b, inp[10], S43, 0xFFEFF47DL) # 55 
+        b = XX(I, b, c, d, a, inp[ 1], S44, 0x85845DD1L) # 56 
+        a = XX(I, a, b, c, d, inp[ 8], S41, 0x6FA87E4FL) # 57 
+        d = XX(I, d, a, b, c, inp[15], S42, 0xFE2CE6E0L) # 58 
+        c = XX(I, c, d, a, b, inp[ 6], S43, 0xA3014314L) # 59 
+        b = XX(I, b, c, d, a, inp[13], S44, 0x4E0811A1L) # 60 
+        a = XX(I, a, b, c, d, inp[ 4], S41, 0xF7537E82L) # 61 
+        d = XX(I, d, a, b, c, inp[11], S42, 0xBD3AF235L) # 62 
+        c = XX(I, c, d, a, b, inp[ 2], S43, 0x2AD7D2BBL) # 63 
+        b = XX(I, b, c, d, a, inp[ 9], S44, 0xEB86D391L) # 64 
+
+        A = (A + a) & 0xffffffffL
+        B = (B + b) & 0xffffffffL
+        C = (C + c) & 0xffffffffL
+        D = (D + d) & 0xffffffffL
+
+        self.A, self.B, self.C, self.D = A, B, C, D
+
+
+    # Down from here all methods follow the Python Standard Library
+    # API of the md5 module.
+
+    def update(self, inBuf):
+        """Add to the current message.
+
+        Update the md5 object with the string arg. Repeated calls
+        are equivalent to a single call with the concatenation of all
+        the arguments, i.e. m.update(a); m.update(b) is equivalent
+        to m.update(a+b).
+
+        The hash is immediately calculated for all full blocks. The final
+        calculation is made in digest(). This allows us to keep an
+        intermediate value for the hash, so that we only need to make
+        minimal recalculation if we call update() to add moredata to
+        the hashed string.
+        """
+
+        leninBuf = long(len(inBuf))
+
+        # Compute number of bytes mod 64.
+        index = (self.count[0] >> 3) & 0x3FL
+
+        # Update number of bits.
+        self.count[0] = self.count[0] + (leninBuf << 3)
+        if self.count[0] < (leninBuf << 3):
+            self.count[1] = self.count[1] + 1
+        self.count[1] = self.count[1] + (leninBuf >> 29)
+
+        partLen = 64 - index
+
+        if leninBuf >= partLen:
+            self.input[index:] = list(inBuf[:partLen])
+            self._transform(_bytelist2long(self.input))
+            i = partLen
+            while i + 63 < leninBuf:
+                self._transform(_bytelist2long(list(inBuf[i:i+64])))
+                i = i + 64
+            else:
+                self.input = list(inBuf[i:leninBuf])
+        else:
+            i = 0
+            self.input = self.input + list(inBuf)
+
+
+    def digest(self):
+        """Terminate the message-digest computation and return digest.
+
+        Return the digest of the strings passed to the update()
+        method so far. This is a 16-byte string which may contain
+        non-ASCII characters, including null bytes.
+        """
+
+        A = self.A
+        B = self.B
+        C = self.C
+        D = self.D
+        input = [] + self.input
+        count = [] + self.count
+
+        index = (self.count[0] >> 3) & 0x3fL
+
+        if index < 56:
+            padLen = 56 - index
+        else:
+            padLen = 120 - index
+
+        padding = ['\200'] + ['\000'] * 63
+        self.update(padding[:padLen])
+
+        # Append length (before padding).
+        bits = _bytelist2long(self.input[:56]) + count
+
+        self._transform(bits)
+
+        # Store state in digest.
+        digest = _long2bytes(self.A << 96, 16)[:4] + \
+                 _long2bytes(self.B << 64, 16)[4:8] + \
+                 _long2bytes(self.C << 32, 16)[8:12] + \
+                 _long2bytes(self.D, 16)[12:]
+
+        self.A = A 
+        self.B = B
+        self.C = C
+        self.D = D
+        self.input = input 
+        self.count = count 
+
+        return digest
+
+
+    def hexdigest(self):
+        """Terminate and return digest in HEX form.
+
+        Like digest() except the digest is returned as a string of
+        length 32, containing only hexadecimal digits. This may be
+        used to exchange the value safely in email or other non-
+        binary environments.
+        """
+
+        return ''.join(['%02x' % ord(c) for c in self.digest()])
+
+    def copy(self):
+        """Return a clone object.
+
+        Return a copy ('clone') of the md5 object. This can be used
+        to efficiently compute the digests of strings that share
+        a common initial substring.
+        """
+
+        return copy.deepcopy(self)
+
+
+# ======================================================================
+# Mimic Python top-level functions from standard library API
+# for consistency with the md5 module of the standard library.
+# ======================================================================
+
+digest_size = 16
+
+def new(arg=None):
+    """Return a new md5 object.
+
+    If arg is present, the method call update(arg) is made.
+    """
+
+    md5 = MD5()
+    if arg:
+        md5.update(arg)
+
+    return md5
+
+
+def md5(arg=None):
+    """Same as new().
+
+    For backward compatibility reasons, this is an alternative
+    name for the new() function.
+    """
+
+    return new(arg)

Added: pypy/trunk/src/pypy/appspace/pysha.py
==============================================================================
--- (empty file)
+++ pypy/trunk/src/pypy/appspace/pysha.py	Thu Nov 18 19:47:08 2004
@@ -0,0 +1,341 @@
+#!/usr/bin/env python
+# -*- coding: iso-8859-1
+
+"""A sample implementation of SHA-1 in pure Python.
+
+   Framework adapted from Dinu Gherman's MD5 implementation by
+   J. Hallén and L. Creighton. SHA-1 implementation based directly on
+   the text of the NIST standard FIPS PUB 180-1.
+"""
+
+
+__date__    = '2004-11-17'
+__version__ = 0.91 # Modernised by J. Hallén and L. Creighton for Pypy
+
+
+import struct, copy
+
+
+# ======================================================================
+# Bit-Manipulation helpers
+#
+#   _long2bytes() was contributed by Barry Warsaw
+#   and is reused here with tiny modifications.
+# ======================================================================
+
+def _long2bytesBigEndian(n, blocksize=0):
+    """Convert a long integer to a byte string.
+
+    If optional blocksize is given and greater than zero, pad the front
+    of the byte string with binary zeros so that the length is a multiple
+    of blocksize.
+    """
+
+    # After much testing, this algorithm was deemed to be the fastest.
+    s = ''
+    pack = struct.pack
+    while n > 0:
+        s = pack('>I', n & 0xffffffffL) + s
+        n = n >> 32
+
+    # Strip off leading zeros.
+    for i in range(len(s)):
+        if s[i] <> '\000':
+            break
+    else:
+        # Only happens when n == 0.
+        s = '\000'
+        i = 0
+
+    s = s[i:]
+
+    # Add back some pad bytes. This could be done more efficiently
+    # w.r.t. the de-padding being done above, but sigh...
+    if blocksize > 0 and len(s) % blocksize:
+        s = (blocksize - len(s) % blocksize) * '\000' + s
+
+    return s
+
+
+def _bytelist2longBigEndian(list):
+    "Transform a list of characters into a list of longs."
+
+    imax = len(list)/4
+    hl = [0L] * imax
+
+    j = 0
+    i = 0
+    while i < imax:
+        b0 = long(ord(list[j])) << 24
+        b1 = long(ord(list[j+1])) << 16
+        b2 = long(ord(list[j+2])) << 8
+        b3 = long(ord(list[j+3]))
+        hl[i] = b0 | b1 | b2 | b3
+        i = i+1
+        j = j+4
+
+    return hl
+
+
+def _rotateLeft(x, n):
+    "Rotate x (32 bit) left n bits circularly."
+
+    return (x << n) | (x >> (32-n))
+
+
+# ======================================================================
+# The SHA transformation functions
+#
+# ======================================================================
+
+def f0_19(B, C, D):
+    return (B & C) | ((~ B) & D)
+
+def f20_39(B, C, D):
+    return B ^ C ^ D
+
+def f40_59(B, C, D):
+    return (B & C) | (B & D) | (C & D)
+
+def f60_79(B, C, D):
+    return B ^ C ^ D
+
+
+f = [f0_19, f20_39, f40_59, f60_79]
+
+# Constants to be used
+K = [
+    0x5A827999L, # ( 0 <= t <= 19)
+    0x6ED9EBA1L, # (20 <= t <= 39)
+    0x8F1BBCDCL, # (40 <= t <= 59)
+    0xCA62C1D6L  # (60 <= t <= 79)
+    ]
+
+class MD5:
+    "An implementation of the MD5 hash function in pure Python."
+
+    def __init__(self):
+        "Initialisation."
+        
+        # Initial message length in bits(!).
+        self.length = 0L
+        self.count = [0, 0]
+
+        # Initial empty message as a sequence of bytes (8 bit characters).
+        self.input = []
+
+        # Call a separate init function, that can be used repeatedly
+        # to start from scratch on the same object.
+        self.init()
+
+
+    def init(self):
+        "Initialize the message-digest and set all fields to zero."
+
+        self.length = 0L
+        self.input = []
+
+        # Initial 160 bit message digest (5 times 32 bit).
+        self.H0 = 0x67452301L
+        self.H1 = 0xEFCDAB89L
+        self.H2 = 0x98BADCFEL
+        self.H3 = 0x10325476L
+        self.H4 = 0xC3D2E1F0L
+
+    def _transform(self, W):
+
+        for t in range(16, 80):
+            W.append(_rotateLeft(
+                W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1) & 0xffffffffL)
+
+        A = self.H0
+        B = self.H1
+        C = self.H2
+        D = self.H3
+        E = self.H4
+
+        """
+        This loop was unrolled to gain about 10% in speed
+        for t in range(0, 80):
+            TEMP = _rotateLeft(A, 5) + f[t/20] + E + W[t] + K[t/20]
+            E = D
+            D = C
+            C = _rotateLeft(B, 30) & 0xffffffffL
+            B = A
+            A = TEMP & 0xffffffffL
+        """
+
+        for t in range(0, 20):
+            TEMP = _rotateLeft(A, 5) + ((B & C) | ((~ B) & D)) + E + W[t] + K[0]
+            E = D
+            D = C
+            C = _rotateLeft(B, 30) & 0xffffffffL
+            B = A
+            A = TEMP & 0xffffffffL
+
+        for t in range(20, 40):
+            TEMP = _rotateLeft(A, 5) + (B ^ C ^ D) + E + W[t] + K[1]
+            E = D
+            D = C
+            C = _rotateLeft(B, 30) & 0xffffffffL
+            B = A
+            A = TEMP & 0xffffffffL
+
+        for t in range(40, 60):
+            TEMP = _rotateLeft(A, 5) + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]
+            E = D
+            D = C
+            C = _rotateLeft(B, 30) & 0xffffffffL
+            B = A
+            A = TEMP & 0xffffffffL
+
+        for t in range(60, 80):
+            TEMP = _rotateLeft(A, 5) + (B ^ C ^ D)  + E + W[t] + K[3]
+            E = D
+            D = C
+            C = _rotateLeft(B, 30) & 0xffffffffL
+            B = A
+            A = TEMP & 0xffffffffL
+
+
+        self.H0 = (self.H0 + A) & 0xffffffffL
+        self.H1 = (self.H1 + B) & 0xffffffffL
+        self.H2 = (self.H2 + C) & 0xffffffffL
+        self.H3 = (self.H3 + D) & 0xffffffffL
+        self.H4 = (self.H4 + E) & 0xffffffffL
+    
+
+    # Down from here all methods follow the Python Standard Library
+    # API of the sha module.
+
+    def update(self, inBuf):
+        """Add to the current message.
+
+        Update the md5 object with the string arg. Repeated calls
+        are equivalent to a single call with the concatenation of all
+        the arguments, i.e. m.update(a); m.update(b) is equivalent
+        to m.update(a+b).
+
+        The hash is immediately calculated for all full blocks. The final
+        calculation is made in digest(). It will calculate 1-2 blocks,
+        depending on how much padding we have to add. This allows us to
+        keep an intermediate value for the hash, so that we only need to
+        make minimal recalculation if we call update() to add more data
+        to the hashed string.
+        """
+
+        leninBuf = long(len(inBuf))
+
+        # Compute number of bytes mod 64.
+        index = (self.count[1] >> 3) & 0x3FL
+
+        # Update number of bits.
+        self.count[1] = self.count[1] + (leninBuf << 3)
+        if self.count[1] < (leninBuf << 3):
+            self.count[0] = self.count[0] + 1
+        self.count[0] = self.count[0] + (leninBuf >> 29)
+
+        partLen = 64 - index
+
+        if leninBuf >= partLen:
+            self.input[index:] = list(inBuf[:partLen])
+            self._transform(_bytelist2longBigEndian(self.input))
+            i = partLen
+            while i + 63 < leninBuf:
+                self._transform(_bytelist2longBigEndian(list(inBuf[i:i+64])))
+                i = i + 64
+            else:
+                self.input = list(inBuf[i:leninBuf])
+        else:
+            i = 0
+            self.input = self.input + list(inBuf)
+
+
+    def digest(self):
+        """Terminate the message-digest computation and return digest.
+
+        Return the digest of the strings passed to the update()
+        method so far. This is a 16-byte string which may contain
+        non-ASCII characters, including null bytes.
+        """
+
+        H0 = self.H0
+        H1 = self.H1
+        H2 = self.H2
+        H3 = self.H3
+        H4 = self.H4
+        input = [] + self.input
+        count = [] + self.count
+
+        index = (self.count[1] >> 3) & 0x3fL
+
+        if index < 56:
+            padLen = 56 - index
+        else:
+            padLen = 120 - index
+
+        padding = ['\200'] + ['\000'] * 63
+        self.update(padding[:padLen])
+
+        # Append length (before padding).
+        bits = _bytelist2longBigEndian(self.input[:56]) + count
+
+        self._transform(bits)
+
+        # Store state in digest.
+        digest = _long2bytesBigEndian(self.H0, 4) + \
+                 _long2bytesBigEndian(self.H1, 4) + \
+                 _long2bytesBigEndian(self.H2, 4) + \
+                 _long2bytesBigEndian(self.H3, 4) + \
+                 _long2bytesBigEndian(self.H4, 4)
+
+        self.H0 = H0 
+        self.H1 = H1 
+        self.H2 = H2
+        self.H3 = H3
+        self.H4 = H4
+        self.input = input 
+        self.count = count 
+
+        return digest
+
+
+    def hexdigest(self):
+        """Terminate and return digest in HEX form.
+
+        Like digest() except the digest is returned as a string of
+        length 32, containing only hexadecimal digits. This may be
+        used to exchange the value safely in email or other non-
+        binary environments.
+        """
+        return ''.join(['%02x' % ord(c) for c in self.digest()])
+
+    def copy(self):
+        """Return a clone object.
+
+        Return a copy ('clone') of the md5 object. This can be used
+        to efficiently compute the digests of strings that share
+        a common initial substring.
+        """
+
+        return copy.deepcopy(self)
+
+
+# ======================================================================
+# Mimic Python top-level functions from standard library API
+# for consistency with the md5 module of the standard library.
+# ======================================================================
+
+digest_size = 16
+
+def new(arg=None):
+    """Return a new md5 object.
+
+    If arg is present, the method call update(arg) is made.
+    """
+
+    md5 = MD5()
+    if arg:
+        md5.update(arg)
+
+    return md5

Added: pypy/trunk/src/pypy/appspace/test/test_md5.py
==============================================================================
--- (empty file)
+++ pypy/trunk/src/pypy/appspace/test/test_md5.py	Thu Nov 18 19:47:08 2004
@@ -0,0 +1,240 @@
+"""A test script to compare MD5 implementations.
+
+A note about performance: the pure Python MD5 takes roughly
+160 sec. per MB of data on a 233 MHz Intel Pentium CPU.
+"""
+
+import string, unittest
+import md5    # Python's implementation in C.
+import pymd5  # The pure Python implementation.
+
+
+# Helpers...
+
+def formatHex(str):
+    "Print a string's HEX code in groups of two digits."
+
+    d = map(None, str)
+    d = map(ord, d)
+    d = map(lambda x:"%02x" % x, d)
+    return string.join(d, " ")
+
+
+def format(str):
+    "Print a string as-is in groups of two characters."
+
+    s = ''
+    for i in range(0, len(str)-1, 2):
+        s = s + "%03s" % str[i:i+2] 
+    return s[1:] 
+
+
+def printDiff(message, d1, d2, expectedResult=None):
+    "Print different outputs for same message."
+    
+    print "Message: '%s'" % message
+    print "Message length: %d" % len(message)
+    if expectedResult:
+        print "%-48s (expected)" % format(expectedResult)
+    print "%-48s (Std. lib. MD5)" % formatHex(d1)
+    print "%-48s (Pure Python MD5)" % formatHex(d2)
+    print
+
+
+# The real comparison function.
+
+def compareImp(message):
+    """Compare two MD5 implementations, C vs. pure Python module.
+
+    For equal digests this returns None, otherwise it returns
+    a tuple of both digests.
+    """
+
+    # Use Python's standard library MD5 compiled C module.    
+    m1 = md5.md5()
+    m1.update(message)
+    d1 = m1.digest()
+    d1h = m1.hexdigest()
+    
+    # Use MD5 module in pure Python.
+    m2 = pymd5.md5()
+    m2.update(message)
+    d2 = m2.digest()
+    d2h = m2.hexdigest()
+
+    # Return None if equal or the different digests if not equal.
+    if d1 == d2 and d1h == d2h:
+        return
+    else:
+        return d1, d2
+
+
+class MD5CompareTestCase(unittest.TestCase):
+    "Compare pure Python MD5 against Python's std. lib. version."
+    
+    def test1(self):
+        "Test cases with known digest result."
+        
+        cases = (
+          ("",
+           "d41d8cd98f00b204e9800998ecf8427e"),
+          ("a",
+           "0cc175b9c0f1b6a831c399e269772661"),
+          ("abc",
+           "900150983cd24fb0d6963f7d28e17f72"),
+          ("message digest",
+           "f96b697d7cb7938d525a2f31aaf161d0"),
+          ("abcdefghijklmnopqrstuvwxyz",
+           "c3fcd3d76192e4007dfb496cca67e13b"),
+          ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+           "d174ab98d277d9f5a5611c2c9f419d9f"),
+          ("1234567890"*8,
+           "57edf4a22be3c955ac49da2e2107b67a"),
+        )
+
+        for i in xrange(len(cases)):
+            res = compareImp(cases[i][0])
+            try:
+                assert res == None
+            except AssertionError, details:
+                d1, d2 = res
+                message, expectedResult = cases[i][0], None
+                if len(cases[i]) == 2:
+                    expectedResult = cases[i][1]
+                printDiff(message, d1, d2, expectedResult)
+
+
+    def test2(self):
+        "Test cases without known digest result."
+        
+        cases = (
+          "123",
+          "1234",
+          "12345",
+          "123456",
+          "1234567",
+          "12345678",
+          "123456789 123456789 123456789 ",
+          "123456789 123456789 ",
+          "123456789 123456789 1",
+          "123456789 123456789 12",
+          "123456789 123456789 123",
+          "123456789 123456789 1234",
+          "123456789 123456789 123456789 1",
+          "123456789 123456789 123456789 12",
+          "123456789 123456789 123456789 123",
+          "123456789 123456789 123456789 1234",
+          "123456789 123456789 123456789 12345",
+          "123456789 123456789 123456789 123456",
+          "123456789 123456789 123456789 1234567",
+          "123456789 123456789 123456789 12345678",
+         )
+
+        for i in xrange(len(cases)):
+            res = compareImp(cases[i][0])
+            try:
+                assert res == None
+            except AssertionError, details:
+                d1, d2 = res
+                message = cases[i][0]
+                printDiff(message, d1, d2)
+
+
+    def test3(self):
+        "Test cases with long messages (can take a while)."
+        
+        cases = (
+          (2**10*'a',),
+          (2**10*'abcd',),
+##          (2**20*'a',),  ## 1 MB, takes about 160 sec. on a 233 Mhz Pentium.
+         )
+
+        for i in xrange(len(cases)):
+            res = compareImp(cases[i][0])
+            try:
+                assert res == None
+            except AssertionError, details:
+                d1, d2 = res
+                message = cases[i][0]
+                printDiff(message, d1, d2)
+
+
+    def test4(self):
+        "Test cases with increasingly growing message lengths."
+
+        i = 0
+        while i  < 2**5:
+            message = i * 'a'
+            res = compareImp(message)
+            try:
+                assert res == None
+            except AssertionError, details:
+                d1, d2 = res
+                printDiff(message, d1, d2)
+            i = i + 1
+
+
+    def test5(self):
+        "Test updating cloned objects."
+
+        cases = (
+          "123",
+          "1234",
+          "12345",
+          "123456",
+          "1234567",
+          "12345678",
+          "123456789 123456789 123456789 ",
+          "123456789 123456789 ",
+          "123456789 123456789 1",
+          "123456789 123456789 12",
+          "123456789 123456789 123",
+          "123456789 123456789 1234",
+          "123456789 123456789 123456789 1",
+          "123456789 123456789 123456789 12",
+          "123456789 123456789 123456789 123",
+          "123456789 123456789 123456789 1234",
+          "123456789 123456789 123456789 12345",
+          "123456789 123456789 123456789 123456",
+          "123456789 123456789 123456789 1234567",
+          "123456789 123456789 123456789 12345678",
+         )
+
+        # Load both with same prefix.    
+        prefix1 = 2**10 * 'a'
+
+        m1 = md5.md5()
+        m1.update(prefix1)
+        m1c = m1.copy()
+
+        m2 = pymd5.md5()
+        m2.update(prefix1)
+        m2c = m2.copy()
+
+        # Update and compare...
+        for i in xrange(len(cases)):
+            message = cases[i][0]
+
+            m1c.update(message)
+            d1 = m1c.hexdigest()
+
+            m2c.update(message)
+            d2 = m2c.hexdigest()
+
+            assert d1 == d2
+
+
+def makeSuite():
+    suite = unittest.TestSuite()
+    
+    suite.addTest(MD5CompareTestCase('test1'))
+    suite.addTest(MD5CompareTestCase('test2'))
+    suite.addTest(MD5CompareTestCase('test3'))
+    suite.addTest(MD5CompareTestCase('test4'))
+    suite.addTest(MD5CompareTestCase('test5'))
+
+    return suite
+
+
+if __name__ == "__main__":
+    unittest.TextTestRunner().run(makeSuite())

Added: pypy/trunk/src/pypy/appspace/test/test_sha.py
==============================================================================
--- (empty file)
+++ pypy/trunk/src/pypy/appspace/test/test_sha.py	Thu Nov 18 19:47:08 2004
@@ -0,0 +1,32 @@
+# Testing sha module (NIST's Secure Hash Algorithm)
+
+# use the three examples from Federal Information Processing Standards
+# Publication 180-1, Secure Hash Standard,  1995 April 17
+# http://www.itl.nist.gov/div897/pubs/fip180-1.htm
+
+import pysha as sha
+import unittest
+
+class SHATestCase(unittest.TestCase):
+    def check(self, data, digest):
+        computed = sha.new(data).hexdigest()
+        self.assert_(computed == digest)
+
+    def test_case_1(self):
+        self.check("abc",
+                   "a9993e364706816aba3e25717850c26c9cd0d89d")
+
+    def test_case_2(self):
+        self.check("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+                   "84983e441c3bd26ebaae4aa1f95129e5e54670f1")
+
+    def test_case_3(self):
+        self.check("a" * 1000000,
+                   "34aa973cd4c4daa4f61eeb2bdbad27316534016f")
+
+def test_main():
+    unittest.main()
+
+
+if __name__ == "__main__":
+    test_main()



More information about the Pypy-commit mailing list