Issue #17276: MD5 as default digestmod for HMAC is deprecated. The HMAC
module supports digestmod names, e.g. hmac.HMAC('sha1').
This commit is contained in:
parent
7f48396cb5
commit
634919a9fa
|
@ -18,13 +18,20 @@ This module implements the HMAC algorithm as described by :rfc:`2104`.
|
|||
|
||||
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 constructor or module for the HMAC object to use.
|
||||
It defaults to the :data:`hashlib.md5` constructor.
|
||||
*digestmod* is the digest name, digest constructor or module for the HMAC
|
||||
object to use. It supports any name suitable to :func:`hashlib.new` and
|
||||
defaults to the :data:`hashlib.md5` constructor.
|
||||
|
||||
.. versionchanged:: 3.4
|
||||
Parameter *key* can be a bytes or bytearray object. Parameter *msg* can
|
||||
be of any type supported by :mod:`hashlib`.
|
||||
|
||||
Paramter *digestmod* can be the name of a hash algorithm.
|
||||
|
||||
.. deprecated:: 3.4
|
||||
MD5 as implicit default digest for *digestmod* is deprecated.
|
||||
|
||||
|
||||
An HMAC object has the following methods:
|
||||
|
||||
.. method:: HMAC.update(msg)
|
||||
|
|
|
@ -842,6 +842,9 @@ Deprecated Python modules, functions and methods
|
|||
* The :mod:`formatter` module is pending deprecation and is slated for removal
|
||||
in Python 3.6.
|
||||
|
||||
* MD5 as default digestmod for :mod:`hmac` is deprecated. Python 3.6 will
|
||||
require an explicit digest name or constructor as *digestmod* argument.
|
||||
|
||||
|
||||
Deprecated functions and types of the C API
|
||||
-------------------------------------------
|
||||
|
|
13
Lib/hmac.py
13
Lib/hmac.py
|
@ -5,6 +5,7 @@ Implements the HMAC algorithm as described by RFC 2104.
|
|||
|
||||
import warnings as _warnings
|
||||
from _operator import _compare_digest as compare_digest
|
||||
import hashlib as _hashlib
|
||||
|
||||
trans_5C = bytes((x ^ 0x5C) for x in range(256))
|
||||
trans_36 = bytes((x ^ 0x36) for x in range(256))
|
||||
|
@ -28,8 +29,11 @@ class HMAC:
|
|||
key: key for the keyed hash object.
|
||||
msg: Initial input for the hash, if provided.
|
||||
digestmod: A module supporting PEP 247. *OR*
|
||||
A hashlib constructor returning a new hash object.
|
||||
A hashlib constructor returning a new hash object. *OR*
|
||||
A hash name suitable for hashlib.new().
|
||||
Defaults to hashlib.md5.
|
||||
Implicit default to hashlib.md5 is deprecated and will be
|
||||
removed in Python 3.6.
|
||||
|
||||
Note: key and msg must be a bytes or bytearray objects.
|
||||
"""
|
||||
|
@ -38,11 +42,14 @@ class HMAC:
|
|||
raise TypeError("key: expected bytes or bytearray, but got %r" % type(key).__name__)
|
||||
|
||||
if digestmod is None:
|
||||
import hashlib
|
||||
digestmod = hashlib.md5
|
||||
_warnings.warn("HMAC() without an explicit digestmod argument "
|
||||
"is deprecated.", PendingDeprecationWarning, 2)
|
||||
digestmod = _hashlib.md5
|
||||
|
||||
if callable(digestmod):
|
||||
self.digest_cons = digestmod
|
||||
elif isinstance(digestmod, str):
|
||||
self.digest_cons = lambda d=b'': _hashlib.new(digestmod, d)
|
||||
else:
|
||||
self.digest_cons = lambda d=b'': digestmod.new(d)
|
||||
|
||||
|
|
|
@ -554,7 +554,7 @@ class IMAP4:
|
|||
import hmac
|
||||
pwd = (self.password.encode('ASCII') if isinstance(self.password, str)
|
||||
else self.password)
|
||||
return self.user + " " + hmac.HMAC(pwd, challenge).hexdigest()
|
||||
return self.user + " " + hmac.HMAC(pwd, challenge, 'md5').hexdigest()
|
||||
|
||||
|
||||
def logout(self):
|
||||
|
|
|
@ -719,7 +719,7 @@ def deliver_challenge(connection, authkey):
|
|||
assert isinstance(authkey, bytes)
|
||||
message = os.urandom(MESSAGE_LENGTH)
|
||||
connection.send_bytes(CHALLENGE + message)
|
||||
digest = hmac.new(authkey, message).digest()
|
||||
digest = hmac.new(authkey, message, 'md5').digest()
|
||||
response = connection.recv_bytes(256) # reject large message
|
||||
if response == digest:
|
||||
connection.send_bytes(WELCOME)
|
||||
|
@ -733,7 +733,7 @@ def answer_challenge(connection, authkey):
|
|||
message = connection.recv_bytes(256) # reject large message
|
||||
assert message[:len(CHALLENGE)] == CHALLENGE, 'message = %r' % message
|
||||
message = message[len(CHALLENGE):]
|
||||
digest = hmac.new(authkey, message).digest()
|
||||
digest = hmac.new(authkey, message, 'md5').digest()
|
||||
connection.send_bytes(digest)
|
||||
response = connection.recv_bytes(256) # reject large message
|
||||
if response != WELCOME:
|
||||
|
|
|
@ -579,7 +579,7 @@ class SMTP:
|
|||
def encode_cram_md5(challenge, user, password):
|
||||
challenge = base64.decodebytes(challenge)
|
||||
response = user + " " + hmac.HMAC(password.encode('ascii'),
|
||||
challenge).hexdigest()
|
||||
challenge, 'md5').hexdigest()
|
||||
return encode_base64(response.encode('ascii'), eol='')
|
||||
|
||||
def encode_plain(user, password):
|
||||
|
|
|
@ -10,7 +10,9 @@ class TestVectorsTestCase(unittest.TestCase):
|
|||
# Test the HMAC module against test vectors from the RFC.
|
||||
|
||||
def md5test(key, data, digest):
|
||||
h = hmac.HMAC(key, data)
|
||||
h = hmac.HMAC(key, data, digestmod=hashlib.md5)
|
||||
self.assertEqual(h.hexdigest().upper(), digest.upper())
|
||||
h = hmac.HMAC(key, data, digestmod='md5')
|
||||
self.assertEqual(h.hexdigest().upper(), digest.upper())
|
||||
|
||||
md5test(b"\x0b" * 16,
|
||||
|
@ -46,6 +48,9 @@ class TestVectorsTestCase(unittest.TestCase):
|
|||
def shatest(key, data, digest):
|
||||
h = hmac.HMAC(key, data, digestmod=hashlib.sha1)
|
||||
self.assertEqual(h.hexdigest().upper(), digest.upper())
|
||||
h = hmac.HMAC(key, data, digestmod='sha1')
|
||||
self.assertEqual(h.hexdigest().upper(), digest.upper())
|
||||
|
||||
|
||||
shatest(b"\x0b" * 20,
|
||||
b"Hi There",
|
||||
|
@ -76,10 +81,13 @@ class TestVectorsTestCase(unittest.TestCase):
|
|||
b"and Larger Than One Block-Size Data"),
|
||||
"e8e99d0f45237d786d6bbaa7965c7808bbff1a91")
|
||||
|
||||
def _rfc4231_test_cases(self, hashfunc):
|
||||
def _rfc4231_test_cases(self, hashfunc, hashname):
|
||||
def hmactest(key, data, hexdigests):
|
||||
h = hmac.HMAC(key, data, digestmod=hashfunc)
|
||||
self.assertEqual(h.hexdigest().lower(), hexdigests[hashfunc])
|
||||
h = hmac.HMAC(key, data, digestmod=hashname)
|
||||
self.assertEqual(h.hexdigest().lower(), hexdigests[hashfunc])
|
||||
|
||||
|
||||
# 4.2. Test Case 1
|
||||
hmactest(key = b'\x0b'*20,
|
||||
|
@ -189,16 +197,16 @@ class TestVectorsTestCase(unittest.TestCase):
|
|||
})
|
||||
|
||||
def test_sha224_rfc4231(self):
|
||||
self._rfc4231_test_cases(hashlib.sha224)
|
||||
self._rfc4231_test_cases(hashlib.sha224, 'sha224')
|
||||
|
||||
def test_sha256_rfc4231(self):
|
||||
self._rfc4231_test_cases(hashlib.sha256)
|
||||
self._rfc4231_test_cases(hashlib.sha256, 'sha256')
|
||||
|
||||
def test_sha384_rfc4231(self):
|
||||
self._rfc4231_test_cases(hashlib.sha384)
|
||||
self._rfc4231_test_cases(hashlib.sha384, 'sha384')
|
||||
|
||||
def test_sha512_rfc4231(self):
|
||||
self._rfc4231_test_cases(hashlib.sha512)
|
||||
self._rfc4231_test_cases(hashlib.sha512, 'sha512')
|
||||
|
||||
def test_legacy_block_size_warnings(self):
|
||||
class MockCrazyHash(object):
|
||||
|
@ -222,6 +230,13 @@ class TestVectorsTestCase(unittest.TestCase):
|
|||
hmac.HMAC(b'a', b'b', digestmod=MockCrazyHash)
|
||||
self.fail('Expected warning about small block_size')
|
||||
|
||||
def test_with_digestmod_warning(self):
|
||||
with self.assertWarns(PendingDeprecationWarning):
|
||||
key = b"\x0b" * 16
|
||||
data = b"Hi There"
|
||||
digest = "9294727A3638BB1C13F48EF8158BFC9D"
|
||||
h = hmac.HMAC(key, data)
|
||||
self.assertEqual(h.hexdigest().upper(), digest)
|
||||
|
||||
|
||||
class ConstructorTestCase(unittest.TestCase):
|
||||
|
|
|
@ -15,12 +15,14 @@ class Pep247Test(unittest.TestCase):
|
|||
self.assertTrue(module.digest_size is None or module.digest_size > 0)
|
||||
self.check_object(module.new, module.digest_size, key)
|
||||
|
||||
def check_object(self, cls, digest_size, key):
|
||||
def check_object(self, cls, digest_size, key, digestmod=None):
|
||||
if key is not None:
|
||||
obj1 = cls(key)
|
||||
obj2 = cls(key, b'string')
|
||||
h1 = cls(key, b'string').digest()
|
||||
obj3 = cls(key)
|
||||
if digestmod is None:
|
||||
digestmod = md5
|
||||
obj1 = cls(key, digestmod=digestmod)
|
||||
obj2 = cls(key, b'string', digestmod=digestmod)
|
||||
h1 = cls(key, b'string', digestmod=digestmod).digest()
|
||||
obj3 = cls(key, digestmod=digestmod)
|
||||
obj3.update(b'string')
|
||||
h2 = obj3.digest()
|
||||
else:
|
||||
|
|
|
@ -59,6 +59,9 @@ Core and Builtins
|
|||
Library
|
||||
-------
|
||||
|
||||
- Issue #17276: MD5 as default digestmod for HMAC is deprecated. The HMAC
|
||||
module supports digestmod names, e.g. hmac.HMAC('sha1').
|
||||
|
||||
- Issue #19449: in csv's writerow, handle non-string keys when generating the
|
||||
error message that certain keys are not in the 'fieldnames' list.
|
||||
|
||||
|
|
Loading…
Reference in New Issue