Version: 2.5.0-PRO | Last Updated: 2026-05-01
CrytoTool respects the people behind the screen. Security is not just about code — it's about protecting people's digital lives. Every security decision prioritizes human safety over convenience.
| Attack | How We Protect | Status |
|---|---|---|
| Master Password Brute-force | Argon2id (64MB memory, 10 iterations, 4 threads) — drastically slows down brute-force | ✅ Protected |
| Physical Device Access | Vault Key only in memory (RAM), cleared on lock; IndexedDB encrypted with AES-GCM | ✅ Protected |
| Browser localStorage Access | Manual encryption keys are now encrypted with Vault Key (AES-256-GCM) before storage | ✅ Protected (from commit 0201163) |
| Algorithm Collision | Standard algorithms: AES-256-GCM, Argon2id, PBKDF2-SHA256, ChaCha20-Poly1305 | ✅ Protected |
| Multi-person Access on Same Device | Auto-lock after inactivity, Progressive Lockout, Self-Destruct option | ✅ Protected |
| Password Loss | 10 unique recovery codes, reset token via email | ✅ Protected |
| Supply Chain Attacks (compromised npm packages) | Regular npm audit, minimal dependencies (hash-wasm, libsodium-wrappers) |
| Scenario | Why Not Fully Protected | Possible Mitigation |
|---|---|---|
| XSS in Application | If attacker can execute JS, they can access cryptoService.vaultKey (object in memory) |
Content Security Policy (CSP) implemented via <meta> tag in index.html (disallows inline scripts) |
| Malicious Browser Extension | Extensions have access to localStorage and can inject scripts | We have no control; people should only install trusted extensions |
| Physical RAM Dumping | If attacker can read browser process memory, Vault Key can be extracted | Use non-extractable CryptoKey in future |
Supply Chain Attack on npm |
A compromised library could exfiltrate data via postMessage or fetch |
npm audit, limiting dependencies, CVE monitoring |
| Social Engineering | A person can be tricked into revealing their password | Education, clear interface |
- Winner: Password Hashing Competition (2015), recommended by OWASP
- Memory-hard: Uses 64MB RAM — drastically slows down GPU brute-force (which have limited memory)
- Tuning: 10 iterations (balance between security and speed on mobile devices)
- Implementation:
hash-wasm(wasm bindings for Argon2id), notlibsodium(which doesn't have Argon2id natively in WASM)
- Authenticated: Provides both confidentiality AND integrity (GCM tag of 128 bits)
- Industry Standard: Recommended by NIST, used by banks and governments
- Native in Browser: Web Crypto API supports AES-GCM natively (hardware acceleration on most devices)
- 256 bits: Resistant to quantum attacks (when they arrive)
- Principle: If it's not on disk, it can't be read from disk
- Storage:
cryptoService.vaultKeyis aCryptoKeyobject (ornull). Set incrypto.ts:36-55on unlock, cleared incrypto.ts:62on lock - localStorage: We don't store the key here, although we could store a
non-extractable CryptoKey— we chose not to for XSS safety - IndexedDB: We don't store keys here because IndexedDB is accessible via JS (XSS)
cryptoPrimitives.ts: Each algorithm is isolated in its own module. No dependencies between them (avoids domino effects)streamCrypto.ts: Streaming is separate from standard encryption. Uses BLAKE2b (libsodium) for key derivation, not Vault Key directlybackupCrypto.ts: Backup key is derived independently (PBKDF2-SHA256, 100k iterations) — doesn't use Vault KeyvaultStorage.ts(updated): Manual keys are encrypted with Vault Key before localStorage. If Vault Key is cleared (lock), stored keys become unreadable
| Component | What it Exposes | Risk |
|---|---|---|
| localStorage | crytotool_salt, crytotool_iv, crytotool_vault_pin_hash (encrypted), crytotool_recovery_codes (encrypted), crytotool_vault_cats, crytotool_theme_config |
Access via XSS, disk reading |
| IndexedDB | Encrypted files, metadata (name, size, date) | Access via XSS (if Vault Key is available) |
| Web Crypto API | CryptoKey objects in memory |
Extraction from RAM (protected by browser's sandbox) |
| URL/History | We don't store sensitive params in URL | Minimal risk (SPA without server) |
| Service Workers | Not used yet | N/A |
| Iframes | Not used | N/A |
- Master Password (only Argon2id hash in memory temporarily)
- Vault Key (only in memory, cleared on lock)
- Manual keys (encrypted in localStorage, encrypted with Vault Key)
- Trigger: 3+ failed unlock attempts (Master Password or PIN)
- Action:
- 3 failures → 30 second lock
- 4 failures → 1 minute lock
- 5+ failures → 5 minute lock
- Implementation:
getBackoffTime(failedAttempts)returns time in seconds
- Configuration:
autoDestructEnabled,autoDestructAttempts,autoDestructInactivity - Trigger 1 (Failed Attempts): If
failedAttempts >= autoDestructAttemptsandautoDestructEnabled === true→ complete wipe of IndexedDB + localStorage - Trigger 2 (Inactivity): If
elapsed >= autoDestructInactivityseconds → countdowndestructCountdownSeconds(default 30s) → complete wipe - Grace Period: Before deletion, a visual countdown runs. A person can cancel if they enter the correct password in time
- Function: Monitors person's activity (
mousedown,mousemove,keydown,touchstart,scroll) - Timer Reset: Any activity resets
lastActivityRef.current - Progressive Actions:
elapsed >= autoBlurSeconds→ blur screen (isBlurred = true)elapsed >= autoLockSeconds→ lock vault (isAuthenticated = false)elapsed >= autoDestructInactivity→ trigger Self-Destruct
- ✅ Check: Argon2id params (memory: 65536, iterations: 10, parallelism: 4)
- ✅ Check:
vaultKeyis set/cleared correctly (not remaining in memory after lock) - ✅ Check:
encryptString()/decryptString()use AES-GCM with randomly generated IV - ✅ Check:
vaultKeyis private — not accessible via XSS
- ✅ Check:
saveAll()encrypts JSON withcryptoService.encryptString() - ✅ Check:
getAll()decrypts before returning - ✅ Check: Legacy format (plaintext) is migrated correctly on first save
- ✅ Check: Uses
cryptoService.encryptString()/decryptString()with Vault Key
- ✅ Check: PBKDF2-SHA256 with 100,000 iterations
- ✅ Check: Random salt (16 bytes) generated for each backup
- ✅ Check: Format
[salt][iv][ciphertext+GCM tag]is respected - ✅ Check: Backup keys generated with 130-bit entropy (26 chars)
- ✅ Check: PIN hash uses SHA-256 with fixed salt
- ✅ Check:
verifyPin()uses constant-time comparison (diff |= ...) - ✅ Check: PIN hash is encrypted with Vault Key before localStorage
- ✅ Check: Each algorithm is isolated (no shared state)
- ✅ Check:
aesCtruses HKDF-like for key derivation (encryption + MAC keys) - ✅ Check:
chacha20Poly1305/xchacha20Poly1305use libsodium (WASM) — intensively audited ⚠️ Note:aesGcmimportsCryptoKeyon each call (performance, but secure)
- ✅ Check:
destructTriggerTimeis saved in localStorage (persists between reloads) - ✅ Check:
lastActivityRefis updated on every activity event - ✅ Check: Recovery codes encrypted with Vault Key
- ✅ Check: All sensitive states (
settingsPassword,vaultPin) are inuseState, not global variables
- Version: Check
package.json - CVE: No known CVEs for
hash-wasm - Risk: WASM binary loaded in browser — if CDN is compromised (not our case, it's local in
node_modules), attacker could inject code - Update:
npm update hash-wasm
- Version: Check
package.json - CVE: No recent CVEs for libsodium (library is intensively audited, written in C)
- Risk: WASM wrapper (31KB) — small attack surface
- Update:
npm update libsodium-wrappers
- Version: Check
package.json - CVE: Monitor
npm auditfor XSS CVEs in animations - Risk: Large library (100KB+) — potentially XSS if not used correctly
- Mitigation: We only use
motion.div,AnimatePresence— no advanced functions
- Version: Check
package.json - CVE: Minimal risk (just SVG icons)
- Risk: Icons are React components — if malicious code injected in SVG, could cause XSS
- Mitigation: We use standard icons, no user-customized SVGs
Before releasing a new version, verify:
-
npm audit— zero high/critical vulnerabilities - Argon2id params unchanged (memory: 65536, iterations: 10)
- AES-GCM IV is randomly generated for each encryption (12 bytes)
- Vault Key is cleared from memory on lock (
cryptoService.clearKeys()) -
vaultStoragesaves keys encrypted (check localStorage forcrytotool_vault_keys— should be{iv, data}, not plaintext) - PIN hash in localStorage is encrypted with Vault Key (check
crytotool_vault_pin_hash) - Recovery codes in localStorage are encrypted with Vault Key (check
crytotool_recovery_codes) - Progressive Lockout works (test 3+ failed attempts)
- Self-Destruct can be triggered manually and automatically
- Recovery codes display correctly and work
- No "users" term in UI (use "people" / "persoane")
- CSP meta tag is present in
index.html
- OWASP Top 10 Client-Side Security Risks: https://owasp.org/www-project-top-10-client-side-security-risks/
- Web Crypto API Security: https://www.w3.org/TR/WebCryptoAPI/
- Argon2 Specification: https://github.com/P-H-C/phc-winner-argon2
- IndexedDB Security (Firefox Private Mode Research): https://dfrws.org/wp-content/uploads/2024/07/Decrypting-IndexedDB-in-private-mode-o_2024_Forensic-Science-International-.pdf
If you discover a vulnerability, report it through GitHub Security Advisories or open an Issue. We respect the people who help us become more secure.