Files

77 lines
2.4 KiB
Python

"""Mot de passe de l'interface (`rtg mot-de-passe`).
Il est rangé hors du site (le dossier du site est souvent servi tel quel par
le serveur web) : ~/.config/rtg/acces.toml sous macOS et Linux,
%APPDATA%\\rtg\\acces.toml sous Windows. Seule une empreinte scrypt salée y
est écrite, jamais le mot de passe.
"""
import base64
import hashlib
import hmac
import os
import secrets
import unicodedata
from rtg.config import ecrire_toml, lire_toml
LONGUEUR_MIN = 10
SCRYPT = {"n": 2 ** 15, "r": 8, "p": 1}
def chemin_acces():
if os.name == "nt":
base = os.environ.get("APPDATA") or os.path.expanduser("~")
else:
base = os.environ.get("XDG_CONFIG_HOME") or os.path.join(os.path.expanduser("~"), ".config")
return os.path.join(base, "rtg", "acces.toml")
def _empreinte(mdp, sel, n, r, p):
# NFC : un « é » tapé dans le Terminal ou dans le navigateur donne les mêmes octets.
mdp = unicodedata.normalize("NFC", mdp)
return hashlib.scrypt(mdp.encode("utf-8"), salt=sel, n=n, r=r, p=p,
maxmem=128 * r * n * 2, dklen=32)
def definir(mdp):
if len(mdp) < LONGUEUR_MIN:
raise ValueError(f"Le mot de passe doit faire au moins {LONGUEUR_MIN} caractères.")
sel = secrets.token_bytes(16)
e = _empreinte(mdp, sel, **SCRYPT)
chemin = chemin_acces()
os.makedirs(os.path.dirname(chemin), exist_ok=True)
ecrire_toml(chemin, {"mot_de_passe": "scrypt${n}${r}${p}${sel}${e}".format(
**SCRYPT, sel=base64.b64encode(sel).decode(), e=base64.b64encode(e).decode())},
entete="Accès à l'interface RTG (empreinte du mot de passe, pas le mot de passe).")
try:
os.chmod(chemin, 0o600)
except OSError:
pass
return chemin
def defini():
return bool(lire_toml(chemin_acces()).get("mot_de_passe"))
def supprimer():
chemin = chemin_acces()
if os.path.exists(chemin):
os.remove(chemin)
return True
return False
def verifier(mdp):
"""Vrai si `mdp` est le mot de passe enregistré (comparaison à temps constant)."""
valeur = lire_toml(chemin_acces()).get("mot_de_passe", "")
try:
algo, n, r, p, sel, e = valeur.split("$")
if algo != "scrypt":
return False
attendu = base64.b64decode(e)
calcule = _empreinte(mdp, base64.b64decode(sel), int(n), int(r), int(p))
except (ValueError, TypeError):
return False
return hmac.compare_digest(attendu, calcule)