Skip to content

Commit 7e01d44

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

File tree

3 files changed

+72
-9
lines changed

3 files changed

+72
-9
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 & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,22 @@
2121
import hmac
2222
import hashlib
2323
import random
24-
import test.support.hashlib_helper as hashlib_helper
2524
import types
2625
import unittest
2726
import unittest.mock as mock
2827
import warnings
2928
from _operator import _compare_digest as operator_compare_digest
29+
from test.support import _4G, bigmemtest
3030
from test.support import check_disallow_instantiation
31+
from test.support import hashlib_helper, import_helper
3132
from test.support.hashlib_helper import (
3233
BuiltinHashFunctionsTrait,
3334
HashFunctionsTrait,
3435
NamedHashFunctionsTrait,
3536
OpenSSLHashFunctionsTrait,
3637
)
37-
from test.support.import_helper import import_fresh_module, import_module
38+
from test.support.import_helper import import_fresh_module
39+
from unittest.mock import patch
3840

3941
try:
4042
import _hashlib
@@ -727,7 +729,7 @@ def setUpClass(cls):
727729
super().setUpClass()
728730
for meth in ['_init_openssl_hmac', '_init_builtin_hmac']:
729731
fn = getattr(cls.hmac.HMAC, meth)
730-
cm = mock.patch.object(cls.hmac.HMAC, meth, autospec=True, wraps=fn)
732+
cm = patch.object(cls.hmac.HMAC, meth, autospec=True, wraps=fn)
731733
cls.enterClassContext(cm)
732734

733735
@classmethod
@@ -949,7 +951,11 @@ class PyConstructorTestCase(ThroughObjectMixin, PyConstructorBaseMixin,
949951

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

954960
def test_hmac_digest_digestmod_parameter(self):
955961
func = self.hmac_digest
@@ -1445,9 +1451,8 @@ def test_hmac_constructor_uses_builtin(self):
14451451
hmac = import_fresh_module("hmac", blocked=["_hashlib"])
14461452

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

14521457
with (
14531458
watch_method(hmac.HMAC, '_init_openssl_hmac') as f,
@@ -1499,6 +1504,52 @@ def test_with_fallback(self):
14991504
finally:
15001505
cache.pop('foo')
15011506

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

15031554
class BuiltinMiscellaneousTests(BuiltinModuleMixin, unittest.TestCase):
15041555
"""HMAC-BLAKE2 is not standardized as BLAKE2 is a keyed hash function.
@@ -1511,7 +1562,7 @@ class BuiltinMiscellaneousTests(BuiltinModuleMixin, unittest.TestCase):
15111562
@classmethod
15121563
def setUpClass(cls):
15131564
super().setUpClass()
1514-
cls.blake2 = import_module("_blake2")
1565+
cls.blake2 = import_helper.import_module("_blake2")
15151566
cls.blake2b = cls.blake2.blake2b
15161567
cls.blake2s = cls.blake2.blake2s
15171568

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)