3
3
Implements the HMAC algorithm as described by RFC 2104.
4
4
"""
5
5
6
- import warnings as _warnings
7
6
try :
8
7
import _hashlib as _hashopenssl
9
8
except ImportError :
14
13
compare_digest = _hashopenssl .compare_digest
15
14
_functype = type (_hashopenssl .openssl_sha256 ) # builtin type
16
15
17
- import hashlib as _hashlib
16
+ try :
17
+ import _hmac
18
+ except ImportError :
19
+ _hmac = None
18
20
19
21
trans_5C = bytes ((x ^ 0x5C ) for x in range (256 ))
20
22
trans_36 = bytes ((x ^ 0x36 ) for x in range (256 ))
23
25
# hashing module used. Use digest_size from the instance of HMAC instead.
24
26
digest_size = None
25
27
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
26
39
27
40
class HMAC :
28
41
"""RFC 2104 HMAC class. Also complies with RFC 4231.
29
42
30
43
This supports the API for Cryptographic Hash Functions (PEP 247).
31
44
"""
45
+
46
+ # Note: self.blocksize is the default blocksize; self.block_size
47
+ # is effective block size as well as the public API attribute.
32
48
blocksize = 64 # 512-bit HMAC; can be changed in subclasses.
33
49
34
50
__slots__ = (
@@ -50,31 +66,45 @@ def __init__(self, key, msg=None, digestmod=''):
50
66
"""
51
67
52
68
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} " )
54
71
55
72
if not digestmod :
56
73
raise TypeError ("Missing required argument 'digestmod'." )
57
74
75
+ self .__init (key , msg , digestmod )
76
+
77
+ def __init (self , key , msg , digestmod ):
58
78
if _hashopenssl and isinstance (digestmod , (str , _functype )):
59
79
try :
60
- self ._init_hmac (key , msg , digestmod )
80
+ self ._init_openssl_hmac (key , msg , digestmod )
81
+ return
61
82
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 )
65
91
66
- def _init_hmac (self , key , msg , digestmod ):
92
+ def _init_openssl_hmac (self , key , msg , digestmod ):
67
93
self ._hmac = _hashopenssl .hmac_new (key , msg , digestmod = digestmod )
68
94
self .digest_size = self ._hmac .digest_size
69
95
self .block_size = self ._hmac .block_size
70
96
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
+
71
104
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 )
78
108
79
109
self ._hmac = None
80
110
self ._outer = digest_cons ()
@@ -84,21 +114,19 @@ def _init_old(self, key, msg, digestmod):
84
114
if hasattr (self ._inner , 'block_size' ):
85
115
blocksize = self ._inner .block_size
86
116
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 )
90
120
blocksize = self .blocksize
91
121
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 )
95
125
blocksize = self .blocksize
96
126
97
127
if len (key ) > blocksize :
98
128
key = digest_cons (key ).digest ()
99
129
100
- # self.blocksize is the default blocksize. self.block_size is
101
- # effective block size as well as the public API attribute.
102
130
self .block_size = blocksize
103
131
104
132
key = key .ljust (blocksize , b'\0 ' )
@@ -164,6 +192,7 @@ def hexdigest(self):
164
192
h = self ._current ()
165
193
return h .hexdigest ()
166
194
195
+
167
196
def new (key , msg = None , digestmod = '' ):
168
197
"""Create a new hashing object and return it.
169
198
@@ -183,7 +212,6 @@ def new(key, msg=None, digestmod=''):
183
212
"""
184
213
return HMAC (key , msg , digestmod )
185
214
186
-
187
215
def digest (key , msg , digest ):
188
216
"""Fast inline implementation of HMAC.
189
217
@@ -199,19 +227,22 @@ def digest(key, msg, digest):
199
227
except _hashopenssl .UnsupportedDigestmodError :
200
228
pass
201
229
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 )
208
237
238
+ def _compute_digest_fallback (key , msg , digest ):
239
+ digest_cons = _get_digest_constructor (digest )
209
240
inner = digest_cons ()
210
241
outer = digest_cons ()
211
242
blocksize = getattr (inner , 'block_size' , 64 )
212
243
if len (key ) > blocksize :
213
244
key = digest_cons (key ).digest ()
214
- key = key + b'\x00 ' * ( blocksize - len ( key ) )
245
+ key = key . ljust ( blocksize , b'\0 ' )
215
246
inner .update (key .translate (trans_36 ))
216
247
outer .update (key .translate (trans_5C ))
217
248
inner .update (msg )
0 commit comments