Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Doc/library/hmac.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
52 changes: 34 additions & 18 deletions Lib/hmac.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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),
Expand All @@ -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.
Expand All @@ -102,19 +118,19 @@ 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):
"""Return a hash object for the current state.

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):
Expand Down
21 changes: 16 additions & 5 deletions Lib/test/test_hmac.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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):
Expand Down
Original file line number Diff line number Diff line change
@@ -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.