[Python-checkins] bpo-40645: Deprecated internal details of hmac.HMAC (GH-20132)

Christian Heimes webhook-mailer at python.org
Sat May 16 19:05:44 EDT 2020


https://github.com/python/cpython/commit/837f9e42e3a1ad03b340661afe85e67d2719334f
commit: 837f9e42e3a1ad03b340661afe85e67d2719334f
branch: master
author: Christian Heimes <christian at python.org>
committer: GitHub <noreply at github.com>
date: 2020-05-17T01:05:40+02:00
summary:

bpo-40645: Deprecated internal details of hmac.HMAC (GH-20132)

files:
A Misc/NEWS.d/next/Library/2020-05-16-19-34-38.bpo-40645.7ibMt-.rst
M Doc/library/hmac.rst
M Lib/hmac.py
M Lib/test/test_hmac.py

diff --git a/Doc/library/hmac.rst b/Doc/library/hmac.rst
index 57ac8bb16120f..5ad348490eaf6 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 b769876e6f774..54a1ef9bdbdcf 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 @@ def __init__(self, key, msg=None, digestmod=''):
             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 @@ def __init__(self, key, msg=None, digestmod=''):
         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 @@ 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_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 @@ def _current(self):
 
         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 08086f0e78c83..1f3ec4cb9172d 100644
--- a/Lib/test/test_hmac.py
+++ b/Lib/test/test_hmac.py
@@ -409,11 +409,11 @@ def test_attributes(self):
         # 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 @@ def test_realcopy(self):
         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 0000000000000..19d5a651eb49a
--- /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.



More information about the Python-checkins mailing list