50 lines
1.6 KiB
Python
50 lines
1.6 KiB
Python
"""Аутентификация: хеширование паролей, проверка, сессии, валидация."""
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import hmac
|
|
import re
|
|
import secrets
|
|
|
|
PBKDF2_ITERATIONS = 200_000
|
|
EMAIL_RE = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$")
|
|
MIN_PASSWORD = 8
|
|
|
|
|
|
def hash_password(password: str, iterations: int = PBKDF2_ITERATIONS) -> str:
|
|
salt = secrets.token_bytes(16)
|
|
dk = hashlib.pbkdf2_hmac("sha256", password.encode("utf-8"), salt, iterations)
|
|
return f"pbkdf2_sha256${iterations}${salt.hex()}${dk.hex()}"
|
|
|
|
|
|
def verify_password(password: str, stored: str | None) -> bool:
|
|
if not stored:
|
|
return False
|
|
try:
|
|
algo, iters, salt_hex, digest = stored.split("$")
|
|
if algo != "pbkdf2_sha256":
|
|
return False
|
|
dk = hashlib.pbkdf2_hmac("sha256", password.encode("utf-8"),
|
|
bytes.fromhex(salt_hex), int(iters))
|
|
return hmac.compare_digest(dk.hex(), digest)
|
|
except (ValueError, AttributeError):
|
|
return False
|
|
|
|
|
|
def new_token() -> str:
|
|
return secrets.token_urlsafe(32)
|
|
|
|
|
|
def valid_email(email: str) -> bool:
|
|
email = (email or "").strip()
|
|
return bool(EMAIL_RE.match(email)) and len(email) <= 254
|
|
|
|
|
|
def validate_registration(email: str, password: str) -> str | None:
|
|
"""Вернуть текст ошибки или None, если всё хорошо."""
|
|
if not valid_email(email):
|
|
return "Некорректный email"
|
|
if len(password or "") < MIN_PASSWORD:
|
|
return f"Пароль должен быть не короче {MIN_PASSWORD} символов"
|
|
return None
|