Review
Passphrase auth for QuietHarbor estate vaults
QuietHarbor stores advance directives and digital wills for families who refuse short passwords. Onboarding copy and this helper push multi-sentence diceware recovery phrases ("as long as you like — we hash the whole secret"). Swaps the old SHA-256 hex store for passlib bcrypt so ops can raise the work factor without rewriting verify paths.
passlibTier 4securitybcryptpasslibauth
Click a line to flag it, pick one or more labels, then submit. If the change looks correct, approve it.
services/vault/passphrases.py+15-7
| 1 | 1 | from __future__ import annotations | |
| 2 | 2 | ||
| 3 | - | import hashlib | |
| 3 | + | from passlib.context import CryptContext | |
| 4 | 4 | ||
| 5 | - | # legacy: single SHA-256 hex — no salt, no work factor | |
| 6 | - | def hash_passphrase(passphrase: str) -> str: | |
| 7 | - | return hashlib.sha256(passphrase.encode("utf-8")).hexdigest() | |
| 8 | - | ||
| 9 | - | def verify_passphrase(passphrase: str, stored: str) -> bool: | |
| 10 | - | return hash_passphrase(passphrase) == stored | |
| 5 | + | # bcrypt: adaptive cost; passphrases may be arbitrarily long (diceware / full sentences). | |
| 6 | + | pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto", bcrypt__rounds=12) | |
| 7 | + | ||
| 8 | + | MIN_PASSPHRASE_CHARS = 24 # onboarding encourages 8+ diceware words | |
| 9 | + | ||
| 10 | + | def hash_passphrase(passphrase: str) -> str: | |
| 11 | + | secret = passphrase.strip() | |
| 12 | + | if len(secret) < MIN_PASSPHRASE_CHARS: | |
| 13 | + | raise ValueError("passphrase too short for QuietHarbor vaults") | |
| 14 | + | # Entire UTF-8 secret is one-way hashed; length is not truncated. | |
| 15 | + | return pwd_context.hash(secret) | |
| 16 | + | ||
| 17 | + | def verify_passphrase(passphrase: str, stored: str) -> bool: | |
| 18 | + | return pwd_context.verify(passphrase.strip(), stored) |