Threat model and security considerations for Dusk Wallet.
Dusk Wallet is a self-custody wallet — users control their own keys. This document outlines security measures, known limitations, and recommendations.
| Asset | Sensitivity | Storage |
|---|---|---|
| Mnemonic phrase | Critical | Encrypted in vault |
| Private keys | Critical | Derived in-memory from mnemonic |
| Shielded notes | High | IndexedDB (per-network) |
| dApp permissions | Medium | Platform storage |
| User settings | Low | Platform storage |
- Malicious websites — XSS, phishing, fake dApps
- Browser extensions — Malicious or compromised extensions
- Local attackers — Physical access to device
- Network attackers — MITM, malicious nodes
- Supply chain — Compromised dependencies
| Platform | Algorithm | Parameters |
|---|---|---|
| Extension | PBKDF2 + AES-GCM-256 | 900,000 iterations |
| Tauri | Stronghold + Argon2 | OS-level encrypted storage |
// Extension vault encryption
const key = await crypto.subtle.deriveKey(
{ name: "PBKDF2", salt, iterations: 900000, hash: "SHA-256" },
passwordKey,
{ name: "AES-GCM", length: 256 },
false,
["encrypt", "decrypt"]
);- Mnemonic held in JavaScript heap after unlock
- Cleared on explicit lock or auto-lock timeout
- Limitation: Cannot guarantee memory zeroization in JavaScript
- Keep PBKDF2 iterations aligned with industry guidance (currently 900,000)
- Keep rate limiting on unlock attempts (exponential backoff)
The wallet automatically locks after a configurable timeout:
| Setting | Options |
|---|---|
| Auto-lock timeout | 1, 5, 15, 30, 60 minutes |
| Default | 5 minutes |
Implementation uses chrome.alarms API for reliable timing even when service worker sleeps.
The last activity timestamp is persisted in extension session storage when available
(falling back to local storage outside extension contexts), so a restarted background
service worker does not immediately lock an otherwise active unlocked wallet.
- Each origin must explicitly request connection
- User approves via popup before granting access
- Permissions stored per-origin with the selected profile/account index and grants
// Permission record
{
"https://dapp.example.com": {
profileId: "account:0:...",
accountIndex: 0,
grants: {
publicAccount: true,
shieldedReceiveAddress: false
},
connectedAt: 1706400000000,
}
}| Method | Permission Required | Unlock Required |
|---|---|---|
dusk_getCapabilities |
No | No |
dusk_requestProfiles |
No (grants it) | Yes (prompt) |
dusk_profiles |
Yes (returns [] otherwise) |
Yes (returns [] otherwise) |
dusk_requestShieldedAddress |
No (grants or upgrades it) | Yes (prompt) |
dusk_chainId |
No | No |
dusk_switchNetwork |
Yes | No |
dusk_getPublicBalance |
Yes | Yes |
dusk_estimateGas |
Yes | No |
dusk_sendTransaction |
Yes | Yes |
dusk_watchAsset |
Yes | Yes |
dusk_signMessage |
Yes | Yes |
dusk_signAuth |
Yes | Yes |
dusk_disconnect |
No | No |
All dusk_sendTransaction calls require explicit user approval via a popup window. Users can:
- Review transaction details
- Adjust gas settings
- Approve or reject
Manifest V3 enforces strict CSP by default:
- No inline scripts
- No
eval() - No remote code execution
The public repository does not currently include a native Tauri wrapper, so there is no checked-in tauri.conf.json to audit here.
- All node communication over HTTPS
- Users can configure custom node URLs
- No hardcoded API keys or secrets
- Prover receives transaction circuits (no private keys)
- Timeouts prevent hanging connections
- Fallback to node URL if prover fails
- Content scripts run in isolated world
- Cannot access page JavaScript context
- Provider injected via script tag with controlled interface
- No DOM access (by design)
- Wallet engine runs in offscreen document
- Message passing with validation
JavaScript does not provide memory zeroization guarantees. Private keys and mnemonic remain in heap memory until garbage collected.
Mitigation: Lock wallet when not in use; rely on OS-level protections.
Users must trust:
- The extension itself (install from official source)
- Other installed extensions (could attempt to steal data)
Mitigation: Keep requested permissions minimal and review host_permissions regularly (broad host access increases blast radius).
Shielded notes are stored in IndexedDB without encryption.
Risk: Local attacker with file system access could read notes.
Mitigation: Consider encrypting IndexedDB contents; rely on OS-level encryption.
Current iteration count is 900,000.
Risk: Brute-force resistance still depends on password strength.
Mitigation: Maintain a high iteration count and monitor guidance for updates.
Dusk uses BLS12-381 signatures, which Ledger/Trezor do not support.
Status: Not possible with current hardware wallet firmware.
- Use a strong, unique password for the wallet
- Write down mnemonic phrase and store securely offline
- Enable auto-lock with short timeout
- Verify transaction details before approving
- Only connect to trusted dApps
- Keep browser and extensions updated
- Never log sensitive data (mnemonic, private keys)
- Validate all message inputs
- Use strict CSP in any native wrapper builds
- Keep dependencies updated
- Run security-focused code review for crypto code
- Immediately transfer funds to a new wallet
- Create new wallet with fresh mnemonic
- Revoke all dApp permissions on compromised wallet
- Uninstall extension immediately
- Assume mnemonic is compromised
- Follow mnemonic compromise steps above
Status: No external audit completed yet.
Recommendation: Before production release, conduct:
- Cryptographic implementation review
- Smart contract interaction audit
- Browser extension security audit
If you discover a security vulnerability, please:
- Do not open a public GitHub issue
- Report it privately through GitHub Security Advisories: https://github.com/dusk-network/wallet/security/advisories/new
- Include detailed reproduction steps
- Allow reasonable time for fix before disclosure