Refs #30472 -- Simplified Argon2PasswordHasher with argon2-cffi 19.1+ API.

This commit is contained in:
Florian Apolloner 2020-06-17 07:46:45 +02:00 committed by Mariusz Felisiak
parent ee49cf4f35
commit faad809e09
4 changed files with 37 additions and 37 deletions

View File

@ -307,14 +307,15 @@ class Argon2PasswordHasher(BasePasswordHasher):
def encode(self, password, salt): def encode(self, password, salt):
argon2 = self._load_library() argon2 = self._load_library()
params = self.params()
data = argon2.low_level.hash_secret( data = argon2.low_level.hash_secret(
password.encode(), password.encode(),
salt.encode(), salt.encode(),
time_cost=self.time_cost, time_cost=params.time_cost,
memory_cost=self.memory_cost, memory_cost=params.memory_cost,
parallelism=self.parallelism, parallelism=params.parallelism,
hash_len=argon2.DEFAULT_HASH_LENGTH, hash_len=params.hash_len,
type=argon2.low_level.Type.I, type=params.type,
) )
return self.algorithm + data.decode('ascii') return self.algorithm + data.decode('ascii')
@ -323,11 +324,7 @@ class Argon2PasswordHasher(BasePasswordHasher):
algorithm, rest = encoded.split('$', 1) algorithm, rest = encoded.split('$', 1)
assert algorithm == self.algorithm assert algorithm == self.algorithm
try: try:
return argon2.low_level.verify_secret( return argon2.PasswordHasher().verify('$' + rest, password)
('$' + rest).encode('ascii'),
password.encode(),
type=argon2.low_level.Type.I,
)
except argon2.exceptions.VerificationError: except argon2.exceptions.VerificationError:
return False return False
@ -347,22 +344,34 @@ class Argon2PasswordHasher(BasePasswordHasher):
} }
def must_update(self, encoded): def must_update(self, encoded):
(algorithm, variety, version, time_cost, memory_cost, parallelism, algorithm, rest = encoded.split('$', 1)
salt, data) = self._decode(encoded)
assert algorithm == self.algorithm assert algorithm == self.algorithm
argon2 = self._load_library() argon2 = self._load_library()
return ( current_params = argon2.extract_parameters('$' + rest)
argon2.low_level.ARGON2_VERSION != version or new_params = self.params()
self.time_cost != time_cost or # Set salt_len to the salt_len of the current parameters because salt
self.memory_cost != memory_cost or # is explicitly passed to argon2.
self.parallelism != parallelism new_params.salt_len = current_params.salt_len
) return current_params != new_params
def harden_runtime(self, password, encoded): def harden_runtime(self, password, encoded):
# The runtime for Argon2 is too complicated to implement a sensible # The runtime for Argon2 is too complicated to implement a sensible
# hardening algorithm. # hardening algorithm.
pass pass
def params(self):
argon2 = self._load_library()
# salt_len is a noop, because we provide our own salt.
return argon2.Parameters(
type=argon2.low_level.Type.I,
version=argon2.low_level.ARGON2_VERSION,
salt_len=argon2.DEFAULT_RANDOM_SALT_LENGTH,
hash_len=argon2.DEFAULT_HASH_LENGTH,
time_cost=self.time_cost,
memory_cost=self.memory_cost,
parallelism=self.parallelism,
)
def _decode(self, encoded): def _decode(self, encoded):
""" """
Split an encoded hash and return: ( Split an encoded hash and return: (
@ -370,24 +379,13 @@ class Argon2PasswordHasher(BasePasswordHasher):
parallelism, salt, data, parallelism, salt, data,
). ).
""" """
bits = encoded.split('$') argon2 = self._load_library()
if len(bits) == 5: algorithm, rest = encoded.split('$', 1)
# Argon2 < 1.3 params = argon2.extract_parameters('$' + rest)
algorithm, variety, raw_params, salt, data = bits variety, *_, salt, data = rest.split('$')
version = 0x10
else:
assert len(bits) == 6
algorithm, variety, raw_version, raw_params, salt, data = bits
assert raw_version.startswith('v=')
version = int(raw_version[len('v='):])
params = dict(bit.split('=', 1) for bit in raw_params.split(','))
assert len(params) == 3 and all(x in params for x in ('t', 'm', 'p'))
time_cost = int(params['t'])
memory_cost = int(params['m'])
parallelism = int(params['p'])
return ( return (
algorithm, variety, version, time_cost, memory_cost, parallelism, algorithm, variety, params.version, params.time_cost,
salt, data, params.memory_cost, params.parallelism, salt, data,
) )

View File

@ -270,7 +270,7 @@ Running all the tests
If you want to run the full suite of tests, you'll need to install a number of If you want to run the full suite of tests, you'll need to install a number of
dependencies: dependencies:
* argon2-cffi_ 16.1.0+ * argon2-cffi_ 19.1.0+
* asgiref_ 3.2+ (required) * asgiref_ 3.2+ (required)
* bcrypt_ * bcrypt_
* docutils_ * docutils_

View File

@ -324,6 +324,8 @@ Miscellaneous
* The :tfilter:`intcomma` and :tfilter:`intword` template filters no longer * The :tfilter:`intcomma` and :tfilter:`intword` template filters no longer
depend on the :setting:`USE_L10N` setting. depend on the :setting:`USE_L10N` setting.
* Support for ``argon2-cffi`` < 19.1.0 is removed.
.. _deprecated-features-3.2: .. _deprecated-features-3.2:
Features deprecated in 3.2 Features deprecated in 3.2

View File

@ -49,7 +49,7 @@ console_scripts =
django-admin = django.core.management:execute_from_command_line django-admin = django.core.management:execute_from_command_line
[options.extras_require] [options.extras_require]
argon2 = argon2-cffi >= 16.1.0 argon2 = argon2-cffi >= 19.1.0
bcrypt = bcrypt bcrypt = bcrypt
[bdist_rpm] [bdist_rpm]