mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	 db5aed931f
			
		
	
	
		db5aed931f
		
			
		
	
	
	
	
		
			
			hashlib.compare_digest uses OpenSSL's CRYPTO_memcmp() function when OpenSSL is available. Note: The _operator module is a builtin module. I don't want to add libcrypto dependency to libpython. Therefore I duplicated the wrapper function and added a copy to _hashopenssl.c.
		
			
				
	
	
		
			203 lines
		
	
	
	
		
			6.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			203 lines
		
	
	
	
		
			6.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """HMAC (Keyed-Hashing for Message Authentication) module.
 | |
| 
 | |
| Implements the HMAC algorithm as described by RFC 2104.
 | |
| """
 | |
| 
 | |
| import warnings as _warnings
 | |
| try:
 | |
|     import _hashlib as _hashopenssl
 | |
| except ImportError:
 | |
|     _hashopenssl = None
 | |
|     _openssl_md_meths = None
 | |
|     from _operator import _compare_digest as compare_digest
 | |
| else:
 | |
|     _openssl_md_meths = frozenset(_hashopenssl.openssl_md_meth_names)
 | |
|     compare_digest = _hashopenssl.compare_digest
 | |
| import hashlib as _hashlib
 | |
| 
 | |
| trans_5C = bytes((x ^ 0x5C) for x in range(256))
 | |
| trans_36 = bytes((x ^ 0x36) for x in range(256))
 | |
| 
 | |
| # The size of the digests returned by HMAC depends on the underlying
 | |
| # hashing module used.  Use digest_size from the instance of HMAC instead.
 | |
| digest_size = None
 | |
| 
 | |
| 
 | |
| 
 | |
| class HMAC:
 | |
|     """RFC 2104 HMAC class.  Also complies with RFC 4231.
 | |
| 
 | |
|     This supports the API for Cryptographic Hash Functions (PEP 247).
 | |
|     """
 | |
|     blocksize = 64  # 512-bit HMAC; can be changed in subclasses.
 | |
| 
 | |
|     __slots__ = (
 | |
|         "_digest_cons", "_inner", "_outer", "block_size", "digest_size"
 | |
|     )
 | |
| 
 | |
|     def __init__(self, key, msg=None, digestmod=''):
 | |
|         """Create a new HMAC object.
 | |
| 
 | |
|         key: bytes or buffer, key for the keyed hash object.
 | |
|         msg: bytes or buffer, Initial input for the hash or None.
 | |
|         digestmod: A hash name suitable for hashlib.new(). *OR*
 | |
|                    A hashlib constructor returning a new hash object. *OR*
 | |
|                    A module supporting PEP 247.
 | |
| 
 | |
|                    Required as of 3.8, despite its position after the optional
 | |
|                    msg argument.  Passing it as a keyword argument is
 | |
|                    recommended, though not required for legacy API reasons.
 | |
|         """
 | |
| 
 | |
|         if not isinstance(key, (bytes, bytearray)):
 | |
|             raise TypeError("key: expected bytes or bytearray, but got %r" % type(key).__name__)
 | |
| 
 | |
|         if not digestmod:
 | |
|             raise TypeError("Missing required parameter 'digestmod'.")
 | |
| 
 | |
|         if callable(digestmod):
 | |
|             self._digest_cons = digestmod
 | |
|         elif isinstance(digestmod, str):
 | |
|             self._digest_cons = lambda d=b'': _hashlib.new(digestmod, d)
 | |
|         else:
 | |
|             self._digest_cons = lambda d=b'': digestmod.new(d)
 | |
| 
 | |
|         self._outer = self._digest_cons()
 | |
|         self._inner = self._digest_cons()
 | |
|         self.digest_size = self._inner.digest_size
 | |
| 
 | |
|         if hasattr(self._inner, 'block_size'):
 | |
|             blocksize = self._inner.block_size
 | |
|             if blocksize < 16:
 | |
|                 _warnings.warn('block_size of %d seems too small; using our '
 | |
|                                'default of %d.' % (blocksize, self.blocksize),
 | |
|                                RuntimeWarning, 2)
 | |
|                 blocksize = self.blocksize
 | |
|         else:
 | |
|             _warnings.warn('No block_size attribute on given digest object; '
 | |
|                            'Assuming %d.' % (self.blocksize),
 | |
|                            RuntimeWarning, 2)
 | |
|             blocksize = self.blocksize
 | |
| 
 | |
|         # self.blocksize is the default blocksize. self.block_size is
 | |
|         # effective block size as well as the public API attribute.
 | |
|         self.block_size = blocksize
 | |
| 
 | |
|         if len(key) > blocksize:
 | |
|             key = self._digest_cons(key).digest()
 | |
| 
 | |
|         key = key.ljust(blocksize, b'\0')
 | |
