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 ))
2426digest_size = None
2527
2628
29+ def _get_digest_constructor (digest_like ):
30+ if callable (digest_like ):
31+ return digest_like
32+ if isinstance (digest_like , str ):
33+ def digest_wrapper (d = b'' ):
34+ import hashlib
35+ return hashlib .new (digest_like , d )
36+ else :
37+ def digest_wrapper (d = b'' ):
38+ return digest_like .new (d )
39+ return digest_wrapper
40+
41+
2742class HMAC :
2843 """RFC 2104 HMAC class. Also complies with RFC 4231.
2944
3045 This supports the API for Cryptographic Hash Functions (PEP 247).
3146 """
47+
48+ # Note: self.blocksize is the default blocksize; self.block_size
49+ # is effective block size as well as the public API attribute.
3250 blocksize = 64 # 512-bit HMAC; can be changed in subclasses.
3351
3452 __slots__ = (
@@ -50,32 +68,47 @@ def __init__(self, key, msg=None, digestmod=''):
5068 """
5169
5270 if not isinstance (key , (bytes , bytearray )):
53- raise TypeError ("key: expected bytes or bytearray, but got %r" % type (key ).__name__ )
71+ raise TypeError (f"key: expected bytes or bytearray, "
72+ f"but got { type (key ).__name__ !r} " )
5473
5574 if not digestmod :
5675 raise TypeError ("Missing required argument 'digestmod'." )
5776
77+ self .__init (key , msg , digestmod )
78+
79+ def __init (self , key , msg , digestmod ):
5880 if _hashopenssl and isinstance (digestmod , (str , _functype )):
5981 try :
60- self ._init_hmac (key , msg , digestmod )
82+ self ._init_openssl_hmac (key , msg , digestmod )
83+ return
6184 except _hashopenssl .UnsupportedDigestmodError :
62- self ._init_old (key , msg , digestmod )
63- else :
64- self ._init_old (key , msg , digestmod )
85+ pass
86+ if _hmac and isinstance (digestmod , str ):
87+ try :
88+ self ._init_builtin_hmac (key , msg , digestmod )
89+ return
90+ except _hmac .UnknownHashError :
91+ pass
92+ self ._init_old (key , msg , digestmod )
6593
66- def _init_hmac (self , key , msg , digestmod ):
94+ def _init_openssl_hmac (self , key , msg , digestmod ):
6795 self ._hmac = _hashopenssl .hmac_new (key , msg , digestmod = digestmod )
6896 self ._inner = self ._outer = None # because the slots are defined
6997 self .digest_size = self ._hmac .digest_size
7098 self .block_size = self ._hmac .block_size
7199
100+ _init_hmac = _init_openssl_hmac # for backward compatibility (if any)
101+
102+ def _init_builtin_hmac (self , key , msg , digestmod ):
103+ self ._hmac = _hmac .new (key , msg , digestmod = digestmod )
104+ self ._inner = self ._outer = None # because the slots are defined
105+ self .digest_size = self ._hmac .digest_size
106+ self .block_size = self ._hmac .block_size
107+
72108 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 )
109+ import warnings
110+
111+ digest_cons = _get_digest_constructor (digestmod )
79112
80113 self ._hmac = None
81114 self ._outer = digest_cons ()
@@ -85,21 +118,19 @@ def _init_old(self, key, msg, digestmod):
85118 if hasattr (self ._inner , 'block_size' ):
86119 blocksize = self ._inner .block_size
87120 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 )
121+ warnings .warn (f" block_size of { blocksize } seems too small; "
122+ f"using our default of { self .blocksize } ." ,
123+ RuntimeWarning , 2 )
91124 blocksize = self .blocksize
92125 else :
93- _warnings .warn (' No block_size attribute on given digest object; '
94- ' Assuming %d.' % ( self .blocksize ) ,
95- RuntimeWarning , 2 )
126+ warnings .warn (" No block_size attribute on given digest object; "
127+ f" Assuming { self .blocksize } ." ,
128+ RuntimeWarning , 2 )
96129 blocksize = self .blocksize
97130
98131 if len (key ) > blocksize :
99132 key = digest_cons (key ).digest ()
100133
101- # self.blocksize is the default blocksize. self.block_size is
102- # effective block size as well as the public API attribute.
103134 self .block_size = blocksize
104135
105136 key = key .ljust (blocksize , b'\0 ' )
@@ -165,6 +196,7 @@ def hexdigest(self):
165196 h = self ._current ()
166197 return h .hexdigest ()
167198
199+
168200def new (key , msg = None , digestmod = '' ):
169201 """Create a new hashing object and return it.
170202
@@ -194,25 +226,29 @@ def digest(key, msg, digest):
194226 A hashlib constructor returning a new hash object. *OR*
195227 A module supporting PEP 247.
196228 """
197- if _hashopenssl is not None and isinstance (digest , (str , _functype )):
229+ if _hashopenssl and isinstance (digest , (str , _functype )):
198230 try :
199231 return _hashopenssl .hmac_digest (key , msg , digest )
200232 except _hashopenssl .UnsupportedDigestmodError :
201233 pass
202234
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 )
235+ if _hmac and isinstance (digest , str ):
236+ try :
237+ return _hmac .compute_digest (key , msg , digest )
238+ except (OverflowError , _hmac .UnknownHashError ):
239+ pass
240+
241+ return _compute_digest_fallback (key , msg , digest )
242+
209243
244+ def _compute_digest_fallback (key , msg , digest ):
245+ digest_cons = _get_digest_constructor (digest )
210246 inner = digest_cons ()
211247 outer = digest_cons ()
212248 blocksize = getattr (inner , 'block_size' , 64 )
213249 if len (key ) > blocksize :
214250 key = digest_cons (key ).digest ()
215- key = key + b'\x00 ' * ( blocksize - len ( key ) )
251+ key = key . ljust ( blocksize , b'\0 ' )
216252 inner .update (key .translate (trans_36 ))
217253 outer .update (key .translate (trans_5C ))
218254 inner .update (msg )
0 commit comments