Skip to content

Commit 0bb6819

Browse files
committed
add HMAC python interface
1 parent 9666eee commit 0bb6819

File tree

1 file changed

+61
-30
lines changed

1 file changed

+61
-30
lines changed

Lib/hmac.py

Lines changed: 61 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
Implements the HMAC algorithm as described by RFC 2104.
44
"""
55

6-
import warnings as _warnings
76
try:
87
import _hashlib as _hashopenssl
98
except ImportError:
@@ -14,7 +13,10 @@
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

1921
trans_5C = bytes((x ^ 0x5C) for x in range(256))
2022
trans_36 = bytes((x ^ 0x36) for x in range(256))
@@ -23,12 +25,26 @@
2325
# hashing module used. Use digest_size from the instance of HMAC instead.
2426
digest_size = None
2527

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
2639

2740
class HMAC:
2841
"""RFC 2104 HMAC class. Also complies with RFC 4231.
2942
3043
This supports the API for Cryptographic Hash Functions (PEP 247).
3144
"""
45+
46+
# Note: self.blocksize is the default blocksize; self.block_size
47+
# is effective block size as well as the public API attribute.
3248
blocksize = 64 # 512-bit HMAC; can be changed in subclasses.
3349

3450
__slots__ = (
@@ -50,31 +66,45 @@ def __init__(self, key, msg=None, digestmod=''):
5066
"""
5167

5268
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}")
5471

5572
if not digestmod:
5673
raise TypeError("Missing required argument 'digestmod'.")
5774

75+
self.__init(key, msg, digestmod)
76+
77+
def __init(self, key, msg, digestmod):
5878
if _hashopenssl and isinstance(digestmod, (str, _functype)):
5979
try:
60-
self._init_hmac(key, msg, digestmod)
80+
self._init_openssl_hmac(key, msg, digestmod)
81+
return
6182
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)
6591

66-
def _init_hmac(self, key, msg, digestmod):
92+
def _init_openssl_hmac(self, key, msg, digestmod):
6793
self._hmac = _hashopenssl.hmac_new(key, msg, digestmod=digestmod)
6894
self.digest_size = self._hmac.digest_size
6995
self.block_size = self._hmac.block_size
7096

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+
71104
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)
78108

79109
self._hmac = None
80110
self._outer = digest_cons()
@@ -84,21 +114,19 @@ def _init_old(self, key, msg, digestmod):
84114
if hasattr(self._inner, 'block_size'):
85115
blocksize = self._inner.block_size
86116
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)
90120
blocksize = self.blocksize
91121
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)
95125
blocksize = self.blocksize
96126

97127
if len(key) > blocksize:
98128
key = digest_cons(key).digest()
99129

100-
# self.blocksize is the default blocksize. self.block_size is
101-
# effective block size as well as the public API attribute.
102130
self.block_size = blocksize
103131

104132
key = key.ljust(blocksize, b'\0')
@@ -164,6 +192,7 @@ def hexdigest(self):
164192
h = self._current()
165193
return h.hexdigest()
166194

195+
167196
def new(key, msg=None, digestmod=''):
168197
"""Create a new hashing object and return it.
169198
@@ -183,7 +212,6 @@ def new(key, msg=None, digestmod=''):
183212
"""
184213
return HMAC(key, msg, digestmod)
185214

186-
187215
def digest(key, msg, digest):
188216
"""Fast inline implementation of HMAC.
189217
@@ -199,19 +227,22 @@ def digest(key, msg, digest):
199227
except _hashopenssl.UnsupportedDigestmodError:
200228
pass
201229

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)
208237

238+
def _compute_digest_fallback(key, msg, digest):
239+
digest_cons = _get_digest_constructor(digest)
209240
inner = digest_cons()
210241
outer = digest_cons()
211242
blocksize = getattr(inner, 'block_size', 64)
212243
if len(key) > blocksize:
213244
key = digest_cons(key).digest()
214-
key = key + b'\x00' * (blocksize - len(key))
245+
key = key.ljust(blocksize, b'\0')
215246
inner.update(key.translate(trans_36))
216247
outer.update(key.translate(trans_5C))
217248
inner.update(msg)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy