mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1502 lines
		
	
	
	
		
			54 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			1502 lines
		
	
	
	
		
			54 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import binascii
 | |
| import functools
 | |
| import hmac
 | |
| import hashlib
 | |
| import random
 | |
| import test.support.hashlib_helper as hashlib_helper
 | |
| import types
 | |
| import unittest
 | |
| import unittest.mock as mock
 | |
| import warnings
 | |
| from _operator import _compare_digest as operator_compare_digest
 | |
| from test.support import check_disallow_instantiation
 | |
| from test.support.import_helper import import_fresh_module, import_module
 | |
| 
 | |
| try:
 | |
|     import _hashlib
 | |
|     from _hashlib import compare_digest as openssl_compare_digest
 | |
| except ImportError:
 | |
|     _hashlib = None
 | |
|     openssl_compare_digest = None
 | |
| 
 | |
| try:
 | |
|     import _sha2 as sha2
 | |
| except ImportError:
 | |
|     sha2 = None
 | |
| 
 | |
| 
 | |
| def requires_builtin_sha2():
 | |
|     return unittest.skipIf(sha2 is None, "requires _sha2")
 | |
| 
 | |
| 
 | |
| class ModuleMixin:
 | |
|     """Mixin with a HMAC module implementation."""
 | |
| 
 | |
|     hmac = None
 | |
| 
 | |
| 
 | |
| class PyModuleMixin(ModuleMixin):
 | |
|     """Pure Python implementation of HMAC.
 | |
| 
 | |
|     The underlying hash functions may be OpenSSL-based or HACL* based,
 | |
|     depending on whether OpenSSL is present or not.
 | |
|     """
 | |
| 
 | |
|     @classmethod
 | |
|     def setUpClass(cls):
 | |
|         super().setUpClass()
 | |
|         cls.hmac = import_fresh_module('hmac', blocked=['_hashlib', '_hmac'])
 | |
| 
 | |
| 
 | |
| @hashlib_helper.requires_builtin_hmac()
 | |
| class BuiltinModuleMixin(ModuleMixin):
 | |
|     """Built-in HACL* implementation of HMAC."""
 | |
| 
 | |
|     @classmethod
 | |
|     def setUpClass(cls):
 | |
|         super().setUpClass()
 | |
|         cls.hmac = import_fresh_module('_hmac')
 | |
| 
 | |
| 
 | |
| # Sentinel object used to detect whether a digestmod is given or not.
 | |
| DIGESTMOD_SENTINEL = object()
 | |
| 
 | |
| 
 | |
| class CreatorMixin:
 | |
|     """Mixin exposing a method creating a HMAC object."""
 | |
| 
 | |
|     def hmac_new(self, key, msg=None, digestmod=DIGESTMOD_SENTINEL):
 | |
|         """Create a new HMAC object.
 | |
| 
 | |
|         Implementations should accept arbitrary 'digestmod' as this
 | |
|         method can be used to test which exceptions are being raised.
 | |
|         """
 | |
|         raise NotImplementedError
 | |
| 
 | |
|     def bind_hmac_new(self, digestmod):
 | |
|         """Return a specialization of hmac_new() with a bound digestmod."""
 | |
|         return functools.partial(self.hmac_new, digestmod=digestmod)
 | |
| 
 | |
| 
 | |
| class DigestMixin:
 | |
|     """Mixin exposing a method computing a HMAC digest."""
 | |
| 
 | |
|     def hmac_digest(self, key, msg=None, digestmod=DIGESTMOD_SENTINEL):
 | |
|         """Compute a HMAC digest.
 | |
| 
 | |
|         Implementations should accept arbitrary 'digestmod' as this
 | |
|         method can be used to test which exceptions are being raised.
 | |
|         """
 | |
|         raise NotImplementedError
 | |
| 
 | |
|     def bind_hmac_digest(self, digestmod):
 | |
|         """Return a specialization of hmac_digest() with a bound digestmod."""
 | |
|         return functools.partial(self.hmac_digest, digestmod=digestmod)
 | |
| 
 | |
| 
 | |
| def _call_newobj_func(new_func, key, msg, digestmod):
 | |
|     if digestmod is DIGESTMOD_SENTINEL:  # to test when digestmod is missing
 | |
|         return new_func(key, msg)  # expected to raise
 | |
|     # functions creating HMAC objects take a 'digestmod' keyword argument
 | |
|     return new_func(key, msg, digestmod=digestmod)
 | |
| 
 | |
| 
 | |
| def _call_digest_func(digest_func, key, msg, digestmod):
 | |
|     if digestmod is DIGESTMOD_SENTINEL:  # to test when digestmod is missing
 | |
|         return digest_func(key, msg)  # expected to raise
 | |
|     # functions directly computing digests take a 'digest' keyword argument
 | |
|     return digest_func(key, msg, digest=digestmod)
 | |
| 
 | |
| 
 | |
| class ThroughObjectMixin(ModuleMixin, CreatorMixin, DigestMixin):
 | |
|     """Mixin delegating to <module>.HMAC() and <module>.HMAC(...).digest().
 | |
| 
 | |
|     Both the C implementation and the Python implementation of HMAC should
 | |
|     expose a HMAC class with the same functionalities.
 | |
|     """
 | |
| 
 | |
|     def hmac_new(self, key, msg=None, digestmod=DIGESTMOD_SENTINEL):
 | |
|         """Create a HMAC object via a module-level class constructor."""
 | |
|         return _call_newobj_func(self.hmac.HMAC, key, msg, digestmod)
 | |
| 
 | |
|     def hmac_digest(self, key, msg=None, digestmod=DIGESTMOD_SENTINEL):
 | |
|         """Call the digest() method on a HMAC object obtained by hmac_new()."""
 | |
|         return _call_newobj_func(self.hmac_new, key, msg, digestmod).digest()
 | |
| 
 | |
| 
 | |
| class ThroughModuleAPIMixin(ModuleMixin, CreatorMixin, DigestMixin):
 | |
|     """Mixin delegating to <module>.new() and <module>.digest()."""
 | |
| 
 | |
|     def hmac_new(self, key, msg=None, digestmod=DIGESTMOD_SENTINEL):
 | |
|         """Create a HMAC object via a module-level function."""
 | |
|         return _call_newobj_func(self.hmac.new, key, msg, digestmod)
 | |
| 
 | |
|     def hmac_digest(self, key, msg=None, digestmod=DIGESTMOD_SENTINEL):
 | |
|         """One-shot HMAC digest computation."""
 | |
|         return _call_digest_func(self.hmac.digest, key, msg, digestmod)
 | |
| 
 | |
| 
 | |
| @hashlib_helper.requires_hashlib()
 | |
| class ThroughOpenSSLAPIMixin(CreatorMixin, DigestMixin):
 | |
|     """Mixin delegating to _hashlib.hmac_new() and _hashlib.hmac_digest()."""
 | |
| 
 | |
|     def hmac_new(self, key, msg=None, digestmod=DIGESTMOD_SENTINEL):
 | |
|         return _call_newobj_func(_hashlib.hmac_new, key, msg, digestmod)
 | |
| 
 | |
|     def hmac_digest(self, key, msg=None, digestmod=DIGESTMOD_SENTINEL):
 | |
|         return _call_digest_func(_hashlib.hmac_digest, key, msg, digestmod)
 | |
| 
 | |
| 
 | |
| class ThroughBuiltinAPIMixin(BuiltinModuleMixin, CreatorMixin, DigestMixin):
 | |
|     """Mixin delegating to _hmac.new() and _hmac.compute_digest()."""
 | |
| 
 | |
|     def hmac_new(self, key, msg=None, digestmod=DIGESTMOD_SENTINEL):
 | |
|         return _call_newobj_func(self.hmac.new, key, msg, digestmod)
 | |
| 
 | |
|     def hmac_digest(self, key, msg=None, digestmod=DIGESTMOD_SENTINEL):
 | |
|         return _call_digest_func(self.hmac.compute_digest, key, msg, digestmod)
 | |
| 
 | |
| 
 | |
| class ObjectCheckerMixin:
 | |
|     """Mixin for checking HMAC objects (pure Python, OpenSSL or built-in)."""
 | |
| 
 | |
|     def check_object(self, h, hexdigest, hashname, digest_size, block_size):
 | |
|         """Check a HMAC object 'h' against the given values."""
 | |
|         self.check_internals(h, hashname, digest_size, block_size)
 | |
|         self.check_hexdigest(h, hexdigest, digest_size)
 | |
| 
 | |
|     def check_internals(self, h, hashname, digest_size, block_size):
 | |
|         """Check the constant attributes of a HMAC object."""
 | |
|         self.assertEqual(h.name, f"hmac-{hashname}")
 | |
|         self.assertEqual(h.digest_size, digest_size)
 | |
|         self.assertEqual(h.block_size, block_size)
 | |
| 
 | |
|     def check_hexdigest(self, h, hexdigest, digest_size):
 | |
|         """Check the HMAC digest of 'h' and its size."""
 | |
|         self.assertEqual(len(h.digest()), digest_size)
 | |
|         self.assertEqual(h.digest(), binascii.unhexlify(hexdigest))
 | |
|         self.assertEqual(h.hexdigest().upper(), hexdigest.upper())
 | |
| 
 | |
| 
 | |
| class AssertersMixin(CreatorMixin, DigestMixin, ObjectCheckerMixin):
 | |
|     """Mixin class for common tests."""
 | |
| 
 | |
|     def hmac_new_by_name(self, key, msg=None, *, hashname):
 | |
