Skip to content

Commit

Permalink
bpo-33604: Raise TypeError on missing hmac arg. (GH-16805)
Browse files Browse the repository at this point in the history
Also updates the documentation to clarify the situation surrounding
the digestmod parameter that is required despite its position in the
argument list as of 3.8.0 as well as removing old python2 era
references to "binary strings".

We indavertently had this raise ValueError in 3.8.0 for the missing
arg.  This is not considered an API change as no reasonable code would
be catching this missing argument error in order to handle it.
(cherry picked from commit f33c57d)

Co-authored-by: Gregory P. Smith <[email protected]>
  • Loading branch information
miss-islington and gpshead authored Oct 18, 2019
1 parent 7773d39 commit c615db6
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 28 deletions.
8 changes: 5 additions & 3 deletions Doc/library/hmac.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@
This module implements the HMAC algorithm as described by :rfc:`2104`.


.. function:: new(key, msg=None, digestmod=None)
.. function:: new(key, msg=None, digestmod='')

Return a new hmac object. *key* is a bytes or bytearray object giving the
secret key. If *msg* is present, the method call ``update(msg)`` is made.
*digestmod* is the digest name, digest constructor or module for the HMAC
object to use. It supports any name suitable to :func:`hashlib.new`.
object to use. It may be any name suitable to :func:`hashlib.new`.
Despite its argument position, it is required.

.. versionchanged:: 3.4
Parameter *key* can be a bytes or bytearray object.
Expand All @@ -28,6 +29,8 @@ This module implements the HMAC algorithm as described by :rfc:`2104`.

.. deprecated-removed:: 3.4 3.8
MD5 as implicit default digest for *digestmod* is deprecated.
The digestmod parameter is now required. Pass it as a keyword
argument to avoid awkwardness when you do not have an initial msg.


.. function:: digest(key, msg, digest)
Expand Down Expand Up @@ -127,7 +130,6 @@ This module also provides the following helper function:
a timing attack could theoretically reveal information about the
types and lengths of *a* and *b*—but not their values.


.. versionadded:: 3.3


Expand Down
53 changes: 29 additions & 24 deletions Lib/hmac.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""HMAC (Keyed-Hashing for Message Authentication) Python module.
"""HMAC (Keyed-Hashing for Message Authentication) module.
Implements the HMAC algorithm as described by RFC 2104.
"""
Expand Down Expand Up @@ -30,23 +30,25 @@ class HMAC:
"""
blocksize = 64 # 512-bit HMAC; can be changed in subclasses.

def __init__(self, key, msg = None, digestmod = None):
def __init__(self, key, msg=None, digestmod=''):
"""Create a new HMAC object.
key: key for the keyed hash object.
msg: Initial input for the hash, if provided.
digestmod: Required. A module supporting PEP 247. *OR*
A hashlib constructor returning a new hash object. *OR*
A hash name suitable for hashlib.new().
key: bytes or buffer, key for the keyed hash object.
msg: bytes or buffer, Initial input for the hash or None.
digestmod: A hash name suitable for hashlib.new(). *OR*
A hashlib constructor returning a new hash object. *OR*
A module supporting PEP 247.
Note: key and msg must be a bytes or bytearray objects.
Required as of 3.8, despite its position after the optional
msg argument. Passing it as a keyword argument is
recommended, though not required for legacy API reasons.
"""

if not isinstance(key, (bytes, bytearray)):
raise TypeError("key: expected bytes or bytearray, but got %r" % type(key).__name__)

if digestmod is None:
raise ValueError('`digestmod` is required.')
if not digestmod:
raise TypeError("Missing required parameter 'digestmod'.")

if callable(digestmod):
self.digest_cons = digestmod
Expand Down Expand Up @@ -90,8 +92,7 @@ def name(self):
return "hmac-" + self.inner.name

def update(self, msg):
"""Update this hashing object with the string msg.
"""
"""Feed data from msg into this hashing object."""
self.inner.update(msg)

def copy(self):
Expand Down Expand Up @@ -119,7 +120,7 @@ def _current(self):
def digest(self):
"""Return the hash value of this hashing object.
This returns a string containing 8-bit data. The object is
This returns the hmac value as bytes. The object is
not altered in any way by this function; you can continue
updating the object after calling this function.
"""
Expand All @@ -132,30 +133,34 @@ def hexdigest(self):
h = self._current()
return h.hexdigest()

def new(key, msg = None, digestmod = None):
def new(key, msg=None, digestmod=''):
"""Create a new hashing object and return it.
key: The starting key for the hash.
msg: if available, will immediately be hashed into the object's starting
state.
key: bytes or buffer, The starting key for the hash.
msg: bytes or buffer, Initial input for the hash, or None.
digestmod: A hash name suitable for hashlib.new(). *OR*
A hashlib constructor returning a new hash object. *OR*
A module supporting PEP 247.
Required as of 3.8, despite its position after the optional
msg argument. Passing it as a keyword argument is
recommended, though not required for legacy API reasons.
You can now feed arbitrary strings into the object using its update()
You can now feed arbitrary bytes into the object using its update()
method, and can ask for the hash value at any time by calling its digest()
method.
or hexdigest() methods.
"""
return HMAC(key, msg, digestmod)


def digest(key, msg, digest):
"""Fast inline implementation of HMAC
"""Fast inline implementation of HMAC.
key: key for the keyed hash object.
msg: input message
key: bytes or buffer, The key for the keyed hash object.
msg: bytes or buffer, Input message.
digest: A hash name suitable for hashlib.new() for best performance. *OR*
A hashlib constructor returning a new hash object. *OR*
A module supporting PEP 247.
Note: key and msg must be a bytes or bytearray objects.
"""
if (_hashopenssl is not None and
isinstance(digest, str) and digest in _openssl_md_meths):
Expand Down
7 changes: 6 additions & 1 deletion Lib/test/test_hmac.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,10 +312,15 @@ def digest(self):
self.fail('Expected warning about small block_size')

def test_with_digestmod_no_default(self):
with self.assertRaises(ValueError):
"""The digestmod parameter is required as of Python 3.8."""
with self.assertRaisesRegex(TypeError, r'required.*digestmod'):
key = b"\x0b" * 16
data = b"Hi There"
hmac.HMAC(key, data, digestmod=None)
with self.assertRaisesRegex(TypeError, r'required.*digestmod'):
hmac.new(key, data)
with self.assertRaisesRegex(TypeError, r'required.*digestmod'):
hmac.HMAC(key, msg=data, digestmod='')


class ConstructorTestCase(unittest.TestCase):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fixed `hmac.new` and `hmac.HMAC` to raise TypeError instead of ValueError
when the digestmod parameter, now required in 3.8, is omitted. Also
clarified the hmac module documentation and docstrings.

0 comments on commit c615db6

Please sign in to comment.