import hashlib import os from .secret_properties import secret_properties class secret_generator(metaclass = secret_properties): def __init__(self, password: str): self.__password = password self.__secret = None @property def password(self) -> str: return self.__password def validate(self, secret: str) -> bool: if not self.__check_secret(secret): return False hashed, salt = self.__split_secret(secret) target_hashed, second_salt = self.__generate_hashed(salt) return hashed == target_hashed def __check_secret(self, secret: str) -> bool: properties = self.__class__ hash_length = properties.hash_hex_length salt_length = properties.salt_hex_length separator = properties.salt_separator if secret.find(separator) != hash_length: return False if len(secret) != hash_length + salt_length + len(separator): return False return True def __split_secret(self, secret: str) -> [bytes, bytes]: properties = self.__class__ separator = properties.salt_separator splited = secret.split(separator) hashed = bytes.fromhex(splited[0]) salt = bytes.fromhex(splited[-1]) return hashed, salt def __generate_hashed(self, salt: bytes | None = None) -> str: properties = self.__class__ rounds = properties.hash_rounds algorithm = properties.hash_algorithm password = self.password.encode("UTF-8") if salt is None: random_size = properties.salt_length salt = os.urandom(random_size) hashed = hashlib.pbkdf2_hmac( algorithm, password, salt, rounds ) return hashed, salt @property def secret(self) -> str: if self.__secret is not None: return self.__secret hashed, salt = self.__generate_hashed() self.__secret = self.__create_secret(hashed, salt) return self.secret def __create_secret(self, hashed: bytes, salt: bytes) -> str: properties = self.__class__ separator = properties.salt_separator result = hashed.hex() + separator + salt.hex() if not self.__check_secret(result): raise Exception("Can not create secret. Check secret settings!") return result