NotASithLord / NotASithLord/peerd

Vault: the raw DK still sits in chrome.storage.session — the real ceiling on R11

Open
#303 0 comments 0 reactions 0 assignees View on GitHub

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

  1. Accept the relock. SW restart ⇒ vault locked. Security-clean, operationally the worst. Possibly acceptable if paired with a fast re-unlock (below).
  2. 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.
  3. A non-extractable handle that survives the SW lifecycle. A CryptoKey is 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).
  4. 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.