import hashlib import base64 from collections import OrderedDict from django.utils.crypto import constant_time_compare from django.utils.encoding import force_bytes from django.utils.translation import gettext_noop as _ from django.contrib.auth.hashers import mask_hash, BasePasswordHasher class SHA512PasswordHasher(BasePasswordHasher): """ The SHA512 password hashing algorithm It is used to migrate passwords from old Symfony2 photos server. https://github.com/symfony/symfony/blob/2.8/src/Symfony/Component/Security/Core/Encoder/MessageDigestPasswordEncoder.php """ algorithm = "sha512" def encode(self, password, iteration, salt): assert password is not None assert salt and '$' not in salt salted = force_bytes(password + "{" + salt + "}") digest = hashlib.sha512(salted).digest() # "stretch" hash for _i in range(1, int(iteration)): digest = hashlib.sha512(digest + salted).digest() digest = base64.b64encode(digest).decode() encoded = "%s$%s$%s$%s" % (self.algorithm, iteration, salt, digest) return encoded[:128] def verify(self, password, encoded): algorithm, iteration, salt, hash = encoded.split('$', 3) assert algorithm == self.algorithm encoded_2 = self.encode(password, iteration, salt) return constant_time_compare(encoded, encoded_2) def safe_summary(self, encoded): algorithm, iteration, salt, hash = encoded.split('$', 3) assert algorithm == self.algorithm return OrderedDict([ (_('algorithm'), algorithm), (_('salt'), mask_hash(salt, show=2)), (_('hash'), mask_hash(hash)), ]) def harden_runtime(self, password, encoded): pass