pypy / lib_pypy / _hashlib.py

from ctypes import *
import ctypes.util

# load the platform-specific cache made by running hashlib.ctc.py
from ctypes_config_cache._hashlib_cache import *

# Note: OpenSSL on OS X only provides md5 and sha1
libpath = ctypes.util.find_library('ssl')
if not libpath:
    raise ImportError('could not find OpenSSL library')
lib = CDLL(libpath) # Linux, OS X
lib.EVP_get_digestbyname.restype = c_void_p
lib.EVP_DigestInit.argtypes = [c_void_p, c_void_p]

def bufferstr(x):
    if isinstance(x, basestring):
        return str(x)
    else:
        return buffer(x)[:]

def patch_fields(fields):
    res = []
    for k, v in fields:
        if k == 'digest':
            res.append((k, POINTER(EVP_MD)))
        else:
            res.append((k, v))
    return res

class EVP_MD_CTX(Structure):
    _fields_ = patch_fields(EVP_MD_CTX._fields_)

# OpenSSL initialization
lib.OpenSSL_add_all_digests()

# taken from evp.h, max size is 512 bit, 64 chars
lib.EVP_MAX_MD_SIZE = 64

class hash(object):
    """
    A hash represents the object used to calculate a checksum of a
    string of information.
    
    Methods:
    
    update() -- updates the current digest with an additional string
    digest() -- return the current digest value
    hexdigest() -- return the current digest as a string of hexadecimal digits
    copy() -- return a copy of the current hash object
    
    Attributes:
    
    name -- the hash algorithm being used by this object
    digest_size -- number of bytes in this hashes output
    """
    def __init__(self, obj, name):
        self.name = name # part of API
        #print 'obj is ', obj
        if isinstance(obj, EVP_MD_CTX):
            self._obj = obj.digest
        else:
            self._obj = obj
    
    def __repr__(self):
        # format is not the same as in C module
        return "<%s HASH object>" % (self.name)
    
    def copy(self):
        "Return a copy of the hash object."
        ctxnew = EVP_MD_CTX()
        lib.EVP_MD_CTX_copy(byref(ctxnew), byref(self._obj))
        return hash(ctxnew, self.name)
    
    def hexdigest(self):
        "Return the digest value as a string of hexadecimal digits."
        dig = self.digest()
        a = []
        for x in dig:
            a.append('%.2x' % ord(x))
        #print '\n--- %r \n' % ''.join(a)
        return ''.join(a)
    
    def digest(self):
        "Return the digest value as a string of binary data."
        tmpctx = self.copy()
        digest_size = tmpctx.digest_size
        dig = create_string_buffer(lib.EVP_MAX_MD_SIZE)
        lib.EVP_DigestFinal(byref(tmpctx._obj), dig, None)
        lib.EVP_MD_CTX_cleanup(byref(tmpctx._obj))
        return dig.raw[:digest_size]
    
    def digest_size(self):
        # XXX This isn't the nicest way, but the EVP_MD_size OpenSSL function
        # XXX is defined as a C macro on OS X and would be significantly 
        # XXX 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)
    digest_size = property(digest_size, None, None) # PEP 247
    digestsize = digest_size # deprecated, was once defined by sha module
    
    def block_size(self):
        # XXX This isn't the nicest way, but the EVP_MD_CTX_block_size OpenSSL function
        # XXX is defined as a C macro on some systems and would be significantly 
        # XXX harder to implement in another way.
        return {
            'md5':     64,
            'sha1':    64,
            'sha224':  64,
            'sha256':  64,
            'sha384': 128,
            'sha512': 128,
            }.get(self.name, 0)
    block_size = property(block_size, None, None)
    
    def update(self, string):
        "Update this hash object's state with the provided string."
        string = bufferstr(string)
        lib.EVP_DigestUpdate(byref(self._obj), c_char_p(string), c_uint(len(string)))

def new(name, string=''):
    """
    Return a new hash object using the named algorithm.
    An optional string argument may be provided and will be
    automatically hashed.
    
    The MD5 and SHA1 algorithms are always supported.
    """
    digest = lib.EVP_get_digestbyname(c_char_p(name))
    
    if not isinstance(name, str):
        raise TypeError("name must be a string")
    if not digest:
        raise ValueError("unknown hash function")
    
    ctx = EVP_MD_CTX()
    lib.EVP_DigestInit(pointer(ctx), digest)

    h = hash(ctx.digest, name)
    if string:
        h.update(string)
    return hash(ctx, name)

# shortcut functions
def openssl_md5(string=''):
    return new('md5', string)

def openssl_sha1(string=''):
    return new('sha1', string)

def openssl_sha224(string=''):
    return new('sha224', string)

def openssl_sha256(string=''):
    return new('sha256', string)

def openssl_sha384(string=''):
    return new('sha384', string)

def openssl_sha512(string=''):
    return new('sha512', string)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.