Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit933dfd7

Browse files
authored
bpo-40645: use C implementation of HMAC (GH-24920)
- [x] fix tests- [ ] add test scenarios for old/new code.Signed-off-by: Christian Heimes <christian@python.org>
1 parent5d6e8c1 commit933dfd7

File tree

6 files changed

+267
-124
lines changed

6 files changed

+267
-124
lines changed

‎Lib/hashlib.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ def __hash_new(name, data=b'', **kwargs):
173173
algorithms_available=algorithms_available.union(
174174
_hashlib.openssl_md_meth_names)
175175
exceptImportError:
176+
_hashlib=None
176177
new=__py_new
177178
__get_hash=__get_builtin_constructor
178179

‎Lib/hmac.py‎

Lines changed: 51 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@
88
import_hashlibas_hashopenssl
99
exceptImportError:
1010
_hashopenssl=None
11-
_openssl_md_meths=None
11+
_functype=None
1212
from_operatorimport_compare_digestascompare_digest
1313
else:
14-
_openssl_md_meths=frozenset(_hashopenssl.openssl_md_meth_names)
1514
compare_digest=_hashopenssl.compare_digest
15+
_functype=type(_hashopenssl.openssl_sha256)# builtin type
16+
1617
importhashlibas_hashlib
1718

1819
trans_5C=bytes((x^0x5C)forxinrange(256))
@@ -23,7 +24,6 @@
2324
digest_size=None
2425

2526

