1616
1717import hashlib as _hashlib
1818
19- trans_5C = bytes ((x ^ 0x5C ) for x in range (256 ))
20- trans_36 = bytes ((x ^ 0x36 ) for x in range (256 ))
19+ # Constants for padding
20+ INNER_PAD = 0x36
21+ OUTER_PAD = 0x5C
2122
2223# The size of the digests returned by HMAC depends on the underlying
23- # hashing module used. Use digest_size from the instance of HMAC instead.
24+ # hashing module used. Use digest_size from the instance of HMAC instead.
2425digest_size = None
2526
2627
2728class HMAC :
28- """RFC 2104 HMAC class. Also complies with RFC 4231.
29+ """RFC 2104 HMAC class. Complies with RFC 4231 and PEP 247."""
2930
30- This supports the API for Cryptographic Hash Functions (PEP 247).
31- """
3231 blocksize = 64 # 512-bit HMAC; can be changed in subclasses.
3332
34- __slots__ = (
35- "_hmac" , "_inner" , "_outer" , "block_size" , "digest_size"
36- )
33+ __slots__ = ("_hmac" , "_inner" , "_outer" , "block_size" , "digest_size" )
3734
38- def __init__ (self , key , msg = None , digestmod = '' ):
35+ def __init__ (self , key : bytes , msg : bytes = None , digestmod : str = '' ) -> None :
3936 """Create a new HMAC object.
4037
4138 key: bytes or buffer, key for the keyed hash object.
4239 msg: bytes or buffer, Initial input for the hash or None.
4340 digestmod: A hash name suitable for hashlib.new(). *OR*
4441 A hashlib constructor returning a new hash object. *OR*
4542 A module supporting PEP 247.
46-
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.
5043 """
51-
5244 if not isinstance (key , (bytes , bytearray )):
53- raise TypeError ("key: expected bytes or bytearray, but got %r" % type (key ).__name__ )
54-
45+ raise TypeError (f"Expected bytes or bytearray for key, got { type (key ).__name__ } " )
5546 if not digestmod :
5647 raise TypeError ("Missing required argument 'digestmod'." )
5748
@@ -62,69 +53,68 @@ def __init__(self, key, msg=None, digestmod=''):
6253 self ._init_old (key , msg , digestmod )
6354 else :
6455 self ._init_old (key , msg , digestmod )
65-
66- def _init_hmac (self , key , msg , digestmod ):
56+
57+ def _init_hmac (self , key : bytes , msg : bytes , digestmod : str ) -> None :
58+ """Initialize HMAC using openssl."""
6759 self ._hmac = _hashopenssl .hmac_new (key , msg , digestmod = digestmod )
6860 self .digest_size = self ._hmac .digest_size
6961 self .block_size = self ._hmac .block_size
7062
71- 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 )
78-
79- self ._hmac = None
63+ def _init_old (self , key : bytes , msg : bytes , digestmod : str ) -> None :
64+ """Initialize HMAC for old methods."""
65+ digest_cons = self ._get_digest_cons (digestmod )
8066 self ._outer = digest_cons ()
8167 self ._inner = digest_cons ()
8268 self .digest_size = self ._inner .digest_size
69+ self ._handle_blocksize (self ._inner .block_size )
8370
84- if hasattr (self ._inner , 'block_size' ):
85- blocksize = self ._inner .block_size
86- 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 )
90- blocksize = self .blocksize
91- else :
92- _warnings .warn ('No block_size attribute on given digest object; '
93- 'Assuming %d.' % (self .blocksize ),
94- RuntimeWarning , 2 )
95- blocksize = self .blocksize
96-
97- if len (key ) > blocksize :
98- key = digest_cons (key ).digest ()
99-
100- # self.blocksize is the default blocksize. self.block_size is
101- # effective block size as well as the public API attribute.
102- self .block_size = blocksize
103-
104- key = key .ljust (blocksize , b'\0 ' )
71+ # Prepare the key
72+ key = self ._prepare_key (key , self .block_size )
10573 self ._outer .update (key .translate (trans_5C ))
10674 self ._inner .update (key .translate (trans_36 ))
10775 if msg is not None :
10876 self .update (msg )
10977
78+ def _get_digest_cons (self , digestmod : str ):
79+ """Returns a suitable digest constructor."""
80+ if callable (digestmod ):
81+ return digestmod
82+ elif isinstance (digestmod , str ):
83+ return lambda d = b'' : _hashlib .new (digestmod , d )
84+ else :
85+ return lambda d = b'' : digestmod .new (d )
86+
87+ def _handle_blocksize (self , blocksize : int ) -> None :
88+ """Handle blocksize warnings."""
89+ if blocksize < 16 :
90+ _warnings .warn (f'block_size of { blocksize } seems too small; using default of { self .blocksize } .' , RuntimeWarning , 2 )
91+ blocksize = self .blocksize
92+ self .block_size = blocksize
93+
94+ def _prepare_key (self , key : bytes , blocksize : int ) -> bytes :
95+ """Prepare the key for the HMAC calculation."""
96+ if len (key ) > blocksize :
97+ key = _hashlib .new (self ._inner .name , key ).digest ()
98+ return key .ljust (blocksize , b'\0 ' )
99+
110100 @property
111- def name (self ):
101+ def name (self ) -> str :
102+ """Return the name of the hashing algorithm."""
112103 if self ._hmac :
113104 return self ._hmac .name
114105 else :
115106 return f"hmac-{ self ._inner .name } "
116107
117- def update (self , msg ) :
118- """Feed data from msg into this hashing object."""
108+ def update (self , msg : bytes ) -> None :
109+ """Feed data into this hashing object."""
119110 inst = self ._hmac or self ._inner
120111 inst .update (msg )
121112
122- def copy (self ):
113+ def copy (self ) -> 'HMAC' :
123114 """Return a separate copy of this hashing object.
124115
125116 An update to this copy won't affect the original object.
126117 """
127- # Call __new__ directly to avoid the expensive __init__.
128118 other = self .__class__ .__new__ (self .__class__ )
129119 other .digest_size = self .digest_size
130120 if self ._hmac :
@@ -148,43 +138,39 @@ def _current(self):
148138 h .update (self ._inner .digest ())
149139 return h
150140
151- def digest (self ):
141+ def digest (self ) -> bytes :
152142 """Return the hash value of this hashing object.
153143
154- This returns the hmac value as bytes. The object is
144+ This returns the hmac value as bytes. The object is
155145 not altered in any way by this function; you can continue
156146 updating the object after calling this function.
157147 """
158148 h = self ._current ()
159149 return h .digest ()
160150
161- def hexdigest (self ):
162- """Like digest(), but returns a string of hexadecimal digits instead.
163- """
151+ def hexdigest (self ) -> str :
152+ """Like digest(), but returns a string of hexadecimal digits instead."""
164153 h = self ._current ()
165154 return h .hexdigest ()
166155
167- def new (key , msg = None , digestmod = '' ):
168- """Create a new hashing object and return it.
156+
157+ def new (key : bytes , msg : bytes = None , digestmod : str = '' ) -> HMAC :
158+ """Create a new HMAC object and return it.
169159
170160 key: bytes or buffer, The starting key for the hash.
171161 msg: bytes or buffer, Initial input for the hash, or None.
172162 digestmod: A hash name suitable for hashlib.new(). *OR*
173163 A hashlib constructor returning a new hash object. *OR*
174164 A module supporting PEP 247.
175165
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.
179-
180166 You can now feed arbitrary bytes into the object using its update()
181167 method, and can ask for the hash value at any time by calling its digest()
182168 or hexdigest() methods.
183169 """
184170 return HMAC (key , msg , digestmod )
185171
186172
187- def digest (key , msg , digest ) :
173+ def digest (key : bytes , msg : bytes , digest : str ) -> bytes :
188174 """Fast inline implementation of HMAC.
189175
190176 key: bytes or buffer, The key for the keyed hash object.
@@ -199,13 +185,7 @@ def digest(key, msg, digest):
199185 except _hashopenssl .UnsupportedDigestmodError :
200186 pass
201187
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-
188+ digest_cons = _get_digest_cons (digest )
209189 inner = digest_cons ()
210190 outer = digest_cons ()
211191 blocksize = getattr (inner , 'block_size' , 64 )
@@ -217,3 +197,4 @@ def digest(key, msg, digest):
217197 inner .update (msg )
218198 outer .update (inner .digest ())
219199 return outer .digest ()
200+
0 commit comments