|         self._outer.update(key.translate(trans_5C))
 | |
|         self._inner.update(key.translate(trans_36))
 | |
|         if msg is not None:
 | |
|             self.update(msg)
 | |
| 
 | |
|     @property
 | |
|     def name(self):
 | |
|         return "hmac-" + self._inner.name
 | |
| 
 | |
|     @property
 | |
|     def digest_cons(self):
 | |
|         return self._digest_cons
 | |
| 
 | |
|     @property
 | |
|     def inner(self):
 | |
|         return self._inner
 | |
| 
 | |
|     @property
 | |
|     def outer(self):
 | |
|         return self._outer
 | |
| 
 | |
|     def update(self, msg):
 | |
|         """Feed data from msg into this hashing object."""
 | |
|         self._inner.update(msg)
 | |
| 
 | |
|     def copy(self):
 | |
|         """Return a separate copy of this hashing object.
 | |
| 
 | |
|         An update to this copy won't affect the original object.
 | |
|         """
 | |
|         # Call __new__ directly to avoid the expensive __init__.
 | |
|         other = self.__class__.__new__(self.__class__)
 | |
|         other._digest_cons = self._digest_cons
 | |
|         other.digest_size = self.digest_size
 | |
|         other._inner = self._inner.copy()
 | |
|         other._outer = self._outer.copy()
 | |
|         return other
 | |
| 
 | |
|     def _current(self):
 | |
|         """Return a hash object for the current state.
 | |
| 
 | |
|         To be used only internally with digest() and hexdigest().
 | |
|         """
 | |
|         h = self._outer.copy()
 | |
|         h.update(self._inner.digest())
 | |
|         return h
 | |
| 
 | |
|     def digest(self):
 | |
|         """Return the hash value of this hashing object.
 | |
| 
 | |
|         This returns the hmac value as bytes.  The object is
 | |
|         not altered in any way by this function; you can continue
 | |
|         updating the object after calling this function.
 | |
|         """
 | |
|         h = self._current()
 | |
|         return h.digest()
 | |
| 
 | |
|     def hexdigest(self):
 | |
|         """Like digest(), but returns a string of hexadecimal digits instead.
 | |
|         """
 | |
|         h = self._current()
 | |
|         return h.hexdigest()
 | |
| 
 | |
| def new(key, msg=None, digestmod=''):
 | |
|     """Create a new hashing object and return it.
 | |
| 
 | |
|     key: bytes or buffer, The starting key for the hash.
 | |
|     msg: bytes or buffer, Initial input for the hash, or None.
 | |
|     digestmod: A hash name suitable for hashlib.new(). *OR*
 | |
|                A hashlib constructor returning a new hash object. *OR*
 | |
|                A module supporting PEP 247.
 | |
| 
 | |
|                Required as of 3.8, despite its position after the optional
 | |
|                msg argument.  Passing it as a keyword argument is
 | |
|                recommended, though not required for legacy API reasons.
 | |
| 
 | |
|     You can now feed arbitrary bytes into the object using its update()
 | |
|     method, and can ask for the hash value at any time by calling its digest()
 | |
|     or hexdigest() methods.
 | |
|     """
 | |
|     return HMAC(key, msg, digestmod)
 | |
| 
 | |
| 
 | |
| def digest(key, msg, digest):
 | |
|     """Fast inline implementation of HMAC.
 | |
| 
 | |
|     key: bytes or buffer, The key for the keyed hash object.
 | |
|     msg: bytes or buffer, Input message.
 | |
|     digest: A hash name suitable for hashlib.new() for best performance. *OR*
 | |
|             A hashlib constructor returning a new hash object. *OR*
 | |
|             A module supporting PEP 247.
 | |
|     """
 | |
|     if (_hashopenssl is not None and
 | |
|             isinstance(digest, str) and digest in _openssl_md_meths):
 | |
|         return _hashopenssl.hmac_digest(key, msg, digest)
 | |
| 
 | |
|     if callable(digest):
 | |
|         digest_cons = digest
 | |
|     elif isinstance(digest, str):
 | |
|         digest_cons = lambda d=b'': _hashlib.new(digest, d)
 | |
|     else:
 | |
|         digest_cons = lambda d=b'': digest.new(d)
 | |
| 
 | |
|     inner = digest_cons()
 | |
|     outer = digest_cons()
 | |
|     blocksize = getattr(inner, 'block_size', 64)
 | |
|     if len(key) > blocksize:
 | |
|         key = digest_cons(key).digest()
 | |
|     key = key + b'\x00' * (blocksize - len(key))
 | |
|     inner.update(key.translate(trans_36))
 | |
|     outer.update(key.translate(trans_5C))
 | |
|     inner.update(msg)
 | |
|     outer.update(inner.digest())
 | |
|     return outer.digest()
 |