# backend/auth/password.py # Argon2id password hashing — OWASP 2024 recommended algorithm. # Parameters: time_cost=2, memory_cost=64MB, parallelism=2 from argon2 import PasswordHasher from argon2.exceptions import VerificationError, VerifyMismatchError ph = PasswordHasher( time_cost=2, memory_cost=65536, # 64 MB parallelism=2, hash_len=32, salt_len=16, ) # Password policy (BUG-028). Applied by the API validators at account creation # and password change so the rules stay consistent across both paths. MIN_PASSWORD_LENGTH = 8 MAX_PASSWORD_LENGTH = 128 def validate_password_strength(password: str) -> str: """Validate a plaintext password against the project policy. Args: password: Candidate password. Returns: The password unchanged when valid. Raises: ValueError: When the password is too short, too long or blank. """ if password is None or len(password) < MIN_PASSWORD_LENGTH: raise ValueError(f"Minimum {MIN_PASSWORD_LENGTH} caractères") if len(password) > MAX_PASSWORD_LENGTH: raise ValueError(f"Maximum {MAX_PASSWORD_LENGTH} caractères") if not password.strip(): raise ValueError("Le mot de passe ne peut pas être vide") return password def hash_password(password: str) -> str: """Hash a password with Argon2id.""" return ph.hash(password) def verify_password(password: str, hashed: str) -> bool: """Verify a password against its Argon2id hash.""" try: return ph.verify(hashed, password) except (VerifyMismatchError, VerificationError): return False def needs_rehash(hashed: str) -> bool: """Check if hash needs updating (parameters changed).""" return ph.check_needs_rehash(hashed)