|         """Alternative implementation of hmac_new().
 | |
| 
 | |
|         This is typically useful when one needs to test against an HMAC
 | |
|         implementation which only recognizes underlying hash functions
 | |
|         by their name (all HMAC implementations must at least recognize
 | |
|         hash functions by their names but some may use aliases such as
 | |
|         `hashlib.sha1` instead of "sha1").
 | |
| 
 | |
|         Unlike hmac_new(), this method may assert the type of 'hashname'
 | |
|         as it should only be used in tests that are expected to create
 | |
|         a HMAC object.
 | |
|         """
 | |
|         self.assertIsInstance(hashname, str)
 | |
|         return self.hmac_new(key, msg, digestmod=hashname)
 | |
| 
 | |
|     def hmac_digest_by_name(self, key, msg=None, *, hashname):
 | |
|         """Alternative implementation of hmac_digest().
 | |
| 
 | |
|         Unlike hmac_digest(), this method may assert the type of 'hashname'
 | |
|         as it should only be used in tests that are expected to compute a
 | |
|         HMAC digest.
 | |
|         """
 | |
|         self.assertIsInstance(hashname, str)
 | |
|         return self.hmac_digest(key, msg, digestmod=hashname)
 | |
| 
 | |
|     def assert_hmac(
 | |
|         self, key, msg, hexdigest, hashfunc, hashname, digest_size, block_size
 | |
|     ):
 | |
|         """Check that HMAC(key, msg) == digest.
 | |
| 
 | |
|         The 'hashfunc' and 'hashname' are used as 'digestmod' values,
 | |
|         thereby allowing to test the underlying dispatching mechanism.
 | |
| 
 | |
|         Note that 'hashfunc' may be a string, a callable, or a PEP-257
 | |
|         module. Note that not all HMAC implementations may recognize the
 | |
|         same set of types for 'hashfunc', but they should always accept
 | |
|         a hash function by its name.
 | |
|         """
 | |
|         if hashfunc == hashname:
 | |
|             choices = [hashname]
 | |
|         else:
 | |
|             choices = [hashfunc, hashname]
 | |
| 
 | |
|         for digestmod in choices:
 | |
|             with self.subTest(digestmod=digestmod):
 | |
|                 self.assert_hmac_new(
 | |
|                     key, msg, hexdigest, digestmod,
 | |
|                     hashname, digest_size, block_size
 | |
|                 )
 | |
|                 self.assert_hmac_hexdigest(
 | |
|                     key, msg, hexdigest, digestmod, digest_size
 | |
|                 )
 | |
|                 self.assert_hmac_common_cases(
 | |
|                     key, msg, hexdigest, digestmod,
 | |
|                     hashname, digest_size, block_size
 | |
|                 )
 | |
|                 self.assert_hmac_extra_cases(
 | |
|                     key, msg, hexdigest, digestmod,
 | |
|                     hashname, digest_size, block_size
 | |
|                 )
 | |
| 
 | |
|         self.assert_hmac_new_by_name(
 | |
|             key, msg, hexdigest, hashname, digest_size, block_size
 | |
|         )
 | |
|         self.assert_hmac_hexdigest_by_name(
 | |
|             key, msg, hexdigest, hashname, digest_size
 | |
|         )
 | |
| 
 | |
|     def assert_hmac_new(
 | |
|         self, key, msg, hexdigest, digestmod, hashname, digest_size, block_size
 | |
|     ):
 | |
|         """Check that HMAC(key, msg) == digest.
 | |
| 
 | |
|         This test uses the `hmac_new()` method to create HMAC objects.
 | |
|         """
 | |
|         self.check_hmac_new(
 | |
|             key, msg, hexdigest, hashname, digest_size, block_size,
 | |
|             hmac_new_func=self.hmac_new,
 | |
|             hmac_new_kwds={'digestmod': digestmod},
 | |
|         )
 | |
| 
 | |
|     def assert_hmac_new_by_name(
 | |
|         self, key, msg, hexdigest, hashname, digest_size, block_size
 | |
|     ):
 | |
|         """Check that HMAC(key, msg) == digest.
 | |
| 
 | |
|         This test uses the `hmac_new_by_name()` method to create HMAC objects.
 | |
|         """
 | |
|         self.check_hmac_new(
 | |
|             key, msg, hexdigest, hashname, digest_size, block_size,
 | |
|             hmac_new_func=self.hmac_new_by_name,
 | |
|             hmac_new_kwds={'hashname': hashname},
 | |
|         )
 | |
| 
 | |
|     def check_hmac_new(
 | |
|         self, key, msg, hexdigest, hashname, digest_size, block_size,
 | |
|         hmac_new_func, hmac_new_kwds=types.MappingProxyType({}),
 | |
|     ):
 | |
|         """Check that HMAC(key, msg) == digest.
 | |
| 
 | |
|         This also tests that using an empty/None initial message and
 | |
|         then calling `h.update(msg)` produces the same result, namely
 | |
|         that HMAC(key, msg) is equivalent to HMAC(key).update(msg).
 | |
|         """
 | |
|         h = hmac_new_func(key, msg, **hmac_new_kwds)
 | |
|         self.check_object(h, hexdigest, hashname, digest_size, block_size)
 | |
| 
 | |
|         def hmac_new_feed(*args):
 | |
|             h = hmac_new_func(key, *args, **hmac_new_kwds)
 | |
|             h.update(msg)
 | |
|             self.check_hexdigest(h, hexdigest, digest_size)
 | |
| 
 | |
|         with self.subTest('no initial message'):
 | |
|             hmac_new_feed()
 | |
|         with self.subTest('initial message is empty'):
 | |
|             hmac_new_feed(b'')
 | |
|         with self.subTest('initial message is None'):
 | |
|             hmac_new_feed(None)
 | |
| 
 | |
|     def assert_hmac_hexdigest(
 | |
|         self, key, msg, hexdigest, digestmod, digest_size,
 | |
|     ):
 | |
|         """Check a HMAC digest computed by hmac_digest()."""
 | |
|         self.check_hmac_hexdigest(
 | |
|             key, msg, hexdigest, digest_size,
 | |
|             hmac_digest_func=self.hmac_digest,
 | |
|             hmac_digest_kwds={'digestmod': digestmod},
 | |
|         )
 | |
| 
 | |
|     def assert_hmac_hexdigest_by_name(
 | |
|         self, key, msg, hexdigest, hashname, digest_size
 | |
|     ):
 | |
|         """Check a HMAC digest computed by hmac_digest_by_name()."""
 | |
|         self.assertIsInstance(hashname, str)
 | |
|         self.check_hmac_hexdigest(
 | |
|             key, msg, hexdigest, digest_size,
 | |
|             hmac_digest_func=self.hmac_digest_by_name,
 | |
|             hmac_digest_kwds={'hashname': hashname},
 | |
|         )
 | |
| 
 | |
|     def check_hmac_hexdigest(
 | |
|         self, key, msg, hexdigest, digest_size,
 | |
|         hmac_digest_func, hmac_digest_kwds=types.MappingProxyType({}),
 | |
|     ):
 | |
|         """Check and return a HMAC digest computed by hmac_digest_func().
 | |
| 
 | |
|         This HMAC digest is computed by:
 | |
| 
 | |
|             hmac_digest_func(key, msg, **hmac_digest_kwds)
 | |
| 
 | |
|         This is typically useful for checking one-shot HMAC functions.
 | |
|         """
 | |
|         d = hmac_digest_func(key, msg, **hmac_digest_kwds)
 | |
|         self.assertEqual(len(d), digest_size)
 | |
|         self.assertEqual(d, binascii.unhexlify(hexdigest))
 | |
|         return d
 | |
| 
 | |
|     def assert_hmac_common_cases(
 | |
|         self, key, msg, hexdigest, digestmod, hashname, digest_size, block_size
 | |
|     ):
 | |
|         """Common tests executed by all subclasses."""
 | |
|         h1 = self.hmac_new_by_name(key, hashname=hashname)
 | |
|         h2 = h1.copy()
 | |
|         h2.update(b"test update should not affect original")
 | |
|         h1.update(msg)
 | |
|         self.check_object(h1, hexdigest, hashname, digest_size, block_size)
 | |
| 
 | |
|     def assert_hmac_extra_cases(
 | |
|         self, key, msg, hexdigest, digestmod, hashname, digest_size, block_size
 | |
|     ):
 | |
|         """Extra tests that can be added in subclasses."""
 | |
| 
 | |
| 
 | |
| class PyAssertersMixin(PyModuleMixin, AssertersMixin):
 | |
| 
 | |
|     def assert_hmac_extra_cases(
 | |
|         self, key, msg, hexdigest, digestmod, hashname, digest_size, block_size
 | |
|     ):
 | |
|         h = self.hmac.HMAC.__new__(self.hmac.HMAC)
 | |
|         h._init_old(key, msg, digestmod=digestmod)
 | |
|         self.check_object(h, hexdigest, hashname, digest_size, block_size)
 | |
| 
 | |
| 
 | |
| class OpenSSLAssertersMixin(ThroughOpenSSLAPIMixin, AssertersMixin):
 | |
| 
 | |
|     def hmac_new_by_name(self, key, msg=None, *, hashname):
 | |
|         self.assertIsInstance(hashname, str)
 | |
|         openssl_func = getattr(_hashlib, f"openssl_{hashname}")
 | |
|         return self.hmac_new(key, msg, digestmod=openssl_func)
 | |
| 
 | |
|     def hmac_digest_by_name(self, key, msg=None, *, hashname):
 | |
|         self.assertIsInstance(hashname, str)
 | |
|         openssl_func = getattr(_hashlib, f"openssl_{hashname}")
 | |
|         return self.hmac_digest(key, msg, digestmod=openssl_func)
 | |
| 
 | |
| 
 | |
