Home | History | Annotate | Download | only in Lib
      1 """HMAC (Keyed-Hashing for Message Authentication) Python module.
      2 
      3 Implements the HMAC algorithm as described by RFC 2104.
      4 """
      5 
      6 import warnings as _warnings
      7 
      8 from operator import _compare_digest as compare_digest
      9 
     10 
     11 trans_5C = "".join ([chr (x ^ 0x5C) for x in xrange(256)])
     12 trans_36 = "".join ([chr (x ^ 0x36) for x in xrange(256)])
     13 
     14 # The size of the digests returned by HMAC depends on the underlying
     15 # hashing module used.  Use digest_size from the instance of HMAC instead.
     16 digest_size = None
     17 
     18 # A unique object passed by HMAC.copy() to the HMAC constructor, in order
     19 # that the latter return very quickly.  HMAC("") in contrast is quite
     20 # expensive.
     21 _secret_backdoor_key = []
     22 
     23 class HMAC:
     24     """RFC 2104 HMAC class.  Also complies with RFC 4231.
     25 
     26     This supports the API for Cryptographic Hash Functions (PEP 247).
     27     """
     28     blocksize = 64  # 512-bit HMAC; can be changed in subclasses.
     29 
     30     def __init__(self, key, msg = None, digestmod = None):
     31         """Create a new HMAC object.
     32 
     33         key:       key for the keyed hash object.
     34         msg:       Initial input for the hash, if provided.
     35         digestmod: A module supporting PEP 247.  *OR*
     36                    A hashlib constructor returning a new hash object.
     37                    Defaults to hashlib.md5.
     38         """
     39 
     40         if key is _secret_backdoor_key: # cheap
     41             return
     42 
     43         if digestmod is None:
     44             import hashlib
     45             digestmod = hashlib.md5
     46 
     47         if hasattr(digestmod, '__call__'):
     48             self.digest_cons = digestmod
     49         else:
     50             self.digest_cons = lambda d='': digestmod.new(d)
     51 
     52         self.outer = self.digest_cons()
     53         self.inner = self.digest_cons()
     54         self.digest_size = self.inner.digest_size
     55 
     56         if hasattr(self.inner, 'block_size'):
     57             blocksize = self.inner.block_size
     58             if blocksize < 16:
     59                 # Very low blocksize, most likely a legacy value like
     60                 # Lib/sha.py and Lib/md5.py have.
     61                 _warnings.warn('block_size of %d seems too small; using our '
     62                                'default of %d.' % (blocksize, self.blocksize),
     63                                RuntimeWarning, 2)
     64                 blocksize = self.blocksize
     65         else:
     66             _warnings.warn('No block_size attribute on given digest object; '
     67                            'Assuming %d.' % (self.blocksize),
     68                            RuntimeWarning, 2)
     69             blocksize = self.blocksize
     70 
     71         if len(key) > blocksize:
     72             key = self.digest_cons(key).digest()
     73 
     74         key = key + chr(0) * (blocksize - len(key))
     75         self.outer.update(key.translate(trans_5C))
     76         self.inner.update(key.translate(trans_36))
     77         if msg is not None:
     78             self.update(msg)
     79 
     80 ##    def clear(self):
     81 ##        raise NotImplementedError, "clear() method not available in HMAC."
     82 
     83     def update(self, msg):
     84         """Update this hashing object with the string msg.
     85         """
     86         self.inner.update(msg)
     87 
     88     def copy(self):
     89         """Return a separate copy of this hashing object.
     90 
     91         An update to this copy won't affect the original object.
     92         """
     93         other = self.__class__(_secret_backdoor_key)
     94         other.digest_cons = self.digest_cons
     95         other.digest_size = self.digest_size
     96         other.inner = self.inner.copy()
     97         other.outer = self.outer.copy()
     98         return other
     99 
    100     def _current(self):
    101         """Return a hash object for the current state.
    102 
    103         To be used only internally with digest() and hexdigest().
    104         """
    105         h = self.outer.copy()
    106         h.update(self.inner.digest())
    107         return h
    108 
    109     def digest(self):
    110         """Return the hash value of this hashing object.
    111 
    112         This returns a string containing 8-bit data.  The object is
    113         not altered in any way by this function; you can continue
    114         updating the object after calling this function.
    115         """
    116         h = self._current()
    117         return h.digest()
    118 
    119     def hexdigest(self):
    120         """Like digest(), but returns a string of hexadecimal digits instead.
    121         """
    122         h = self._current()
    123         return h.hexdigest()
    124 
    125 def new(key, msg = None, digestmod = None):
    126     """Create a new hashing object and return it.
    127 
    128     key: The starting key for the hash.
    129     msg: if available, will immediately be hashed into the object's starting
    130     state.
    131 
    132     You can now feed arbitrary strings into the object using its update()
    133     method, and can ask for the hash value at any time by calling its digest()
    134     method.
    135     """
    136     return HMAC(key, msg, digestmod)
    137