183 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			183 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# This HMAC implementation comes directly from the HMAC implementation
 | 
						|
# included in Python 3.10.4, and is almost completely identical. It has
 | 
						|
# been modified to be a pure Python implementation, that is not dependent
 | 
						|
# on the system having OpenSSL binaries installed.
 | 
						|
 | 
						|
import warnings as _warnings
 | 
						|
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__ = (
 | 
						|
        "_hmac", "_inner", "_outer", "block_size", "digest_size"
 | 
						|
    )
 | 
						|
 | 
						|
    def __init__(self, key, msg=None, digestmod=_hashlib.sha256):
 | 
						|
        """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'.")
 | 
						|
 | 
						|
        self._hmac_init(key, msg, digestmod)
 | 
						|
 | 
						|
    def _hmac_init(self, key, msg, digestmod):
 | 
						|
        if callable(digestmod):
 | 
						|
            digest_cons = digestmod
 | 
						|
        elif isinstance(digestmod, str):
 | 
						|
            digest_cons = lambda d=b'': _hashlib.new(digestmod, d)
 | 
						|
        else:
 | 
						|
            digest_cons = lambda d=b'': digestmod.new(d)
 | 
						|
 | 
						|
        self._hmac = None
 | 
						|
        self._outer = digest_cons()
 | 
						|
        self._inner = 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
 | 
						|
 | 
						|
        if len(key) > blocksize:
 | 
						|
            key = digest_cons(key).digest()
 | 
						|
 | 
						|
        # self.blocksize is the default blocksize. self.block_size is
 | 
						|
        # effective block size as well as the public API attribute.
 | 
						|
        self.block_size = blocksize
 | 
						|
 | 
						|
        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):
 | 
						|
        if self._hmac:
 | 
						|
            return self._hmac.name
 | 
						|
        else:
 | 
						|
            return f"hmac-{self._inner.name}"
 | 
						|
 | 
						|
    def update(self, msg):
 | 
						|
        """Feed data from msg into this hashing object."""
 | 
						|
        inst = self._hmac or self._inner
 | 
						|
        inst.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_size = self.digest_size
 | 
						|
        if self._hmac:
 | 
						|
            other._hmac = self._hmac.copy()
 | 
						|
            other._inner = other._outer = None
 | 
						|
        else:
 | 
						|
            other._hmac = None
 | 
						|
            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().
 | 
						|
        """
 | 
						|
        if self._hmac:
 | 
						|
            return self._hmac
 | 
						|
        else:
 | 
						|
            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=_hashlib.sha256):
 | 
						|
    """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 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() |