| class BuiltinAssertersMixin(ThroughBuiltinAPIMixin, AssertersMixin):
 | |
|     pass
 | |
| 
 | |
| 
 | |
| class HashFunctionsTrait:
 | |
|     """Trait class for 'hashfunc' in hmac_new() and hmac_digest()."""
 | |
| 
 | |
|     ALGORITHMS = [
 | |
|         'md5', 'sha1',
 | |
|         'sha224', 'sha256', 'sha384', 'sha512',
 | |
|         'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512',
 | |
|     ]
 | |
| 
 | |
|     # By default, a missing algorithm skips the test that uses it.
 | |
|     _ = property(lambda self: self.skipTest("missing hash function"))
 | |
|     md5 = sha1 = _
 | |
|     sha224 = sha256 = sha384 = sha512 = _
 | |
|     sha3_224 = sha3_256 = sha3_384 = sha3_512 = _
 | |
|     del _
 | |
| 
 | |
| 
 | |
| class WithOpenSSLHashFunctions(HashFunctionsTrait):
 | |
|     """Test a HMAC implementation with an OpenSSL-based callable 'hashfunc'."""
 | |
| 
 | |
|     @classmethod
 | |
|     def setUpClass(cls):
 | |
|         super().setUpClass()
 | |
| 
 | |
|         for name in cls.ALGORITHMS:
 | |
|             @property
 | |
|             @hashlib_helper.requires_openssl_hashdigest(name)
 | |
|             def func(self, *, __name=name):  # __name needed to bind 'name'
 | |
|                 return getattr(_hashlib, f'openssl_{__name}')
 | |
|             setattr(cls, name, func)
 | |
| 
 | |
| 
 | |
| class WithNamedHashFunctions(HashFunctionsTrait):
 | |
|     """Test a HMAC implementation with a named 'hashfunc'."""
 | |
| 
 | |
|     @classmethod
 | |
|     def setUpClass(cls):
 | |
|         super().setUpClass()
 | |
| 
 | |
|         for name in cls.ALGORITHMS:
 | |
|             setattr(cls, name, name)
 | |
| 
 | |
| 
 | |
| class RFCTestCaseMixin(AssertersMixin, HashFunctionsTrait):
 | |
|     """Test HMAC implementations against RFC 2202/4231 and NIST test vectors.
 | |
| 
 | |
|     - Test vectors for MD5 and SHA-1 are taken from RFC 2202.
 | |
|     - Test vectors for SHA-2 are taken from RFC 4231.
 | |
|     - Test vectors for SHA-3 are NIST's test vectors [1].
 | |
| 
 | |
|     [1] https://csrc.nist.gov/projects/message-authentication-codes
 | |
|     """
 | |
| 
 | |
|     def test_md5_rfc2202(self):
 | |
|         def md5test(key, msg, hexdigest):
 | |
|             self.assert_hmac(key, msg, hexdigest, self.md5, "md5", 16, 64)
 | |
| 
 | |
|         md5test(b"\x0b" * 16,
 | |
|                 b"Hi There",
 | |
|                 "9294727a3638bb1c13f48ef8158bfc9d")
 | |
| 
 | |
|         md5test(b"Jefe",
 | |
|                 b"what do ya want for nothing?",
 | |
|                 "750c783e6ab0b503eaa86e310a5db738")
 | |
| 
 | |
|         md5test(b"\xaa" * 16,
 | |
|                 b"\xdd" * 50,
 | |
|                 "56be34521d144c88dbb8c733f0e8b3f6")
 | |
| 
 | |
|         md5test(bytes(range(1, 26)),
 | |
|                 b"\xcd" * 50,
 | |
|                 "697eaf0aca3a3aea3a75164746ffaa79")
 | |
| 
 | |
|         md5test(b"\x0C" * 16,
 | |
|                 b"Test With Truncation",
 | |
|                 "56461ef2342edc00f9bab995690efd4c")
 | |
| 
 | |
|         md5test(b"\xaa" * 80,
 | |
|                 b"Test Using Larger Than Block-Size Key - Hash Key First",
 | |
|                 "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd")
 | |
| 
 | |
|         md5test(b"\xaa" * 80,
 | |
|                 (b"Test Using Larger Than Block-Size Key "
 | |
|                  b"and Larger Than One Block-Size Data"),
 | |
|                 "6f630fad67cda0ee1fb1f562db3aa53e")
 | |
| 
 | |
|     def test_sha1_rfc2202(self):
 | |
|         def shatest(key, msg, hexdigest):
 | |
|             self.assert_hmac(key, msg, hexdigest, self.sha1, "sha1", 20, 64)
 | |
| 
 | |
|         shatest(b"\x0b" * 20,
 | |
|                 b"Hi There",
 | |
|                 "b617318655057264e28bc0b6fb378c8ef146be00")
 | |
| 
 | |
|         shatest(b"Jefe",
 | |
|                 b"what do ya want for nothing?",
 | |
|                 "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79")
 | |
| 
 | |
|         shatest(b"\xAA" * 20,
 | |
|                 b"\xDD" * 50,
 | |
|                 "125d7342b9ac11cd91a39af48aa17b4f63f175d3")
 | |
| 
 | |
|         shatest(bytes(range(1, 26)),
 | |
|                 b"\xCD" * 50,
 | |
|                 "4c9007f4026250c6bc8414f9bf50c86c2d7235da")
 | |
| 
 | |
|         shatest(b"\x0C" * 20,
 | |
|                 b"Test With Truncation",
 | |
|                 "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04")
 | |
| 
 | |
|         shatest(b"\xAA" * 80,
 | |
|                 b"Test Using Larger Than Block-Size Key - Hash Key First",
 | |
|                 "aa4ae5e15272d00e95705637ce8a3b55ed402112")
 | |
| 
 | |
|         shatest(b"\xAA" * 80,
 | |
|                 (b"Test Using Larger Than Block-Size Key "
 | |
|                  b"and Larger Than One Block-Size Data"),
 | |
|                 "e8e99d0f45237d786d6bbaa7965c7808bbff1a91")
 | |
| 
 | |
|     def test_sha2_224_rfc4231(self):
 | |
|         self._test_sha2_rfc4231(self.sha224, 'sha224', 28, 64)
 | |
| 
 | |
|     def test_sha2_256_rfc4231(self):
 | |
|         self._test_sha2_rfc4231(self.sha256, 'sha256', 32, 64)
 | |
| 
 | |
|     def test_sha2_384_rfc4231(self):
 | |
|         self._test_sha2_rfc4231(self.sha384, 'sha384', 48, 128)
 | |
| 
 | |
|     def test_sha2_512_rfc4231(self):
 | |
|         self._test_sha2_rfc4231(self.sha512, 'sha512', 64, 128)
 | |
| 
 | |
|     def _test_sha2_rfc4231(self, hashfunc, hashname, digest_size, block_size):
 | |
|         def hmactest(key, msg, hexdigests):
 | |
|             hexdigest = hexdigests[hashname]
 | |
| 
 | |
|             self.assert_hmac(
 | |
|                 key, msg, hexdigest,
 | |
|                 hashfunc=hashfunc,
 | |
|                 hashname=hashname,
 | |
|                 digest_size=digest_size,
 | |
|                 block_size=block_size
 | |
|             )
 | |
| 
 | |
|         # 4.2.  Test Case 1
 | |
|         hmactest(key=b'\x0b' * 20,
 | |
|                  msg=b'Hi There',
 | |
|                  hexdigests={
 | |
|                      'sha224': '896fb1128abbdf196832107cd49df33f'
 | |
|                                '47b4b1169912ba4f53684b22',
 | |
|                      'sha256': 'b0344c61d8db38535ca8afceaf0bf12b'
 | |
|                                '881dc200c9833da726e9376c2e32cff7',
 | |
|                      'sha384': 'afd03944d84895626b0825f4ab46907f'
 | |
|                                '15f9dadbe4101ec682aa034c7cebc59c'
 | |
|                                'faea9ea9076ede7f4af152e8b2fa9cb6',
 | |
|                      'sha512': '87aa7cdea5ef619d4ff0b4241a1d6cb0'
 | |
|                                '2379f4e2ce4ec2787ad0b30545e17cde'
 | |
|                                'daa833b7d6b8a702038b274eaea3f4e4'
 | |
|                                'be9d914eeb61f1702e696c203a126854',
 | |
|                  })
 | |
| 
 | |
|         # 4.3.  Test Case 2
 | |
|         hmactest(key=b'Jefe',
 | |
|                  msg=b'what do ya want for nothing?',
 | |
|                  hexdigests={
 | |
|                      'sha224': 'a30e01098bc6dbbf45690f3a7e9e6d0f'
 | |
|                                '8bbea2a39e6148008fd05e44',
 | |
|                      'sha256': '5bdcc146bf60754e6a042426089575c7'
 | |
|                                '5a003f089d2739839dec58b964ec3843',
 | |
|                      'sha384': 'af45d2e376484031617f78d2b58a6b1b'
 | |
|                                '9c7ef464f5a01b47e42ec3736322445e'
 | |
|                                '8e2240ca5e69e2c78b3239ecfab21649',
 | |
|                      'sha512': '164b7a7bfcf819e2e395fbe73b56e0a3'
 | |
|                                '87bd64222e831fd610270cd7ea250554'
 | |
|                                '9758bf75c05a994a6d034f65f8f0e6fd'
 | |
|                                'caeab1a34d4a6b4b636e070a38bce737',
 | |
|                  })
 | |
| 
 | |
|         # 4.4.  Test Case 3
 | |
