diff options
Diffstat (limited to 'tests/auth_tests/test_hashers.py')
-rw-r--r-- | tests/auth_tests/test_hashers.py | 58 |
1 files changed, 57 insertions, 1 deletions
diff --git a/tests/auth_tests/test_hashers.py b/tests/auth_tests/test_hashers.py index d79a246276..ecd3f276a9 100644 --- a/tests/auth_tests/test_hashers.py +++ b/tests/auth_tests/test_hashers.py @@ -10,9 +10,10 @@ from django.contrib.auth.hashers import ( check_password, get_hasher, identify_hasher, is_password_usable, make_password, ) -from django.test import SimpleTestCase +from django.test import SimpleTestCase, mock from django.test.utils import override_settings from django.utils import six +from django.utils.encoding import force_bytes try: import crypt @@ -214,6 +215,28 @@ class TestUtilsHashPass(SimpleTestCase): finally: hasher.rounds = old_rounds + @skipUnless(bcrypt, "bcrypt not installed") + def test_bcrypt_harden_runtime(self): + hasher = get_hasher('bcrypt') + self.assertEqual('bcrypt', hasher.algorithm) + + with mock.patch.object(hasher, 'rounds', 4): + encoded = make_password('letmein', hasher='bcrypt') + + with mock.patch.object(hasher, 'rounds', 6), \ + mock.patch.object(hasher, 'encode', side_effect=hasher.encode): + hasher.harden_runtime('wrong_password', encoded) + + # Increasing rounds from 4 to 6 means an increase of 4 in workload, + # therefore hardening should run 3 times to make the timing the + # same (the original encode() call already ran once). + self.assertEqual(hasher.encode.call_count, 3) + + # Get the original salt (includes the original workload factor) + algorithm, data = encoded.split('$', 1) + expected_call = (('wrong_password', force_bytes(data[:29])),) + self.assertEqual(hasher.encode.call_args_list, [expected_call] * 3) + def test_unusable(self): encoded = make_password(None) self.assertEqual(len(encoded), len(UNUSABLE_PASSWORD_PREFIX) + UNUSABLE_PASSWORD_SUFFIX_LENGTH) @@ -337,6 +360,25 @@ class TestUtilsHashPass(SimpleTestCase): finally: hasher.iterations = old_iterations + def test_pbkdf2_harden_runtime(self): + hasher = get_hasher('default') + self.assertEqual('pbkdf2_sha256', hasher.algorithm) + + with mock.patch.object(hasher, 'iterations', 1): + encoded = make_password('letmein') + + with mock.patch.object(hasher, 'iterations', 6), \ + mock.patch.object(hasher, 'encode', side_effect=hasher.encode): + hasher.harden_runtime('wrong_password', encoded) + + # Encode should get called once ... + self.assertEqual(hasher.encode.call_count, 1) + + # ... with the original salt and 5 iterations. + algorithm, iterations, salt, hash = encoded.split('$', 3) + expected_call = (('wrong_password', salt, 5),) + self.assertEqual(hasher.encode.call_args, expected_call) + def test_pbkdf2_upgrade_new_hasher(self): hasher = get_hasher('default') self.assertEqual('pbkdf2_sha256', hasher.algorithm) @@ -365,6 +407,20 @@ class TestUtilsHashPass(SimpleTestCase): self.assertTrue(check_password('letmein', encoded, setter)) self.assertTrue(state['upgraded']) + def test_check_password_calls_harden_runtime(self): + hasher = get_hasher('default') + encoded = make_password('letmein') + + with mock.patch.object(hasher, 'harden_runtime'), \ + mock.patch.object(hasher, 'must_update', return_value=True): + # Correct password supplied, no hardening needed + check_password('letmein', encoded) + self.assertEqual(hasher.harden_runtime.call_count, 0) + + # Wrong password supplied, hardening needed + check_password('wrong_password', encoded) + self.assertEqual(hasher.harden_runtime.call_count, 1) + def test_load_library_no_algorithm(self): with self.assertRaises(ValueError) as e: BasePasswordHasher()._load_library() |