diff --git a/bundled-addons/aegis/index.js b/bundled-addons/aegis/index.js index 47b79921..2158551c 100644 --- a/bundled-addons/aegis/index.js +++ b/bundled-addons/aegis/index.js @@ -2988,12 +2988,18 @@ function registerPanelMessages(api) { if (next.lockOnClose) api.storage.set("aegis/session/enc", null); return sessionStatusFor(api); }); - api.onMessage("sessionEnable", (p, m) => { + api.onMessage("sessionEnable", async (p, m) => { fromPanel(m); const pw = String(p && p.masterPassword || ""); if (!pw) throw new Error("master password required"); const ss = safeStorageOr(api); - if (!ss || !ss.isEncryptionAvailable()) throw new Error("OS keystore unavailable — remember-me needs Windows DPAPI / macOS Keychain / libsecret"); + // Same bar as the PIN: Linux's basic_text "keystore" is a constant key, + // i.e. the master password in the clear. + if (!osSealUsable(ss)) throw new Error("OS keystore unavailable — remember-me needs Windows DPAPI / macOS Keychain / libsecret"); + // Store only a password the vault accepts. + try { await api.vault.lifecycle.unlock(pw); } + catch { throw new Error("wrong master password"); } + noteMasterVerified(api); const enc = ss.encryptString(pw).toString("base64"); api.storage.set("aegis/session/enc", { encPwB64: enc, savedAt: Date.now() }); // Force lockOnClose = false alongside — semantically they're the same @@ -3034,7 +3040,7 @@ function sessionStatusFor(api) { lockOnClose: !!cfg.lockOnClose, idleMinutes: Number(cfg.idleMinutes) || 0, hasSession: !!(blob && blob.encPwB64), - safeStorageAvailable: !!(ss && ss.isEncryptionAvailable && ss.isEncryptionAvailable()), + safeStorageAvailable: osSealUsable(ss), }; } @@ -3064,7 +3070,12 @@ async function tryAutoUnlock(api) { const blob = api.storage.get("aegis/session/enc", null); if (!blob || !blob.encPwB64) return; const ss = safeStorageOr(api); - if (!ss || !ss.isEncryptionAvailable()) return; + if (!osSealUsable(ss)) { + // Sealed under no real keystore: it is the master password in the + // clear on disk. Never use it; remove it. + api.storage.set("aegis/session/enc", null); + return; + } try { const pw = ss.decryptString(Buffer.from(blob.encPwB64, "base64")); await api.vault.lifecycle.unlock(pw); diff --git a/bundled-addons/aegis/panel.js b/bundled-addons/aegis/panel.js index 5ac60308..683e2f93 100644 --- a/bundled-addons/aegis/panel.js +++ b/bundled-addons/aegis/panel.js @@ -4820,7 +4820,7 @@ function renderSessionSettings() { } else if (sessionState.lockOnClose) { hint.textContent = "On — Aegis asks for the master password (or PIN) every time Theseus starts."; } else { - hint.textContent = "Off — Aegis stays signed in across Theseus restarts. Master password is stored in the OS keystore under this user only."; + hint.textContent = "Off — Aegis stays signed in across Theseus restarts. The master password is stored in the OS keystore under this user only, so anything that runs as your Windows account can read it; the PIN's security-chip protection does not cover it."; } } }