feat: add a password lock and an encrypted vault in extension
Extension needs storage that's encrypted at rest for private keys it'll create. This PR adds that storage, a password to unlock it, and the screens around it but nothing writes to the vault yet.
The vault lives in src/background/vault/, and only the background touches it. We derive an AES-256-GCM key from the user's password with PBKDF2-SHA-256 (600,000 iterations), encrypt the vault's contents as one JSON document, and store it under local:vault. A wrong password just fails decryption. It's all Web Crypto, so there's no new dependency.
Code that needs the vault calls vault.defineItem(key, { fallback }), which works like a WXT storage item so storing the signing keys will look like:
const signingKeys = vault.defineItem<StoredKey[]>('signingKeys', {
fallback: [],
})
Chrome usually stops an idle service worker after about 30 seconds, and anything decrypted in memory goes with it. So the user doesn't have to retype the password every time, the key is cached in session storage, as MetaMask does. Session storage is memory-only and the browser clears it on restart, so the vault survives an idle worker but locks when the browser closes.
Ignore the UI, it's a placeholder.
https://github.com/user-attachments/assets/dd03af48-a4ae-4d10-869c-b2f8d0a28647