kopia lustrzana https://github.com/micropython/micropython-lib
python-stdlib/hmac: Update to work with built-in hash functions.
This library was non-functional unless used with the micropython-lib pure-Python implementation of hashlib, even if the device provides sha1 and sha256. This updates hmac to be significantly more RAM efficient (removes the 512-byte table), and functional with the built-in hash functions. The only unsupported function is "copy", but this is non-critical, and now fails with a NotSupportedError. Signed-off-by: Jim Mussared <jim.mussared@gmail.com>pull/447/merge
rodzic
9fdf046e27
commit
f95260d7e3
|
@ -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)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
srctype = cpython
|
||||
type = module
|
||||
version = 3.4.2-3
|
||||
depends = warnings, hashlib
|
||||
|
|
|
@ -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)
|
||||
|
|
Ładowanie…
Reference in New Issue