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 ))
24
26
digest_size = None
25
27
26
28
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
+
27
42
class HMAC :
28
43
"""RFC 2104 HMAC class. Also complies with RFC 4231.
29
44
30
45
This supports the API for Cryptographic Hash Functions (PEP 247).
31
46
"""
47
+
48
+ # Note: self.blocksize is the default blocksize; self.block_size
49
+ # is effective block size as well as the public API attribute.
32
50
blocksize = 64 # 512-bit HMAC; can be changed in subclasses.
33
51
34
52
__slots__ = (
@@ -50,32 +68,47 @@ def __init__(self, key, msg=None, digestmod=''):
50
68
"""
51
69
52
70
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} " )
54
73
55
74
if not digestmod :
56
75
raise TypeError ("Missing required argument 'digestmod'." )
57
76
77
+ self .__init (key , msg , digestmod )
78
+
79
+ def __init (self , key , msg , digestmod ):
58
80
if _hashopenssl and isinstance (digestmod , (str , _functype )):
59
81
try :
60
- self ._init_hmac (key , msg , digestmod )
82
+ self ._init_openssl_hmac (key , msg , digestmod )
83
+ return
61
84
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 )
65
93
66
- def _init_hmac (self , key , msg , digestmod ):
94
+ def _init_openssl_hmac (self , key , msg , digestmod ):
67
95
self ._hmac = _hashopenssl .hmac_new (key , msg , digestmod = digestmod )
68
96
self ._inner = self ._outer = None # because the slots are defined
69
97
self .digest_size = self ._hmac .digest_size
70
98
self .block_size = self ._hmac .block_size
71
99
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
+
72
108
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 )
79
112
80
113
self ._hmac = None
81
114
self ._outer = digest_cons ()
@@ -85,21 +118,19 @@ def _init_old(self, key, msg, digestmod):
85
118
if hasattr (self ._inner , 'block_size' ):
86
119
blocksize = self ._inner .block_size
87
120
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 )
91
124
blocksize = self .blocksize
92
125
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 )
96
129
blocksize = self .blocksize
97
130
98
131
if len (key ) > blocksize :
99
132
key = digest_cons (key ).digest ()
100
133
101
- # self.blocksize is the default blocksize. self.block_size is
102
- # effective block size as well as the public API attribute.
103
134
self .block_size = blocksize
104
135
105
136
key = key .ljust (blocksize , b'\0 ' )
@@ -165,6 +196,7 @@ def hexdigest(self):
165
196
h = self ._current ()
166
197
return h .hexdigest ()
167
198
199
+
168
200
def new (key , msg = None , digestmod = '' ):
169
201
"""Create a new hashing object and return it.
170
202
@@ -194,25 +226,29 @@ def digest(key, msg, digest):
194
226
A hashlib constructor returning a new hash object. *OR*
195
227
A module supporting PEP 247.
196
228
"""
197
- if _hashopenssl is not None and isinstance (digest , (str , _functype )):
229
+ if _hashopenssl and isinstance (digest , (str , _functype )):
198
230
try :
199
231
return _hashopenssl .hmac_digest (key , msg , digest )
200
232
except _hashopenssl .UnsupportedDigestmodError :
201
233
pass
202
234
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
+
209
243
244
+ def _compute_digest_fallback (key , msg , digest ):
245
+ digest_cons = _get_digest_constructor (digest )
210
246
inner = digest_cons ()
211
247
outer = digest_cons ()
212
248
blocksize = getattr (inner , 'block_size' , 64 )
213
249
if len (key ) > blocksize :
214
250
key = digest_cons (key ).digest ()
215
- key = key + b'\x00 ' * ( blocksize - len ( key ) )
251
+ key = key . ljust ( blocksize , b'\0 ' )
216
252
inner .update (key .translate (trans_36 ))
217
253
outer .update (key .translate (trans_5C ))
218
254
inner .update (msg )
0 commit comments