-
-
Notifications
You must be signed in to change notification settings - Fork 34.5k
bpo-40645: use C implementation of HMAC #24920
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
5cb5319
6dd9fa9
3782553
f76bf5d
504e08e
83ae2d3
9ff2d86
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,10 +8,8 @@ | |
| import _hashlib as _hashopenssl | ||
| except ImportError: | ||
| _hashopenssl = None | ||
| _openssl_md_meths = None | ||
| from _operator import _compare_digest as compare_digest | ||
| else: | ||
| _openssl_md_meths = frozenset(_hashopenssl.openssl_md_meth_names) | ||
| compare_digest = _hashopenssl.compare_digest | ||
| import hashlib as _hashlib | ||
|
|
||
|
|
@@ -23,7 +21,6 @@ | |
| digest_size = None | ||
|
|
||
|
|
||
|
|
||
| class HMAC: | ||
| """RFC 2104 HMAC class. Also complies with RFC 4231. | ||
|
|
||
|
|
@@ -32,7 +29,7 @@ class HMAC: | |
| blocksize = 64 # 512-bit HMAC; can be changed in subclasses. | ||
|
|
||
| __slots__ = ( | ||
| "_digest_cons", "_inner", "_outer", "block_size", "digest_size" | ||
| "_hmac", "_inner", "_outer", "_name", "block_size", "digest_size" | ||
| ) | ||
|
|
||
| def __init__(self, key, msg=None, digestmod=''): | ||
|
|
@@ -55,15 +52,32 @@ def __init__(self, key, msg=None, digestmod=''): | |
| if not digestmod: | ||
| raise TypeError("Missing required parameter 'digestmod'.") | ||
|
|
||
| if ( | ||
| _hashopenssl is not None and | ||
| (digestname := _hashlib._digestmod_to_name(digestmod)) | ||
| ): | ||
| self._init_hmac(key, msg, digestname) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This makes the assumption that if we have presumably the easiest way around this while retaining the optimal openssl computation when possible is to catch an exception from a test should be added to cover this case. (via a custom digestmod-like-object with a name and blocksize perhaps) |
||
| self._inner = self._outer = None | ||
| else: | ||
| self._init_old(key, msg, digestmod) | ||
| self._hmac = None | ||
|
|
||
| def _init_hmac(self, key, msg, digestname): | ||
| self._hmac = _hashopenssl.hmac_new(key, msg, digestmod=digestname) | ||
| self._name = digestname | ||
| self.digest_size = self._hmac.digest_size | ||
| self.block_size = self._hmac.block_size | ||
|
|
||
| def _init_old(self, key, msg, digestmod): | ||
| if callable(digestmod): | ||
| self._digest_cons = digestmod | ||
| digest_cons = digestmod | ||
| elif isinstance(digestmod, str): | ||
| self._digest_cons = lambda d=b'': _hashlib.new(digestmod, d) | ||
| digest_cons = lambda d=b'': _hashlib.new(digestmod, d) | ||
| else: | ||
| self._digest_cons = lambda d=b'': digestmod.new(d) | ||
| digest_cons = lambda d=b'': digestmod.new(d) | ||
|
|
||
| self._outer = self._digest_cons() | ||
| self._inner = self._digest_cons() | ||
| self._outer = digest_cons() | ||
| self._inner = digest_cons() | ||
| self.digest_size = self._inner.digest_size | ||
|
|
||
| if hasattr(self._inner, 'block_size'): | ||
|
|
@@ -79,13 +93,13 @@ def __init__(self, key, msg=None, digestmod=''): | |
| RuntimeWarning, 2) | ||
| blocksize = self.blocksize | ||
|
|
||
| if len(key) > blocksize: | ||
| key = digest_cons(key).digest() | ||
|
|
||
| # self.blocksize is the default blocksize. self.block_size is | ||
| # effective block size as well as the public API attribute. | ||
| self.block_size = blocksize | ||
|
|
||
| if len(key) > blocksize: | ||
| key = self._digest_cons(key).digest() | ||
|
|
||
| key = key.ljust(blocksize, b'\0') | ||
| self._outer.update(key.translate(trans_5C)) | ||
| self._inner.update(key.translate(trans_36)) | ||
|
|
@@ -94,23 +108,14 @@ def __init__(self, key, msg=None, digestmod=''): | |
|
|
||
| @property | ||
| def name(self): | ||
| return "hmac-" + self._inner.name | ||
|
|
||
| @property | ||
| def digest_cons(self): | ||
| return self._digest_cons | ||
|
|
||
| @property | ||
| def inner(self): | ||
| return self._inner | ||
|
|
||
| @property | ||
| def outer(self): | ||
| return self._outer | ||
| return "hmac-" + self._name | ||
|
|
||
| def update(self, msg): | ||
| """Feed data from msg into this hashing object.""" | ||
| self._inner.update(msg) | ||
| obj = self._hmac | ||
|
tiran marked this conversation as resolved.
Outdated
|
||
| if obj is None: | ||
| obj = self._inner | ||
| obj.update(msg) | ||
|
|
||
| def copy(self): | ||
| """Return a separate copy of this hashing object. | ||
|
|
@@ -119,10 +124,15 @@ def copy(self): | |
| """ | ||
| # Call __new__ directly to avoid the expensive __init__. | ||
| other = self.__class__.__new__(self.__class__) | ||
| other._digest_cons = self._digest_cons | ||
| other.digest_size = self.digest_size | ||
| other._inner = self._inner.copy() | ||
| other._outer = self._outer.copy() | ||
| other._name = self._name | ||
| if self._hmac is not None: | ||
|
tiran marked this conversation as resolved.
Outdated
|
||
| other._hmac = self._hmac.copy() | ||
| other._inner = other._outer = None | ||
| else: | ||
| other._hmac = None | ||
| other._inner = self._inner.copy() | ||
| other._outer = self._outer.copy() | ||
| return other | ||
|
|
||
| def _current(self): | ||
|
|
@@ -141,14 +151,20 @@ def digest(self): | |
| not altered in any way by this function; you can continue | ||
| updating the object after calling this function. | ||
| """ | ||
| h = self._current() | ||
| return h.digest() | ||
| if self._hmac is not None: | ||
| return self._hmac.digest() | ||
| else: | ||
| h = self._current() | ||
|
tiran marked this conversation as resolved.
Outdated
|
||
| return h.digest() | ||
|
|
||
| def hexdigest(self): | ||
| """Like digest(), but returns a string of hexadecimal digits instead. | ||
| """ | ||
| h = self._current() | ||
| return h.hexdigest() | ||
| if self._hmac is not None: | ||
| return self._hmac.hexdigest() | ||
| else: | ||
| h = self._current() | ||
| return h.hexdigest() | ||
|
|
||
| def new(key, msg=None, digestmod=''): | ||
| """Create a new hashing object and return it. | ||
|
|
@@ -179,9 +195,11 @@ def digest(key, msg, digest): | |
| A hashlib constructor returning a new hash object. *OR* | ||
| A module supporting PEP 247. | ||
| """ | ||
| if (_hashopenssl is not None and | ||
| isinstance(digest, str) and digest in _openssl_md_meths): | ||
| return _hashopenssl.hmac_digest(key, msg, digest) | ||
| if ( | ||
| _hashopenssl is not None and | ||
| (digestname := _hashlib._digestmod_to_name(digest)) | ||
| ): | ||
| return _hashopenssl.hmac_digest(key, msg, digestname) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fallback logic needed here as when digestname isn't supported by openssl. |
||
|
|
||
| if callable(digest): | ||
| digest_cons = digest | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.