"""Password hashing (Argon2id, spec section 43) and session tokens.""" from __future__ import annotations import secrets import uuid from datetime import UTC, datetime, timedelta from argon2 import PasswordHasher from argon2.exceptions import VerifyMismatchError _hasher = PasswordHasher() # Argon2id by default def hash_password(password: str) -> str: return _hasher.hash(password) def verify_password(password_hash: str, password: str) -> bool: try: return _hasher.verify(password_hash, password) except VerifyMismatchError: return False except Exception: # Malformed hash etc. — never crash authentication on it. return False def generate_password(length: int = 24) -> str: """Cryptographically random password for MQTT clients (spec section 11).""" alphabet = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789" return "".join(secrets.choice(alphabet) for _ in range(length)) def generate_session_id() -> uuid.UUID: return uuid.uuid4() def session_expiry(ttl_seconds: int) -> datetime: return datetime.now(UTC) + timedelta(seconds=ttl_seconds)