Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 125 additions & 72 deletions Lib/test/test_hmac.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@

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)
Expand All @@ -21,34 +28,91 @@ def wrapper(*args, **kwargs):

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",
Expand Down Expand Up @@ -82,26 +146,14 @@ def md5test(key, data, digest):
@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")
Expand Down Expand Up @@ -133,37 +185,15 @@ def shatest(key, data, digest):

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,
Expand Down Expand Up @@ -385,6 +415,14 @@ def test_withmodule(self):
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):

Expand All @@ -395,9 +433,9 @@ def test_exercise_all_methods(self):
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.")

Expand Down Expand Up @@ -450,6 +488,21 @@ def test_equality(self):
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):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
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.
Loading