https://github.com/python/cpython/commit/815dc4726c5cddd0b8b8d3737ce00fbd6d480316
commit: 815dc4726c5cddd0b8b8d3737ce00fbd6d480316
branch: main
author: Bénédikt Tran <[email protected]>
committer: picnixz <[email protected]>
date: 2026-10-21T10:06:49-07:00
summary:
gh-155428: align `hmac.digest` and `hmac.HMAC.digest` outputs when using small
block sizes (#155429)
files:
A Misc/NEWS.d/next/Library/2026-08-09-14-26-48.gh-issue-155428.wi0_fI.rst
M Lib/hmac.py
M Lib/test/test_hmac.py
diff --git a/Lib/hmac.py b/Lib/hmac.py
index 61e0823e68be21..ae23d65d2f604a 100644
--- a/Lib/hmac.py
+++ b/Lib/hmac.py
@@ -49,6 +49,27 @@ def digest_wrapper(d=b''):
return digest_wrapper
+def _select_blocksize(obj, default, stacklevel=3):
+ if hasattr(obj, 'block_size'):
+ blocksize = obj.block_size
+ if blocksize < 16:
+ import warnings
+
+ warnings.warn(f"block_size of {blocksize} seems too small; "
+ f"using our default of {default}.",
+ RuntimeWarning, stacklevel=stacklevel)
+ blocksize = default
+ else:
+ import warnings
+
+ warnings.warn("No block_size attribute on given digest object; "
+ f"Assuming {default}.",
+ RuntimeWarning, stacklevel=stacklevel)
+ blocksize = default
+ return blocksize
+
+
+
class HMAC:
"""RFC 2104 HMAC class. Also complies with RFC 4231.
@@ -116,8 +137,6 @@ def _init_builtin_hmac(self, key, msg, digestmod):
self.block_size = self._hmac.block_size
def _init_old(self, key, msg, digestmod):
- import warnings
-
digest_cons = _get_digest_constructor(digestmod)
if _is_shake_constructor(digest_cons):
raise ValueError(f"unsupported hash algorithm {digestmod}")
@@ -127,19 +146,7 @@ def _init_old(self, key, msg, digestmod):
self._inner = digest_cons()
self.digest_size = self._inner.digest_size
- if hasattr(self._inner, 'block_size'):
- blocksize = self._inner.block_size
- if blocksize < 16:
- warnings.warn(f"block_size of {blocksize} seems too small; "
- f"using our default of {self.blocksize}.",
- RuntimeWarning, 2)
- blocksize = self.blocksize # pragma: no cover
- else:
- warnings.warn("No block_size attribute on given digest object; "
- f"Assuming {self.blocksize}.",
- RuntimeWarning, 2)
- blocksize = self.blocksize # pragma: no cover
-
+ blocksize = _select_blocksize(self._inner, self.blocksize)
if len(key) > blocksize:
key = digest_cons(key).digest()
@@ -275,7 +282,7 @@ def _compute_digest_fallback(key, msg, digest):
raise ValueError(f"unsupported hash algorithm {digest}")
inner = digest_cons()
outer = digest_cons()
- blocksize = getattr(inner, 'block_size', 64)
+ blocksize = _select_blocksize(inner, HMAC.blocksize)
if len(key) > blocksize:
key = digest_cons(key).digest()
key = key.ljust(blocksize, b'\0')
diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py
index e44113edf3f422..722a274e4a48e1 100644
--- a/Lib/test/test_hmac.py
+++ b/Lib/test/test_hmac.py
@@ -1501,17 +1501,36 @@ def update(self, v):
self._x.update(v)
def digest(self):
return self._x.digest()
+ def copy(self):
+ h = MockCrazyHash()
+ h._x = self._x.copy()
+ return h
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')
+ with self.assertRaises(RuntimeWarning):
+ hmac.digest(b'a', b'b', 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')
+ with self.assertRaises(RuntimeWarning):
+ hmac.digest(b'a', b'b', MockCrazyHash)
+ self.fail('Expected warning about small block_size')
+
+ # gh-155428: hmac.HMAC() and hmac.digest() must both
+ # fall back to the default block size (64 for SHA-256).
+ expect = hmac.digest(b'a', b'b', 'sha256')
+ with warnings.catch_warnings():
+ warnings.simplefilter('ignore', RuntimeWarning)
+ h = hmac.HMAC(b'a', b'b', digestmod=MockCrazyHash)
+ self.assertEqual(h.digest(), expect)
+ self.assertEqual(hmac.digest(b'a', b'b', MockCrazyHash), expect)
@hashlib_helper.requires_hashdigest('sha256')
def test_with_fallback(self):
diff --git
a/Misc/NEWS.d/next/Library/2026-08-09-14-26-48.gh-issue-155428.wi0_fI.rst
b/Misc/NEWS.d/next/Library/2026-08-09-14-26-48.gh-issue-155428.wi0_fI.rst
new file mode 100644
index 00000000000000..f0c6201cbe592e
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-08-09-14-26-48.gh-issue-155428.wi0_fI.rst
@@ -0,0 +1,3 @@
+Ensure that :func:`hmac.digest` and :meth:`hmac.HMAC.digest` output
+identical values when using a digest's block size smaller than 16. Patch by
+Bénédikt Tran.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]