NotASithLord / NotASithLord/peerd
Vault: the raw DK still sits in chrome.storage.session — the real ceiling on R11
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 410
- Forks
- 45
- Avg merge
- 11d 5h
- Merged PRs (30d)
- 1
Description
#298 made the resident vault data key non-extractable and bounded the session mirror (lifetime, lock semantics, fail-closed on stale/malformed/legacy records). It explicitly did not close this, and the post-merge review agrees it is the largest residual inside that PR.
The shape
Two copies of the data key, with different properties:
| form | can in-origin code get bytes? | |
|---|---|---|
| resident (normal ops) | non-extractable CryptoKey handle |
no — exportKey rejects |
| SW-restart mirror | raw encoded DK in chrome.storage.session |
yes |
So during the unlocked window, code with extension-origin/session-storage access can still retrieve portable DK bytes. The non-extractability property is real and worth having — it defeats accidental key-reference leaks and some heap-compromise classes — but "peerd can never extract its vault key" is not yet true, and the threat model correctly says so (R11).
Why the mirror exists
MV3 evicts the service worker. Without the mirror, every eviction relocks the vault, which is operationally hostile.
Options, roughly ordered by security cleanliness
- Accept the relock. SW restart ⇒ vault locked. Security-clean, operationally the worst. Possibly acceptable if paired with a fast re-unlock (below).
- Platform-backed re-unlock. WebAuthn PRF (already shipped as an unlock factor —
peerd-egress/vault/webauthn.js) to re-derive after restart. Moves the resume capability behind a platform authenticator instead of an origin-readable record. Cost: a user gesture per restart, which may be frequent. - A non-extractable handle that survives the SW lifecycle. A
CryptoKeyis structured-cloneable and IDB-persistable — the DPoP key already relies on exactly this (dpop/keys.js). If a wrapping key can be persisted as a handle, the mirror could hold the DK as ciphertext under a key peerd cannot read, rather than raw bytes. This is the option worth investigating first: the primitive is already proven in-tree. The open question is whether an IDB-resident non-extractable key is reachable early enough in SW startup, and whether that merely relocates the trust boundary rather than raising it (anything that can read the record can also ask the handle to unwrap). - OS keystore / native companion. Strongest, but breaks the no-backend, extension-only posture.
Definition of done
Either the raw bytes are gone from the origin-readable record, or the decision to keep them is made explicitly with the alternatives written down and the residual restated. Option 3 needs a spike before anyone commits.
Related: extension/peerd-egress/vault/vault.js, THREAT-MODEL.md R11.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with extension/peerd-egress/vault/vault.js and compare its session-mirror handling with the IDB-persisted non-extractable key approach in dpop/keys.js. Read THREAT-MODEL.md R11 and investigate whether a persisted handle is available early enough during service-worker startup. Done means the raw DK is removed from the origin-readable record, or the alternatives and residual risk are explicitly documented.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript
- Domain
- security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100