Wallet Security¶
This page details the cryptographic storage, session management, and encoding compatibility mechanisms used by the XE web wallet.
Seed encryption¶
Private keys (seeds) are encrypted at rest using AES-GCM with PBKDF2-derived keys.
Key derivation¶
| Parameter | Value |
|---|---|
| Algorithm | PBKDF2 |
| Hash | SHA-256 |
| Iterations | 100,000 |
| Key length | 256 bits |
| Salt | Random, per wallet |
The user's passphrase is fed through PBKDF2 to produce a 256-bit AES key. Each wallet has its own random salt, so the same passphrase produces different keys for different wallets.
Encryption¶
| Parameter | Value |
|---|---|
| Algorithm | AES-GCM |
| IV | Random, per encryption |
| Input | 32-byte hex seed |
| Output | Base64-encoded ciphertext |
A fresh random IV is generated every time a seed is encrypted (on wallet creation, import, or passphrase change). This ensures that re-encrypting the same seed produces different ciphertext.
Important
The IV must be unique per encryption. Reusing an IV with the same key completely breaks AES-GCM's security guarantees.
Storage schema¶
Wallet data is stored in localStorage as a JSON object:
{
"version": 1,
"activeId": "w_1709123456789",
"wallets": [
{
"id": "w_1709123456789",
"name": "default",
"encrypted": "<base64 ciphertext>",
"salt": "<base64 salt>",
"iv": "<base64 IV>"
}
]
}
| Field | Description |
|---|---|
version |
Schema version (currently 1) |
activeId |
ID of the currently selected wallet |
wallets[] |
Array of wallet entries |
wallets[].id |
Unique ID (w_ + creation timestamp) |
wallets[].name |
User-assigned display name |
wallets[].encrypted |
Base64-encoded AES-GCM ciphertext of the seed |
wallets[].salt |
Base64-encoded PBKDF2 salt |
wallets[].iv |
Base64-encoded AES-GCM initialization vector |
Note
No plaintext seed or private key is ever written to localStorage. Only the encrypted ciphertext, salt, and IV are persisted.
Session management¶
Idle timeout¶
The wallet automatically locks after 5 minutes (300,000 ms) of inactivity.
Monitored events:
mousemovemousedownkeydowntouchstartscroll
Any of these events resets the idle timer. When the timer expires:
- Lock the wallet (zero out all in-memory seeds)
- Stop the auto-receive poller
- Clear application state
- Redirect to the unlock screen
Lock behavior¶
When locked, all decrypted seeds are overwritten with zeros and dereferenced. The wallet transitions to a state where only the unlock form is accessible. Re-entering the passphrase decrypts all wallet seeds and resumes normal operation.
Key operations¶
| Operation | Description |
|---|---|
| Unlock | Decrypt all wallet seeds using the passphrase. Derives AES keys via PBKDF2, decrypts each seed with AES-GCM. |
| Lock | Zero out all in-memory seeds, stop poller, clear state. |
| Add wallet | Generate or import a seed, encrypt with current passphrase, store in localStorage. |
| Remove wallet | Delete a wallet entry from localStorage. Requires at least one wallet to remain. |
| Rename wallet | Update the name field in localStorage. |
| Reveal seed | Re-authenticate with passphrase, then display the decrypted seed. |
| Change passphrase | Re-encrypt all wallet seeds with a new passphrase. Generates new salts and IVs. |
Passphrase change
Changing the passphrase re-encrypts every wallet seed with fresh salt and IV. If the process is interrupted (e.g., browser crash), some wallets may be encrypted with the old passphrase and some with the new. The wallet handles this gracefully by attempting both on unlock.
Canonical encoding compatibility¶
The wallet must produce byte-identical canonical block encodings to the Go node for hashing and signing to work correctly. The JavaScript implementation mirrors the Go MarshalBlockCanonical function exactly:
| Constant | Value | Shared between Go and JS |
|---|---|---|
VERSION_BYTE |
0x02 |
Yes |
| Send type | 0x01 |
Yes |
| Receive type | 0x02 |
Yes |
| Claim type | 0x03 |
Yes |
| Lease type | 0x04 |
Yes |
| LeaseAccept type | 0x05 |
Yes |
| LeaseSettle type | 0x06 |
Yes |
Field ordering, padding, and endianness are identical. See Binary Encoding for the full specification.
See also¶
- Wallet Overview -- feature summary and tech stack
- Wallet Features -- detailed feature reference
- Cryptography -- ed25519 and hashing primitives
- Binary Encoding -- canonical block encoding