diff -r 646c2388d8f5 Doc/library/hmac.rst --- a/Doc/library/hmac.rst Sun Aug 18 12:43:24 2013 +0200 +++ b/Doc/library/hmac.rst Sun Aug 18 17:32:07 2013 +0200 @@ -18,13 +18,22 @@ 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 :func:`hashlib.md5` constructor. + *digestmod* is the digest constructor, module or hash name for the HMAC + object to use. The hash name may be any string suitable for + :func:`hashlib.new`. It defaults to the :func:`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`. + Parameter *digestmod* may be a string suitable for :func:`hashlib.new`, + too. + + .. deprecated:: 3.4 + func:`hashlib.md5` as implicit default is deprecated. HMAC-MD5 is still + supported with ``HMAC.new(key, msg, digestmod='md5')``. + + An HMAC object has the following methods: .. method:: HMAC.update(msg) diff -r 646c2388d8f5 Lib/hmac.py --- a/Lib/hmac.py Sun Aug 18 12:43:24 2013 +0200 +++ b/Lib/hmac.py Sun Aug 18 17:32:07 2013 +0200 @@ -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.5. 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.", DeprecationWarning, 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 -r 646c2388d8f5 Lib/imaplib.py --- a/Lib/imaplib.py Sun Aug 18 12:43:24 2013 +0200 +++ b/Lib/imaplib.py Sun Aug 18 17:32:07 2013 +0200 @@ -542,7 +542,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 -r 646c2388d8f5 Lib/multiprocessing/connection.py --- a/Lib/multiprocessing/connection.py Sun Aug 18 12:43:24 2013 +0200 +++ b/Lib/multiprocessing/connection.py Sun Aug 18 17:32:07 2013 +0200 @@ -718,7 +718,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) @@ -732,7 +732,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 -r 646c2388d8f5 Lib/smtplib.py --- a/Lib/smtplib.py Sun Aug 18 12:43:24 2013 +0200 +++ b/Lib/smtplib.py Sun Aug 18 17:32:07 2013 +0200 @@ -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 -r 646c2388d8f5 Lib/test/test_hmac.py --- a/Lib/test/test_hmac.py Sun Aug 18 12:43:24 2013 +0200 +++ b/Lib/test/test_hmac.py Sun Aug 18 17:32:07 2013 +0200 @@ -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,7 +230,21 @@ hmac.HMAC(b'a', b'b', digestmod=MockCrazyHash) self.fail('Expected warning about small block_size') + def test_with_digestmod_warning(self): + def md5test(key, data, digest): + h = hmac.HMAC(key, data) + self.assertEqual(h.hexdigest().upper(), digest.upper()) + with warnings.catch_warnings(): + warnings.simplefilter('error', DeprecationWarning) + with self.assertRaises(DeprecationWarning): + md5test(b"\x0b" * 16, + b"Hi There", + "9294727A3638BB1C13F48EF8158BFC9D") + + md5test(b"\x0b" * 16, + b"Hi There", + "9294727A3638BB1C13F48EF8158BFC9D") class ConstructorTestCase(unittest.TestCase): diff -r 646c2388d8f5 Lib/test/test_pep247.py --- a/Lib/test/test_pep247.py Sun Aug 18 12:43:24 2013 +0200 +++ b/Lib/test/test_pep247.py Sun Aug 18 17:32:07 2013 +0200 @@ -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: