Wallet Security

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

ParameterValue
AlgorithmPBKDF2
HashSHA-256
Iterations100,000
Key length256 bits
SaltRandom, 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

ParameterValue
AlgorithmAES-GCM
IVRandom, per encryption
Input32-byte hex seed
OutputBase64-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.

#Storage schema

Wallet data is stored in localStorage as a JSON object:

json
{  "version": 1,  "activeId": "w_1709123456789",  "wallets": [    {      "id": "w_1709123456789",      "name": "default",      "encrypted": "<base64 ciphertext>",      "salt": "<base64 salt>",      "iv": "<base64 IV>"    }  ]}
FieldDescription
versionSchema version (currently 1)
activeIdID of the currently selected wallet
wallets[]Array of wallet entries
wallets[].idUnique ID (w_ + creation timestamp)
wallets[].nameUser-assigned display name
wallets[].encryptedBase64-encoded AES-GCM ciphertext of the seed
wallets[].saltBase64-encoded PBKDF2 salt
wallets[].ivBase64-encoded AES-GCM initialization vector

#Session management

#Idle timeout

The wallet automatically locks after 5 minutes (300,000 ms) of inactivity.

Monitored events:

  • mousemove
  • mousedown
  • keydown
  • touchstart
  • scroll

Any of these events resets the idle timer. When the timer expires:

  1. Lock the wallet (zero out all in-memory seeds)
  2. Stop the auto-receive poller
  3. Clear application state
  4. 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

OperationDescription
UnlockDecrypt all wallet seeds using the passphrase. Derives AES keys via PBKDF2, decrypts each seed with AES-GCM.
LockZero out all in-memory seeds, stop poller, clear state.
Add walletGenerate or import a seed, encrypt with current passphrase, store in localStorage.
Remove walletDelete a wallet entry from localStorage. Requires at least one wallet to remain.
Rename walletUpdate the name field in localStorage.
Reveal seedRe-authenticate with passphrase, then display the decrypted seed.
Change passphraseRe-encrypt all wallet seeds with a new passphrase. Generates new salts and IVs.

#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:

ConstantValueShared between Go and JS
VERSION_BYTE0x02Yes
Send type0x01Yes
Receive type0x02Yes
Claim type0x03Yes
Lease type0x04Yes
LeaseAccept type0x05Yes
LeaseSettle type0x06Yes

Field ordering, padding, and endianness are identical. See Binary Encoding for the full specification.

#See also