Aegis: stay-unlocked needs a real keystore and the right password
Remember-me sealed the master password with whatever safeStorage offered, which on Linux without a keyring is a constant key, i.e. the password in the clear in the add-on store; and it stored any string without checking it. It now uses the same keystore test as the PIN, verifies the password with the vault first, and drops a blob sealed under no real keystore. Settings says that remember-me leaves the master password readable to anything running as the user, which the PIN's TPM protection does not change.
This commit is contained in:
parent
e72c0ed96b
commit
d38da383d9
2 changed files with 16 additions and 5 deletions
|
|
@ -2988,12 +2988,18 @@ function registerPanelMessages(api) {
|
||||||
if (next.lockOnClose) api.storage.set("aegis/session/enc", null);
|
if (next.lockOnClose) api.storage.set("aegis/session/enc", null);
|
||||||
return sessionStatusFor(api);
|
return sessionStatusFor(api);
|
||||||
});
|
});
|
||||||
api.onMessage("sessionEnable", (p, m) => {
|
api.onMessage("sessionEnable", async (p, m) => {
|
||||||
fromPanel(m);
|
fromPanel(m);
|
||||||
const pw = String(p && p.masterPassword || "");
|
const pw = String(p && p.masterPassword || "");
|
||||||
if (!pw) throw new Error("master password required");
|
if (!pw) throw new Error("master password required");
|
||||||
const ss = safeStorageOr(api);
|
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");
|
const enc = ss.encryptString(pw).toString("base64");
|
||||||
api.storage.set("aegis/session/enc", { encPwB64: enc, savedAt: Date.now() });
|
api.storage.set("aegis/session/enc", { encPwB64: enc, savedAt: Date.now() });
|
||||||
// Force lockOnClose = false alongside — semantically they're the same
|
// Force lockOnClose = false alongside — semantically they're the same
|
||||||
|
|
@ -3034,7 +3040,7 @@ function sessionStatusFor(api) {
|
||||||
lockOnClose: !!cfg.lockOnClose,
|
lockOnClose: !!cfg.lockOnClose,
|
||||||
idleMinutes: Number(cfg.idleMinutes) || 0,
|
idleMinutes: Number(cfg.idleMinutes) || 0,
|
||||||
hasSession: !!(blob && blob.encPwB64),
|
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);
|
const blob = api.storage.get("aegis/session/enc", null);
|
||||||
if (!blob || !blob.encPwB64) return;
|
if (!blob || !blob.encPwB64) return;
|
||||||
const ss = safeStorageOr(api);
|
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 {
|
try {
|
||||||
const pw = ss.decryptString(Buffer.from(blob.encPwB64, "base64"));
|
const pw = ss.decryptString(Buffer.from(blob.encPwB64, "base64"));
|
||||||
await api.vault.lifecycle.unlock(pw);
|
await api.vault.lifecycle.unlock(pw);
|
||||||
|
|
|
||||||
|
|
@ -4820,7 +4820,7 @@ function renderSessionSettings() {
|
||||||
} else if (sessionState.lockOnClose) {
|
} else if (sessionState.lockOnClose) {
|
||||||
hint.textContent = "On — Aegis asks for the master password (or PIN) every time Theseus starts.";
|
hint.textContent = "On — Aegis asks for the master password (or PIN) every time Theseus starts.";
|
||||||
} else {
|
} 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.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue