"""PBKDF2 algorithm implementation.""" from __future__ import annotations import base64 import binascii import hashlib import hmac import os DEFAULT_SALT_BYTES = 16 DEFAULT_ITERATIONS = int(os.environ.get("PBKDF2_ITERATIONS", "200000")) class PBKDF2Algorithm: """PBKDF2-HMAC-SHA256 password hashing algorithm.""" identifier = "pbkdf2" def hash( self, password: str, *, iterations: int | None = None, salt_bytes: int = DEFAULT_SALT_BYTES, ) -> tuple[str, str]: """Hash a password using PBKDF2-HMAC-SHA256.""" iterations = iterations or DEFAULT_ITERATIONS salt = os.urandom(salt_bytes) derived = hashlib.pbkdf2_hmac( "sha256", password.encode("utf-8"), salt, iterations, ) return ( base64.b64encode(salt).decode("utf-8"), base64.b64encode(derived).decode("utf-8"), ) def verify( self, password: str, salt_b64: str, hash_b64: str, *, iterations: int | None = None, ) -> bool: """Verify a password against PBKDF2 salt and hash.""" iterations = iterations or DEFAULT_ITERATIONS try: salt = base64.b64decode(salt_b64, validate=True) stored_hash = base64.b64decode(hash_b64, validate=True) except (binascii.Error, ValueError): return False derived = hashlib.pbkdf2_hmac( "sha256", password.encode("utf-8"), salt, iterations, ) return hmac.compare_digest(derived, stored_hash) from algorithms import register_algorithm # Auto-register when module is imported register_algorithm(PBKDF2Algorithm())