From 837f9e42e3a1ad03b340661afe85e67d2719334f Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sun, 17 May 2020 01:05:40 +0200 Subject: [PATCH] bpo-40645: Deprecated internal details of hmac.HMAC (GH-20132) --- Doc/library/hmac.rst | 6 +++ Lib/hmac.py | 52 ++++++++++++------- Lib/test/test_hmac.py | 21 ++++++-- .../2020-05-16-19-34-38.bpo-40645.7ibMt-.rst | 3 ++ 4 files changed, 59 insertions(+), 23 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2020-05-16-19-34-38.bpo-40645.7ibMt-.rst diff --git a/Doc/library/hmac.rst b/Doc/library/hmac.rst index 57ac8bb1612..5ad348490ea 100644 --- a/Doc/library/hmac.rst +++ b/Doc/library/hmac.rst @@ -114,6 +114,12 @@ A hash object has the following attributes: .. versionadded:: 3.4 +.. deprecated:: 3.9 + + The undocumented attributes ``HMAC.digest_cons``, ``HMAC.inner``, and + ``HMAC.outer`` are internal implementation details and will be removed in + Python 3.10. + This module also provides the following helper function: .. function:: compare_digest(a, b) diff --git a/Lib/hmac.py b/Lib/hmac.py index b769876e6f7..54a1ef9bdbd 100644 --- a/Lib/hmac.py +++ b/Lib/hmac.py @@ -30,6 +30,10 @@ class HMAC: """ blocksize = 64 # 512-bit HMAC; can be changed in subclasses. + __slots__ = ( + "_digest_cons", "_inner", "_outer", "block_size", "digest_size" + ) + def __init__(self, key, msg=None, digestmod=''): """Create a new HMAC object. @@ -51,18 +55,18 @@ class HMAC: raise TypeError("Missing required parameter 'digestmod'.") if callable(digestmod): - self.digest_cons = digestmod + self._digest_cons = digestmod elif isinstance(digestmod, str): - self.digest_cons = lambda d=b'': _hashlib.new(digestmod, d) + self._digest_cons = lambda d=b'': _hashlib.new(digestmod, d) else: - self.digest_cons = lambda d=b'': digestmod.new(d) + self._digest_cons = lambda d=b'': digestmod.new(d) - self.outer = self.digest_cons() - self.inner = self.digest_cons() - self.digest_size = self.inner.digest_size + self._outer = self._digest_cons() + self._inner = self._digest_cons() + self.digest_size = self._inner.digest_size - if hasattr(self.inner, 'block_size'): - blocksize = self.inner.block_size + if hasattr(self._inner, 'block_size'): + blocksize = self._inner.block_size if blocksize < 16: _warnings.warn('block_size of %d seems too small; using our ' 'default of %d.' % (blocksize, self.blocksize), @@ -79,21 +83,33 @@ class HMAC: self.block_size = blocksize if len(key) > blocksize: - key = self.digest_cons(key).digest() + 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)) + self._outer.update(key.translate(trans_5C)) + self._inner.update(key.translate(trans_36)) if msg is not None: self.update(msg) @property def name(self): - return "hmac-" + self.inner.name + 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 def update(self, msg): """Feed data from msg into this hashing object.""" - self.inner.update(msg) + self._inner.update(msg) def copy(self): """Return a separate copy of this hashing object. @@ -102,10 +118,10 @@ class HMAC: """ # Call __new__ directly to avoid the expensive __init__. other = self.__class__.__new__(self.__class__) - other.digest_cons = self.digest_cons + other._digest_cons = self._digest_cons other.digest_size = self.digest_size - other.inner = self.inner.copy() - other.outer = self.outer.copy() + other._inner = self._inner.copy() + other._outer = self._outer.copy() return other def _current(self): @@ -113,8 +129,8 @@ class HMAC: To be used only internally with digest() and hexdigest(). """ - h = self.outer.copy() - h.update(self.inner.digest()) + h = self._outer.copy() + h.update(self._inner.digest()) return h def digest(self): diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py index 08086f0e78c..1f3ec4cb917 100644 --- a/Lib/test/test_hmac.py +++ b/Lib/test/test_hmac.py @@ -409,11 +409,11 @@ class CopyTestCase(unittest.TestCase): # Testing if attributes are of same type. h1 = hmac.HMAC(b"key", digestmod="sha256") h2 = h1.copy() - self.assertTrue(h1.digest_cons == h2.digest_cons, + self.assertTrue(h1._digest_cons == h2._digest_cons, "digest constructors don't match.") - self.assertEqual(type(h1.inner), type(h2.inner), + self.assertEqual(type(h1._inner), type(h2._inner), "Types of inner don't match.") - self.assertEqual(type(h1.outer), type(h2.outer), + self.assertEqual(type(h1._outer), type(h2._outer), "Types of outer don't match.") @hashlib_helper.requires_hashdigest('sha256') @@ -423,10 +423,21 @@ class CopyTestCase(unittest.TestCase): h2 = h1.copy() # Using id() in case somebody has overridden __eq__/__ne__. self.assertTrue(id(h1) != id(h2), "No real copy of the HMAC instance.") - self.assertTrue(id(h1.inner) != id(h2.inner), + self.assertTrue(id(h1._inner) != id(h2._inner), "No real copy of the attribute 'inner'.") - self.assertTrue(id(h1.outer) != id(h2.outer), + self.assertTrue(id(h1._outer) != id(h2._outer), "No real copy of the attribute 'outer'.") + self.assertEqual(h1._inner, h1.inner) + self.assertEqual(h1._outer, h1.outer) + self.assertEqual(h1._digest_cons, h1.digest_cons) + + @hashlib_helper.requires_hashdigest('sha256') + def test_properties(self): + # deprecated properties + h1 = hmac.HMAC(b"key", digestmod="sha256") + self.assertEqual(h1._inner, h1.inner) + self.assertEqual(h1._outer, h1.outer) + self.assertEqual(h1._digest_cons, h1.digest_cons) @hashlib_helper.requires_hashdigest('sha256') def test_equality(self): diff --git a/Misc/NEWS.d/next/Library/2020-05-16-19-34-38.bpo-40645.7ibMt-.rst b/Misc/NEWS.d/next/Library/2020-05-16-19-34-38.bpo-40645.7ibMt-.rst new file mode 100644 index 00000000000..19d5a651eb4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-05-16-19-34-38.bpo-40645.7ibMt-.rst @@ -0,0 +1,3 @@ +The :class:`hmac.HMAC` exposes internal implementation details. The +attributes ``digest_cons``, ``inner``, and ``outer`` are deprecated and will +be removed in the future.