]> granicus.if.org Git - python/commitdiff
Issue #17276: MD5 as default digestmod for HMAC is deprecated. The HMAC
authorChristian Heimes <christian@cheimes.de>
Wed, 20 Nov 2013 16:23:06 +0000 (17:23 +0100)
committerChristian Heimes <christian@cheimes.de>
Wed, 20 Nov 2013 16:23:06 +0000 (17:23 +0100)
module supports digestmod names, e.g. hmac.HMAC('sha1').

Doc/library/hmac.rst
Doc/whatsnew/3.4.rst
Lib/hmac.py
Lib/imaplib.py
Lib/multiprocessing/connection.py
Lib/smtplib.py
Lib/test/test_hmac.py
Lib/test/test_pep247.py
Misc/NEWS

index 1d928ea86002a2d9edfeda66c222135e8767b219..8fa1435f881a043669e4ea633205a164fe03b6c4 100644 (file)
@@ -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)
index 19b20bf0f04cc31dca174cbe8ead51ac31d33bb1..42a6fef67eef81853825c54e50a4393410c2c35e 100644 (file)
@@ -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
 -------------------------------------------
index d13b205bbb9eeee98dc601cb2cdec949c9aeb670..873327bf37c1d36cfb05023f276b9982cb85af69 100644 (file)
@@ -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)
 
index 0994f2bd01eda98a3c9db0febecf84d83dbd6d2c..b29487acdd0341327a63a9fb80bdad5b67167fef 100644 (file)
@@ -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):
index 27fda9f22c68d18b3bf3cca6b09443e42480f2a8..8ad6fd5f3e5cb2811cb09749ecfb2ff700088c9f 100644 (file)
@@ -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:
index e1a32ed0c80de7590074c265ac36403eb9ff7ee9..69ae845dca43c69dc14b8fa02b3e801ce333ff6a 100644 (file)
@@ -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):
index efd63ad7be500903825c62da26b6aa258a81baf0..eb23b26578cb288d66152de0d055cb1d32678e5b 100644 (file)
@@ -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):
index 7f104728565b9f8438bb000cbb1d5f52bb9158c8..b85a26ad65685f671d9f33e70497ba1e9bc51171 100644 (file)
@@ -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:
index 62f1c3b98ae8f193b3551676ceb29fe63fe83001..1aaf8c159bbef80d543a89acc7667a63c59e286f 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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.