1- """HMAC (Keyed-Hashing for Message Authentication) Python module.
1+ """HMAC (Keyed-Hashing for Message Authentication) module.
22
33Implements the HMAC algorithm as described by RFC 2104.
44"""
55
66import warnings as _warnings
7- from _operator import _compare_digest as compare_digest
7+ try :
8+ import _hashlib as _hashopenssl
9+ except ImportError :
10+ _hashopenssl = None
11+ _functype = None
12+ from _operator import _compare_digest as compare_digest
13+ else :
14+ compare_digest = _hashopenssl .compare_digest
15+ _functype = type (_hashopenssl .openssl_sha256 ) # builtin type
16+
817import hashlib as _hashlib
918
1019trans_5C = bytes ((x ^ 0x5C ) for x in range (256 ))
1524digest_size = None
1625
1726
18-
1927class HMAC :
2028 """RFC 2104 HMAC class. Also complies with RFC 4231.
2129
2230 This supports the API for Cryptographic Hash Functions (PEP 247).
2331 """
2432 blocksize = 64 # 512-bit HMAC; can be changed in subclasses.
2533
26- def __init__ (self , key , msg = None , digestmod = None ):
34+ __slots__ = (
35+ "_hmac" , "_inner" , "_outer" , "block_size" , "digest_size"
36+ )
37+
38+ def __init__ (self , key , msg = None , digestmod = '' ):
2739 """Create a new HMAC object.
2840
29- key: key for the keyed hash object.
30- msg: Initial input for the hash, if provided .
31- digestmod: A module supporting PEP 247. *OR*
41+ key: bytes or buffer, key for the keyed hash object.
42+ msg: bytes or buffer, Initial input for the hash or None .
43+ digestmod: A hash name suitable for hashlib.new(). *OR*
3244 A hashlib constructor returning a new hash object. *OR*
33- A hash name suitable for hashlib.new().
34- Defaults to hashlib.md5.
35- Implicit default to hashlib.md5 is deprecated and will be
36- removed in Python 3.6.
45+ A module supporting PEP 247.
3746
38- Note: key and msg must be a bytes or bytearray objects.
47+ Required as of 3.8, despite its position after the optional
48+ msg argument. Passing it as a keyword argument is
49+ recommended, though not required for legacy API reasons.
3950 """
4051
4152 if not isinstance (key , (bytes , bytearray )):
4253 raise TypeError ("key: expected bytes or bytearray, but got %r" % type (key ).__name__ )
4354
44- if digestmod is None :
45- _warnings .warn ("HMAC() without an explicit digestmod argument "
46- "is deprecated." , PendingDeprecationWarning , 2 )
47- digestmod = _hashlib .md5
55+ if not digestmod :
56+ raise TypeError ("Missing required parameter 'digestmod'." )
57+
58+ if _hashopenssl and isinstance (digestmod , (str , _functype )):
59+ try :
60+ self ._init_hmac (key , msg , digestmod )
61+ except _hashopenssl .UnsupportedDigestmodError :
62+ self ._init_old (key , msg , digestmod )
63+ else :
64+ self ._init_old (key , msg , digestmod )
65+
66+ def _init_hmac (self , key , msg , digestmod ):
67+ self ._hmac = _hashopenssl .hmac_new (key , msg , digestmod = digestmod )
68+ self .digest_size = self ._hmac .digest_size
69+ self .block_size = self ._hmac .block_size
4870
71+ def _init_old (self , key , msg , digestmod ):
4972 if callable (digestmod ):
50- self . digest_cons = digestmod
73+ digest_cons = digestmod
5174 elif isinstance (digestmod , str ):
52- self . digest_cons = lambda d = b'' : _hashlib .new (digestmod , d )
75+ digest_cons = lambda d = b'' : _hashlib .new (digestmod , d )
5376 else :
54- self . digest_cons = lambda d = b'' : digestmod .new (d )
77+ digest_cons = lambda d = b'' : digestmod .new (d )
5578
56- self .outer = self .digest_cons ()
57- self .inner = self .digest_cons ()
58- self .digest_size = self .inner .digest_size
79+ self ._hmac = None
80+ self ._outer = digest_cons ()
81+ self ._inner = digest_cons ()
82+ self .digest_size = self ._inner .digest_size
5983
60- if hasattr (self .inner , 'block_size' ):
61- blocksize = self .inner .block_size
84+ if hasattr (self ._inner , 'block_size' ):
85+ blocksize = self ._inner .block_size
6286 if blocksize < 16 :
6387 _warnings .warn ('block_size of %d seems too small; using our '
6488 'default of %d.' % (blocksize , self .blocksize ),
@@ -70,27 +94,30 @@ def __init__(self, key, msg = None, digestmod = None):
7094 RuntimeWarning , 2 )
7195 blocksize = self .blocksize
7296
97+ if len (key ) > blocksize :
98+ key = digest_cons (key ).digest ()
99+
73100 # self.blocksize is the default blocksize. self.block_size is
74101 # effective block size as well as the public API attribute.
75102 self .block_size = blocksize
76103
77- if len (key ) > blocksize :
78- key = self .digest_cons (key ).digest ()
79-
80104 key = key .ljust (blocksize , b'\0 ' )
81- self .outer .update (key .translate (trans_5C ))
82- self .inner .update (key .translate (trans_36 ))
105+ self ._outer .update (key .translate (trans_5C ))
106+ self ._inner .update (key .translate (trans_36 ))
83107 if msg is not None :
84108 self .update (msg )
85109
86110 @property
87111 def name (self ):
88- return "hmac-" + self .inner .name
112+ if self ._hmac :
113+ return self ._hmac .name
114+ else :
115+ return f"hmac-{ self ._inner .name } "
89116
90117 def update (self , msg ):
91- """Update this hashing object with the string msg.
92- """
93- self . inner .update (msg )
118+ """Feed data from msg into this hashing object."""
119+ inst = self . _hmac or self . _inner
120+ inst .update (msg )
94121
95122 def copy (self ):
96123 """Return a separate copy of this hashing object.
@@ -99,25 +126,32 @@ def copy(self):
99126 """
100127 # Call __new__ directly to avoid the expensive __init__.
101128 other = self .__class__ .__new__ (self .__class__ )
102- other .digest_cons = self .digest_cons
103129 other .digest_size = self .digest_size
104- other .inner = self .inner .copy ()
105- other .outer = self .outer .copy ()
130+ if self ._hmac :
131+ other ._hmac = self ._hmac .copy ()
132+ other ._inner = other ._outer = None
133+ else :
134+ other ._hmac = None
135+ other ._inner = self ._inner .copy ()
136+ other ._outer = self ._outer .copy ()
106137 return other
107138
108139 def _current (self ):
109140 """Return a hash object for the current state.
110141
111142 To be used only internally with digest() and hexdigest().
112143 """
113- h = self .outer .copy ()
114- h .update (self .inner .digest ())
115- return h
144+ if self ._hmac :
145+ return self ._hmac
146+ else :
147+ h = self ._outer .copy ()
148+ h .update (self ._inner .digest ())
149+ return h
116150
117151 def digest (self ):
118152 """Return the hash value of this hashing object.
119153
120- This returns a string containing 8-bit data . The object is
154+ This returns the hmac value as bytes . The object is
121155 not altered in any way by this function; you can continue
122156 updating the object after calling this function.
123157 """
@@ -130,15 +164,56 @@ def hexdigest(self):
130164 h = self ._current ()
131165 return h .hexdigest ()
132166
133- def new (key , msg = None , digestmod = None ):
167+ def new (key , msg = None , digestmod = '' ):
134168 """Create a new hashing object and return it.
135169
136- key: The starting key for the hash.
137- msg: if available, will immediately be hashed into the object's starting
138- state.
170+ key: bytes or buffer, The starting key for the hash.
171+ msg: bytes or buffer, Initial input for the hash, or None.
172+ digestmod: A hash name suitable for hashlib.new(). *OR*
173+ A hashlib constructor returning a new hash object. *OR*
174+ A module supporting PEP 247.
175+
176+ Required as of 3.8, despite its position after the optional
177+ msg argument. Passing it as a keyword argument is
178+ recommended, though not required for legacy API reasons.
139179
140- You can now feed arbitrary strings into the object using its update()
180+ You can now feed arbitrary bytes into the object using its update()
141181 method, and can ask for the hash value at any time by calling its digest()
142- method .
182+ or hexdigest() methods .
143183 """
144184 return HMAC (key , msg , digestmod )
185+
186+
187+ def digest (key , msg , digest ):
188+ """Fast inline implementation of HMAC.
189+
190+ key: bytes or buffer, The key for the keyed hash object.
191+ msg: bytes or buffer, Input message.
192+ digest: A hash name suitable for hashlib.new() for best performance. *OR*
193+ A hashlib constructor returning a new hash object. *OR*
194+ A module supporting PEP 247.
195+ """
196+ if _hashopenssl is not None and isinstance (digest , (str , _functype )):
197+ try :
198+ return _hashopenssl .hmac_digest (key , msg , digest )
199+ except _hashopenssl .UnsupportedDigestmodError :
200+ pass
201+
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 )
208+
209+ inner = digest_cons ()
210+ outer = digest_cons ()
211+ blocksize = getattr (inner , 'block_size' , 64 )
212+ if len (key ) > blocksize :
213+ key = digest_cons (key ).digest ()
214+ key = key + b'\x00 ' * (blocksize - len (key ))
215+ inner .update (key .translate (trans_36 ))
216+ outer .update (key .translate (trans_5C ))
217+ inner .update (msg )
218+ outer .update (inner .digest ())
219+ return outer .digest ()
0 commit comments