apps/web/src/lib/cryptoStore.ts generates the device's ECDH identity keypair with extractable: false and only ever persists the exported PUBLIC key JWK to IndexedDB — the private CryptoKey is discarded immediately after generation. getIdentityPrivateKey() silently calls generateKey again and returns a brand-new, unrelated private key on every invocation, so every downstream primitive operates on nonsense key material that changes on every read.
The fix: IndexedDB's structured-clone algorithm natively supports storing a CryptoKey object directly, even a non-extractable one. Store the actual CryptoKeyPair (or at minimum the private CryptoKey) object itself in the existing keys object store.
Acceptance criteria:
generateIdentityKeyPair/storeIdentityKeyPair persist the private CryptoKey object itself via structured clone into IndexedDB
getIdentityPrivateKey() retrieves and returns the actually-stored private CryptoKey; two calls in the same session and across a simulated page reload return the same key material
- A page reload does not regenerate or lose the identity key
- A test confirms the private key persists across a simulated reload and that signing/deriving operations using the retrieved key succeed
apps/web/src/lib/cryptoStore.tsgenerates the device's ECDH identity keypair withextractable: falseand only ever persists the exported PUBLIC key JWK to IndexedDB — the privateCryptoKeyis discarded immediately after generation.getIdentityPrivateKey()silently callsgenerateKeyagain and returns a brand-new, unrelated private key on every invocation, so every downstream primitive operates on nonsense key material that changes on every read.The fix: IndexedDB's structured-clone algorithm natively supports storing a
CryptoKeyobject directly, even a non-extractable one. Store the actualCryptoKeyPair(or at minimum the privateCryptoKey) object itself in the existingkeysobject store.Acceptance criteria:
generateIdentityKeyPair/storeIdentityKeyPairpersist the privateCryptoKeyobject itself via structured clone into IndexedDBgetIdentityPrivateKey()retrieves and returns the actually-stored privateCryptoKey; two calls in the same session and across a simulated page reload return the same key material