This document provides a detailed overview of the security measures implemented in Distordia Q-Wallet.
- Quantum Security
- Storage Security
- Session Management
- Code Security
- Threat Model
- Alternative Approaches
- Security Auditing
Distordia Q-Wallet achieves quantum resistance through the Nexus blockchain's SigChain (Signature Chain) architecture. This is not a feature of the wallet itself, but rather an inherent property of how Nexus secures transactions.
Security Rating: A+ (Quantum-Resistant)
Quantum computers threaten traditional cryptocurrency security through:
- Shor's Algorithm: Can break RSA and ECC by deriving private keys from public keys
- Key Reuse: Most blockchains expose public keys on-chain, creating permanent attack surface
- Future Vulnerability: "Store now, decrypt later" attacks on historical transactions
The Nexus blockchain uses Signature Chains (SigChains) which provide quantum resistance through:
-
One-Time Keypairs
- Each transaction generates a new keypair
- Private key signs the transaction once, then discarded
- Public key revealed only at transaction broadcast
-
No Key Reuse
- Once a keypair is used for a transaction, it is NEVER reused
- Even if quantum computers derive the private key from the public key, the key is already obsolete
- Historical transactions cannot be reversed (coins already moved to new addresses with new keypairs)
-
Hardware-Like Security Model
- Similar to hardware wallets that generate new addresses per transaction
- Each transaction proves chain of ownership from genesis to current state
- Mathematical chain links transactions without exposing long-term keypairs
-
Forward Secrecy
- Compromising one transaction's keypair doesn't affect other transactions
- Past transactions remain secure even if future keys are compromised
- Quantum computer would need to break EVERY individual transaction in real-time
| Security Aspect | Traditional (Bitcoin/Ethereum) | Nexus SigChains |
|---|---|---|
| Public Key Exposure | Permanent on-chain | One-time per transaction |
| Key Reuse | Common (address reuse) | Never (architectural) |
| Quantum Vulnerability | High (Shor's algorithm) | Minimal (keys expire) |
| Post-Quantum Ready | Requires protocol upgrade | Already implemented |
| Attack Window | Unlimited time | Single transaction window |
- Future-Proof Security: Protected against quantum computers without user action
- Transparent Protection: SigChain security is automatic and invisible to users
- No Performance Cost: One-time keypairs don't slow down transactions
- Long-Term Safety: Historical transactions remain secure indefinitely
The wallet leverages Nexus SigChain security by:
- Proper Session Management: Each login session uses SigChain authentication
- Transaction Signing: All transactions automatically use one-time keypairs via Nexus API
- No Manual Key Management: Users never handle individual keypairs directly
- Visual Indicator: Quantum-resistant badge displayed when wallet is unlocked
What QWallet Does:
- ✅ Properly uses Nexus SigChain architecture
- ✅ Maintains secure session handling
- ✅ Protects credentials in memory
- ✅ Displays quantum security status to users
What QWallet Cannot Control:
⚠️ Network transport security (depends on TLS/HTTPS version)⚠️ Node operator security practices⚠️ Browser security vulnerabilities⚠️ User device security (malware, keyloggers)
Recommendations:
- Use TLS 1.3 or higher for node connections
- Connect only to trusted Nexus nodes
- Keep browser and OS updated
- Use antivirus/anti-malware software
- Never install untrusted browser extensions
For more information on Nexus SigChains and quantum resistance:
- Nexus Whitepaper: https://nexus.io/whitepaper
- SigChain Technical Documentation: See Nexus API documentation
- Quantum Computing Threats to Cryptography: NIST Post-Quantum Cryptography standards
The wallet uses Chrome's session storage API for all sensitive data. This is the same approach used by industry-standard browser wallets like MetaMask.
- Memory-only storage: Data is kept in RAM, NEVER written to disk
- Browser lifecycle: Data persists only while browser is running
- Automatic cleanup: Cleared when browser closes
- Isolated: Not accessible from web pages or other extensions
- Secure context: Protected by browser's security sandbox
If a browser doesn't fully support chrome.storage.session:
- Memory-only encryption key: Generated randomly per session, stored in JavaScript memory
- Key lifetime: Lost when extension reloads/closes
- Encrypted on disk: Data encrypted before storage, but key never persisted
- Unrecoverable: Without the memory-only key, encrypted data is useless
- Still cleared on close: Manual cleanup removes encrypted data
Why this is secure:
- Encryption key never written to disk (lives in memory only)
- Key lost = data unrecoverable
- Better than plaintext storage
- Automatically upgrades when chrome.storage.session available
Sensitive Data (chrome.storage.session or encrypted):
{
session: "UUID-session-id-from-nexus", // Session token
pin: "1234", // User's PIN
genesis: "hash-value", // Account identifier
username: "myusername" // Non-sensitive
}Persistent Data (chrome.storage.local - unencrypted):
{
nodeUrl: "https://api.distordia.com", // API endpoint
approvedDomains: ["example.com"], // dApp permissions
// NO SENSITIVE DATA STORED HERE
}✅ Advantages:
- No disk exposure - Data in RAM only (or encrypted with memory-only key)
- Automatic cleanup - Cleared on browser close
- Industry standard - Same approach as MetaMask, other major wallets
- No key management - Encryption key (if needed) lives in memory only
- Browser-protected - Leverages Chrome's security model
- Graceful degradation - Fallback for browsers without session storage
- Memory accessible while running - Vulnerable to memory dumps (inherent to browser wallets)
- Public computers - Must explicitly logout, don't rely on browser close alone
- No persistence - Must re-authenticate each browser session
- Fallback complexity - Encryption adds code complexity (but improves security)
1. User Login
└─> Create session on Nexus blockchain (username/password/PIN)
└─> Store session ID + PIN in chrome.storage.session
└─> Session unlocked with PIN
2. Active Use
└─> Session ID used for all blockchain operations
└─> PIN required for transaction approval
└─> Lock status synced with blockchain
3. Logout/Browser Close
└─> Retrieve PIN from session storage
└─> Call sessions/terminate/local API with PIN
└─> Clear chrome.storage.session data
└─> Session terminated on blockchain
Problem: Nexus API requires PIN to terminate sessions on multi-user nodes
Solution: Store PIN in memory-only storage (chrome.storage.session)
Why this is secure:
- PIN never written to disk
- Cleared automatically on browser close
- Only used for session termination
- Alternative would be leaving active sessions on blockchain (worse security)
When you logout or close the browser:
// 1. Load session data from storage
const session = await storage.getSession();
const pin = await storage.getPin();
// 2. ATTEMPT to terminate on blockchain
try {
await api.request('sessions/terminate/local', {
pin: pin,
session: session.session
});
console.log('Session terminated on blockchain');
} catch (error) {
console.error('Failed to terminate on blockchain:', error);
// SECURITY: Continue with local cleanup anyway
}
// 3. ALWAYS clear local storage (regardless of step 2 success)
await storage.clearSession(); // Removes session + PINCRITICAL SECURITY PRINCIPLE:
- ✅ Local storage is ALWAYS cleared, even if blockchain termination fails
- ✅ Public computer safety - Sensitive data never left on machine
⚠️ Blockchain session - May remain active if node offline (expires in ~24 hours)- 🔒 Local security prioritized over remote cleanup
What happens if the node is offline when you logout/close browser?
- Wallet attempts termination (tries to contact node)
- Network error occurs (timeout, connection failed)
- Local storage is CLEARED ANYWAY ✅
- Session ID removed
- PIN removed
- Genesis/username removed
- Blockchain session remains active
⚠️ - Will expire naturally (typically 24 hours)
- Cannot be used without local session ID (which is now deleted)
- No risk to local machine security
Why this is secure:
- 🔒 Sensitive data never left on computer
- 🛡️ Compromised computer cannot access blockchain session (no local credentials)
- ⏳ Orphaned blockchain session times out automatically
- 🏛️ Safe for public computer use
The extension enforces a strict Content Security Policy to prevent code injection attacks:
{
"content_security_policy": {
"extension_pages": "script-src 'self'; object-src 'self'"
}
}What this blocks:
- ❌ All inline
<script>tags - ❌ All
eval()andFunction()constructors - ❌ All event handlers in HTML (
onclick=,onerror=, etc.) - ❌ External scripts from untrusted sources
- ✅ Only allows scripts from the extension package itself
Why this is critical:
- Prevents XSS (Cross-Site Scripting) attacks
- Blocks code injection even if bugs exist
- Defense-in-depth security layer
- Industry standard for browser extensions
Example protection:
// Even if a malicious API returns this:
const maliciousResponse = '<script>stealCredentials()</script>';
// And buggy code does this:
container.innerHTML = maliciousResponse;
// CSP blocks the script from executing!
// Browser console shows: "Refused to execute inline script"All user-controlled and external data is sanitized before display:
Safe DOM Manipulation:
// ❌ UNSAFE (vulnerable to XSS)
container.innerHTML = userInput;
// ✅ SAFE (CSP-compliant)
container.textContent = userInput;
// or
const div = document.createElement('div');
div.textContent = userInput;
container.appendChild(div);Implementation:
- ✅ All
innerHTMLusage eliminated from extension pages - ✅ User inputs displayed via
textContent(auto-escapes) - ✅ Dynamic content created with
createElement()+textContent - ✅ No string concatenation for HTML generation
Protection layers:
- Input validation - Sanitize before use
- Output encoding - Use
textContentinstead ofinnerHTML - Content Security Policy - Block execution even if bypassed
Protection against brute force attacks:
Login attempts:
// After failed login:
- Button disabled for 2 seconds
- Visual feedback: "Wait 2s..."
- Limits attempts to ~30 per minute
- Prevents automated brute forceImplementation:
// popup.js
try {
await wallet.login(username, password, pin);
} catch (error) {
// Rate limit: 2-second lockout
loginBtn.disabled = true;
loginBtn.textContent = 'Wait 2s...';
setTimeout(() => {
loginBtn.disabled = false;
loginBtn.textContent = 'Login';
}, 2000);
}Additional protections:
- Nexus API enforces server-side rate limiting
- Account lockout after too many failed attempts (API-level)
- Failed attempts logged for security monitoring
All user inputs are validated before processing:
Transaction validation:
// Amount validation
if (isNaN(amount) || amount <= 0) {
throw new Error('Invalid amount');
}
// Reference validation (64-bit unsigned integer)
if (reference !== undefined) {
const refNum = BigInt(reference);
if (refNum < 0n || refNum > 18446744073709551615n) {
throw new Error('Invalid reference');
}
}
// Address validation
if (!to || to.trim() === '') {
throw new Error('Recipient address required');
}URL validation:
// HTTPS enforcement for remote nodes
const urlObj = new URL(nodeUrl);
if (urlObj.hostname !== 'localhost' &&
urlObj.hostname !== '127.0.0.1' &&
!urlObj.hostname.startsWith('192.168.') &&
!urlObj.hostname.startsWith('10.') &&
urlObj.protocol === 'http:') {
throw new Error('HTTPS required for remote connections');
}What we DON'T use:
- ❌
eval()- Never used - ❌
Function()constructor - Never used - ❌
setTimeout(string)- Never used - ❌
setInterval(string)- Never used - ❌
innerHTMLwith dynamic content - Eliminated - ❌ Inline event handlers - Not used
- ❌
dangerouslySetInnerHTML- Not applicable (vanilla JS)
Code verification:
# Verify no unsafe patterns
grep -r "eval(" *.js # ✅ None found
grep -r "innerHTML" *.js # ✅ Only safe debug logs
grep -r "onclick=" *.html # ✅ None in extension pages| Threat | Protection |
|---|---|
| Stolen computer (powered off) | ✅ No sensitive data on disk (RAM-only or encrypted with memory key) |
| Stolen computer (after browser close) | ✅ All session data cleared from RAM, encryption key lost |
| Public computer with offline node | ✅ Local data cleared regardless of network status |
| Malicious extension | ✅ Isolated storage, chrome.storage.session not accessible |
| Malicious website | ✅ Content script isolation, no direct access to wallet |
| Network MITM | ✅ HTTPS enforcement for remote nodes |
| Unauthorized transactions | ✅ PIN required for all transactions |
| Session hijacking | ✅ Sessions terminated on logout/close (local data always cleared) |
| Disk forensics (fallback mode) | ✅ Data encrypted, key in memory only (unrecoverable) |
| XSS attacks | ✅ Content Security Policy + textContent usage |
| Code injection | ✅ CSP blocks eval(), inline scripts, and unsafe patterns |
| Brute force login | ✅ Rate limiting (2-second delay after failed attempts) |
| Malicious API responses | ✅ CSP prevents script execution from compromised node |
| Threat | Why | Mitigation |
|---|---|---|
| Memory dumps while running | Browser limitation | Use hardware wallet for large amounts |
| Compromised browser | Root access = game over | Keep browser/OS updated |
| Keyloggers | Can capture PIN/password | Use secure, trusted computers |
| Physical access (while running) | Can interact with open browser | Lock screen when away |
| Malicious node | Node sees transactions | Use trusted nodes only |
Implementation:
// Generate memory-only encryption key (lost on reload)
this.encryptionKey = generateRandomKey(); // Lives in JavaScript memory
// Encrypt before storing
const encrypted = encrypt(sensitiveData, this.encryptionKey);
chrome.storage.local.set({ encrypted }); // Encrypted data on disk
// On extension reload: Key lost = data unrecoverableWhy this works:
- ✅ Encryption key NEVER written to disk
- ✅ Key lives in JavaScript memory only
- ✅ Extension reload/close = key lost forever
- ✅ Encrypted data on disk is useless without key
- ✅ Better than plaintext fallback
Option: Store encrypted PIN/session on disk with persistent key
Why we don't:
// Where to store encryption key?
const encryptedPIN = encrypt(PIN, encryptionKey);
chrome.storage.local.set({ encryptedPIN }); // On disk
// Problem: Where does encryptionKey come from?
// 1. Hardcoded in extension? → No security (source is public/auditable)
// 2. Stored on disk? → Defeats encryption purpose
// 3. Derived from password? → Must store password (circular problem)
// 4. User enters each time? → Same as just entering PINConclusion: Persistent encryption doesn't add real security
Option: Use hardware device (Ledger, Trezor, etc.)
Challenges:
- Browser extension limitations (no USB access in Manifest V3)
- Nexus doesn't have hardware wallet support yet
- Would require desktop/mobile app
Future: Consider for mobile/desktop versions
Option: Never store PIN, require entry each time
Trade-offs:
✅ Pros:
- PIN never in memory longer than needed
- User actively confirms logout
❌ Cons:
- Can't terminate session on browser close
- Leaves active sessions on blockchain (security risk)
- Worse user experience (can't close browser safely)
Decision: Storing PIN in session memory is more secure overall
This wallet is open source for several reasons:
- Community review - Anyone can audit the code
- No hidden backdoors - Verify we're not stealing credentials
- Industry standard - All major wallets are open source
- Trust through transparency - Don't trust, verify
Critical files:
manifest.json - Content Security Policy configuration
services/storage.js - Session storage implementation
services/wallet.js - Session management and PIN handling
background.js - Service worker, session cleanup
services/nexus-api.js - API communication (no credential leakage)
popup.js - UI logic (XSS prevention, rate limiting)
content.js - Content script injection
inpage.js - dApp provider (isolated execution)
Key security checks:
- ✅ No credentials sent to external servers
- ✅ PIN/session only in chrome.storage.session
- ✅ Proper cleanup on logout/browser close
- ✅ HTTPS enforcement for remote nodes
- ✅ Session termination on blockchain
- ✅ Content Security Policy properly configured
- ✅ No eval() or Function() usage
- ✅ No innerHTML with dynamic content
- ✅ Rate limiting on authentication
- ✅ Input validation on all user inputs
Found a vulnerability? Please report responsibly:
- Do NOT open a public GitHub issue
- Contact: [security contact email]
- Allow time for fix before disclosure
- Provide detailed reproduction steps
- ✅ Use strong, unique password and PIN
- ✅ Only connect to trusted dApps
- ✅ Verify transaction details before approving
- ✅ Keep browser and OS updated
- ✅ Use HTTPS for remote nodes
- ✅ Click Logout button (don't just close browser)
- ✅ Verify logout success before walking away
- ✅ On public computers: Logout + clear browser data
⚠️ Consider using Nexus desktop wallet for large holdings⚠️ Browser wallets are convenient but not as secure as hardware⚠️ Don't use on untrusted/public computers
| Feature | Q-Wallet | MetaMask | Nexus Desktop |
|---|---|---|---|
| Storage | chrome.storage.session | chrome.storage.session | Encrypted file on disk |
| Auto-clear | Browser close | Browser close | No (persistent) |
| Session term | Yes (with PIN) | N/A (Ethereum different) | Yes |
| Hardware support | No | Yes (Ledger/Trezor) | No |
| Platform | Browser extension | Browser extension | Desktop app |
Conclusion: Q-Wallet uses the same security model as industry-standard browser wallets, adapted for Nexus blockchain's session architecture.
// chrome.storage.session (MV3)
- Max storage: 10 MB
- Cleared: On browser close
- Persistence: Session only
- Sync: Not synced across devices
- Access: Extension only
// chrome.storage.local
- Max storage: 5 MB (unlimitedStorage permission)
- Cleared: Never (until user clears browser data)
- Persistence: Permanent
- Sync: Not synced (we use local, not sync)
- Access: Extension only// Session storage (memory-only)
'session' // Nexus session ID
'pin' // User's PIN
'session_session' // Fallback key (MV2 compatibility)
'session_pin' // Fallback key (MV2 compatibility)
// Local storage (persistent)
'nodeUrl' // API endpoint
'approvedDomains' // dApp permissions
'walletConfig' // Non-sensitive settingsThe security architecture of Q-Wallet represents a careful balance between:
- Security - Memory-only storage, automatic cleanup
- Usability - No constant PIN prompts, safe browser close
- Industry standards - Same approach as trusted wallets
- Blockchain integration - Proper session termination
For most users, this provides excellent security for day-to-day use. For very large amounts or maximum security, consider the Nexus desktop wallet or hardware wallet solutions (when available).