feat(auth): per-account X25519 identity keypair (Phase A1, #3) #6
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/3-phase-a1-x25519-keypair"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Phase A1 of the multi-person sharing ADR (
docs/adr/multi-person-sharing.md). Each account now gets an X25519 keypair at registration:The keypair is generated client-side; the backend adds no crypto deps and stores everything verbatim — the zero-knowledge contract is preserved.
This PR only introduces the keypair and threads it through the auth flows. It is not consumed yet. It unblocks Phase B (sharing) without touching the data model or the ~11 frontend encrypt call sites (that's Phase A2, per-profile DEKs).
Scope decision
The ADR's Phase A bundled two independent changes. Per discussion on #3, this PR does only the keypair (A1). Per-profile DEKs (A2) ship separately so each is reviewable on its own.
Changes
Backend (all new fields are
Option<String>→ backward compatible):models/user.rs:identity_public_key,identity_private_key_wrapped,identity_private_key_wrapped_ivhandlers/auth.rs:RegisterRequest+AuthResponsecarry the fields;register+loginecho themchange_password/recover_password— the account DEK value is unchanged across both flows, so the wrapped private key is too (verified)auth_tests.rs: round-trip through register/login; optional-fields backward compatFrontend:
crypto/keys.ts:generateIdentityKeyPair,wrapIdentityPrivateKey,unwrapIdentityPrivateKey+ in-memory store mirroring the DEK storetypes/api.ts:AuthTokens+RegisterRequestextended (also removes an existingas anycast)useStore.ts:registergenerates+wraps,login/UnlockPageunwrap,logoutclearsVerification
cargo test— all 19 backend tests pass (Mongo available);cargo clippy -- -D warningscleannpm test— all 28 frontend tests pass (3 new X25519 tests skip in this jsdom runtime, which lacks X25519 Web Crypto; they'll run in browser/newer Node)tsc --noEmit— cleanNot in scope (follow-ups)
account DEK → profile DEK → datatier) + multi-profile UIAccessClaimsstruct (#5)Closes nothing yet (tracking issue stays open for Phase A2/B/C/D). Refs #3.
Phase A1 of the multi-person sharing ADR (docs/adr/multi-person-sharing.md). Each account now gets an X25519 keypair at registration: the public half stored plaintext, the private half wrapped under the account DEK and stored as opaque ciphertext. The keypair is generated client-side; the backend adds no crypto deps and stores everything verbatim, preserving the zero-knowledge contract. This change only introduces the keypair and threads it through the auth flows — it is not consumed yet. It unblocks Phase B (profile sharing) without touching the data model or the ~11 frontend encrypt call sites, which is Phase A2 (per-profile DEKs). Backend: - User model: identity_public_key, identity_private_key_wrapped{,_iv} (all Option<String>, backward compatible). - RegisterRequest/AuthResponse carry the 3 fields; register + login echo them. No changes to change_password/recover (DEK value is unchanged across both, so the wrapped private key is too). - 2 new integration tests: round-trip through register/login, and optional-fields backward compat. Frontend: - crypto/keys.ts: generateIdentityKeyPair, wrapIdentityPrivateKey, unwrapIdentityPrivateKey + in-memory identity store mirroring the DEK. - types/api.ts: AuthTokens + RegisterRequest extended (removes an existing `as any` cast). - useStore register/login/logout + UnlockPage unwrap the private key alongside the DEK. - 3 new crypto tests (X25519 lifecycle, wrong-DEK rejection, distinct shared secrets); skip gracefully where the runtime lacks X25519. Backend: cargo test + clippy green. Frontend: npm test + tsc green. Also gitignore .zcode/ (local tooling artifact).