diff --git a/django/contrib/auth/hashers.py b/django/contrib/auth/hashers.py index 344176be69..7a9084b746 100644 --- a/django/contrib/auth/hashers.py +++ b/django/contrib/auth/hashers.py @@ -302,8 +302,8 @@ class Argon2PasswordHasher(BasePasswordHasher): library = 'argon2' time_cost = 2 - memory_cost = 512 - parallelism = 2 + memory_cost = 102400 + parallelism = 8 def encode(self, password, salt): argon2 = self._load_library() @@ -363,7 +363,7 @@ class Argon2PasswordHasher(BasePasswordHasher): argon2 = self._load_library() # salt_len is a noop, because we provide our own salt. return argon2.Parameters( - type=argon2.low_level.Type.I, + type=argon2.low_level.Type.ID, version=argon2.low_level.ARGON2_VERSION, salt_len=argon2.DEFAULT_RANDOM_SALT_LENGTH, hash_len=argon2.DEFAULT_HASH_LENGTH, diff --git a/docs/releases/3.2.txt b/docs/releases/3.2.txt index 11da13daba..b6db027448 100644 --- a/docs/releases/3.2.txt +++ b/docs/releases/3.2.txt @@ -50,6 +50,15 @@ Minor features * The default iteration count for the PBKDF2 password hasher is increased from 216,000 to 260,000. +* The default variant for the Argon2 password hasher is changed to Argon2id. + ``memory_cost`` and ``parallelism`` are increased to 102,400 and 8 + respectively to match the ``argon2-cffi`` defaults. + + Increasing the ``memory_cost`` pushes the required memory from 512 KB to 100 + MB. This is still rather conservative but can lead to problems in memory + constrained environments. If this is the case, the existing hasher can be + subclassed to override the defaults. + :mod:`django.contrib.contenttypes` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/auth_tests/test_hashers.py b/tests/auth_tests/test_hashers.py index 5ca422764b..fa7332970a 100644 --- a/tests/auth_tests/test_hashers.py +++ b/tests/auth_tests/test_hashers.py @@ -497,13 +497,13 @@ class TestUtilsHashPassArgon2(SimpleTestCase): def test_argon2(self): encoded = make_password('lètmein', hasher='argon2') self.assertTrue(is_password_usable(encoded)) - self.assertTrue(encoded.startswith('argon2$')) + self.assertTrue(encoded.startswith('argon2$argon2id$')) self.assertTrue(check_password('lètmein', encoded)) self.assertFalse(check_password('lètmeinz', encoded)) self.assertEqual(identify_hasher(encoded).algorithm, 'argon2') # Blank passwords blank_encoded = make_password('', hasher='argon2') - self.assertTrue(blank_encoded.startswith('argon2$')) + self.assertTrue(blank_encoded.startswith('argon2$argon2id$')) self.assertTrue(is_password_usable(blank_encoded)) self.assertTrue(check_password('', blank_encoded)) self.assertFalse(check_password(' ', blank_encoded)) @@ -523,15 +523,15 @@ class TestUtilsHashPassArgon2(SimpleTestCase): def test_argon2_upgrade(self): self._test_argon2_upgrade('time_cost', 'time cost', 1) - self._test_argon2_upgrade('memory_cost', 'memory cost', 16) + self._test_argon2_upgrade('memory_cost', 'memory cost', 64) self._test_argon2_upgrade('parallelism', 'parallelism', 1) def test_argon2_version_upgrade(self): hasher = get_hasher('argon2') state = {'upgraded': False} encoded = ( - 'argon2$argon2i$m=8,t=1,p=1$c29tZXNhbHQ$gwQOXSNhxiOxPOA0+PY10P9QFO' - '4NAYysnqRt1GSQLE55m+2GYDt9FEjPMHhP2Cuf0nOEXXMocVrsJAtNSsKyfg' + 'argon2$argon2id$v=19$m=102400,t=2,p=8$Y041dExhNkljRUUy$TMa6A8fPJh' + 'CAUXRhJXCXdw' ) def setter(password):