817f71790ba4
add vault.ts unlockWithKey (bypass Argon2id, use pre-derived key) and getDerivedKeyHex
- time
- 2026-05-05 00:18 UTC · 5 months ago
- machine
- koh2
- parent
- 567532481e30
↓ peach-browser-817f7179.tar.gzatlas at this save
files changed1 file
src/background/vault.ts+42 −0src/background/vault.ts+42 −0
@@ -702,6 +702,48 @@702702 return derivedKey !== null;703703 }704704 705+// Opens the vault using a pre-derived AES-256 key (skipping Argon2id).706+// Used by Android biometric unlock — the key was derived on the native side.707+export async function unlockWithKey(708+ masterKeyHex: string,709+): Promise<{ ok: true } | { ok: false; error: string }> {710+ try {711+ const keyBytes = unhex(masterKeyHex);712+ const masterKey = await crypto.subtle.importKey(713+ "raw",714+ keyBytes,715+ { name: "AES-GCM" },716+ false,717+ ["decrypt"],718+ );719+ const payload = await tryDecryptSlot(await loadSlot(SLOT_A), masterKey);720+ if (!payload) return { ok: false, error: "invalid_key" };721+722+ derivedKey = masterKey;723+ vaultMode = "real";724+ entries = payload.entries;725+ duressConfigured = payload.duressConfigured;726+ buildDomainIndexSync(entries);727+ await loadSettings();728+ touchAutoLock();729+ return { ok: true };730+ } catch {731+ return { ok: false, error: "invalid_key" };732+ }733+}734+735+// Returns the current in-memory derived key as a hex string.736+// Used by the biometric enable flow in Settings to store the key in Android Keystore.737+export function getDerivedKeyHex(): string {738+ if (!derivedKey) throw new Error("vault_not_unlocked");739+ return hex(derivedKeyBytes());740+}741+742+function derivedKeyBytes(): Uint8Array {743+ const buf = derivedKey!.exportKey("raw") as ArrayBuffer;744+ return new Uint8Array(buf);745+}746+705747 export function getEntries(): VaultEntry[] {706748 if (!derivedKey) throw new Error("vault locked");707749 return entries.filter((entry) => !entry.deletedAt);