|         hmactest(key=b'\xaa' * 20,
 | |
|                  msg=b'\xdd' * 50,
 | |
|                  hexdigests={
 | |
|                      'sha224': '7fb3cb3588c6c1f6ffa9694d7d6ad264'
 | |
|                                '9365b0c1f65d69d1ec8333ea',
 | |
|                      'sha256': '773ea91e36800e46854db8ebd09181a7'
 | |
|                                '2959098b3ef8c122d9635514ced565fe',
 | |
|                      'sha384': '88062608d3e6ad8a0aa2ace014c8a86f'
 | |
|                                '0aa635d947ac9febe83ef4e55966144b'
 | |
|                                '2a5ab39dc13814b94e3ab6e101a34f27',
 | |
|                      'sha512': 'fa73b0089d56a284efb0f0756c890be9'
 | |
|                                'b1b5dbdd8ee81a3655f83e33b2279d39'
 | |
|                                'bf3e848279a722c806b485a47e67c807'
 | |
|                                'b946a337bee8942674278859e13292fb',
 | |
|                  })
 | |
| 
 | |
|         # 4.5.  Test Case 4
 | |
|         hmactest(key=bytes(x for x in range(0x01, 0x19 + 1)),
 | |
|                  msg=b'\xcd' * 50,
 | |
|                  hexdigests={
 | |
|                      'sha224': '6c11506874013cac6a2abc1bb382627c'
 | |
|                                'ec6a90d86efc012de7afec5a',
 | |
|                      'sha256': '82558a389a443c0ea4cc819899f2083a'
 | |
|                                '85f0faa3e578f8077a2e3ff46729665b',
 | |
|                      'sha384': '3e8a69b7783c25851933ab6290af6ca7'
 | |
|                                '7a9981480850009cc5577c6e1f573b4e'
 | |
|                                '6801dd23c4a7d679ccf8a386c674cffb',
 | |
|                      'sha512': 'b0ba465637458c6990e5a8c5f61d4af7'
 | |
|                                'e576d97ff94b872de76f8050361ee3db'
 | |
|                                'a91ca5c11aa25eb4d679275cc5788063'
 | |
|                                'a5f19741120c4f2de2adebeb10a298dd',
 | |
|                  })
 | |
| 
 | |
|         # 4.7.  Test Case 6
 | |
|         hmactest(key=b'\xaa' * 131,
 | |
|                  msg=b'Test Using Larger Than Block-Siz'
 | |
|                      b'e Key - Hash Key First',
 | |
|                  hexdigests={
 | |
|                      'sha224': '95e9a0db962095adaebe9b2d6f0dbce2'
 | |
|                                'd499f112f2d2b7273fa6870e',
 | |
|                      'sha256': '60e431591ee0b67f0d8a26aacbf5b77f'
 | |
|                                '8e0bc6213728c5140546040f0ee37f54',
 | |
|                      'sha384': '4ece084485813e9088d2c63a041bc5b4'
 | |
|                                '4f9ef1012a2b588f3cd11f05033ac4c6'
 | |
|                                '0c2ef6ab4030fe8296248df163f44952',
 | |
|                      'sha512': '80b24263c7c1a3ebb71493c1dd7be8b4'
 | |
|                                '9b46d1f41b4aeec1121b013783f8f352'
 | |
|                                '6b56d037e05f2598bd0fd2215d6a1e52'
 | |
|                                '95e64f73f63f0aec8b915a985d786598',
 | |
|                  })
 | |
| 
 | |
|         # 4.8.  Test Case 7
 | |
|         hmactest(key=b'\xaa' * 131,
 | |
|                  msg=b'This is a test using a larger th'
 | |
|                      b'an block-size key and a larger t'
 | |
|                      b'han block-size data. The key nee'
 | |
|                      b'ds to be hashed before being use'
 | |
|                      b'd by the HMAC algorithm.',
 | |
|                  hexdigests={
 | |
|                      'sha224': '3a854166ac5d9f023f54d517d0b39dbd'
 | |
|                                '946770db9c2b95c9f6f565d1',
 | |
|                      'sha256': '9b09ffa71b942fcb27635fbcd5b0e944'
 | |
|                                'bfdc63644f0713938a7f51535c3a35e2',
 | |
|                      'sha384': '6617178e941f020d351e2f254e8fd32c'
 | |
|                                '602420feb0b8fb9adccebb82461e99c5'
 | |
|                                'a678cc31e799176d3860e6110c46523e',
 | |
|                      'sha512': 'e37b6a775dc87dbaa4dfa9f96e5e3ffd'
 | |
|                                'debd71f8867289865df5a32d20cdc944'
 | |
|                                'b6022cac3c4982b10d5eeb55c3e4de15'
 | |
|                                '134676fb6de0446065c97440fa8c6a58',
 | |
|                  })
 | |
| 
 | |
|     def test_sha3_224_nist(self):
 | |
|         for key, msg, hexdigest in [
 | |
|             (
 | |
|                 bytes(range(28)),
 | |
|                 b'Sample message for keylen<blocklen',
 | |
|                 '332cfd59347fdb8e576e77260be4aba2d6dc53117b3bfb52c6d18c04'
 | |
|             ), (
 | |
|                 bytes(range(144)),
 | |
|                 b'Sample message for keylen=blocklen',
 | |
|                 'd8b733bcf66c644a12323d564e24dcf3fc75f231f3b67968359100c7'
 | |
|             ), (
 | |
|                 bytes(range(172)),
 | |
|                 b'Sample message for keylen>blocklen',
 | |
|                 '078695eecc227c636ad31d063a15dd05a7e819a66ec6d8de1e193e59'
 | |
|             )
 | |
|         ]:
 | |
|             self.assert_hmac(
 | |
|                 key, msg, hexdigest,
 | |
|                 hashfunc=self.sha3_224, hashname='sha3_224',
 | |
|                 digest_size=28, block_size=144
 | |
|             )
 | |
| 
 | |
|     def test_sha3_256_nist(self):
 | |
|         for key, msg, hexdigest in [
 | |
|             (
 | |
|                 bytes(range(32)),
 | |
|                 b'Sample message for keylen<blocklen',
 | |
|                 '4fe8e202c4f058e8dddc23d8c34e4673'
 | |
|                 '43e23555e24fc2f025d598f558f67205'
 | |
|             ), (
 | |
|                 bytes(range(136)),
 | |
|                 b'Sample message for keylen=blocklen',
 | |
|                 '68b94e2e538a9be4103bebb5aa016d47'
 | |
|                 '961d4d1aa906061313b557f8af2c3faa'
 | |
|             ), (
 | |
|                 bytes(range(168)),
 | |
|                 b'Sample message for keylen>blocklen',
 | |
|                 '9bcf2c238e235c3ce88404e813bd2f3a'
 | |
|                 '97185ac6f238c63d6229a00b07974258'
 | |
|             )
 | |
|         ]:
 | |
|             self.assert_hmac(
 | |
|                 key, msg, hexdigest,
 | |
|                 hashfunc=self.sha3_256, hashname='sha3_256',
 | |
|                 digest_size=32, block_size=136
 | |
|             )
 | |
| 
 | |
|     def test_sha3_384_nist(self):
 | |
|         for key, msg, hexdigest in [
 | |
|             (
 | |
|                 bytes(range(48)),
 | |
|                 b'Sample message for keylen<blocklen',
 | |
|                 'd588a3c51f3f2d906e8298c1199aa8ff'
 | |
|                 '6296218127f6b38a90b6afe2c5617725'
 | |
|                 'bc99987f79b22a557b6520db710b7f42'
 | |
|             ), (
 | |
|                 bytes(range(104)),
 | |
|                 b'Sample message for keylen=blocklen',
 | |
|                 'a27d24b592e8c8cbf6d4ce6fc5bf62d8'
 | |
|                 'fc98bf2d486640d9eb8099e24047837f'
 | |
|                 '5f3bffbe92dcce90b4ed5b1e7e44fa90'
 | |
|             ), (
 | |
|                 bytes(range(152)),
 | |
|                 b'Sample message for keylen>blocklen',
 | |
|                 'e5ae4c739f455279368ebf36d4f5354c'
 | |
|                 '95aa184c899d3870e460ebc288ef1f94'
 | |
|                 '70053f73f7c6da2a71bcaec38ce7d6ac'
 | |
|             )
 | |
|         ]:
 | |
|             self.assert_hmac(
 | |
|                 key, msg, hexdigest,
 | |
|                 hashfunc=self.sha3_384, hashname='sha3_384',
 | |
|                 digest_size=48, block_size=104
 | |
|             )
 | |
| 
 | |
|     def test_sha3_512_nist(self):
 | |
|         for key, msg, hexdigest in [
 | |
|             (
 | |
|                 bytes(range(64)),
 | |
|                 b'Sample message for keylen<blocklen',
 | |
|                 '4efd629d6c71bf86162658f29943b1c3'
 | |
|                 '08ce27cdfa6db0d9c3ce81763f9cbce5'
 | |
|                 'f7ebe9868031db1a8f8eb7b6b95e5c5e'
 | |
|                 '3f657a8996c86a2f6527e307f0213196'
 | |
|             ), (
 | |
|                 bytes(range(72)),
 | |
|                 b'Sample message for keylen=blocklen',
 | |
|                 '544e257ea2a3e5ea19a590e6a24b724c'
 | |
|                 'e6327757723fe2751b75bf007d80f6b3'
 | |
|                 '60744bf1b7a88ea585f9765b47911976'
 | |
|                 'd3191cf83c039f5ffab0d29cc9d9b6da'
 | |
|             ), (
 | |
|                 bytes(range(136)),
 | |
|                 b'Sample message for keylen>blocklen',
 | |
|                 '5f464f5e5b7848e3885e49b2c385f069'
 | |
|                 '4985d0e38966242dc4a5fe3fea4b37d4'
 | |
|                 '6b65ceced5dcf59438dd840bab22269f'
 | |
|                 '0ba7febdb9fcf74602a35666b2a32915'
 | |
|             )
 | |
|         ]:
 | |
