You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(identity): key the device-seed cache by account (spec 9.3) + zeroize handoff buffers
Audit blocker: the Minister-derived seed was adopted into the single
un-keyed localStorage slot, silently clobbering any pre-existing local
random-seed identity on the next Minister login and letting accounts
sharing a browser overwrite each other's seeds.
- Adoption now writes ONLY an account-scoped slot
(deforum_device_seed_b64:<userNullifierHash> - the nullifier hash is a
stable 1:1 stand-in for the Minister sub, which never reaches the
client). The legacy un-keyed slot is never an adoption target, so a
pre-existing local seed stays intact and restorable.
- vault.ts owns slot resolution: reads and generic writes use the scoped
slot only when the session is scoped AND that slot exists, else the
legacy slot - byte-identical behavior when no fragment ever arrived.
The root layout pins the scope on every full page load.
- device.ts routes through the vault store (drops its duplicate key
handling).
- New fail-closed branch: a fragment with no signed-in account scope
derives nothing (never writes the shared legacy slot).
- Best-effort zeroization of the decoded mix secret, per-app secret, and
derived seed buffers after use.
- Tests: non-clobber of a pre-existing legacy seed, distinct slots for
two accounts in one browser, deterministic re-derivation across page
loads, and the null-scope fail-closed path; golden vector unchanged.
// FAIL CLOSED: the fragment arrived but the RP mix secret is not
64
62
// provisioned (or too short). No anonymous identity is derived; the
65
63
// existing seed behavior stands. Surfaced so the operator can see the
66
64
// misconfiguration - never logs any secret value.
65
+
mix?.fill(0);
67
66
console.warn(
68
67
'minister-anon: a minister_anon fragment arrived but MINISTER_ANON_RP_MIX_SECRET is unset or shorter than 32 bytes; anonymous identity NOT derived (fail-closed)'
69
68
);
70
69
return;
71
70
}
71
+
if(scope===null){
72
+
// FAIL CLOSED: a fragment arrived with no signed-in account to scope the
73
+
// seed to (should be impossible - the OIDC callback sets the session
74
+
// cookie before its 3xx). Never write the shared legacy slot (spec 9.3).
75
+
mix.fill(0);
76
+
console.warn(
77
+
'minister-anon: a minister_anon fragment arrived without a signed-in account to scope the seed to; anonymous identity NOT derived (fail-closed)'
0 commit comments