"""Passlib module to understand hashed password format of Werkzeug """ __version__ = '0.1.0' from base64 import b64encode from passlib.crypto.digest import pbkdf2_hmac from passlib.exc import InvalidHashError from passlib.registry import register_crypt_handler_path import passlib.utils.handlers as uh from passlib.utils.binary import AB64_CHARS, ab64_decode, ab64_encode from passlib.utils.compat import u SALT_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" class werkzeug_pbkdf2_sha256(uh.HasSalt, uh.HasRounds, uh.GenericHandler): # pylint: disable=invalid-name """Passlib handler for Werkzeug’s PBKDF2:SHA-256 passwords """ checksum_chars = '0123456789abcdef' checksum_size = 64 default_rounds = 150000 default_salt_chars = SALT_CHARS default_salt_size = 8 _digest = 'sha256' ident = u('pbkdf2:sha256') min_salt_size = 1 name = 'werkzeug_pbkdf2_sha256' salt_chars = SALT_CHARS # pylint: disable=arguments-differ @classmethod def from_string(cls, password_hash, **context): """Create a new hash object from a string """ try: full_method, salt, checksum = password_hash.split('$') except ValueError as exc: raise InvalidHashError(cls) from exc method, digest, rounds = full_method.split(':') if ':'.join((method, digest)) != cls.ident: raise InvalidHashError(cls) if checksum: bytes.fromhex(checksum) return cls(salt=salt, checksum=checksum, rounds=int(rounds)) def to_string(self): full_method = ':'.join((self.ident, str(self.rounds))) return '$'.join((full_method, self.salt, self.checksum)) def _calc_checksum(self, secret): checksum = pbkdf2_hmac(self._digest, secret, self.salt, self.rounds) return checksum.hex() register_crypt_handler_path('werkzeug_pbkdf2_sha256', 'passlib_werkzeug')