|             self.assert_hmac(
 | |
|                 key, msg, hexdigest,
 | |
|                 hashfunc=self.sha3_512, hashname='sha3_512',
 | |
|                 digest_size=64, block_size=72
 | |
|             )
 | |
| 
 | |
| 
 | |
| class PyRFCTestCase(ThroughObjectMixin, PyAssertersMixin,
 | |
|                     WithOpenSSLHashFunctions, RFCTestCaseMixin,
 | |
|                     unittest.TestCase):
 | |
|     """Python implementation of HMAC using hmac.HMAC().
 | |
| 
 | |
|     The underlying hash functions are OpenSSL-based.
 | |
|     """
 | |
| 
 | |
| 
 | |
| class PyDotNewRFCTestCase(ThroughModuleAPIMixin, PyAssertersMixin,
 | |
|                           WithOpenSSLHashFunctions, RFCTestCaseMixin,
 | |
|                           unittest.TestCase):
 | |
|     """Python implementation of HMAC using hmac.new().
 | |
| 
 | |
|     The underlying hash functions are OpenSSL-based.
 | |
|     """
 | |
| 
 | |
| 
 | |
| class OpenSSLRFCTestCase(OpenSSLAssertersMixin,
 | |
|                          WithOpenSSLHashFunctions, RFCTestCaseMixin,
 | |
|                          unittest.TestCase):
 | |
|     """OpenSSL implementation of HMAC.
 | |
| 
 | |
|     The underlying hash functions are also OpenSSL-based.
 | |
|     """
 | |
| 
 | |
| 
 | |
| class BuiltinRFCTestCase(BuiltinAssertersMixin,
 | |
|                          WithNamedHashFunctions, RFCTestCaseMixin,
 | |
|                          unittest.TestCase):
 | |
|     """Built-in HACL* implementation of HMAC.
 | |
| 
 | |
|     The underlying hash functions are also HACL*-based.
 | |
|     """
 | |
| 
 | |
|     def assert_hmac_extra_cases(
 | |
|         self, key, msg, hexdigest, digestmod, hashname, digest_size, block_size
 | |
|     ):
 | |
|         # assert one-shot HMAC at the same time
 | |
|         with self.subTest(key=key, msg=msg, hashname=hashname):
 | |
|             func = getattr(self.hmac, f'compute_{hashname}')
 | |
|             self.assertTrue(callable(func))
 | |
|             self.check_hmac_hexdigest(key, msg, hexdigest, digest_size, func)
 | |
| 
 | |
| 
 | |
| # TODO(picnixz): once we have a HACL* HMAC, we should also test the Python
 | |
| # implementation of HMAC with a HACL*-based hash function. For now, we only
 | |
| # test it partially via the '_sha2' module, but for completeness we could
 | |
| # also test the RFC test vectors against all possible implementations.
 | |
| 
 | |
| 
 | |
| class DigestModTestCaseMixin(CreatorMixin, DigestMixin):
 | |
|     """Tests for the 'digestmod' parameter for hmac_new() and hmac_digest()."""
 | |
| 
 | |
|     def assert_raises_missing_digestmod(self):
 | |
|         """A context manager catching errors when a digestmod is missing."""
 | |
|         return self.assertRaisesRegex(TypeError,
 | |
|                                       "[M|m]issing.*required.*digestmod")
 | |
| 
 | |
|     def assert_raises_unknown_digestmod(self):
 | |
|         """A context manager catching errors when a digestmod is unknown."""
 | |
|         return self.assertRaisesRegex(ValueError, "[Uu]nsupported.*")
 | |
| 
 | |
|     def test_constructor_missing_digestmod(self):
 | |
|         catcher = self.assert_raises_missing_digestmod
 | |
|         self.do_test_constructor_missing_digestmod(catcher)
 | |
| 
 | |
|     def test_constructor_unknown_digestmod(self):
 | |
|         catcher = self.assert_raises_unknown_digestmod
 | |
|         self.do_test_constructor_unknown_digestmod(catcher)
 | |
| 
 | |
|     def do_test_constructor_missing_digestmod(self, catcher):
 | |
|         for func, args, kwds in self.cases_missing_digestmod_in_constructor():
 | |
|             with self.subTest(args=args, kwds=kwds), catcher():
 | |
|                 func(*args, **kwds)
 | |
| 
 | |
|     def do_test_constructor_unknown_digestmod(self, catcher):
 | |
|         for func, args, kwds in self.cases_unknown_digestmod_in_constructor():
 | |
|             with self.subTest(args=args, kwds=kwds), catcher():
 | |
|                 func(*args, **kwds)
 | |
| 
 | |
|     def cases_missing_digestmod_in_constructor(self):
 | |
|         raise NotImplementedError
 | |
| 
 | |
|     def make_missing_digestmod_cases(self, func, missing_like=()):
 | |
|         """Generate cases for missing digestmod tests.
 | |
| 
 | |
|         Only the Python implementation should consider "falsey" 'digestmod'
 | |
|         values as being equivalent to a missing one.
 | |
|         """
 | |
|         key, msg = b'unused key', b'unused msg'
 | |
|         choices = [DIGESTMOD_SENTINEL, *missing_like]
 | |
|         return self._invalid_digestmod_cases(func, key, msg, choices)
 | |
| 
 | |
|     def cases_unknown_digestmod_in_constructor(self):
 | |
|         raise NotImplementedError
 | |
| 
 | |
|     def make_unknown_digestmod_cases(self, func, bad_digestmods):
 | |
|         """Generate cases for unknown digestmod tests."""
 | |
|         key, msg = b'unused key', b'unused msg'
 | |
|         return self._invalid_digestmod_cases(func, key, msg, bad_digestmods)
 | |
| 
 | |
|     def _invalid_digestmod_cases(self, func, key, msg, choices):
 | |
|         cases = []
 | |
|         for digestmod in choices:
 | |
|             kwargs = {'digestmod': digestmod}
 | |
|             cases.append((func, (key,), kwargs))
 | |
|             cases.append((func, (key, msg), kwargs))
 | |
|             cases.append((func, (key,), kwargs | {'msg': msg}))
 | |
|         return cases
 | |
| 
 | |
| 
 | |
| class ConstructorTestCaseMixin(CreatorMixin, DigestMixin, ObjectCheckerMixin):
 | |
|     """HMAC constructor tests based on HMAC-SHA-2/256."""
 | |
| 
 | |
|     key = b"key"
 | |
|     msg = b"hash this!"
 | |
|     res = "6c845b47f52b3b47f6590c502db7825aad757bf4fadc8fa972f7cd2e76a5bdeb"
 | |
| 
 | |
|     def do_test_constructor(self, hmac_on_key_and_msg):
 | |
|         self.do_test_constructor_invalid_types(hmac_on_key_and_msg)
 | |
|         self.do_test_constructor_supported_types(hmac_on_key_and_msg)
 | |
| 
 | |
|     def do_test_constructor_invalid_types(self, hmac_on_key_and_msg):
 | |
|         self.assertRaises(TypeError, hmac_on_key_and_msg, 1)
 | |
|         self.assertRaises(TypeError, hmac_on_key_and_msg, "key")
 | |
| 
 | |
|         self.assertRaises(TypeError, hmac_on_key_and_msg, b"key", 1)
 | |
|         self.assertRaises(TypeError, hmac_on_key_and_msg, b"key", "msg")
 | |
| 
 | |
|     def do_test_constructor_supported_types(self, hmac_on_key_and_msg):
 | |
|         for tp_key in [bytes, bytearray]:
 | |
|             for tp_msg in [bytes, bytearray, memoryview]:
 | |
|                 with self.subTest(tp_key=tp_key, tp_msg=tp_msg):
 | |
|                     h = hmac_on_key_and_msg(tp_key(self.key), tp_msg(self.msg))
 | |
|                     self.assertEqual(h.name, "hmac-sha256")
 | |
|                     self.assertEqual(h.hexdigest(), self.res)
 | |
| 
 | |
|     @hashlib_helper.requires_hashdigest("sha256")
 | |
|     def test_constructor(self):
 | |
|         self.do_test_constructor(self.bind_hmac_new("sha256"))
 | |
| 
 | |
|     @hashlib_helper.requires_hashdigest("sha256")
 | |
|     def test_digest(self):
 | |
|         digest = self.hmac_digest(self.key, self.msg, "sha256")
 | |
|         self.assertEqual(digest, binascii.unhexlify(self.res))
 | |
| 
 | |
| 
 | |
| class PyConstructorBaseMixin(PyModuleMixin,
 | |
|                              DigestModTestCaseMixin,
 | |
|                              ConstructorTestCaseMixin):
 | |
| 
 | |
|     def cases_missing_digestmod_in_constructor(self):
 | |
|         func, choices = self.hmac_new, ['', None, False]
 | |
|         return self.make_missing_digestmod_cases(func, choices)
 | |
| 
 | |
|     def cases_unknown_digestmod_in_constructor(self):
 | |
|         func, choices = self.hmac_new, ['unknown']
 | |
|         return self.make_unknown_digestmod_cases(func, choices)
 | |
| 
 | |
|     @requires_builtin_sha2()
 | |
|     def test_constructor_with_module(self):
 | |
|         self.do_test_constructor(self.bind_hmac_new(sha2.sha256))
 | |
| 
 | |
|     @requires_builtin_sha2()
 | |
|     def test_digest_with_module(self):
 | |
|         digest = self.hmac_digest(self.key, self.msg, sha2.sha256)
 | |
|         self.assertEqual(digest, binascii.unhexlify(self.res))
 | |
| 
 | |
| 
 | |
| class PyConstructorTestCase(ThroughObjectMixin, PyConstructorBaseMixin,
 | |
|                             unittest.TestCase):
 | |
|     """Test the hmac.HMAC() pure Python constructor."""
 | |
| 
 | |
| 
 | |
| class PyModuleConstructorTestCase(ThroughModuleAPIMixin, PyConstructorBaseMixin,
 | |
|                                   unittest.TestCase):
 | |
|     """Test the hmac.new() and hmac.digest() functions."""
 | |
| 
 | |
|     def test_hmac_digest_digestmod_parameter(self):
 | |
|         func = self.hmac_digest
 | |
| 
 | |
|         def raiser():
 | |
|             raise RuntimeError("custom exception")
 | |
| 
 | |
|         with self.assertRaisesRegex(RuntimeError, "custom exception"):
 | |
|             func(b'key', b'msg', raiser)
 | |
| 
 | |
|         with self.assertRaisesRegex(ValueError, 'hash type'):
 | |
|             func(b'key', b'msg', 'unknown')
 | |
| 
 | |
|         with self.assertRaisesRegex(AttributeError, 'new'):
 | |
|             func(b'key', b'msg', 1234)
 | |
|         with self.assertRaisesRegex(AttributeError, 'new'):
 | |
|             func(b'key', b'msg', None)
 | |
| 
 | |
| 
 | |
| class ExtensionConstructorTestCaseMixin(DigestModTestCaseMixin,
 | |
|                                         ConstructorTestCaseMixin):
 | |
| 
 | |
|     @property
 | |
|     def obj_type(self):
 | |
|         """The underlying (non-instantiable) C class."""
 | |
|         raise NotImplementedError
 | |
| 
 | |
|     @property
 | |
|     def exc_type(self):
 | |
|         """The exact exception class raised upon invalid 'digestmod' values."""
 | |
|         raise NotImplementedError
 | |
| 
 | |
|     def test_internal_types(self):
 | |
|         # internal C types are immutable and cannot be instantiated
 | |
|         check_disallow_instantiation(self, self.obj_type)
 | |
|         with self.assertRaisesRegex(TypeError, "immutable type"):
 | |
|             self.obj_type.value = None
 | |
| 
 | |
|     def assert_raises_unknown_digestmod(self):
 | |
|         self.assertIsSubclass(self.exc_type, ValueError)
 | |
|         return self.assertRaises(self.exc_type)
 | |
| 
 | |
|     def cases_missing_digestmod_in_constructor(self):
 | |
|         return self.make_missing_digestmod_cases(self.hmac_new)
 | |
| 
 | |
|     def cases_unknown_digestmod_in_constructor(self):
 | |
|         func, choices = self.hmac_new, ['unknown', 1234]
 | |
|         return self.make_unknown_digestmod_cases(func, choices)
 | |
| 
 | |
| 
 | |
| class OpenSSLConstructorTestCase(ThroughOpenSSLAPIMixin,
 | |
|                                  ExtensionConstructorTestCaseMixin,
 | |
|                                  unittest.TestCase):
 | |
| 
 | |
|     @property
 | |
|     def obj_type(self):
 | |
|         return _hashlib.HMAC
 | |
| 
 | |
|     @property
 | |
|     def exc_type(self):
 | |
|         return _hashlib.UnsupportedDigestmodError
 | |
| 
 | |
|     def test_hmac_digest_digestmod_parameter(self):
 | |
|         for value in [object, 'unknown', 1234, None]:
 | |
|             with (
 | |
|                 self.subTest(value=value),
 | |
|                 self.assert_raises_unknown_digestmod(),
 | |
|             ):
 | |
|                 self.hmac_digest(b'key', b'msg', value)
 | |
| 
 | |
| 
 | |
| class BuiltinConstructorTestCase(ThroughBuiltinAPIMixin,
 | |
|                                  ExtensionConstructorTestCaseMixin,
 | |
|                                  unittest.TestCase):
 | |
| 
 | |
|     @property
 | |
|     def obj_type(self):
 | |
|         return self.hmac.HMAC
 | |
| 
 | |
|     @property
 | |
|     def exc_type(self):
 | |
|         return self.hmac.UnknownHashError
 | |
| 
 | |
|     def test_hmac_digest_digestmod_parameter(self):
 | |
|         for value in [object, 'unknown', 1234, None]:
 | |
|             with (
 | |
|                 self.subTest(value=value),
 | |
|                 self.assert_raises_unknown_digestmod(),
 | |
|             ):
 | |
|                 self.hmac_digest(b'key', b'msg', value)
 | |
| 
 | |
| 
 | |
| class SanityTestCaseMixin(CreatorMixin):
 | |
|     """Sanity checks for HMAC objects and their object interface.
 | |
| 
 | |
|     The tests here use a common digestname and do not check all supported
 | |
|     hash functions.
 | |
|     """
 | |
| 
 | |
|     # The underlying HMAC class to test. May be in C or in Python.
 | |
|     hmac_class: type
 | |
|     # The underlying hash function name (should be accepted by the HMAC class).
 | |
|     digestname: str
 | |
|     # The expected digest and block sizes (must be hardcoded).
 | |
|     digest_size: int
 | |
|     block_size: int
 | |
| 
 | |
|     def test_methods(self):
 | |
|         h = self.hmac_new(b"my secret key", digestmod=self.digestname)
 | |
|         self.assertIsInstance(h, self.hmac_class)
 | |
|         self.assertIsNone(h.update(b"compute the hash of this text!"))
 | |
|         self.assertIsInstance(h.digest(), bytes)
 | |
|         self.assertIsInstance(h.hexdigest(), str)
 | |
|         self.assertIsInstance(h.copy(), self.hmac_class)
 | |
| 
 | |
|     def test_properties(self):
 | |
|         h = self.hmac_new(b"my secret key", digestmod=self.digestname)
 | |
|         self.assertEqual(h.name, f"hmac-{self.digestname}")
 | |
|         self.assertEqual(h.digest_size, self.digest_size)
 | |
|         self.assertEqual(h.block_size, self.block_size)
 | |
| 
 | |
|     def test_repr(self):
 | |
|         # HMAC object representation may differ across implementations
 | |
|         raise NotImplementedError
 | |
| 
 | |
| 
 | |
| @hashlib_helper.requires_hashdigest('sha256')
 | |
| class PySanityTestCase(ThroughObjectMixin, PyModuleMixin, SanityTestCaseMixin,
 | |
|                        unittest.TestCase):
 | |
| 
 | |
|     @classmethod
 | |
|     def setUpClass(cls):
 | |
|         super().setUpClass()
 | |
|         cls.hmac_class = cls.hmac.HMAC
 | |
|         cls.digestname = 'sha256'
 | |
|         cls.digest_size = 32
 | |
|         cls.block_size = 64
 | |
| 
 | |
|     def test_repr(self):
 | |
|         h = self.hmac_new(b"my secret key", digestmod=self.digestname)
 | |
|         self.assertStartsWith(repr(h), "<hmac.HMAC object at")
 | |
| 
 | |
| 
 | |
| @hashlib_helper.requires_openssl_hashdigest('sha256')
 | |
| class OpenSSLSanityTestCase(ThroughOpenSSLAPIMixin, SanityTestCaseMixin,
 | |
|                             unittest.TestCase):
 | |
| 
 | |
|     @classmethod
 | |
|     def setUpClass(cls):
 | |
|         super().setUpClass()
 | |
|         cls.hmac_class = _hashlib.HMAC
 | |
|         cls.digestname = 'sha256'
 | |
|         cls.digest_size = 32
 | |
|         cls.block_size = 64
 | |
| 
 | |
|     def test_repr(self):
 | |
|         h = self.hmac_new(b"my secret key", digestmod=self.digestname)
 | |
|         self.assertStartsWith(repr(h), f"<{self.digestname} HMAC object @")
 | |
| 
 | |
| 
 | |
| class BuiltinSanityTestCase(ThroughBuiltinAPIMixin, SanityTestCaseMixin,
 | |
|                             unittest.TestCase):
 | |
| 
 | |
|     @classmethod
 | |
|     def setUpClass(cls):
 | |
|         super().setUpClass()
 | |
|         cls.hmac_class = cls.hmac.HMAC
 | |
|         cls.digestname = 'sha256'
 | |
|         cls.digest_size = 32
 | |
|         cls.block_size = 64
 | |
| 
 | |
|     def test_repr(self):
 | |
|         h = self.hmac_new(b"my secret key", digestmod=self.digestname)
 | |
|         self.assertStartsWith(repr(h), f"<{self.digestname} HMAC object @")
 | |
| 
 | |
| 
 | |
| class UpdateTestCaseMixin:
 | |
|     """Tests for the update() method (streaming HMAC)."""
 | |
| 
 | |
|     def HMAC(self, key, msg=None):
 | |
|         """Create a HMAC object."""
 | |
|         raise NotImplementedError
 | |
| 
 | |
|     @property
 | |
|     def gil_minsize(self):
 | |
|         """Get the maximal input length for the GIL to be held."""
 | |
|         raise NotImplementedError
 | |
| 
 | |
|     def check_update(self, key, chunks):
 | |
|         chunks = list(chunks)
 | |
|         msg = b''.join(chunks)
 | |
|         h1 = self.HMAC(key, msg)
 | |
| 
 | |
|         h2 = self.HMAC(key)
 | |
|         for chunk in chunks:
 | |
|             h2.update(chunk)
 | |
| 
 | |
|         self.assertEqual(h1.digest(), h2.digest())
 | |
