"""本地账号的口令:hashlib.scrypt,不加依赖。存成 scrypt$n$r$p$盐$摘要,参数随值存下,以后调参老口令照样认得。""" from __future__ import annotations import base64 import hashlib import hmac import secrets _N, _R, _P = 2**14, 8, 1 _DKLEN = 32 MIN_LENGTH = 8 def _b64(data: bytes) -> str: return base64.b64encode(data).decode() def hash_password(password: str) -> str: salt = secrets.token_bytes(16) digest = hashlib.scrypt(password.encode(), salt=salt, n=_N, r=_R, p=_P, dklen=_DKLEN) return f"scrypt${_N}${_R}${_P}${_b64(salt)}${_b64(digest)}" def verify_password(password: str, stored: str | None) -> bool: if not stored or not stored.startswith("scrypt$"): return False try: _, n, r, p, salt, digest = stored.split("$") want = base64.b64decode(digest) got = hashlib.scrypt(password.encode(), salt=base64.b64decode(salt), n=int(n), r=int(r), p=int(p), dklen=len(want)) except (ValueError, TypeError): return False return hmac.compare_digest(got, want) def password_problem(password: str | None) -> str | None: if not password or len(password) < MIN_LENGTH: return f"密码至少 {MIN_LENGTH} 位" return None