Skip to content

Commit 1082bd5

Browse files
committed
fix how large messages and keys are rejected by HMAC
1 parent 1e67293 commit 1082bd5

File tree

3 files changed

+72
-10
lines changed

3 files changed

+72
-10
lines changed

Lib/hmac.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,13 +241,23 @@ def digest(key, msg, digest):
241241
if _hashopenssl and isinstance(digest, (str, _functype)):
242242
try:
243243
return _hashopenssl.hmac_digest(key, msg, digest)
244+
except OverflowError:
245+
try:
246+
return _hashopenssl.hmac_new(key, msg, digest).digest()
247+
except _hashopenssl.UnsupportedDigestmodError:
248+
pass
244249
except _hashopenssl.UnsupportedDigestmodError:
245250
pass
246251

247252
if _hmac and isinstance(digest, str):
248253
try:
249254
return _hmac.compute_digest(key, msg, digest)
250-
except (OverflowError, _hmac.UnknownHashError):
255+
except OverflowError:
256+
try:
257+
return _hmac.new(key, msg, digest).digest()
258+
except _hmac.UnknownHashError:
259+
pass
260+
except _hmac.UnknownHashError:
251261
pass
252262

253263
return _compute_digest_fallback(key, msg, digest)

Lib/test/test_hmac.py

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,21 @@
2121
import hmac
2222
import hashlib
2323
import random
24-
import test.support.hashlib_helper as hashlib_helper
2524
import types
2625
import unittest
27-
import unittest.mock as mock
2826
import warnings
2927
from _operator import _compare_digest as operator_compare_digest
28+
from test.support import _4G, bigmemtest
3029
from test.support import check_disallow_instantiation
30+
from test.support import hashlib_helper, import_helper
3131
from test.support.hashlib_helper import (
3232
BuiltinHashFunctionsTrait,
3333
HashFunctionsTrait,
3434
NamedHashFunctionsTrait,
3535
OpenSSLHashFunctionsTrait,
3636
)
37-
from test.support.import_helper import import_fresh_module, import_module
37+
from test.support.import_helper import import_fresh_module
38+
from unittest.mock import patch
3839

3940
try:
4041
import _hashlib
@@ -727,7 +728,7 @@ def setUpClass(cls):
727728
super().setUpClass()
728729
for meth in ['_init_openssl_hmac', '_init_builtin_hmac']:
729730
fn = getattr(cls.hmac.HMAC, meth)
730-
cm = mock.patch.object(cls.hmac.HMAC, meth, autospec=True, wraps=fn)
731+
cm = patch.object(cls.hmac.HMAC, meth, autospec=True, wraps=fn)
731732
cls.enterClassContext(cm)
732733

