summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorChristian Heimes <christian@python.org>2020-05-17 11:49:10 (GMT)
committerGitHub <noreply@github.com>2020-05-17 11:49:10 (GMT)
commit54f2898fe7e4ca1f239e96284af3cc5b34d2ae02 (patch)
tree39080535b3c75be9a15cf5d9664381e24fe9de6c /Lib
parent46545000c2a30b46aed717b546bc09e5bae7148f (diff)
downloadcpython-54f2898fe7e4ca1f239e96284af3cc5b34d2ae02.zip
cpython-54f2898fe7e4ca1f239e96284af3cc5b34d2ae02.tar.gz
cpython-54f2898fe7e4ca1f239e96284af3cc5b34d2ae02.tar.bz2
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>
Diffstat (limited to 'Lib')
-rw-r--r--Lib/test/test_hmac.py197
1 files changed, 125 insertions, 72 deletions
diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py
index 1f3ec4c..7a52e39 100644
--- a/Lib/test/test_hmac.py
+++ b/Lib/test/test_hmac.py
@@ -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):
- @hashlib_helper.requires_hashdigest('md5', openssl=True)
- def test_md5_vectors(self):
- # Test the HMAC module against test vectors from the RFC.
+ 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)
+ )
- def md5test(key, data, digest):
- h = hmac.HMAC(key, data, digestmod=hashlib.md5)
+ 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, "hmac-md5")
- self.assertEqual(h.digest_size, 16)
- self.assertEqual(h.block_size, 64)
+ 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='md5')
+ 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())
- 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)
+ @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):
+ 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)
-
- 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])
+ digest = 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):