33Implements the HMAC algorithm as described by RFC 2104.
44"""
55
6- import warnings as _warnings
76try :
87 import _hashlib as _hashopenssl
98except ImportError :
1413 compare_digest = _hashopenssl .compare_digest
1514 _functype = type (_hashopenssl .openssl_sha256 ) # builtin type
1615
17- import hashlib as _hashlib
16+ try :
17+ import _hmac
18+ except ImportError :
19+ _hmac = None
1820
1921trans_5C = bytes ((x ^ 0x5C ) for x in range (256 ))
2022trans_36 = bytes ((x ^ 0x36 ) for x in range (256 ))
2325# hashing module used. Use digest_size from the instance of HMAC instead.
2426digest_size = None
2527
28+ def _get_digest_constructor (digest_like ):
29+ if callable (digest_like ):
30+ return digest_like
31+ if isinstance (digest_like , str ):
32+ def digest_wrapper (d = b'' ):
33+ import hashlib
34+ return hashlib .new (digest_like , d )
35+ else :
36+ def digest_wrapper (d = b'' ):
37+ return digest_like .new (d )
38+ return digest_wrapper
2639
2740class HMAC :
2841 """RFC 2104 HMAC class. Also complies with RFC 4231.
2942
3043 This supports the API for Cryptographic Hash Functions (PEP 247).
3144 """
45+
46+ # Note: self.blocksize is the default blocksize; self.block_size
47+ # is effective block size as well as the public API attribute.
3248 blocksize = 64 # 512-bit HMAC; can be changed in subclasses.
3349
3450 __slots__ = (
@@ -50,31 +66,47 @@ def __init__(self, key, msg=None, digestmod=''):
5066 """
5167
5268 if not isinstance (key , (bytes , bytearray )):
53- raise TypeError ("key: expected bytes or bytearray, but got %r" % type (key ).__name__ )
69+ raise TypeError (f"key: expected bytes or bytearray, "
70+ f"but got { type (key ).__name__ !r} " )
5471
5572 if not digestmod :
5673 raise TypeError ("Missing required argument 'digestmod'." )
5774
75+ self .__init (key , msg , digestmod )
76+
77+ def __init (self , key , msg , digestmod ):
5878 if _hashopenssl and isinstance (digestmod , (str , _functype )):
5979 try :
60- self ._init_hmac (key , msg , digestmod )
80+ self ._init_openssl_hmac (key , msg , digestmod )
81+ return
6182 except _hashopenssl .UnsupportedDigestmodError :
62- self ._init_old (key , msg , digestmod )
63- else :
64- self ._init_old (key , msg , digestmod )
83+ pass
84+ if _hmac and isinstance (digestmod , str ):
85+ try :
86+ self ._init_builtin_hmac (key , msg , digestmod )
87+ return
88+ except _hmac .UnknownHashError :
89+ pass
90+ self ._init_old (key , msg , digestmod )
6591
66- def _init_hmac (self , key , msg , digestmod ):
92+ def _init_openssl_hmac (self , key , msg , digestmod ):
6793 self ._hmac = _hashopenssl .hmac_new (key , msg , digestmod = digestmod )
94+ self ._inner = self ._outer = None # because the slots are defined
95+ self .digest_size = self ._hmac .digest_size
96+ self .block_size = self ._hmac .block_size
97+
98+ _init_hmac = _init_openssl_hmac # for backward compatibility (if any)
99+
100+ def _init_builtin_hmac (self , key , msg , digestmod ):
101+ self ._hmac = _hmac .new (key , msg , digestmod = digestmod )
102+ self ._inner = self ._outer = None # because the slots are defined
68103 self .digest_size = self ._hmac .digest_size
69104 self .block_size = self ._hmac .block_size
70105
71106 def _init_old (self , key , msg , digestmod ):
72- if callable (digestmod ):
73- digest_cons = digestmod
74- elif isinstance (digestmod , str ):
75- digest_cons = lambda d = b'' : _hashlib .new (digestmod , d )
76- else :
77- digest_cons = lambda d = b'' : digestmod .new (d )
107+ import warnings
108+
109+ digest_cons = _get_digest_constructor (digestmod )
78110
79111 self ._hmac = None
80112 self ._outer = digest_cons ()
@@ -84,21 +116,19 @@ def _init_old(self, key, msg, digestmod):
84116 if hasattr (self ._inner , 'block_size' ):
85117 blocksize = self ._inner .block_size
86118 if blocksize < 16 :
87- _warnings .warn (' block_size of %d seems too small; using our '
88- ' default of %d.' % ( blocksize , self .blocksize ) ,
89- RuntimeWarning , 2 )
119+ warnings .warn (f" block_size of { blocksize } seems too small; "
120+ f"using our default of { self .blocksize } ." ,
121+ RuntimeWarning , 2 )
90122 blocksize = self .blocksize
91123 else :
92- _warnings .warn (' No block_size attribute on given digest object; '
93- ' Assuming %d.' % ( self .blocksize ) ,
94- RuntimeWarning , 2 )
124+ warnings .warn (" No block_size attribute on given digest object; "
125+ f" Assuming { self .blocksize } ." ,
126+ RuntimeWarning , 2 )
95127 blocksize = self .blocksize
96128
97129 if len (key ) > blocksize :
98130 key = digest_cons (key ).digest ()
99131
100- # self.blocksize is the default blocksize. self.block_size is
101- # effective block size as well as the public API attribute.
102132 self .block_size = blocksize
103133
104134 key = key .ljust (blocksize , b'\0 ' )
@@ -183,7 +213,6 @@ def new(key, msg=None, digestmod=''):
183213 """
184214 return HMAC (key , msg , digestmod )
185215
186-
187216def digest (key , msg , digest ):
188217 """Fast inline implementation of HMAC.
189218
@@ -199,19 +228,22 @@ def digest(key, msg, digest):
199228 except _hashopenssl .UnsupportedDigestmodError :
200229 pass
201230
202- if callable (digest ):
203- digest_cons = digest
204- elif isinstance (digest , str ):
205- digest_cons = lambda d = b'' : _hashlib .new (digest , d )
206- else :
207- digest_cons = lambda d = b'' : digest .new (d )
231+ if _hmac is not None and isinstance (digest , str ):
232+ try :
233+ return _hmac .compute_digest (key , msg , digest )
234+ except (OverflowError , _hmac .UnknownHashError ):
235+ pass
236+
237+ return _compute_digest_fallback (key , msg , digest )
208238
239+ def _compute_digest_fallback (key , msg , digest ):
240+ digest_cons = _get_digest_constructor (digest )
209241 inner = digest_cons ()
210242 outer = digest_cons ()
211243 blocksize = getattr (inner , 'block_size' , 64 )
212244 if len (key ) > blocksize :
213245 key = digest_cons (key ).digest ()
214- key = key + b'\x00 ' * ( blocksize - len ( key ) )
246+ key = key . ljust ( blocksize , b'\0 ' )
215247 inner .update (key .translate (trans_36 ))
216248 outer .update (key .translate (trans_5C ))
217249 inner .update (msg )
0 commit comments