41 lines
1.1 KiB
Python
41 lines
1.1 KiB
Python
import base64
|
|
import hashlib
|
|
import hmac
|
|
import secrets
|
|
|
|
ALGORITHM = "pbkdf2_sha256"
|
|
ITERATIONS = 390_000
|
|
|
|
|
|
def _b64encode(raw: bytes) -> str:
|
|
return base64.urlsafe_b64encode(raw).decode("ascii").rstrip("=")
|
|
|
|
|
|
def _b64decode(value: str) -> bytes:
|
|
padding = "=" * (-len(value) % 4)
|
|
return base64.urlsafe_b64decode(value + padding)
|
|
|
|
|
|
def hash_password(password: str, iterations: int = ITERATIONS) -> str:
|
|
salt = secrets.token_bytes(16)
|
|
digest = hashlib.pbkdf2_hmac("sha256", password.encode("utf-8"), salt, iterations)
|
|
return f"{ALGORITHM}${iterations}${_b64encode(salt)}${_b64encode(digest)}"
|
|
|
|
|
|
def verify_password(password: str, stored_hash: str) -> bool:
|
|
try:
|
|
algorithm, iterations, salt, expected = stored_hash.split("$", 3)
|
|
except ValueError:
|
|
return hmac.compare_digest(password, stored_hash)
|
|
|
|
if algorithm != ALGORITHM:
|
|
return False
|
|
|
|
digest = hashlib.pbkdf2_hmac(
|
|
"sha256",
|
|
password.encode("utf-8"),
|
|
_b64decode(salt),
|
|
int(iterations),
|
|
)
|
|
return hmac.compare_digest(_b64encode(digest), expected)
|