FrostVault
A zero-knowledge Windows password manager with deception built in.
The Problem
Mainstream password managers are cloud-first and ask you to trust a third party with your most sensitive secrets. FrostVault keeps everything local and encrypted — and fights back when someone tries to break in.
My Role
Built solo with AI-assisted development — cryptographic design, application logic, the PySide6 UI, and Windows packaging.
Highlights
- AES-256-GCM authenticated encryption for all vault data at rest
- Honey Vault serves believable decoy credentials under a wrong master key or duress
- Freeze Mode and Ice Crystal Fingerprints provide tamper detection
- Ships as a single standalone Windows executable — no installer, no cloud
Stack
Constraints
- Offline-first: vault data must never leave the user's machine.
- Single-binary distribution — no installer, no runtime to ship.
- Threat model includes coerced access, not just remote attackers.
System Architecture
Key Trade-offs
The decisions worth defending — what I chose, what I turned down, and why.
Storage architecture
Chose
Single encrypted local file
Rejected
Cloud-synced vault
Removes the third-party trust dependency entirely; matches the offline-first threat model at the cost of cross-device sync.
Wrong-password behaviour
Chose
Honey Vault returns plausible decoys
Rejected
Hard fail with a clear error
A loud failure tells an attacker they have the wrong key. A believable decoy buys time and frustrates duress scenarios.
Crypto stack
Chose
AES-256-GCM via the `cryptography` library
Rejected
Custom-built primitives
Authenticated encryption out of the box, audited implementation, zero novel crypto risk.
What I'd Do Differently
An honest retrospective — the stuff I'd change with more time, more users, or a second pass.
- 1Add an automated integrity self-test that runs on every launch and surfaces tampering visibly to the user.
- 2Move key derivation parameters to a versioned header so future Argon2 tuning doesn't break old vaults.
- 3Ship a portable Linux build — Windows-only narrows the audience more than I expected.
Technical Deep-Dive
Architecture, specifications, and implementation details.
Threat Model
FrostVault v1.0.0 — IceLegends
#1. STRIDE Analysis
STRIDE is a threat classification framework: Spoofing · Tampering · Repudiation · Information Disclosure · Denial of Service · Elevation of Privilege.
##S — Spoofing
| Threat | Description | Mitigation | Residual Risk |
|---|---|---|---|
| S1 | Attacker presents a fake FrostVault window to harvest master password | App runs locally; no authentication handshake to intercept | Low — attacker needs prior physical/remote access |
| S2 | Malicious vault.db file planted to trigger exploit | Input validated; AES-GCM tag verification rejects tampered blobs | Very Low |
| S3 | Attacker copies vault.db and runs own FrostVault instance | Without master password, derived key is unrecoverable (Argon2id) | Negligible |
##T — Tampering
| Threat | Description | Mitigation | Residual Risk |
|---|---|---|---|
| T1 | Attacker modifies vault.db ciphertext | GCM auth tag fails; decrypt() raises InvalidTag exception | Very Low |
| T2 | Attacker replaces crypto.py with weakened version | Code signing (future); PyInstaller bundle integrity | Medium (unsigned binary) |
| T3 | Attacker modifies fail_count table to reset honey trigger | Honey vault is a deception layer, not a security boundary | Low |
##R — Repudiation
| Threat | Description | Mitigation | Residual Risk |
|---|---|---|---|
| R1 | Attacker denies accessing the vault | intrusion_log table records all failed attempts with timestamps | Medium — log is local, not write-protected |
| R2 | User denies having created a vault entry | No audit log for successful operations in v1.0 | Low (single-user app) |
##I — Information Disclosure
| Threat | Description | Mitigation | Residual Risk |
|---|---|---|---|
| I1 | vault.db file stolen from disk | All credential fields AES-256-GCM encrypted; master password never stored | Low |
| I2 | Process memory dump while vault is unlocked | Derived key + plaintext entries in Python memory | Medium — Python does not securely zero memory |
| I3 | Windows page file / hibernation file contains decrypted data | No mitigation in v1.0 | Medium |
| I4 | Clipboard contains password after copy | 30-second auto-clear via QTimer | Low |
| I5 | Screen recording / shoulder surfing | Freeze Mode (Ctrl+Shift+F) calculator camouflage | Low |
| I6 | Application crash dump contains sensitive data | No crash reporting configured; dumps go to Windows Error Reporting | Medium |
| I7 | Password visible in Entry dialog | Echo mode = Password; eye-button toggle requires user intent | Very Low |
##D — Denial of Service
| Threat | Description | Mitigation | Residual Risk |
|---|---|---|---|
| D1 | Attacker deletes or corrupts vault.db | No backup mechanism in v1.0 | Medium — data loss risk |
| D2 | Attacker fills disk preventing vault writes | SQLite write failure → unhandled exception in v1.0 | Low |
| D3 | Argon2id exhausts RAM during unlock | 64 MB allocation; negligible on modern hardware | Very Low |
##E — Elevation of Privilege
| Threat | Description | Mitigation | Residual Risk |
|---|---|---|---|
| E1 | App runs with elevated privileges unnecessarily | PrivilegesRequired=lowest in installer; user-level only | Very Low |
| E2 | keyboard library (global hotkey) requires elevated access | keyboard library uses OS hooks at user level on Windows | Very Low |
| E3 | SQLite injection via credential fields | All DB interactions use parameterized queries | Very Low |
#2. Attacker Profiles
| Profile | Goal | Capability | Primary Threat |
|---|---|---|---|
| Casual attacker | Access saved passwords | Physical device access, no technical skill | Honey Vault, Freeze Mode |
| Technical insider | Read vault.db from disk | File system access, Python knowledge | AES-256-GCM encryption |
| Remote attacker | Exfiltrate vault data | Remote code execution on victim machine | No network surface; local-only |
| Forensic investigator | Recover plaintext from memory | RAM dump tools, disk forensics | Memory exposure (residual risk I2, I3) |
| Nation-state | Cryptanalytic attack | Cryptanalysis resources | AES-256 / Argon2id — no known attacks |
#3. Out of Scope (v1.0.0)
The following threats are explicitly out of scope and are not mitigated in this version:
- Keyloggers — If a keylogger captures the master password as it is typed, all protections fail. Mitigation requires OS-level secure input (e.g., Secure Desktop) — planned for v2.0.
- Evil maid attack — Attacker with physical access who replaces the FrostVault binary. Requires code signing and binary verification — planned for v2.0.
- Side-channel attacks — Timing/power analysis against Argon2id. Out of scope for a local desktop app.
- Secure memory zeroing — Python's garbage collector does not allow deterministic memory zeroing. A future C extension could zero memory explicitly.
- Backup / sync — No encrypted backup mechanism. Data loss on disk failure is the user's responsibility.
#4. Risk Summary Matrix
│ Likelihood
Impact │ Very Low │ Low │ Medium │ High
───────────┼────────────┼────────┼─────────┼──────
Critical │ │ │ I3(swap)│
High │ T2(tamper)│I2(mem) │ │
Medium │ S1,R1 │ D1,I6 │ │
Low │ T1,E1,I7 │S3,T3 │ │
Highest priority items for v2.0:
- Secure memory zeroing (I2)
- Code signing to prevent binary tampering (T2)
- Encrypted automatic backup (D1)
- Secure input mode / virtual keyboard option (keylogger defense)
#5. Compliance Alignment
| Standard | Relevant Controls | FrostVault Status |
|---|---|---|
| OWASP ASVS L1 | Password hashing, encryption at rest | ✅ Meets |
| NIST SP 800-63B | Memory-hard KDF for stored passwords | ✅ Meets (Argon2id) |
| NIST SP 800-38D | AEAD encryption | ✅ Meets (AES-256-GCM) |
| CIS Benchmark | Auto-lock on idle | ✅ Meets (5-min timeout) |
| GDPR Art. 32 | Appropriate technical measures for personal data | ✅ Meets (encryption at rest, local only) |