|         self.assertEqual(h1.hexdigest(), h2.hexdigest())
 | |
| 
 | |
|     def test_update(self):
 | |
|         key, msg = random.randbytes(16), random.randbytes(16)
 | |
|         with self.subTest(key=key, msg=msg):
 | |
|             self.check_update(key, [msg])
 | |
| 
 | |
|     def test_update_large(self):
 | |
|         gil_minsize = self.gil_minsize
 | |
|         key = random.randbytes(16)
 | |
|         top = random.randbytes(gil_minsize + 1)
 | |
|         bot = random.randbytes(gil_minsize + 1)
 | |
|         self.check_update(key, [top, bot])
 | |
| 
 | |
|     def test_update_exceptions(self):
 | |
|         h = self.HMAC(b"key")
 | |
|         for msg in ['invalid msg', 123, (), []]:
 | |
|             with self.subTest(msg=msg):
 | |
|                 self.assertRaises(TypeError, h.update, msg)
 | |
| 
 | |
| 
 | |
| @requires_builtin_sha2()
 | |
| class PyUpdateTestCase(PyModuleMixin, UpdateTestCaseMixin, unittest.TestCase):
 | |
| 
 | |
|     def HMAC(self, key, msg=None):
 | |
|         return self.hmac.HMAC(key, msg, digestmod='sha256')
 | |
| 
 | |
|     @property
 | |
|     def gil_minsize(self):
 | |
|         return sha2._GIL_MINSIZE
 | |
| 
 | |
| 
 | |
| @hashlib_helper.requires_openssl_hashdigest('sha256')
 | |
| class OpenSSLUpdateTestCase(UpdateTestCaseMixin, unittest.TestCase):
 | |
| 
 | |
|     def HMAC(self, key, msg=None):
 | |
|         return _hashlib.hmac_new(key, msg, digestmod='sha256')
 | |
| 
 | |
|     @property
 | |
|     def gil_minsize(self):
 | |
|         return _hashlib._GIL_MINSIZE
 | |
| 
 | |
| 
 | |
| class BuiltinUpdateTestCase(BuiltinModuleMixin,
 | |
|                             UpdateTestCaseMixin, unittest.TestCase):
 | |
| 
 | |
|     def HMAC(self, key, msg=None):
 | |
|         # Even if Python does not build '_sha2', the HACL* sources
 | |
|         # are still built, making it possible to use SHA-2 hashes.
 | |
|         return self.hmac.new(key, msg, digestmod='sha256')
 | |
| 
 | |
|     @property
 | |
|     def gil_minsize(self):
 | |
|         return self.hmac._GIL_MINSIZE
 | |
| 
 | |
| 
 | |
| class CopyBaseTestCase:
 | |
| 
 | |
|     def test_attributes(self):
 | |
|         raise NotImplementedError
 | |
| 
 | |
|     def test_realcopy(self):
 | |
|         raise NotImplementedError
 | |
| 
 | |
| 
 | |
| @hashlib_helper.requires_hashdigest('sha256')
 | |
| class PythonCopyTestCase(CopyBaseTestCase, unittest.TestCase):
 | |
| 
 | |
|     def test_attributes(self):
 | |
|         # Testing if attributes are of same type.
 | |
|         h1 = hmac.HMAC.__new__(hmac.HMAC)
 | |
|         h1._init_old(b"key", b"msg", digestmod="sha256")
 | |
|         self.assertIsNone(h1._hmac)
 | |
|         self.assertIsNotNone(h1._inner)
 | |
|         self.assertIsNotNone(h1._outer)
 | |
| 
 | |
|         h2 = h1.copy()
 | |
|         self.assertIsNone(h2._hmac)
 | |
|         self.assertIsNotNone(h2._inner)
 | |
|         self.assertIsNotNone(h2._outer)
 | |
|         self.assertEqual(type(h1._inner), type(h2._inner))
 | |
|         self.assertEqual(type(h1._outer), type(h2._outer))
 | |
| 
 | |
|     def test_realcopy(self):
 | |
|         # Testing if the copy method created a real copy.
 | |
|         h1 = hmac.HMAC.__new__(hmac.HMAC)
 | |
|         h1._init_old(b"key", b"msg", digestmod="sha256")
 | |
|         h2 = h1.copy()
 | |
|         # Using id() in case somebody has overridden __eq__/__ne__.
 | |
|         self.assertNotEqual(id(h1), id(h2))
 | |
|         self.assertNotEqual(id(h1._inner), id(h2._inner))
 | |
|         self.assertNotEqual(id(h1._outer), id(h2._outer))
 | |
| 
 | |
|     def test_equality(self):
 | |
|         # Testing if the copy has the same digests.
 | |
|         h1 = hmac.HMAC(b"key", digestmod="sha256")
 | |
|         h1.update(b"some random text")
 | |
|         h2 = h1.copy()
 | |
|         self.assertEqual(h1.digest(), h2.digest())
 | |
|         self.assertEqual(h1.hexdigest(), h2.hexdigest())
 | |
| 
 | |
|     def test_equality_new(self):
 | |
|         # Testing if the copy has the same digests with hmac.new().
 | |
|         h1 = hmac.new(b"key", digestmod="sha256")
 | |
|         h1.update(b"some random text")
 | |
|         h2 = h1.copy()
 | |
|         # Using id() in case somebody has overridden __eq__/__ne__.
 | |
|         self.assertNotEqual(id(h1), id(h2))
 | |
|         self.assertEqual(h1.digest(), h2.digest())
 | |
|         self.assertEqual(h1.hexdigest(), h2.hexdigest())
 | |
| 
 | |
| 
 | |
| class ExtensionCopyTestCase(CopyBaseTestCase):
 | |
| 
 | |
|     def init(self, h):
 | |
|         """Call the dedicate init() method to test."""
 | |
|         raise NotImplementedError
 | |
| 
 | |
|     def test_attributes(self):
 | |
|         # Testing if attributes are of same type.
 | |
|         h1 = hmac.HMAC.__new__(hmac.HMAC)
 | |
| 
 | |
|         self.init(h1)
 | |
|         self.assertIsNotNone(h1._hmac)
 | |
|         self.assertIsNone(h1._inner)
 | |
|         self.assertIsNone(h1._outer)
 | |
| 
 | |
|         h2 = h1.copy()
 | |
|         self.assertIsNotNone(h2._hmac)
 | |
|         self.assertIsNone(h2._inner)
 | |
|         self.assertIsNone(h2._outer)
 | |
| 
 | |
|     def test_realcopy(self):
 | |
|         h1 = hmac.HMAC.__new__(hmac.HMAC)
 | |
|         self.init(h1)
 | |
|         h2 = h1.copy()
 | |
|         # Using id() in case somebody has overridden __eq__/__ne__.
 | |
|         self.assertNotEqual(id(h1._hmac), id(h2._hmac))
 | |
| 
 | |
| 
 | |
| @hashlib_helper.requires_openssl_hashdigest('sha256')
 | |
| class OpenSSLCopyTestCase(ExtensionCopyTestCase, unittest.TestCase):
 | |
| 
 | |
|     def init(self, h):
 | |
|         h._init_openssl_hmac(b"key", b"msg", digestmod="sha256")
 | |
| 
 | |
| 
 | |
| @hashlib_helper.requires_builtin_hmac()
 | |
| class BuiltinCopyTestCase(ExtensionCopyTestCase, unittest.TestCase):
 | |
| 
 | |
|     def init(self, h):
 | |
|         # Even if Python does not build '_sha2', the HACL* sources
 | |
|         # are still built, making it possible to use SHA-2 hashes.
 | |
|         h._init_builtin_hmac(b"key", b"msg", digestmod="sha256")
 | |
| 
 | |
| 
 | |
| class CompareDigestMixin:
 | |
| 
 | |
|     @staticmethod
 | |
|     def compare_digest(a, b):
 | |
|         """Implementation of 'a == b' to test."""
 | |
|         raise NotImplementedError
 | |
| 
 | |
|     def assert_digest_equal(self, a, b):
 | |
|         with self.subTest(a=a, b=b):
 | |
|             self.assertTrue(self.compare_digest(a, b))
 | |
|         with self.subTest(a=b, b=a):
 | |
|             self.assertTrue(self.compare_digest(b, a))
 | |
| 
 | |
|     def assert_digest_not_equal(self, a, b):
 | |
|         with self.subTest(a=a, b=b):
 | |
|             self.assertFalse(self.compare_digest(a, b))
 | |
|         with self.subTest(a=b, b=a):
 | |
|             self.assertFalse(self.compare_digest(b, a))
 | |
| 
 | |
|     def test_exceptions(self):
 | |
|         for a, b in [
 | |
|             # Testing input type exception handling
 | |
|             (100, 200), (100, b"foobar"), ("foobar", b"foobar"),
 | |
|             # non-ASCII strings
 | |
|             ("fooä", "fooä")
 | |
|         ]:
 | |
|             self.assertRaises(TypeError, self.compare_digest, a, b)
 | |
|             self.assertRaises(TypeError, self.compare_digest, b, a)
 | |
| 
 | |
|     def test_bytes(self):
 | |
|         # Testing bytes of different lengths
 | |
|         a, b = b"foobar", b"foo"
 | |
|         self.assert_digest_not_equal(a, b)
 | |
|         a, b = b"\xde\xad\xbe\xef", b"\xde\xad"
 | |
|         self.assert_digest_not_equal(a, b)
 | |
| 
 | |
|         # Testing bytes of same lengths, different values
 | |
|         a, b = b"foobar", b"foobaz"
 | |
|         self.assert_digest_not_equal(a, b)
 | |
