diff --git a/django/utils/crypto.py b/django/utils/crypto.py index 1edbb43eb3..70a07e7fde 100644 --- a/django/utils/crypto.py +++ b/django/utils/crypto.py @@ -50,7 +50,7 @@ def salted_hmac(key_salt, value, secret=None): # line is redundant and could be replaced by key = key_salt + secret, since # the hmac module does the same thing for keys longer than the block size. # However, we need to ensure that we *always* do this. - return hmac.new(key, msg=value, digestmod=hashlib.sha1) + return hmac.new(key, msg=smart_bytes(value), digestmod=hashlib.sha1) def get_random_string(length=12, @@ -99,7 +99,7 @@ def _bin_to_long(x): This is a clever optimization for fast xor vector math """ - return int(x.encode('hex'), 16) + return int(binascii.hexlify(x), 16) def _long_to_bin(x, hex_format_string): @@ -112,13 +112,14 @@ def _long_to_bin(x, hex_format_string): def _fast_hmac(key, msg, digest): """ - A trimmed down version of Python's HMAC implementation + A trimmed down version of Python's HMAC implementation. + + This function operates on bytes. """ dig1, dig2 = digest(), digest() - key = smart_bytes(key) if len(key) > dig1.block_size: key = digest(key).digest() - key += chr(0) * (dig1.block_size - len(key)) + key += b'\x00' * (dig1.block_size - len(key)) dig1.update(key.translate(_trans_36)) dig1.update(msg) dig2.update(key.translate(_trans_5c)) diff --git a/tests/regressiontests/utils/crypto.py b/tests/regressiontests/utils/crypto.py index 2bdc5ba530..52a286cb27 100644 --- a/tests/regressiontests/utils/crypto.py +++ b/tests/regressiontests/utils/crypto.py @@ -1,4 +1,6 @@ +from __future__ import unicode_literals +import binascii import math import timeit import hashlib @@ -108,15 +110,15 @@ class TestUtilsCryptoPBKDF2(unittest.TestCase): "c4007d5298f9033c0241d5ab69305e7b64eceeb8d" "834cfec"), }, - # Check leading zeros are not stripped (#17481) + # Check leading zeros are not stripped (#17481) { - "args": { - "password": chr(186), - "salt": "salt", - "iterations": 1, - "dklen": 20, - "digest": hashlib.sha1, - }, + "args": { + "password": b'\xba', + "salt": "salt", + "iterations": 1, + "dklen": 20, + "digest": hashlib.sha1, + }, "result": '0053d3b91a7f1e54effebd6d68771e8a6e0b2c5b', }, ] @@ -124,12 +126,14 @@ class TestUtilsCryptoPBKDF2(unittest.TestCase): def test_public_vectors(self): for vector in self.rfc_vectors: result = pbkdf2(**vector['args']) - self.assertEqual(result.encode('hex'), vector['result']) + self.assertEqual(binascii.hexlify(result).decode('ascii'), + vector['result']) def test_regression_vectors(self): for vector in self.regression_vectors: result = pbkdf2(**vector['args']) - self.assertEqual(result.encode('hex'), vector['result']) + self.assertEqual(binascii.hexlify(result).decode('ascii'), + vector['result']) def test_performance_scalability(self): """ @@ -140,11 +144,11 @@ class TestUtilsCryptoPBKDF2(unittest.TestCase): # to run the test suite and false positives caused by imprecise # measurement. n1, n2 = 200000, 800000 - elapsed = lambda f: timeit.Timer(f, + elapsed = lambda f: timeit.Timer(f, 'from django.utils.crypto import pbkdf2').timeit(number=1) t1 = elapsed('pbkdf2("password", "salt", iterations=%d)' % n1) t2 = elapsed('pbkdf2("password", "salt", iterations=%d)' % n2) measured_scale_exponent = math.log(t2 / t1, n2 / n1) - # This should be less than 1. We allow up to 1.2 so that tests don't + # This should be less than 1. We allow up to 1.2 so that tests don't # fail nondeterministically too often. self.assertLess(measured_scale_exponent, 1.2)