photo26/photo21/hashers.py
2022-03-11 17:18:14 +01:00

55 lines
1.9 KiB
Python

# This file is part of photo21
# Copyright (C) 2022 Amicale des élèves de l'ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
import base64
import hashlib
from collections import OrderedDict
from django.contrib.auth.hashers import BasePasswordHasher, mask_hash
from django.utils.crypto import constant_time_compare
from django.utils.encoding import force_bytes
from django.utils.translation import gettext_noop as _
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