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,45 @@ 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 )
6894 self .digest_size = self ._hmac .digest_size
6995 self .block_size = self ._hmac .block_size
7096
97+ _init_hmac = _init_openssl_hmac # for backward compatibility (if any)
98+
99+ def _init_builtin_hmac (self , key , msg , digestmod ):
100+ self ._hmac = _hmac .new (key , msg , digestmod = digestmod )
101+ self .digest_size = self ._hmac .digest_size
102+ self .block_size = self ._hmac .block_size
103+
71104 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 )
105+ import warnings
106+
107+ digest_cons = _get_digest_constructor (digestmod )
78108
79109 self ._hmac = None
80110 self ._outer = digest_cons ()
@@ -84,21 +114,19 @@ def _init_old(self, key, msg, digestmod):
84114 if hasattr (self ._inner , 'block_size' ):
85115 blocksize = self ._inner .block_size
86116 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 )
117+ warnings .warn (f" block_size of { blocksize } seems too small; "
118+ f"using our default of { self .blocksize } ." ,
119+ RuntimeWarning , 2 )
90120 blocksize = self .blocksize
91121 else :
92- _warnings .warn (' No block_size attribute on given digest object; '
93- ' Assuming %d.' % ( self .blocksize ) ,
94- RuntimeWarning , 2 )
122+ warnings .warn (" No block_size attribute on given digest object; "
123+ f" Assuming { self .blocksize } ." ,
124+ RuntimeWarning , 2 )
95125 blocksize = self .blocksize
96126
97127 if len (key ) > blocksize :
98128 key = digest_cons (key ).digest ()
99129
100- # self.blocksize is the default blocksize. self.block_size is
101- # effective block size as well as the public API attribute.
102130 self .block_size = blocksize
103131
104132 key = key .ljust (blocksize , b'\0 ' )
@@ -164,6 +192,7 @@ def hexdigest(self):
164192 h = self ._current ()
165193 return h .hexdigest ()
166194
195+
167196def new (key , msg = None , digestmod = '' ):
168197 """Create a new hashing object and return it.
169198
@@ -183,7 +212,6 @@ def new(key, msg=None, digestmod=''):
183212 """
184213 return HMAC (key , msg , digestmod )
185214
186-
187215def digest (key , msg , digest ):
188216 """Fast inline implementation of HMAC.
189217
@@ -199,19 +227,22 @@ def digest(key, msg, digest):
199227 except _hashopenssl .UnsupportedDigestmodError :
200228 pass
201229
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 )
230+ if _hmac is not None and isinstance (digest , str ):
231+ try :
232+ return _hmac .compute_digest (key , msg , digest )
233+ except (OverflowError , _hmac .UnknownHashError ):
234+ pass
235+
236+ return _compute_digest_fallback (key , msg , digest )
208237
238+ def _compute_digest_fallback (key , msg , digest ):
239+ digest_cons = _get_digest_constructor (digest )
209240 inner = digest_cons ()
210241 outer = digest_cons ()
211242 blocksize = getattr (inner , 'block_size' , 64 )
212243 if len (key ) > blocksize :
213244 key = digest_cons (key ).digest ()
214- key = key + b'\x00 ' * ( blocksize - len ( key ) )
245+ key = key . ljust ( blocksize , b'\0 ' )
215246 inner .update (key .translate (trans_36 ))
216247 outer .update (key .translate (trans_5C ))
217248 inner .update (msg )
0 commit comments