Appearance
Security Model
This document covers how the wallet protects private material, manages sessions, and what trade-offs have been accepted.
Vault encryption
All private data is stored in an encrypted vault. The encryption pipeline (defined in src/domain/crypto.ts):
- Key derivation - PBKDF2 with 600,000 iterations, SHA-256 hash, 16-byte random salt
- Cipher - AES-GCM with a 256-bit derived key and 12-byte random IV
- Plaintext - JSON-serialized
VaultPlaintextcontaining accounts, settings, permissions, and network-scoped data
The iteration count has a validated range of 10,000 to 1,000,000. Vaults created with older iteration counts are transparently migrated to 600,000 on unlock.
Storage at rest
The encrypted vault is stored in chrome.storage.local under the key tao_wallet_encrypted_v1. This is the only location where account secrets (mnemonics, derived keys) persist, and they are always encrypted.
The EncryptedVault structure contains the KDF parameters (salt, iterations), cipher parameters (IV), and the ciphertext. No plaintext secrets touch disk.
Session management
When the user unlocks the vault, a session is created containing the derived CryptoKey, the salt, and the iteration count. This session is stored in chrome.storage.session under the key tao_wallet_unlock_session_v1.
chrome.storage.session is memory-backed - it survives MV3 service worker hibernation but is cleared when the browser closes. This is a deliberate choice: MV3 service workers can be suspended at any time, and the session must survive those suspensions without writing decrypted material to disk.
Password requirements
Wallet passwords must be at least 8 characters. New passwords (on creation or change) must also meet strength requirements: at least one uppercase letter, one digit, and one special character.
These requirements are enforced in the UI during wallet creation (CreatePassword.tsx), password change (SettingsSecurityPassword.tsx), and backup restore with new password (RestoreBackup.tsx).
Auto-lock
The wallet locks itself after a configurable period of inactivity (default varies by user setting). Auto-lock is implemented via chrome.alarms - when the alarm fires, the in-memory session is cleared, signing keys are zeroed, and the vault returns to a locked state.
Activity is tracked on non-passive background operations. Read-only actions (balance checks, state queries) do not extend the session.
To configure auto-lock, see Security settings.
Passkey unlock
The wallet supports passkey (WebAuthn) as an alternative unlock method. The implementation uses the PRF (pseudo-random function) extension to derive a wrapping key from the authenticator. This wrapping key encrypts the vault session material, which is then stored alongside passkey metadata in chrome.storage.local.
On unlock, the PRF output is used to decrypt the stored session, bypassing password entry. Passkey enrollment requires the side panel display mode due to browser API constraints.
Public metadata caching
Some data is stored unencrypted in chrome.storage.local for performance:
accountsPublicCache- public account info (addresses, labels, types) so the locked wallet can show account counts and respond towallet_getAccountsfor authorized originsproxyRelationshipsCache- proxy delegation topology (public on-chain data)networkConfigCache- selected RPC endpoint and genesis hash
None of these contain secrets. This is consistent with how MetaMask and the Polkadot.js extension handle public metadata.
Accepted trade-offs
These are accepted trade-offs with full rationale:
- AES key in session storage - the derived CryptoKey lives in
chrome.storage.sessionwhile unlocked. Memory-backed, cleared on browser close. Physical-access attack surface only. - Metadata on disk without encryption - public addresses and network topology are cached unencrypted for performance. No secrets exposed.
- Clipboard overwrite edge case - the 30-second auto-clear for copied seed phrases may overwrite unrelated clipboard content. Same behavior as 1Password and Bitwarden.
- Unmount before clipboard clear - navigating away from the seed display before the 30-second timer fires prevents clipboard clearing. Short window, user is actively navigating.
Threat model
- Phishing dApps - mitigated by explicit per-request approval with the requesting origin displayed prominently in the approval window
- Origin spoofing - the origin is taken from the content script's
window.location.origin, not from the dApp's self-reported data - Storage exfiltration - at-rest storage contains only the encrypted vault. Extracting
chrome.storage.localyields ciphertext protected by PBKDF2 + AES-GCM - MV3 worker restarts - the unlocked session is memory-only (via
chrome.storage.session) and intentionally lost when the browser closes