26-
2727
classHMAC:
2828
"""RFC 2104 HMAC class. Also complies with RFC 4231.
2929
@@ -32,7 +32,7 @@ class HMAC:
3232
blocksize=64# 512-bit HMAC; can be changed in subclasses.
3333

3434
__slots__= (
35-
"_digest_cons","_inner","_outer","block_size","digest_size"
35+
"_hmac","_inner","_outer","block_size","digest_size"
3636
)
3737

3838
def__init__(self,key,msg=None,digestmod=''):
@@ -55,15 +55,30 @@ def __init__(self, key, msg=None, digestmod=''):
5555
ifnotdigestmod:
5656
raiseTypeError("Missing required parameter 'digestmod'.")
5757

58+
if_hashopensslandisinstance(digestmod, (str,_functype)):
59+
try:
60+
self._init_hmac(key,msg,digestmod)
61+
except_hashopenssl.UnsupportedDigestmodError:
62+
self._init_old(key,msg,digestmod)
63+
else:
64+
self._init_old(key,msg,digestmod)
65+
66+
def_init_hmac(self,key,msg,digestmod):
67+
self._hmac=_hashopenssl.hmac_new(key,msg,digestmod=digestmod)
68+
self.digest_size=self._hmac.digest_size
69+
self.block_size=self._hmac.block_size
70+
71+
def_init_old(self,key,msg,digestmod):
5872
ifcallable(digestmod):
59-
self._digest_cons=digestmod
73+
digest_cons=digestmod
6074
elifisinstance(digestmod,str):
61-
self._digest_cons=lambdad=b'':_hashlib.new(digestmod,d)
75+
digest_cons=lambdad=b'':_hashlib.new(digestmod,d)
6276
else:
63-
self._digest_cons=lambdad=b'':digestmod.new(d)
77+
digest_cons=lambdad=b'':digestmod.new(d)
6478

65-
self._outer=self._digest_cons()
66-
self._inner=self._digest_cons()
79+
self._hmac=None
80+
self._outer=digest_cons()
81+
self._inner=digest_cons()
6782
self.digest_size=self._inner.digest_size
6883

6984
ifhasattr(self._inner,'block_size'):
@@ -79,13 +94,13 @@ def __init__(self, key, msg=None, digestmod=''):
7994
RuntimeWarning,2)
8095
blocksize=self.blocksize
8196

97+
iflen(key)>blocksize:
98+
key=digest_cons(key).digest()
99+
82100
# self.blocksize is the default blocksize. self.block_size is
83101
# effective block size as well as the public API attribute.
84102
self.block_size=blocksize
85103

86-
iflen(key)>blocksize:
87-
key=self._digest_cons(key).digest()
88-
89104
key=key.ljust(blocksize,b'\0')
90105
self._outer.update(key.translate(trans_5C))
91106
self._inner.update(key.translate(trans_36))
@@ -94,23 +109,15 @@ def __init__(self, key, msg=None, digestmod=''):
94109

95110
@property
96111
defname(self):
97-
return"hmac-"+self._inner.name
98-
99-
@property
100-
defdigest_cons(self):
101-
returnself._digest_cons
102-
103-
@property
104-
definner(self):
105-
returnself._inner
106-
107-
@property
108-
defouter(self):
109-
returnself._outer
112+
ifself._hmac:
113+
returnself._hmac.name
114+
else:
115+
returnf"hmac-{self._inner.name}"
110116

111117
defupdate(self,msg):
112118
"""Feed data from msg into this hashing object."""
113-
self._inner.update(msg)
119+
inst=self._hmacorself._inner
120+
inst.update(msg)
114121

115122
defcopy(self):
116123
"""Return a separate copy of this hashing object.
@@ -119,20 +126,27 @@ def copy(self):
119126
"""
120127
# Call __new__ directly to avoid the expensive __init__.
121128
other=self.__class__.__new__(self.__class__)
122-
other._digest_cons=self._digest_cons
123129
other.digest_size=self.digest_size
124-
other._inner=self._inner.copy()
125-
other._outer=self._outer.copy()
130+
ifself._hmac:
131+
other._hmac=self._hmac.copy()
132+
other._inner=other._outer=None
133+
else:
134+
other._hmac=None
135+
other._inner=self._inner.copy()
136+
other._outer=self._outer.copy()
126137
returnother
127138

128139
def_current(self):
129140
"""Return a hash object for the current state.
130141
131142
To be used only internally with digest() and hexdigest().
132143
"""
133-
h=self._outer.copy()
134-
h.update(self._inner.digest())
135-
returnh
144+
ifself._hmac:
145+
returnself._hmac
146+
else:
147+
h=self._outer.copy()
148+
h.update(self._inner.digest())
149+
returnh
136150

137151
defdigest(self):
138152
"""Return the hash value of this hashing object.
@@ -179,9 +193,11 @@ def digest(key, msg, digest):
179193
A hashlib constructor returning a new hash object. *OR*
180194
A module supporting PEP 247.
181195
"""
182-
if (_hashopensslisnotNoneand
183-
isinstance(digest,str)anddigestin_openssl_md_meths):
184-
return_hashopenssl.hmac_digest(key,msg,digest)
196+
if_hashopensslisnotNoneandisinstance(digest, (str,_functype)):
197+
try:
198+
return_hashopenssl.hmac_digest(key,msg,digest)
199+
except_hashopenssl.UnsupportedDigestmodError:
200+
pass
185201

186202
ifcallable(digest):
187203
digest_cons=digest

‎Lib/test/test_hmac.py‎

Lines changed: 69 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,21 @@
1111
from_operatorimport_compare_digestasoperator_compare_digest
1212

1313
try:
14+
import_hashlibas_hashopenssl
1415
from_hashlibimportHMACasC_HMAC
1516
from_hashlibimporthmac_newasc_hmac_new
1617
from_hashlibimportcompare_digestasopenssl_compare_digest
1718
exceptImportError:
19+
_hashopenssl=None
1820
C_HMAC=None
1921
c_hmac_new=None
2022
openssl_compare_digest=None
2123

24+
try:
25+
import_sha256assha256_module
26+
exceptImportError:
27+
sha256_module=None
28+
2229

2330
defignore_warning(func):
2431
@functools.wraps(func)
@@ -32,22 +39,27 @@ def wrapper(*args, **kwargs):
3239

3340
classTestVectorsTestCase(unittest.TestCase):
3441

35-
defasssert_hmac(
36-
self,key,data,digest,hashfunc,hashname,digest_size,block_size
42+
defassert_hmac_internals(
43+
self,h,digest,hashname,digest_size,block_size
3744
):
38-
h=hmac.HMAC(key,data,digestmod=hashfunc)
3945
self.assertEqual(h.hexdigest().upper(),digest.upper())
4046
self.assertEqual(h.digest(),binascii.unhexlify(digest))
4147
self.assertEqual(h.name,f"hmac-{hashname}")
4248
self.assertEqual(h.digest_size,digest_size)
4349
self.assertEqual(h.block_size,block_size)
4450

51+
defassert_hmac(
52+
self,key,data,digest,hashfunc,hashname,digest_size,block_size
53+
):
54+
h=hmac.HMAC(key,data,digestmod=hashfunc)
55+
self.assert_hmac_internals(
56+
h,digest,hashname,digest_size,block_size
57+
)
58+
4559
h=hmac.HMAC(key,data,digestmod=hashname)
46-
self.assertEqual(h.hexdigest().upper(),digest.upper())
47-
self.assertEqual(h.digest(),binascii.unhexlify(digest))
48-
self.assertEqual(h.name,f"hmac-{hashname}")
49-
self.assertEqual(h.digest_size,digest_size)
50-
self.assertEqual(h.block_size,block_size)
60+
self.assert_hmac_internals(
61+
h,digest,hashname,digest_size,block_size
62+
)
5163

5264
h=hmac.HMAC(key,digestmod=hashname)
5365
h2=h.copy()
@@ -56,11 +68,9 @@ def asssert_hmac(
5668
self.assertEqual(h.hexdigest().upper(),digest.upper())
5769

5870
h=hmac.new(key,data,digestmod=hashname)
59-
self.assertEqual(h.hexdigest().upper(),digest.upper())
60-
self.assertEqual(h.digest(),binascii.unhexlify(digest))
61-
self.assertEqual(h.name,f"hmac-{hashname}")
62-
self.assertEqual(h.digest_size,digest_size)
63-
self.assertEqual(h.block_size,block_size)
71+
self.assert_hmac_internals(
72+
h,digest,hashname,digest_size,block_size
73+
)
6474

6575
h=hmac.new(key,None,digestmod=hashname)
6676
h.update(data)
@@ -81,36 +91,43 @@ def asssert_hmac(
8191
hmac.digest(key,data,digest=hashfunc),
8292
binascii.unhexlify(digest)
8393
)
84-
withunittest.mock.patch('hmac._openssl_md_meths', {}):
85-
self.assertEqual(
86-
hmac.digest(key,data,digest=hashname),
87-
binascii.unhexlify(digest)
88-
)
89-
self.assertEqual(
90-
hmac.digest(key,data,digest=hashfunc),
91-
binascii.unhexlify(digest)
92-
)
94+
95+
h=hmac.HMAC.__new__(hmac.HMAC)
96+
h._init_old(key,data,digestmod=hashname)
97+
self.assert_hmac_internals(
98+
h,digest,hashname,digest_size,block_size
99+
)
93100

94101
ifc_hmac_newisnotNone:
95102
h=c_hmac_new(key,data,digestmod=hashname)
96-
self.assertEqual(h.hexdigest().upper(),digest.upper())
97-
self.assertEqual(h.digest(),binascii.unhexlify(digest))
98-
self.assertEqual(h.name,f"hmac-{hashname}")
99-
self.assertEqual(h.digest_size,digest_size)
100-
self.assertEqual(h.block_size,block_size)
103+
self.assert_hmac_internals(
104+
h,digest,hashname,digest_size,block_size
105+
)
101106

102107
h=c_hmac_new(key,digestmod=hashname)
103108
h2=h.copy()
104109
h2.update(b"test update")
105110
h.update(data)
106111
self.assertEqual(h.hexdigest().upper(),digest.upper())
107112

113+
func=getattr(_hashopenssl,f"openssl_{hashname}")
114+
h=c_hmac_new(key,data,digestmod=func)
115+
self.assert_hmac_internals(
116+
h,digest,hashname,digest_size,block_size
117+
)
118+
119+
h=hmac.HMAC.__new__(hmac.HMAC)
120+
h._init_hmac(key,data,digestmod=hashname)
121+
self.assert_hmac_internals(
122+
h,digest,hashname,digest_size,block_size
123+
)
124+
108125
@hashlib_helper.requires_hashdigest('md5',openssl=True)
109126
deftest_md5_vectors(self):
110127
# Test the HMAC module against test vectors from the RFC.
111128

112129
defmd5test(key,data,digest):
113-
self.asssert_hmac(
130+
self.assert_hmac(
114131
key,data,digest,
115132
hashfunc=hashlib.md5,
116133
hashname="md5",
@@ -150,7 +167,7 @@ def md5test(key, data, digest):
150167
@hashlib_helper.requires_hashdigest('sha1',openssl=True)
151168
deftest_sha_vectors(self):
152169
defshatest(key,data,digest):
153-
self.asssert_hmac(
170+
self.assert_hmac(
154171
key,data,digest,
155172
hashfunc=hashlib.sha1,
156173
hashname="sha1",
@@ -191,7 +208,7 @@ def _rfc4231_test_cases(self, hashfunc, hash_name, digest_size, block_size):
191208
defhmactest(key,data,hexdigests):
192209
digest=hexdigests[hashfunc]
193210

194-
self.asssert_hmac(
211+
self.assert_hmac(
195212
key,data,digest,
196213
hashfunc=hashfunc,
197214
hashname=hash_name,
@@ -427,6 +444,15 @@ def test_internal_types(self):
427444
):
428445
C_HMAC()
429446

447+
@unittest.skipUnless(sha256_moduleisnotNone,'need _sha256')
448+
deftest_with_sha256_module(self):
449+
h=hmac.HMAC(b"key",b"hash this!",digestmod=sha256_module.sha256)
450+
self.assertEqual(h.hexdigest(),self.expected)
451+
self.assertEqual(h.name,"hmac-sha256")
452+
453+
digest=hmac.digest(b"key",b"hash this!",sha256_module.sha256)
454+
self.assertEqual(digest,binascii.unhexlify(self.expected))
455+
430456

431457
classSanityTestCase(unittest.TestCase):
432458

@@ -447,39 +473,37 @@ def test_exercise_all_methods(self):
447473
classCopyTestCase(unittest.TestCase):
448474

449475
@hashlib_helper.requires_hashdigest('sha256')
450-
deftest_attributes(self):
476+
deftest_attributes_old(self):
451477
# Testing if attributes are of same type.
452-
h1=hmac.HMAC(b"key",digestmod="sha256")
478+
h1=hmac.HMAC.__new__(hmac.HMAC)
479+
h1._init_old(b"key",b"msg",digestmod="sha256")
453480
h2=h1.copy()
454-
self.assertTrue(h1._digest_cons==h2._digest_cons,
455-
"digest constructors don't match.")
456481
self.assertEqual(type(h1._inner),type(h2._inner),
457482
"Types of inner don't match.")
458483
self.assertEqual(type(h1._outer),type(h2._outer),
459484
"Types of outer don't match.")
460485

461486
@hashlib_helper.requires_hashdigest('sha256')
462-
deftest_realcopy(self):
487+
deftest_realcopy_old(self):
463488
# Testing if the copy method created a real copy.
464-
h1=hmac.HMAC(b"key",digestmod="sha256")
489+
h1=hmac.HMAC.__new__(hmac.HMAC)
490+
h1._init_old(b"key",b"msg",digestmod="sha256")
465491
h2=h1.copy()
466492
# Using id() in case somebody has overridden __eq__/__ne__.
467493
self.assertTrue(id(h1)!=id(h2),"No real copy of the HMAC instance.")
468494
self.assertTrue(id(h1._inner)!=id(h2._inner),
469495
"No real copy of the attribute 'inner'.")
470496
self.assertTrue(id(h1._outer)!=id(h2._outer),
471497
"No real copy of the attribute 'outer'.")
472-
self.assertEqual(h1._inner,h1.inner)
473-
self.assertEqual(h1._outer,h1.outer)
474-
self.assertEqual(h1._digest_cons,h1.digest_cons)
498+
self.assertIs(h1._hmac,None)
475499

500+
@unittest.skipIf(_hashopensslisNone,"test requires _hashopenssl")
476501
@hashlib_helper.requires_hashdigest('sha256')
477-
deftest_properties(self):
478-
# deprecated properties
479-
h1=hmac.HMAC(b"key",digestmod="sha256")
480-
self.assertEqual(h1._inner,h1.inner)
481-
self.assertEqual(h1._outer,h1.outer)
482-
self.assertEqual(h1._digest_cons,h1.digest_cons)
502+
deftest_realcopy_hmac(self):
503+
h1=hmac.HMAC.__new__(hmac.HMAC)
504+
h1._init_hmac(b"key",b"msg",digestmod="sha256")
505+
h2=h1.copy()
506+
self.assertTrue(id(h1._hmac)!=id(h2._hmac))
483507

484508
@hashlib_helper.requires_hashdigest('sha256')
485509
deftest_equality(self):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
The:mod:`hmac` module now uses OpenSSL's HMAC implementation when digestmod
2+
argument is a hash name or builtin hash function.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp