00e39a3f85
Phase 1 complete — Operator Foundation: - Postgres 16 in Docker-in-LXC (asyncpg pool, 5-table schema, PgStore, migrations) - Operator auth (argon2id, signed stateless cookies, slowapi 5/min rate limit) - VC issuer key migration SQLite→Postgres (archive-before-active, R-VC-MIG-01) - Operator bootstrap CLI (create-operator.py, idempotent) - Backup cron script + G-008 restore drill - Graceful degradation (server starts without Postgres) - 272 tests pass, 33 skip (Postgres-requiring), 0 fail ---ci--- project: praxis phase: 1 milestone: v0.4 status: complete requirements: covered: [REQ-MT-01, REQ-AUTH-01, REQ-NFR-AUTH-01, REQ-NFR-MT-01, REQ-MT-02] partial: [] ---/ci---
44 lines
1.4 KiB
Python
44 lines
1.4 KiB
Python
"""Argon2id password hashing (TASK-03-01, D-041, REQ-NFR-AUTH-01).
|
|
|
|
Uses argon2-cffi PasswordHasher with defaults that exceed OWASP minimums
|
|
(time_cost=3, memory_cost=64MiB, parallelism=4 — RESEARCH-v0.4 §2.1).
|
|
Single operator, low-frequency logins → hashing latency < 1s is
|
|
acceptable (R-AUTH-02).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from argon2 import PasswordHasher
|
|
from argon2.exceptions import VerifyMismatchError
|
|
|
|
_ph = PasswordHasher()
|
|
|
|
|
|
def hash_password(plain: str) -> str:
|
|
"""Hash a plaintext password with argon2id. Returns the encoded hash string."""
|
|
return _ph.hash(plain)
|
|
|
|
|
|
def verify_password(stored_hash: str, plain: str) -> bool:
|
|
"""Verify a plaintext password against a stored argon2id hash.
|
|
|
|
Returns False on mismatch (no exception) so the login flow can apply a
|
|
uniform 401 + rate-limit-increment path on any auth failure.
|
|
"""
|
|
try:
|
|
_ph.verify(stored_hash, plain)
|
|
return True
|
|
except VerifyMismatchError:
|
|
return False
|
|
except Exception:
|
|
return False
|
|
|
|
|
|
def needs_rehash(stored_hash: str) -> bool:
|
|
"""True if the stored hash was produced with weaker params than the
|
|
current PasswordHasher defaults. The login flow rehashes + updates the
|
|
store when this returns True (param upgrades without forcing a reset)."""
|
|
return _ph.check_needs_rehash(stored_hash)
|
|
|
|
|
|
__all__ = ["hash_password", "verify_password", "needs_rehash"] |