[Python-checkins] cpython: Issue #17276: MD5 as default digestmod for HMAC is deprecated. The HMAC
christian.heimes
python-checkins at python.org
Wed Nov 20 17:23:32 CET 2013
http://hg.python.org/cpython/rev/86107e7e6ee5
changeset: 87296:86107e7e6ee5
parent: 87294:1b58f14f5d60
user: Christian Heimes <christian at cheimes.de>
date: Wed Nov 20 17:23:06 2013 +0100
summary:
Issue #17276: MD5 as default digestmod for HMAC is deprecated. The HMAC
module supports digestmod names, e.g. hmac.HMAC('sha1').
files:
Doc/library/hmac.rst | 11 ++++++-
Doc/whatsnew/3.4.rst | 3 ++
Lib/hmac.py | 13 +++++++--
Lib/imaplib.py | 2 +-
Lib/multiprocessing/connection.py | 4 +-
Lib/smtplib.py | 2 +-
Lib/test/test_hmac.py | 27 ++++++++++++++----
Lib/test/test_pep247.py | 12 ++++---
Misc/NEWS | 3 ++
9 files changed, 57 insertions(+), 20 deletions(-)
diff --git a/Doc/library/hmac.rst b/Doc/library/hmac.rst
--- a/Doc/library/hmac.rst
+++ b/Doc/library/hmac.rst
@@ -18,13 +18,20 @@
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)
diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst
--- a/Doc/whatsnew/3.4.rst
+++ b/Doc/whatsnew/3.4.rst
@@ -842,6 +842,9 @@
* 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
-------------------------------------------
diff --git a/Lib/hmac.py b/Lib/hmac.py
--- a/Lib/hmac.py
+++ b/Lib/hmac.py
@@ -5,6 +5,7 @@
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 @@
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 @@
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)
diff --git a/Lib/imaplib.py b/Lib/imaplib.py
--- a/Lib/imaplib.py
+++ b/Lib/imaplib.py
@@ -554,7 +554,7 @@
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):
diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py
--- a/Lib/multiprocessing/connection.py
+++ b/Lib/multiprocessing/connection.py
@@ -719,7 +719,7 @@
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 @@
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:
diff --git a/Lib/smtplib.py b/Lib/smtplib.py
--- a/Lib/smtplib.py
+++ b/Lib/smtplib.py
@@ -579,7 +579,7 @@
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):
diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py
--- a/Lib/test/test_hmac.py
+++ b/Lib/test/test_hmac.py
@@ -10,7 +10,9 @@
# 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 @@
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 @@
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 @@
})
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 @@
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):
diff --git a/Lib/test/test_pep247.py b/Lib/test/test_pep247.py
--- a/Lib/test/test_pep247.py
+++ b/Lib/test/test_pep247.py
@@ -15,12 +15,14 @@
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:
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -59,6 +59,9 @@
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.
--
Repository URL: http://hg.python.org/cpython
More information about the Python-checkins
mailing list