feat(crypto): adopt Argon2id (argon2id-v2) on register + master-pw change
Phase 2 of CODE_AUDIT §1.2 — live adoption of the Argon2id foundation.
Verified at runtime: a rotated account shows hash_algo=argon2id-v2 with
argon2_m=19456,t=2,p=1 in vault.db.
Server (never runs Argon2 — zero-knowledge, only stores/echoes params):
- DB: users.argon2_m/t/p columns (default 0 = PBKDF2).
- PM.Handler.Auth: HASH_ALGO_ARGON2 + param bounds, ReadArgon2Params /
AppendArgon2Params helpers. /register and /change-master-password accept
hashAlgo='argon2id-v2' + argon2:{m,t,p} and persist them; /login/challenge
echoes them. Verify path (VerifierToStoredHash/CheckVerifier) is
KDF-agnostic — the 64-hex verifier is SHA256-wrapped as for any -v2 scheme.
Client (app.js):
- state.argon2Params, cached from the challenge and persisted to
sessionStorage + the quick-unlock / PIN cold-start blobs (so a cold-started
session can still derive-from-password for reauth/rotation).
- Register + master-pw rotation derive with argon2id-v2 + ARGON2_DEFAULT_PARAMS
(OWASP m=19MiB,t=2,p=1) and send the params. Rotation re-encrypts the whole
vault under the new Argon2 key (natural migration point). Existing accounts
stay PBKDF2 until they rotate.
- Params threaded through every derive-from-password site (login, reauth,
recovery setup, change-pw current verifier). Cold-start verifier-from-raw-key
paths need no params (isDecoupledVerifierAlgo handles the -v2 wrap).
Tests: +2 param-contract tests (register<->login determinism, param
sensitivity). 42/42. Assets rebuilt to embed js/argon2.js.
Docs: CLAUDE.md auth-hash section rewritten (4 markers); CODE_AUDIT §1.2 +
table + plan marked done.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -424,6 +424,14 @@ begin
|
||||
// (600 000 as of 2026). Login flow transparently re-hashes legacy users
|
||||
// and re-encrypts their entries on the client side.
|
||||
AddColumnIfMissing('users', 'kdf_iterations', 'INTEGER DEFAULT 100000');
|
||||
// Argon2id KDF parameters (memory KiB / time cost / parallelism). 0 = the
|
||||
// account uses PBKDF2 (kdf_iterations above); non-zero = hash_algo is an
|
||||
// 'argon2id-*' scheme and these drive the client-side key derivation.
|
||||
// Stored per-user (like kdf_iterations) so the cost can be tuned later
|
||||
// without breaking existing accounts.
|
||||
AddColumnIfMissing('users', 'argon2_m', 'INTEGER DEFAULT 0');
|
||||
AddColumnIfMissing('users', 'argon2_t', 'INTEGER DEFAULT 0');
|
||||
AddColumnIfMissing('users', 'argon2_p', 'INTEGER DEFAULT 0');
|
||||
AddColumnIfMissing('recovery_keys', 'remaining_uses', 'INTEGER DEFAULT 5');
|
||||
// Server-side preferences blob (JSON). Synced across devices on login,
|
||||
// saved on every change from the JS settings panel. Device-specific
|
||||
|
||||
Reference in New Issue
Block a user