Skip to content

Commit fe28b30

Browse files
committed
gh-155426: correctly accept bytes-like keys in hmac.digest
This only affects keys of length smaller than the HMAC blocksize since larger keys are hashed first.
1 parent 998b890 commit fe28b30

3 files changed

Lines changed: 27 additions & 6 deletions

File tree

Lib/hmac.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,8 @@ def _compute_digest_fallback(key, msg, digest):
275275
blocksize = getattr(inner, 'block_size', 64)
276276
if len(key) > blocksize:
277277
key = digest_cons(key).digest()
278+
if not (hasattr(key, "ljust") and hasattr(key, "translate")):
279+
key = bytes(key)
278280
key = key.ljust(blocksize, b'\0')
279281
inner.update(key.translate(trans_36))
280282
outer.update(key.translate(trans_5C))

Lib/test/test_hmac.py

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1459,6 +1459,11 @@ class OperatorCompareDigestTestCase(CompareDigestMixin, unittest.TestCase):
14591459
class PyMiscellaneousTests(unittest.TestCase):
14601460
"""Miscellaneous tests for the pure Python HMAC module."""
14611461

1462+
@staticmethod
1463+
def mock__compute_digest_fallback(hmac):
1464+
fn = getattr(hmac, meth := "_compute_digest_fallback")
1465+
return patch.object(hmac, meth, autospec=True, wraps=fn)
1466+
14621467
@hashlib_helper.requires_builtin_hmac()
14631468
def test_hmac_constructor_uses_builtin(self):
14641469
# Block the OpenSSL implementation and check that
@@ -1519,6 +1524,15 @@ def test_with_fallback(self):
15191524
finally:
15201525
cache.pop('foo')
15211526

1527+
@hashlib_helper.requires_hashdigest("md5")
1528+
def test_hmac_digest_accept_memoryview_for_key(self):
1529+
hmac = import_fresh_module("hmac", blocked=["_hashlib", "_hmac"])
1530+
with self.mock__compute_digest_fallback(hmac) as slow:
1531+
# The bytes(...) path is only meant for small keys. Large
1532+
# keys are already coerced to bytes since they are hased.
1533+
hmac.digest(memoryview(b"small"), b"world", "md5")
1534+
slow.assert_called_once()
1535+
15221536
@hashlib_helper.requires_openssl_hashdigest("md5")
15231537
@bigmemtest(size=_4G + 5, memuse=2, dry_run=False)
15241538
def test_hmac_digest_overflow_error_openssl_only(self, size):
@@ -1542,11 +1556,11 @@ def do_test_hmac_digest_overflow_error_switch_to_slow(self, hmac, size):
15421556
bigkey = b'K' * size
15431557
bigmsg = b'M' * size
15441558

1545-
with patch.object(hmac, "_compute_digest_fallback") as slow:
1559+
with self.mock__compute_digest_fallback(hmac) as slow:
15461560
hmac.digest(bigkey, b'm', "md5")
15471561
slow.assert_called_once()
15481562

1549-
with patch.object(hmac, "_compute_digest_fallback") as slow:
1563+
with self.mock__compute_digest_fallback(hmac) as slow:
15501564
hmac.digest(b'k', bigmsg, "md5")
15511565
slow.assert_called_once()
15521566

@@ -1556,10 +1570,12 @@ def test_hmac_digest_no_overflow_error_in_fallback(self, size):
15561570
hmac = import_fresh_module("hmac", blocked=["_hashlib", "_hmac"])
15571571

15581572
for key, msg in [(b'K' * size, b'm'), (b'k', b'M' * size)]:
1559-
with self.subTest(keysize=len(key), msgsize=len(msg)):
1560-
with patch.object(hmac, "_compute_digest_fallback") as slow:
1561-
hmac.digest(key, msg, "md5")
1562-
slow.assert_called_once()
1573+
with (
1574+
self.subTest(keysize=len(key), msgsize=len(msg)),
1575+
self.mock__compute_digest_fallback(hmac) as slow,
1576+
):
1577+
hmac.digest(key, msg, "md5")
1578+
slow.assert_called_once()
15631579

15641580

15651581
class BuiltinMiscellaneousTests(BuiltinModuleMixin, unittest.TestCase):
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Make sure that the pure Python fallback of :func:`hmac.digest` accept
2+
bytes-like keys and not just :class:`bytes` instances. Patch by Bénédikt
3+
Tran.

0 commit comments

Comments
 (0)