SmartEDT/backend/auth/passwords.py

53 lines
1.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""密码哈希与校验。
当前实现使用 PBKDF2-HMAC-SHA256内置 hashlib以避免引入额外依赖。
存储格式:
pbkdf2_sha256$<iterations>$<salt_b64url>$<dk_b64url>
"""
from __future__ import annotations
import base64
import hashlib
import hmac
import secrets
_ALGO = "pbkdf2_sha256"
_ITERATIONS = 210_000
_SALT_BYTES = 16
_DKLEN = 32
def hash_password(password: str) -> str:
"""对明文密码进行哈希并返回可存储字符串。"""
if not isinstance(password, str) or not password:
raise ValueError("password required")
salt = secrets.token_bytes(_SALT_BYTES)
dk = hashlib.pbkdf2_hmac("sha256", password.encode("utf-8"), salt, _ITERATIONS, dklen=_DKLEN)
salt_b64 = base64.urlsafe_b64encode(salt).decode("ascii").rstrip("=")
dk_b64 = base64.urlsafe_b64encode(dk).decode("ascii").rstrip("=")
return f"{_ALGO}${_ITERATIONS}${salt_b64}${dk_b64}"
def verify_password(password: str, stored_hash: str) -> bool:
"""校验明文密码是否匹配已存储的哈希。"""
try:
algo, iters_s, salt_b64, dk_b64 = stored_hash.split("$", 3)
if algo != _ALGO:
return False
iterations = int(iters_s)
salt = _b64url_decode(salt_b64)
expected = _b64url_decode(dk_b64)
except Exception:
return False
dk = hashlib.pbkdf2_hmac("sha256", password.encode("utf-8"), salt, iterations, dklen=len(expected))
return hmac.compare_digest(dk, expected)
def _b64url_decode(value: str) -> bytes:
"""解码不带 padding 的 base64url 字符串。"""
padded = value + "=" * (-len(value) % 4)
return base64.urlsafe_b64decode(padded.encode("ascii"))