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,32 +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 )
6894 self ._inner = self ._outer = None # because the slots are defined
6995 self .digest_size = self ._hmac .digest_size
7096 self .block_size = self ._hmac .block_size
7197
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
103+ self .digest_size = self ._hmac .digest_size
104+ self .block_size = self ._hmac .block_size
105+
72106 def _init_old (self , key , msg , digestmod ):
73- if callable (digestmod ):
74- digest_cons = digestmod
75- elif isinstance (digestmod , str ):
76- digest_cons = lambda d = b'' : _hashlib .new (digestmod , d )
77- else :
78- digest_cons = lambda d = b'' : digestmod .new (d )
107+ import warnings
108+
109+ digest_cons = _get_digest_constructor (digestmod )
79110
80111 self ._hmac = None
81112 self ._outer = digest_cons ()
@@ -85,21 +116,19 @@ def _init_old(self, key, msg, digestmod):
85116 if hasattr (self ._inner , 'block_size' ):
86117 blocksize = self ._inner .block_size
87118 if blocksize < 16 :
88- _warnings .warn (' block_size of %d seems too small; using our '
89- ' default of %d.' % ( blocksize , self .blocksize ) ,
90- RuntimeWarning , 2 )
119+ warnings .warn (f" block_size of { blocksize } seems too small; "
120+ f"using our default of { self .blocksize } ." ,
121+ RuntimeWarning , 2 )
91122 blocksize = self .blocksize
92123 else :
93- _warnings .warn (' No block_size attribute on given digest object; '
94- ' Assuming %d.' % ( self .blocksize ) ,
95- RuntimeWarning , 2 )
124+ warnings .warn (" No block_size attribute on given digest object; "
125+ f" Assuming { self .blocksize } ." ,
126+ RuntimeWarning , 2 )
96127 blocksize = self .blocksize
97128
98129 if len (key ) > blocksize :
99130 key = digest_cons (key ).digest ()
100131
101- # self.blocksize is the default blocksize. self.block_size is
102- # effective block size as well as the public API attribute.
103132 self .block_size = blocksize
104133
105134 key = key .ljust (blocksize , b'\0 ' )
@@ -184,7 +213,6 @@ def new(key, msg=None, digestmod=''):
184213 """
185214 return HMAC (key , msg , digestmod )
186215
187-
188216def digest (key , msg , digest ):
189217 """Fast inline implementation of HMAC.
190218
@@ -200,19 +228,22 @@ def digest(key, msg, digest):
200228 except _hashopenssl .UnsupportedDigestmodError :
201229 pass
202230
203- if callable (digest ):
204- digest_cons = digest
205- elif isinstance (digest , str ):
206- digest_cons = lambda d = b'' : _hashlib .new (digest , d )
207- else :
208- 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 )
209238
239+ def _compute_digest_fallback (key , msg , digest ):
240+ digest_cons = _get_digest_constructor (digest )
210241 inner = digest_cons ()
211242 outer = digest_cons ()
212243 blocksize = getattr (inner , 'block_size' , 64 )
213244 if len (key ) > blocksize :
214245 key = digest_cons (key ).digest ()
215- key = key + b'\x00 ' * ( blocksize - len ( key ) )
246+ key = key . ljust ( blocksize , b'\0 ' )
216247 inner .update (key .translate (trans_36 ))
217248 outer .update (key .translate (trans_5C ))
218249 inner .update (msg )
0 commit comments