mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
bpo-40645: Implement HMAC in C (GH-20129)
The internal module ``_hashlib`` wraps and exposes OpenSSL's HMAC API. The new code will be used in Python 3.10 after the internal implementation details of the pure Python HMAC module are no longer part of the public API. The code is based on a patch by Petr Viktorin for RHEL and Python 3.6. Co-Authored-By: Petr Viktorin <encukou@gmail.com>
This commit is contained in:
parent
46545000c2
commit
54f2898fe7
4 changed files with 694 additions and 89 deletions
|
@ -8,6 +8,13 @@ import warnings
|
|||
|
||||
from test.support import hashlib_helper
|
||||
|
||||
try:
|
||||
from _hashlib import HMAC as C_HMAC
|
||||
from _hashlib import hmac_new as c_hmac_new
|
||||
except ImportError:
|
||||
C_HMAC = None
|
||||
c_hmac_new = None
|
||||
|
||||
|
||||
def ignore_warning(func):
|
||||
@functools.wraps(func)
|
||||
|
@ -21,34 +28,91 @@ def ignore_warning(func):
|
|||
|
||||
class TestVectorsTestCase(unittest.TestCase):
|
||||
|
||||
def asssert_hmac(
|
||||
self, key, data, digest, hashfunc, hashname, digest_size, block_size
|
||||
):
|
||||
h = hmac.HMAC(key, data, digestmod=hashfunc)
|
||||
self.assertEqual(h.hexdigest().upper(), digest.upper())
|
||||
self.assertEqual(h.digest(), binascii.unhexlify(digest))
|
||||
self.assertEqual(h.name, f"hmac-{hashname}")
|
||||
self.assertEqual(h.digest_size, digest_size)
|
||||
self.assertEqual(h.block_size, block_size)
|
||||
|
||||
h = hmac.HMAC(key, data, digestmod=hashname)
|
||||
self.assertEqual(h.hexdigest().upper(), digest.upper())
|
||||
self.assertEqual(h.digest(), binascii.unhexlify(digest))
|
||||
self.assertEqual(h.name, f"hmac-{hashname}")
|
||||
self.assertEqual(h.digest_size, digest_size)
|
||||
self.assertEqual(h.block_size, block_size)
|
||||
|
||||
h = hmac.HMAC(key, digestmod=hashname)
|
||||
h2 = h.copy()
|
||||
h2.update(b"test update")
|
||||
h.update(data)
|
||||
self.assertEqual(h.hexdigest().upper(), digest.upper())
|
||||
|
||||
h = hmac.new(key, data, digestmod=hashname)
|
||||
self.assertEqual(h.hexdigest().upper(), digest.upper())
|
||||
self.assertEqual(h.digest(), binascii.unhexlify(digest))
|
||||
self.assertEqual(h.name, f"hmac-{hashname}")
|
||||
self.assertEqual(h.digest_size, digest_size)
|
||||
self.assertEqual(h.block_size, block_size)
|
||||
|
||||
h = hmac.new(key, None, digestmod=hashname)
|
||||
h.update(data)
|
||||
self.assertEqual(h.hexdigest().upper(), digest.upper())
|
||||
|
||||
h = hmac.new(key, digestmod=hashname)
|
||||
h.update(data)
|
||||
self.assertEqual(h.hexdigest().upper(), digest.upper())
|
||||
|
||||
h = hmac.new(key, data, digestmod=hashfunc)
|
||||
self.assertEqual(h.hexdigest().upper(), digest.upper())
|
||||
|
||||
self.assertEqual(
|
||||
hmac.digest(key, data, digest=hashname),
|
||||
binascii.unhexlify(digest)
|
||||
)
|
||||
self.assertEqual(
|
||||
hmac.digest(key, data, digest=hashfunc),
|
||||
binascii.unhexlify(digest)
|
||||
)
|
||||
with unittest.mock.patch('hmac._openssl_md_meths', {}):
|
||||
self.assertEqual(
|
||||
hmac.digest(key, data, digest=hashname),
|
||||
binascii.unhexlify(digest)
|
||||
)
|
||||
self.assertEqual(
|
||||
hmac.digest(key, data, digest=hashfunc),
|
||||
binascii.unhexlify(digest)
|
||||
)
|
||||
|
||||
if c_hmac_new is not None:
|
||||
h = c_hmac_new(key, data, digestmod=hashname)
|
||||
self.assertEqual(h.hexdigest().upper(), digest.upper())
|
||||
self.assertEqual(h.digest(), binascii.unhexlify(digest))
|
||||
self.assertEqual(h.name, f"hmac-{hashname}")
|
||||
self.assertEqual(h.digest_size, digest_size)
|
||||
self.assertEqual(h.block_size, block_size)
|
||||
|
||||
h = c_hmac_new(key, digestmod=hashname)
|
||||
h2 = h.copy()
|
||||
h2.update(b"test update")
|
||||
h.update(data)
|
||||
self.assertEqual(h.hexdigest().upper(), digest.upper())
|
||||
|
||||
@hashlib_helper.requires_hashdigest('md5', openssl=True)
|
||||
def test_md5_vectors(self):
|
||||
# Test the HMAC module against test vectors from the RFC.
|
||||
|
||||
def md5test(key, data, digest):
|
||||
h = hmac.HMAC(key, data, digestmod=hashlib.md5)
|
||||
self.assertEqual(h.hexdigest().upper(), digest.upper())
|
||||
self.assertEqual(h.digest(), binascii.unhexlify(digest))
|
||||
self.assertEqual(h.name, "hmac-md5")
|
||||
self.assertEqual(h.digest_size, 16)
|
||||
self.assertEqual(h.block_size, 64)
|
||||
|
||||
h = hmac.HMAC(key, data, digestmod='md5')
|
||||
self.assertEqual(h.hexdigest().upper(), digest.upper())
|
||||
self.assertEqual(h.digest(), binascii.unhexlify(digest))
|
||||
self.assertEqual(h.name, "hmac-md5")
|
||||
self.assertEqual(h.digest_size, 16)
|
||||
self.assertEqual(h.block_size, 64)
|
||||
|
||||
self.assertEqual(
|
||||
hmac.digest(key, data, digest='md5'),
|
||||
binascii.unhexlify(digest)
|
||||
self.asssert_hmac(
|
||||
key, data, digest,
|
||||
hashfunc=hashlib.md5,
|
||||
hashname="md5",
|
||||
digest_size=16,
|
||||
block_size=64
|
||||
)
|
||||
with unittest.mock.patch('hmac._openssl_md_meths', {}):
|
||||
self.assertEqual(
|
||||
hmac.digest(key, data, digest='md5'),
|
||||
binascii.unhexlify(digest)
|
||||
)
|
||||
|
||||
md5test(b"\x0b" * 16,
|
||||
b"Hi There",
|
||||
|
@ -82,26 +146,14 @@ class TestVectorsTestCase(unittest.TestCase):
|
|||
@hashlib_helper.requires_hashdigest('sha1', openssl=True)
|
||||
def test_sha_vectors(self):
|
||||
def shatest(key, data, digest):
|
||||
h = hmac.HMAC(key, data, digestmod=hashlib.sha1)
|
||||
self.assertEqual(h.hexdigest().upper(), digest.upper())
|
||||
self.assertEqual(h.digest(), binascii.unhexlify(digest))
|
||||
self.assertEqual(h.name, "hmac-sha1")
|
||||
self.assertEqual(h.digest_size, 20)
|
||||
self.assertEqual(h.block_size, 64)
|
||||
|
||||
h = hmac.HMAC(key, data, digestmod='sha1')
|
||||
self.assertEqual(h.hexdigest().upper(), digest.upper())
|
||||
self.assertEqual(h.digest(), binascii.unhexlify(digest))
|
||||
self.assertEqual(h.name, "hmac-sha1")
|
||||
self.assertEqual(h.digest_size, 20)
|
||||
self.assertEqual(h.block_size, 64)
|
||||
|
||||
self.assertEqual(
|
||||
hmac.digest(key, data, digest='sha1'),
|
||||
binascii.unhexlify(digest)
|
||||
self.asssert_hmac(
|
||||
key, data, digest,
|
||||
hashfunc=hashlib.sha1,
|
||||
hashname="sha1",
|
||||
digest_size=20,
|
||||
block_size=64
|
||||
)
|
||||
|
||||
|
||||
shatest(b"\x0b" * 20,
|
||||
b"Hi There",
|
||||
"b617318655057264e28bc0b6fb378c8ef146be00")
|
||||
|
@ -133,37 +185,15 @@ class TestVectorsTestCase(unittest.TestCase):
|
|||
|
||||
def _rfc4231_test_cases(self, hashfunc, hash_name, digest_size, block_size):
|
||||
def hmactest(key, data, hexdigests):
|
||||
hmac_name = "hmac-" + hash_name
|
||||
h = hmac.HMAC(key, data, digestmod=hashfunc)
|
||||
self.assertEqual(h.hexdigest().lower(), hexdigests[hashfunc])
|
||||
self.assertEqual(h.name, hmac_name)
|
||||
self.assertEqual(h.digest_size, digest_size)
|
||||
self.assertEqual(h.block_size, block_size)
|
||||
digest = hexdigests[hashfunc]
|
||||
|
||||
h = hmac.HMAC(key, data, digestmod=hash_name)
|
||||
self.assertEqual(h.hexdigest().lower(), hexdigests[hashfunc])
|
||||
self.assertEqual(h.name, hmac_name)
|
||||
self.assertEqual(h.digest_size, digest_size)
|
||||
self.assertEqual(h.block_size, block_size)
|
||||
|
||||
self.assertEqual(
|
||||
hmac.digest(key, data, digest=hashfunc),
|
||||
binascii.unhexlify(hexdigests[hashfunc])
|
||||
self.asssert_hmac(
|
||||
key, data, digest,
|
||||
hashfunc=hashfunc,
|
||||
hashname=hash_name,
|
||||
digest_size=digest_size,
|
||||
block_size=block_size
|
||||
)
|
||||
self.assertEqual(
|
||||
hmac.digest(key, data, digest=hash_name),
|
||||
binascii.unhexlify(hexdigests[hashfunc])
|
||||
)
|
||||
|
||||
with unittest.mock.patch('hmac._openssl_md_meths', {}):
|
||||
self.assertEqual(
|
||||
hmac.digest(key, data, digest=hashfunc),
|
||||
binascii.unhexlify(hexdigests[hashfunc])
|
||||
)
|
||||
self.assertEqual(
|
||||
hmac.digest(key, data, digest=hash_name),
|
||||
binascii.unhexlify(hexdigests[hashfunc])
|
||||
)
|
||||
|
||||
# 4.2. Test Case 1
|
||||
hmactest(key = b'\x0b'*20,
|
||||
|
@ -385,6 +415,14 @@ class ConstructorTestCase(unittest.TestCase):
|
|||
except Exception:
|
||||
self.fail("Constructor call with hashlib.sha256 raised exception.")
|
||||
|
||||
@unittest.skipUnless(C_HMAC is not None, 'need _hashlib')
|
||||
def test_internal_types(self):
|
||||
# internal types like _hashlib.C_HMAC are not constructable
|
||||
with self.assertRaisesRegex(
|
||||
TypeError, "cannot create 'HMAC' instance"
|
||||
):
|
||||
C_HMAC()
|
||||
|
||||
|
||||
class SanityTestCase(unittest.TestCase):
|
||||
|
||||
|
@ -395,9 +433,9 @@ class SanityTestCase(unittest.TestCase):
|
|||
try:
|
||||
h = hmac.HMAC(b"my secret key", digestmod="sha256")
|
||||
h.update(b"compute the hash of this text!")
|
||||
dig = h.digest()
|
||||
dig = h.hexdigest()
|
||||
h2 = h.copy()
|
||||
h.digest()
|
||||
h.hexdigest()
|
||||
h.copy()
|
||||
except Exception:
|
||||
self.fail("Exception raised during normal usage of HMAC class.")
|
||||
|
||||
|
@ -450,6 +488,21 @@ class CopyTestCase(unittest.TestCase):
|
|||
self.assertEqual(h1.hexdigest(), h2.hexdigest(),
|
||||
"Hexdigest of copy doesn't match original hexdigest.")
|
||||
|
||||
@hashlib_helper.requires_hashdigest('sha256')
|
||||
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()
|
||||
self.assertTrue(
|
||||
id(h1) != id(h2), "No real copy of the HMAC instance."
|
||||
)
|
||||
self.assertEqual(h1.digest(), h2.digest(),
|
||||
"Digest of copy doesn't match original digest.")
|
||||
self.assertEqual(h1.hexdigest(), h2.hexdigest(),
|
||||
"Hexdigest of copy doesn't match original hexdigest.")
|
||||
|
||||
|
||||
class CompareDigestTestCase(unittest.TestCase):
|
||||
|
||||
def test_compare_digest(self):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue