from __future__ import annotations import hashlib import hmac import secrets SCRYPT_N = 2**14 SCRYPT_R = 8 SCRYPT_P = 1 SALT_BYTES = 16 KEY_BYTES = 32 def hash_password(password: str) -> str: if not password: raise ValueError("密码不能为空。") salt = secrets.token_bytes(SALT_BYTES) derived = hashlib.scrypt( password.encode("utf-8"), salt=salt, n=SCRYPT_N, r=SCRYPT_R, p=SCRYPT_P, dklen=KEY_BYTES, ) return "$".join( [ "scrypt", str(SCRYPT_N), str(SCRYPT_R), str(SCRYPT_P), salt.hex(), derived.hex(), ] ) def verify_password(password: str, encoded: str) -> bool: try: algorithm, n_raw, r_raw, p_raw, salt_hex, expected_hex = encoded.split("$") if algorithm != "scrypt": return False derived = hashlib.scrypt( password.encode("utf-8"), salt=bytes.fromhex(salt_hex), n=int(n_raw), r=int(r_raw), p=int(p_raw), dklen=len(bytes.fromhex(expected_hex)), ) except (TypeError, ValueError): return False return hmac.compare_digest(derived.hex(), expected_hex) def generate_session_token() -> str: return secrets.token_urlsafe(48) def generate_csrf_token() -> str: return secrets.token_urlsafe(32) def hash_token(token: str) -> str: return hashlib.sha256(token.encode("utf-8")).hexdigest() def validate_new_password(password: str, username: str = "admin") -> list[str]: errors: list[str] = [] if len(password) < 10: errors.append("密码至少需要 10 个字符") if password.lower() == username.lower(): errors.append("密码不能与用户名相同") if not any(character.isalpha() for character in password): errors.append("密码至少需要包含一个字母") if not any(character.isdigit() for character in password): errors.append("密码至少需要包含一个数字") return errors