diff --git a/django/contrib/auth/hashers.py b/django/contrib/auth/hashers.py index 99d3d5ff6a..0e44614fcb 100644 --- a/django/contrib/auth/hashers.py +++ b/django/contrib/auth/hashers.py @@ -344,7 +344,10 @@ class Argon2PasswordHasher(BasePasswordHasher): algorithm, rest = encoded.split('$', 1) assert algorithm == self.algorithm params = argon2.extract_parameters('$' + rest) - variety, *_, salt, hash = rest.split('$') + variety, *_, b64salt, hash = rest.split('$') + # Add padding. + b64salt += '=' * (-len(b64salt) % 4) + salt = base64.b64decode(b64salt).decode('latin1') return { 'algorithm': algorithm, 'hash': hash, diff --git a/tests/auth_tests/test_hashers.py b/tests/auth_tests/test_hashers.py index ec056660b7..55c436b993 100644 --- a/tests/auth_tests/test_hashers.py +++ b/tests/auth_tests/test_hashers.py @@ -526,6 +526,16 @@ class TestUtilsHashPassArgon2(SimpleTestCase): self.assertIs(check_password('secret', encoded), True) self.assertIs(check_password('wrong', encoded), False) + def test_argon2_decode(self): + salt = 'abcdefghijk' + encoded = make_password('lètmein', salt=salt, hasher='argon2') + hasher = get_hasher('argon2') + decoded = hasher.decode(encoded) + self.assertEqual(decoded['memory_cost'], hasher.memory_cost) + self.assertEqual(decoded['parallelism'], hasher.parallelism) + self.assertEqual(decoded['salt'], salt) + self.assertEqual(decoded['time_cost'], hasher.time_cost) + def test_argon2_upgrade(self): self._test_argon2_upgrade('time_cost', 'time cost', 1) self._test_argon2_upgrade('memory_cost', 'memory cost', 64)