mirror of
https://github.com/django/django.git
synced 2025-10-26 07:06:08 +00:00
Fixed #32275 -- Added scrypt password hasher.
Co-authored-by: Mariusz Felisiak <felisiak.mariusz@gmail.com>
This commit is contained in:
committed by
Mariusz Felisiak
parent
65b880b726
commit
1783b3cb24
@@ -520,6 +520,7 @@ PASSWORD_HASHERS = [
|
||||
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
|
||||
'django.contrib.auth.hashers.Argon2PasswordHasher',
|
||||
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
|
||||
'django.contrib.auth.hashers.ScryptPasswordHasher',
|
||||
]
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = []
|
||||
|
||||
@@ -517,6 +517,81 @@ class BCryptPasswordHasher(BCryptSHA256PasswordHasher):
|
||||
digest = None
|
||||
|
||||
|
||||
class ScryptPasswordHasher(BasePasswordHasher):
|
||||
"""
|
||||
Secure password hashing using the Scrypt algorithm.
|
||||
"""
|
||||
algorithm = 'scrypt'
|
||||
block_size = 8
|
||||
maxmem = 0
|
||||
parallelism = 1
|
||||
work_factor = 2 ** 14
|
||||
|
||||
def encode(self, password, salt, n=None, r=None, p=None):
|
||||
self._check_encode_args(password, salt)
|
||||
n = n or self.work_factor
|
||||
r = r or self.block_size
|
||||
p = p or self.parallelism
|
||||
hash_ = hashlib.scrypt(
|
||||
password.encode(),
|
||||
salt=salt.encode(),
|
||||
n=n,
|
||||
r=r,
|
||||
p=p,
|
||||
maxmem=self.maxmem,
|
||||
dklen=64,
|
||||
)
|
||||
hash_ = base64.b64encode(hash_).decode('ascii').strip()
|
||||
return '%s$%d$%s$%d$%d$%s' % (self.algorithm, n, salt, r, p, hash_)
|
||||
|
||||
def decode(self, encoded):
|
||||
algorithm, work_factor, salt, block_size, parallelism, hash_ = encoded.split('$', 6)
|
||||
assert algorithm == self.algorithm
|
||||
return {
|
||||
'algorithm': algorithm,
|
||||
'work_factor': int(work_factor),
|
||||
'salt': salt,
|
||||
'block_size': int(block_size),
|
||||
'parallelism': int(parallelism),
|
||||
'hash': hash_,
|
||||
}
|
||||
|
||||
def verify(self, password, encoded):
|
||||
decoded = self.decode(encoded)
|
||||
encoded_2 = self.encode(
|
||||
password,
|
||||
decoded['salt'],
|
||||
decoded['work_factor'],
|
||||
decoded['block_size'],
|
||||
decoded['parallelism'],
|
||||
)
|
||||
return constant_time_compare(encoded, encoded_2)
|
||||
|
||||
def safe_summary(self, encoded):
|
||||
decoded = self.decode(encoded)
|
||||
return {
|
||||
_('algorithm'): decoded['algorithm'],
|
||||
_('work factor'): decoded['work_factor'],
|
||||
_('block size'): decoded['block_size'],
|
||||
_('parallelism'): decoded['parallelism'],
|
||||
_('salt'): mask_hash(decoded['salt']),
|
||||
_('hash'): mask_hash(decoded['hash']),
|
||||
}
|
||||
|
||||
def must_update(self, encoded):
|
||||
decoded = self.decode(encoded)
|
||||
return (
|
||||
decoded['work_factor'] != self.work_factor or
|
||||
decoded['block_size'] != self.block_size or
|
||||
decoded['parallelism'] != self.parallelism
|
||||
)
|
||||
|
||||
def harden_runtime(self, password, encoded):
|
||||
# The runtime for Scrypt is too complicated to implement a sensible
|
||||
# hardening algorithm.
|
||||
pass
|
||||
|
||||
|
||||
class SHA1PasswordHasher(BasePasswordHasher):
|
||||
"""
|
||||
The SHA1 password hashing algorithm (not recommended)
|
||||
|
||||
Reference in New Issue
Block a user