Multi-device E2E sync: ECDH, QR pairing, and the honest path from relay to P2P
Crypto suite choice
ECDH P-256 + HKDF-SHA256 + AES-256-GCM aligns with WebCrypto availability across browsers and Workers. X25519 + ChaCha20-Poly1305 can be preferable in some native stacks—Phase 6 prioritized portability and cross-team review surface.
HKDF info / salt labels must match byte-for-byte across clients. Copy-paste bugs here produce “pairing succeeded in logs, decrypt fails in app” — the worst class of failure.
Three-step flow (conceptual)
sync/initiate— mint QR nonce; short TTL (~60s) to limit blast radius.sync/approve— primary device signs attestation; server verifies JWS (ES256 family consistency with your passport keys).sync/complete— enroll long-lived device signing key; rotate session material.
QR payload: include v for versioning, session_id, did, qr_nonce, ephemeral pubkey, expires_at. JSON → base64url for QR string (CBOR deferred to future revision if size constraints bite).
Audit-only field
sync_encryption_key_hash = SHA-256 of ephemeral ECDH public material for audit correlation only. Never feed this hash into decrypt paths—doing so turns an audit artifact into a footgun for future engineers.
Relay vs P2P (honest staging)
Vault ciphertext may traverse a DB-backed relay in some flows while P2P ships. Canon expects true P2P for identity payloads long-term; @canon-deviation documents relay until WebRTC/DataChannel paths are default for capable clients (see P2P sync plan).
Tests (minimum)
- Nonce uniqueness and replay rejection.
- Stale/expired session → 400 with structured problem details.
- Partial DB indexes for pending session sweeps (ops hygiene).
- Cross-client HKDF vector parity (fixture shared across TS/Rust if applicable).
Companion: selective disclosure + L2 anchoring. Cross-cutting: P2P sync.
— Part of