diff --git a/python-stdlib/hmac/hmac.py b/python-stdlib/hmac/hmac.py index fb745740..28631042 100644 --- a/python-stdlib/hmac/hmac.py +++ b/python-stdlib/hmac/hmac.py @@ -1,161 +1,87 @@ -"""HMAC (Keyed-Hashing for Message Authentication) Python module. - -Implements the HMAC algorithm as described by RFC 2104. -""" - -import warnings as _warnings - -# from _operator import _compare_digest as compare_digest -import hashlib as _hashlib - -PendingDeprecationWarning = None -RuntimeWarning = None - -trans_5C = bytes((x ^ 0x5C) for x in range(256)) -trans_36 = bytes((x ^ 0x36) for x in range(256)) - - -def translate(d, t): - return bytes(t[x] for x in d) - - -# 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 +# Implements the hmac module from the Python standard library. 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. - def __init__(self, key, msg=None, digestmod=None): - """Create a new HMAC object. - - key: key for the keyed hash object. - msg: Initial input for the hash, if provided. - digestmod: A module supporting PEP 247. *OR* - A hashlib constructor returning a new hash object. *OR* - A hash name suitable for hashlib.new(). - Defaults to hashlib.md5. - Implicit default to hashlib.md5 is deprecated and will be - removed in Python 3.6. - - Note: key and msg must be a bytes or bytearray objects. - """ - if not isinstance(key, (bytes, bytearray)): - raise TypeError("key: expected bytes or bytearray, but got %r" % type(key).__name__) + raise TypeError("key: expected bytes/bytearray") + + import hashlib if digestmod is None: - _warnings.warn( - "HMAC() without an explicit digestmod argument " "is deprecated.", - PendingDeprecationWarning, - 2, - ) - digestmod = _hashlib.md5 + # TODO: Default hash algorithm is now deprecated. + digestmod = hashlib.md5 if callable(digestmod): - self.digest_cons = digestmod + # A hashlib constructor returning a new hash object. + make_hash = digestmod # A elif isinstance(digestmod, str): - self.digest_cons = lambda d=b"": _hashlib.new(digestmod, d) + # A hash name suitable for hashlib.new(). + make_hash = lambda d=b"": hashlib.new(digestmod, d) # B else: - self.digest_cons = lambda d=b"": digestmod.new(d) + # A module supporting PEP 247. + make_hash = digestmod.new # C - self.outer = self.digest_cons() - self.inner = self.digest_cons() - self.digest_size = self.inner.digest_size + self._outer = make_hash() + self._inner = make_hash() - 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.digest_size = getattr(self._inner, "digest_size", None) + # If the provided hash doesn't support block_size (e.g. built-in + # hashlib), 64 is the correct default for all built-in hash + # functions (md5, sha1, sha256). + self.block_size = getattr(self._inner, "block_size", 64) - # self.blocksize is the default blocksize. self.block_size is - # effective block size as well as the public API attribute. - self.block_size = blocksize + # Truncate to digest_size if greater than block_size. + if len(key) > self.block_size: + key = make_hash(key).digest() - if len(key) > blocksize: - key = self.digest_cons(key).digest() + # Pad to block size. + key = key + bytes(self.block_size - len(key)) + + self._outer.update(bytes(x ^ 0x5C for x in key)) + self._inner.update(bytes(x ^ 0x36 for x in key)) - key = key + bytes(blocksize - len(key)) - self.outer.update(translate(key, trans_5C)) - self.inner.update(translate(key, trans_36)) if msg is not None: self.update(msg) @property def name(self): - return "hmac-" + self.inner.name + return "hmac-" + getattr(self._inner, "name", type(self._inner).__name__) def update(self, msg): - """Update this hashing object with the string msg.""" - self.inner.update(msg) + 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. - """ + if not hasattr(self._inner, "copy"): + # Not supported for built-in hash functions. + raise NotImplementedError() # Call __new__ directly to avoid the expensive __init__. other = self.__class__.__new__(self.__class__) - other.digest_cons = self.digest_cons + other.block_size = self.block_size other.digest_size = self.digest_size - other.inner = self.inner.copy() - other.outer = self.outer.copy() + 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()) + h = self._outer + if hasattr(h, "copy"): + # built-in hash functions don't support this, and as a result, + # digest() will finalise the hmac and further calls to + # update/digest will fail. + h = h.copy() + h.update(self._inner.digest()) return h def digest(self): - """Return the hash value of this hashing object. - - This returns a string containing 8-bit data. 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() + import binascii + + return str(binascii.hexlify(self.digest()), "utf-8") def new(key, msg=None, digestmod=None): - """Create a new hashing object and return it. - - key: The starting key for the hash. - msg: if available, will immediately be hashed into the object's starting - state. - - You can now feed arbitrary strings into the object using its update() - method, and can ask for the hash value at any time by calling its digest() - method. - """ return HMAC(key, msg, digestmod) diff --git a/python-stdlib/hmac/metadata.txt b/python-stdlib/hmac/metadata.txt index 1f744f8f..ee8bd9f9 100644 --- a/python-stdlib/hmac/metadata.txt +++ b/python-stdlib/hmac/metadata.txt @@ -1,4 +1,3 @@ srctype = cpython type = module version = 3.4.2-3 -depends = warnings, hashlib diff --git a/python-stdlib/hmac/test_hmac.py b/python-stdlib/hmac/test_hmac.py index 815219ae..d155dd6a 100644 --- a/python-stdlib/hmac/test_hmac.py +++ b/python-stdlib/hmac/test_hmac.py @@ -1,10 +1,14 @@ import hmac -from hashlib.sha256 import sha256 -from hashlib.sha512 import sha512 + +# Uncomment to use micropython-lib hashlib (supports sha512) +# import sys +# sys.path.append('../hashlib') + +import hashlib msg = b"zlutoucky kun upel dabelske ody" -dig = hmac.new(b"1234567890", msg=msg, digestmod=sha256).hexdigest() +dig = hmac.new(b"1234567890", msg=msg, digestmod=hashlib.sha256).hexdigest() print("c735e751e36b08fb01e25794bdb15e7289b82aecdb652c8f4f72f307b39dad39") print(dig) @@ -12,22 +16,25 @@ print(dig) if dig != "c735e751e36b08fb01e25794bdb15e7289b82aecdb652c8f4f72f307b39dad39": raise Exception("Error") -dig = hmac.new(b"1234567890", msg=msg, digestmod=sha512).hexdigest() +if hasattr(hashlib, "sha512"): + dig = hmac.new(b"1234567890", msg=msg, digestmod=hashlib.sha512).hexdigest() -print( - "59942f31b6f5473fb4eb630fabf5358a49bc11d24ebc83b114b4af30d6ef47ea14b673f478586f520a0b9c53b27c8f8dd618c165ef586195bd4e98293d34df1a" -) -print(dig) + print( + "59942f31b6f5473fb4eb630fabf5358a49bc11d24ebc83b114b4af30d6ef47ea14b673f478586f520a0b9c53b27c8f8dd618c165ef586195bd4e98293d34df1a" + ) + print(dig) -if ( - dig - != "59942f31b6f5473fb4eb630fabf5358a49bc11d24ebc83b114b4af30d6ef47ea14b673f478586f520a0b9c53b27c8f8dd618c165ef586195bd4e98293d34df1a" -): - raise Exception("Error") + if ( + dig + != "59942f31b6f5473fb4eb630fabf5358a49bc11d24ebc83b114b4af30d6ef47ea14b673f478586f520a0b9c53b27c8f8dd618c165ef586195bd4e98293d34df1a" + ): + raise Exception("Error") +else: + print("sha512 not supported") key = b"\x06\x1au\x90|Xz;o\x1b<\xafGL\xbfn\x8a\xc94YPfC^\xb9\xdd)\x7f\xaf\x85\xa1\xed\x82\xbexp\xaf\x13\x1a\x9d" -dig = hmac.new(key[:20], msg=msg, digestmod=sha256).hexdigest() +dig = hmac.new(key[:20], msg=msg, digestmod=hashlib.sha256).hexdigest() print("59e332b881df09fdecf569c8b142b27fc989638720aeda2813f82442b6e3d91b") print(dig) @@ -35,7 +42,7 @@ print(dig) if dig != "59e332b881df09fdecf569c8b142b27fc989638720aeda2813f82442b6e3d91b": raise Exception("Error") -dig = hmac.new(key[:32], msg=msg, digestmod=sha256).hexdigest() +dig = hmac.new(key[:32], msg=msg, digestmod=hashlib.sha256).hexdigest() print("b72fed815cd71acfa3a2f5cf2343679565fa18e7cd92226ab443aabd1fd7b7b0") print(dig) @@ -43,7 +50,7 @@ print(dig) if dig != "b72fed815cd71acfa3a2f5cf2343679565fa18e7cd92226ab443aabd1fd7b7b0": raise Exception("Error") -dig = hmac.new(key, msg=msg, digestmod=sha256).hexdigest() +dig = hmac.new(key, msg=msg, digestmod=hashlib.sha256).hexdigest() print("4e51beae6c2b0f90bb3e99d8e93a32d168b6c1e9b7d2130e2d668a3b3e10358d") print(dig)