"""Generation et verification des jetons de partage. Deux secrets distincts sont produits a la creation d'un partage : * ``share_token`` : identifiant public, present dans /s/, lecture seule. * ``management_token`` : secret de gestion (PUT/DELETE), jamais dans une URL, stocke uniquement dans l'IndexedDB du proprietaire. Seul le hash SHA-256 du management token est conserve en base. """ from __future__ import annotations import hashlib import secrets # 12 octets -> 16 caracteres url-safe, ~96 bits d'entropie : non enumerable. SHARE_TOKEN_BYTES = 12 # 32 octets -> ~256 bits d'entropie pour le secret de gestion. MANAGEMENT_TOKEN_BYTES = 32 def generate_share_token() -> str: return secrets.token_urlsafe(SHARE_TOKEN_BYTES) def generate_management_token() -> str: return secrets.token_urlsafe(MANAGEMENT_TOKEN_BYTES) def hash_token(token: str) -> str: """Hash du secret de gestion. Le token est deja un secret aleatoire de haute entropie : un SHA-256 simple suffit (pas de risque de force brute par dictionnaire, contrairement a un mot de passe choisi par un humain). """ return hashlib.sha256(token.encode("utf-8")).hexdigest() def verify_token(token: str, expected_hash: str) -> bool: """Comparaison a temps constant.""" if not token or not expected_hash: return False return secrets.compare_digest(hash_token(token), expected_hash)