Skip to content

Commit 1a80008

Browse files
[3.14] gh-155428: align hmac.digest and hmac.HMAC.digest outputs when using small block sizes (GH-155429) (#160061)
gh-155428: align `hmac.digest` and `hmac.HMAC.digest` outputs when using small block sizes (GH-155429) (cherry picked from commit 815dc47) Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
1 parent 46bd838 commit 1a80008

3 files changed

Lines changed: 45 additions & 16 deletions

File tree

‎Lib/hmac.py‎

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,27 @@ def digest_wrapper(d=b''):
3939
return digest_wrapper
4040

4141

42+
def _select_blocksize(obj, default, stacklevel=3):
43+
if hasattr(obj, 'block_size'):
44+
blocksize = obj.block_size
45+
if blocksize < 16:
46+
import warnings
47+
48+
warnings.warn(f"block_size of {blocksize} seems too small; "
49+
f"using our default of {default}.",
50+
RuntimeWarning, stacklevel=stacklevel)
51+
blocksize = default
52+
else:
53+
import warnings
54+
55+
warnings.warn("No block_size attribute on given digest object; "
56+
f"Assuming {default}.",
57+
RuntimeWarning, stacklevel=stacklevel)
58+
blocksize = default
59+
return blocksize
60+
61+
62+
4263
class HMAC:
4364
"""RFC 2104 HMAC class. Also complies with RFC 4231.
4465
@@ -106,28 +127,14 @@ def _init_builtin_hmac(self, key, msg, digestmod):
106127
self.block_size = self._hmac.block_size
107128

108129
def _init_old(self, key, msg, digestmod):
109-
import warnings
110-
111130
digest_cons = _get_digest_constructor(digestmod)
112131

113132
self._hmac = None
114133
self._outer = digest_cons()
115134
self._inner = digest_cons()
116135
self.digest_size = self._inner.digest_size
117136

118-
if hasattr(self._inner, 'block_size'):
119-
blocksize = self._inner.block_size
120-
if blocksize < 16:
121-
warnings.warn(f"block_size of {blocksize} seems too small; "
122-
f"using our default of {self.blocksize}.",
123-
RuntimeWarning, 2)
124-
blocksize = self.blocksize # pragma: no cover
125-
else:
126-
warnings.warn("No block_size attribute on given digest object; "
127-
f"Assuming {self.blocksize}.",
128-
RuntimeWarning, 2)
129-
blocksize = self.blocksize # pragma: no cover
130-
137+
blocksize = _select_blocksize(self._inner, self.blocksize)
131138
if len(key) > blocksize:
132139
key = digest_cons(key).digest()
133140

@@ -261,7 +268,7 @@ def _compute_digest_fallback(key, msg, digest):
261268
digest_cons = _get_digest_constructor(digest)
262269
inner = digest_cons()
263270
outer = digest_cons()
264-
blocksize = getattr(inner, 'block_size', 64)
271+
blocksize = _select_blocksize(inner, HMAC.blocksize)
265272
if len(key) > blocksize:
266273
key = digest_cons(key).digest()
267274
key = key.ljust(blocksize, b'\0')

‎Lib/test/test_hmac.py‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1494,17 +1494,36 @@ def update(self, v):
14941494
self._x.update(v)
14951495
def digest(self):
14961496
return self._x.digest()
1497+
def copy(self):
1498+
h = MockCrazyHash()
1499+
h._x = self._x.copy()
1500+
return h
14971501

14981502
with warnings.catch_warnings():
14991503
warnings.simplefilter('error', RuntimeWarning)
15001504
with self.assertRaises(RuntimeWarning):
15011505
hmac.HMAC(b'a', b'b', digestmod=MockCrazyHash)
15021506
self.fail('Expected warning about missing block_size')
1507+
with self.assertRaises(RuntimeWarning):
1508+
hmac.digest(b'a', b'b', MockCrazyHash)
1509+
self.fail('Expected warning about missing block_size')
15031510

15041511
MockCrazyHash.block_size = 1
15051512
with self.assertRaises(RuntimeWarning):
15061513
hmac.HMAC(b'a', b'b', digestmod=MockCrazyHash)
15071514
self.fail('Expected warning about small block_size')
1515+
with self.assertRaises(RuntimeWarning):
1516+
hmac.digest(b'a', b'b', MockCrazyHash)
1517+
self.fail('Expected warning about small block_size')
1518+
1519+
# gh-155428: hmac.HMAC() and hmac.digest() must both
1520+
# fall back to the default block size (64 for SHA-256).
1521+
expect = hmac.digest(b'a', b'b', 'sha256')
1522+
with warnings.catch_warnings():
1523+
warnings.simplefilter('ignore', RuntimeWarning)
1524+
h = hmac.HMAC(b'a', b'b', digestmod=MockCrazyHash)
1525+
self.assertEqual(h.digest(), expect)
1526+
self.assertEqual(hmac.digest(b'a', b'b', MockCrazyHash), expect)
15081527

15091528
@hashlib_helper.requires_hashdigest('sha256')
15101529
def test_with_fallback(self):
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Ensure that :func:`hmac.digest` and :meth:`hmac.HMAC.digest` output
2+
identical values when using a digest's block size smaller than 16. Patch by
3+
Bénédikt Tran.

0 commit comments

Comments
 (0)