"""Password hashing and login security utilities.""" import hashlib import secrets import time def hash_password(password: str) -> str: """Hash a password using SHA-256 + random salt (16 bytes). Format: salt_hex:hash_hex (64 + 64 = 128 chars) Fallback for bcrypt — we use SHA-256 for SQLite simplicity but with proper salt per password.""" salt = secrets.token_hex(16) h = hashlib.sha256(f"{salt}:{password}".encode()).hexdigest() return f"{salt}:{h}" def verify_password(password: str, stored: str) -> bool: """Verify a password against its stored hash.""" try: salt, h = stored.split(":", 1) expected = hashlib.sha256(f"{salt}:{password}".encode()).hexdigest() return h == expected except (ValueError, AttributeError): return False def is_locked(locked_until: str | None) -> bool: """Check if account is temporarily locked.""" if not locked_until: return False try: return float(locked_until) > time.time() except (ValueError, TypeError): return False