733734
@classmethod
@@ -949,7 +950,11 @@ class PyConstructorTestCase(ThroughObjectMixin, PyConstructorBaseMixin,
949950

950951
class PyModuleConstructorTestCase(ThroughModuleAPIMixin, PyConstructorBaseMixin,
951952
unittest.TestCase):
952-
"""Test the hmac.new() and hmac.digest() functions."""
953+
"""Test the hmac.new() and hmac.digest() functions.
954+
955+
Note that "self.hmac" is imported by blocking "_hashlib" and "_hmac".
956+
For testing functions in "hmac", extend PyMiscellaneousTests instead.
957+
"""
953958

954959
def test_hmac_digest_digestmod_parameter(self):
955960
func = self.hmac_digest
@@ -1445,9 +1450,8 @@ def test_hmac_constructor_uses_builtin(self):
14451450
hmac = import_fresh_module("hmac", blocked=["_hashlib"])
14461451

14471452
def watch_method(cls, name):
1448-
return mock.patch.object(
1449-
cls, name, autospec=True, wraps=getattr(cls, name)
1450-
)
1453+
wraps = getattr(cls, name)
1454+
return patch.object(cls, name, autospec=True, wraps=wraps)
14511455

14521456
with (
14531457
watch_method(hmac.HMAC, '_init_openssl_hmac') as f,
@@ -1499,6 +1503,52 @@ def test_with_fallback(self):
14991503
finally:
15001504
cache.pop('foo')
15011505

1506+
@hashlib_helper.requires_openssl_hashdigest("md5")
1507+
@bigmemtest(size=_4G, memuse=2, dry_run=False)
1508+
def test_hmac_digest_overflow_error_openssl_only(self, size):
1509+
self.do_test_hmac_digest_overflow_error_fast(size, openssl=True)
1510+
1511+
@hashlib_helper.requires_builtin_hashdigest("_md5", "md5")
1512+
@bigmemtest(size=_4G , memuse=2, dry_run=False)
1513+
def test_hmac_digest_overflow_error_builtin_only(self, size):
1514+
self.do_test_hmac_digest_overflow_error_fast(size, openssl=False)
1515+
1516+
def do_test_hmac_digest_overflow_error_fast(self, size, *, openssl):
1517+
"""Check that C hmac.digest() works for large inputs."""
1518+
1519+
if openssl:
1520+
hmac = import_fresh_module("hmac", blocked=["_hashlib"])
1521+
c_module_name, c_method_name = "_hmac", "new"
1522+
else:
1523+
hmac = import_fresh_module("hmac", blocked=["_hmac"])
1524+
c_module_name, c_method_name = "_hashlib", "hmac_new"
1525+
1526+
cext = import_helper.import_module(c_module_name)
1527+
cnew = getattr(cext, c_method_name)
1528+
1529+
bigkey = b'K' * size
1530+
bigmsg = b'M' * size
1531+
1532+
with patch.object(hmac, "_compute_digest_fallback") as slow:
1533+
with patch.object(cext, c_method_name, wraps=cnew) as new:
1534+
self.assertIsInstance(hmac.digest(bigkey, b'm', "md5"), bytes)
1535+
new.assert_called_once()
1536+
with patch.object(cext, c_method_name, wraps=cnew) as new:
1537+
self.assertIsInstance(hmac.digest(b'k', bigmsg, "md5"), bytes)
1538+
new.assert_called_once()
1539+
slow.assert_not_called()
1540+
1541+
@hashlib_helper.requires_hashdigest("md5", openssl=True)
1542+
@bigmemtest(size=_4G, memuse=2, dry_run=False)
1543+
def test_hmac_digest_no_overflow_error_in_fallback(self, size):
1544+
hmac = import_fresh_module("hmac", blocked=["_hashlib", "_hmac"])
1545+
1546+
for key, msg in [(b'K' * size, b'm'), (b'k', b'M' * size)]:
1547+
with self.subTest(keysize=len(key), msgsize=len(msg)):
1548+
with patch.object(hmac, "_compute_digest_fallback") as slow:
1549+
self.assertIsInstance(hmac.digest(key, msg, "md5"), bytes)
1550+
slow.assert_called_once()
1551+
15021552

15031553
class BuiltinMiscellaneousTests(BuiltinModuleMixin, unittest.TestCase):
15041554
"""HMAC-BLAKE2 is not standardized as BLAKE2 is a keyed hash function.
@@ -1511,7 +1561,7 @@ class BuiltinMiscellaneousTests(BuiltinModuleMixin, unittest.TestCase):
15111561
@classmethod
15121562
def setUpClass(cls):
15131563
super().setUpClass()
1514-
cls.blake2 = import_module("_blake2")
1564+
cls.blake2 = import_helper.import_module("_blake2")
15151565
cls.blake2b = cls.blake2.blake2b
15161566
cls.blake2s = cls.blake2.blake2s
15171567

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
One-shot :func:`hmac.digest` now properly handles large keys and messages
2+
by delegating to :func:`hmac.new` when possible. Patch by Bénédikt Tran.

0 commit comments

Comments
 (0)