|         a, b = b"\xde\xad\xbe\xef", b"\xab\xad\x1d\xea"
 | |
|         self.assert_digest_not_equal(a, b)
 | |
| 
 | |
|         # Testing bytes of same lengths, same values
 | |
|         a, b = b"foobar", b"foobar"
 | |
|         self.assert_digest_equal(a, b)
 | |
|         a, b = b"\xde\xad\xbe\xef", b"\xde\xad\xbe\xef"
 | |
|         self.assert_digest_equal(a, b)
 | |
| 
 | |
|     def test_bytearray(self):
 | |
|         # Testing bytearrays of same lengths, same values
 | |
|         a, b = bytearray(b"foobar"), bytearray(b"foobar")
 | |
|         self.assert_digest_equal(a, b)
 | |
| 
 | |
|         # Testing bytearrays of different lengths
 | |
|         a, b = bytearray(b"foobar"), bytearray(b"foo")
 | |
|         self.assert_digest_not_equal(a, b)
 | |
| 
 | |
|         # Testing bytearrays of same lengths, different values
 | |
|         a, b = bytearray(b"foobar"), bytearray(b"foobaz")
 | |
|         self.assert_digest_not_equal(a, b)
 | |
| 
 | |
|     def test_mixed_types(self):
 | |
|         # Testing byte and bytearray of same lengths, same values
 | |
|         a, b = bytearray(b"foobar"), b"foobar"
 | |
|         self.assert_digest_equal(a, b)
 | |
| 
 | |
|         # Testing byte bytearray of different lengths
 | |
|         a, b = bytearray(b"foobar"), b"foo"
 | |
|         self.assert_digest_not_equal(a, b)
 | |
| 
 | |
|         # Testing byte and bytearray of same lengths, different values
 | |
|         a, b = bytearray(b"foobar"), b"foobaz"
 | |
|         self.assert_digest_not_equal(a, b)
 | |
| 
 | |
|     def test_string(self):
 | |
|         # Testing str of same lengths
 | |
|         a, b = "foobar", "foobar"
 | |
|         self.assert_digest_equal(a, b)
 | |
| 
 | |
|         # Testing str of different lengths
 | |
|         a, b = "foo", "foobar"
 | |
|         self.assert_digest_not_equal(a, b)
 | |
| 
 | |
|         # Testing str of same lengths, different values
 | |
|         a, b = "foobar", "foobaz"
 | |
|         self.assert_digest_not_equal(a, b)
 | |
| 
 | |
|     def test_string_subclass(self):
 | |
|         class S(str):
 | |
|             def __eq__(self, other):
 | |
|                 raise ValueError("should not be called")
 | |
| 
 | |
|         a, b = S("foobar"), S("foobar")
 | |
|         self.assert_digest_equal(a, b)
 | |
|         a, b = S("foobar"), "foobar"
 | |
|         self.assert_digest_equal(a, b)
 | |
|         a, b = S("foobar"), S("foobaz")
 | |
|         self.assert_digest_not_equal(a, b)
 | |
| 
 | |
|     def test_bytes_subclass(self):
 | |
|         class B(bytes):
 | |
|             def __eq__(self, other):
 | |
|                 raise ValueError("should not be called")
 | |
| 
 | |
|         a, b = B(b"foobar"), B(b"foobar")
 | |
|         self.assert_digest_equal(a, b)
 | |
|         a, b = B(b"foobar"), b"foobar"
 | |
|         self.assert_digest_equal(a, b)
 | |
|         a, b = B(b"foobar"), B(b"foobaz")
 | |
|         self.assert_digest_not_equal(a, b)
 | |
| 
 | |
| 
 | |
| class HMACCompareDigestTestCase(CompareDigestMixin, unittest.TestCase):
 | |
|     compare_digest = hmac.compare_digest
 | |
| 
 | |
|     def test_compare_digest_func(self):
 | |
|         if openssl_compare_digest is not None:
 | |
|             self.assertIs(self.compare_digest, openssl_compare_digest)
 | |
|         else:
 | |
|             self.assertIs(self.compare_digest, operator_compare_digest)
 | |
| 
 | |
| 
 | |
| @hashlib_helper.requires_hashlib()
 | |
| class OpenSSLCompareDigestTestCase(CompareDigestMixin, unittest.TestCase):
 | |
|     compare_digest = openssl_compare_digest
 | |
| 
 | |
| 
 | |
| class OperatorCompareDigestTestCase(CompareDigestMixin, unittest.TestCase):
 | |
|     compare_digest = operator_compare_digest
 | |
| 
 | |
| 
 | |
| class PyMiscellaneousTests(unittest.TestCase):
 | |
|     """Miscellaneous tests for the pure Python HMAC module."""
 | |
| 
 | |
|     @hashlib_helper.requires_builtin_hmac()
 | |
|     def test_hmac_constructor_uses_builtin(self):
 | |
|         # Block the OpenSSL implementation and check that
 | |
|         # HMAC() uses the built-in implementation instead.
 | |
|         hmac = import_fresh_module("hmac", blocked=["_hashlib"])
 | |
| 
 | |
|         def watch_method(cls, name):
 | |
|             return mock.patch.object(
 | |
|                 cls, name, autospec=True, wraps=getattr(cls, name)
 | |
|             )
 | |
| 
 | |
|         with (
 | |
|             watch_method(hmac.HMAC, '_init_openssl_hmac') as f,
 | |
|             watch_method(hmac.HMAC, '_init_builtin_hmac') as g,
 | |
|         ):
 | |
|             _ = hmac.HMAC(b'key', b'msg', digestmod="sha256")
 | |
|             f.assert_not_called()
 | |
|             g.assert_called_once()
 | |
| 
 | |
|     @hashlib_helper.requires_hashdigest('sha256')
 | |
|     def test_hmac_delegated_properties(self):
 | |
|         h = hmac.HMAC(b'key', b'msg', digestmod="sha256")
 | |
|         self.assertEqual(h.name, "hmac-sha256")
 | |
|         self.assertEqual(h.digest_size, 32)
 | |
|         self.assertEqual(h.block_size, 64)
 | |
| 
 | |
|     @hashlib_helper.requires_hashdigest('sha256')
 | |
|     def test_legacy_block_size_warnings(self):
 | |
|         class MockCrazyHash(object):
 | |
|             """Ain't no block_size attribute here."""
 | |
|             def __init__(self, *args):
 | |
|                 self._x = hashlib.sha256(*args)
 | |
|                 self.digest_size = self._x.digest_size
 | |
|             def update(self, v):
 | |
|                 self._x.update(v)
 | |
|             def digest(self):
 | |
|                 return self._x.digest()
 | |
| 
 | |
|         with warnings.catch_warnings():
 | |
|             warnings.simplefilter('error', RuntimeWarning)
 | |
|             with self.assertRaises(RuntimeWarning):
 | |
|                 hmac.HMAC(b'a', b'b', digestmod=MockCrazyHash)
 | |
|                 self.fail('Expected warning about missing block_size')
 | |
| 
 | |
|             MockCrazyHash.block_size = 1
 | |
|             with self.assertRaises(RuntimeWarning):
 | |
|                 hmac.HMAC(b'a', b'b', digestmod=MockCrazyHash)
 | |
|                 self.fail('Expected warning about small block_size')
 | |
| 
 | |
|     @hashlib_helper.requires_hashdigest('sha256')
 | |
|     def test_with_fallback(self):
 | |
|         cache = getattr(hashlib, '__builtin_constructor_cache')
 | |
|         try:
 | |
|             cache['foo'] = hashlib.sha256
 | |
|             hexdigest = hmac.digest(b'key', b'message', 'foo').hex()
 | |
|             expected = ('6e9ef29b75fffc5b7abae527d58fdadb'
 | |
|                         '2fe42e7219011976917343065f58ed4a')
 | |
|             self.assertEqual(hexdigest, expected)
 | |
|         finally:
 | |
|             cache.pop('foo')
 | |
| 
 | |
| 
 | |
| class BuiltinMiscellaneousTests(BuiltinModuleMixin, unittest.TestCase):
 | |
|     """HMAC-BLAKE2 is not standardized as BLAKE2 is a keyed hash function.
 | |
| 
 | |
|     In particular, there is no official test vectors for HMAC-BLAKE2.
 | |
|     However, we can test that the HACL* interface is correctly used by
 | |
|     checking against the pure Python implementation output.
 | |
|     """
 | |
| 
 | |
|     @classmethod
 | |
|     def setUpClass(cls):
 | |
|         super().setUpClass()
 | |
|         cls.blake2 = import_module("_blake2")
 | |
|         cls.blake2b = cls.blake2.blake2b
 | |
|         cls.blake2s = cls.blake2.blake2s
 | |
| 
 | |
|     def assert_hmac_blake_correctness(self, digest, key, msg, hashfunc):
 | |
|         self.assertIsInstance(digest, bytes)
 | |
|         expect = hmac._compute_digest_fallback(key, msg, hashfunc)
 | |
|         self.assertEqual(digest, expect)
 | |
| 
 | |
|     def test_compute_blake2b_32(self):
 | |
|         key, msg = random.randbytes(8), random.randbytes(16)
 | |
|         digest = self.hmac.compute_blake2b_32(key, msg)
 | |
|         self.assert_hmac_blake_correctness(digest, key, msg, self.blake2b)
 | |
| 
 | |
|     def test_compute_blake2s_32(self):
 | |
|         key, msg = random.randbytes(8), random.randbytes(16)
 | |
|         digest = self.hmac.compute_blake2s_32(key, msg)
 | |
|         self.assert_hmac_blake_correctness(digest, key, msg, self.blake2s)
 | |
| 
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     unittest.main()
 | 
