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.
System Architecture
FrostVault v1.0.0 — IceLegends
#1. Overview
FrostVault is a single-user, local-first desktop password manager for Windows. All vault data is stored on the user's machine; no network traffic is generated during normal operation. The application is structured as a layered architecture with strict separation between the security core, business logic, feature modules, and presentation layer.
#2. Architectural Layers
┌─────────────────────────────────────────────────────────┐
│ PRESENTATION LAYER │
│ LoginScreen · VaultDashboard · EntryDialog │
│ GeneratorWidget · MascotWidget · IceCrystalWidget │
├─────────────────────────────────────────────────────────┤
│ FEATURE LAYER │
│ FreezeMode · HoneyVault · AutoLock │
├─────────────────────────────────────────────────────────┤
│ BUSINESS LOGIC LAYER │
│ Vault · PasswordStrength · Clipboard │
├─────────────────────────────────────────────────────────┤
│ SECURITY CORE │
│ Crypto (AES-256-GCM + Argon2id) · Database (SQLite) │
└─────────────────────────────────────────────────────────┘
Each layer may only call downward. UI widgets never touch crypto.py directly; all cryptographic operations go through vault.py.
#3. Component Map
FrostVault/
├── main.py Entry point, MainWindow, screen router
│
├── core/
│ ├── crypto.py Argon2id hashing · AES-256-GCM encrypt/decrypt
│ ├── database.py SQLite schema · CRUD operations
│ ├── vault.py Session management · plaintext-in-memory guard
│ └── honey_vault.py Decoy entry generator · intrusion logger
│
├── features/
│ ├── freeze_mode.py Calculator overlay · global hotkey (Ctrl+Shift+F)
│ ├── ice_crystal.py Deterministic snowflake renderer (QPainter)
│ └── auto_lock.py Idle timeout → vault lock
│
├── ui/
│ ├── theme.py PALETTE constants · global QSS stylesheet
│ ├── login_screen.py Master password entry · setup mode
│ ├── vault_dashboard.py Credential list · navigation · search
│ ├── entry_dialog.py Add/Edit credential form
│ ├── generator_widget.py Password generator panel
│ └── mascot_widget.py Flurry — animated polar bear mascot
│
└── utils/
├── password_strength.py zxcvbn-based scoring + crack time estimation
└── clipboard.py Copy-to-clipboard with 30-second auto-clear
#4. Application Startup Flow
main.py
│
├─ QApplication created
├─ Global QSS stylesheet applied (PALETTE)
├─ Database.init() ──► creates vault.db if not present
├─ Vault instantiated (locked state)
├─ HoneyVaultModule instantiated
│
├─ MainWindow created (QStackedWidget)
│ page 0: LoginScreen
│ page 1: VaultDashboard
│
├─ LoginScreen.set_setup_mode(True) ← if no master hash in DB
│ or
│ LoginScreen.set_setup_mode(False) ← if vault exists
│
└─ App event loop starts
#5. Screen Routing
LoginScreen
│
│ login_success signal
▼
MainWindow._on_login()
│
├─ honey.should_show_honey() == True → VaultDashboard.activate(is_honey=True)
│
└─ vault.is_unlocked == True → VaultDashboard.activate(is_honey=False)
VaultDashboard
│
│ lock_requested signal
▼
MainWindow._on_lock()
│
├─ vault.lock() (wipes key from memory)
├─ Stack switches back to LoginScreen
└─ Mascot resets to idle
#6. Inter-Module Signal Map
| Emitter | Signal | Receiver | Effect |
|---|---|---|---|
LoginScreen | login_success | MainWindow._on_login | Switch to dashboard |
VaultDashboard | lock_requested | MainWindow._on_lock | Lock vault, show login |
GeneratorWidget | password_selected(str) | VaultDashboard._on_generated_password | Pre-fill EntryDialog |
EntryCard | edit_requested(dict) | VaultDashboard._on_edit_entry | Open edit dialog |
EntryCard | delete_requested(int) | VaultDashboard._on_delete_entry | Confirm + delete |
AutoLock | lock_triggered | MainWindow._on_lock | Idle timeout lock |
FreezeMode | internal | MainWindow | Calculator overlay on/off |
#7. Data Flow — Credential Save
User fills EntryDialog
│
▼
EntryDialog._on_save()
validates required fields
│
▼
Vault.add_entry(title, username, password, url, notes, category)
│
├─ crypto.encrypt(title, derived_key) → ciphertext blob
├─ crypto.encrypt(username, derived_key) → ciphertext blob
├─ crypto.encrypt(password, derived_key) → ciphertext blob
├─ crypto.encrypt(url, derived_key) → ciphertext blob
└─ crypto.encrypt(notes, derived_key) → ciphertext blob
│
▼
database.insert_entry(encrypted blobs)
│
▼
SQLite: entries table (all fields encrypted at rest)
#8. Technology Stack
| Component | Technology | Version |
|---|---|---|
| Language | Python | 3.12 |
| GUI Framework | PySide6 (Qt6) | 6.11.0 |
| Encryption | cryptography (AES-256-GCM) | ≥42.0 |
| Key Derivation | argon2-cffi (Argon2id) | ≥23.1 |
| Password Scoring | zxcvbn | ≥4.4 |
| Database | SQLite 3 (stdlib) | — |
| Global Hotkeys | keyboard | ≥0.13 |
| Build | PyInstaller | ≥6.0 |
| Installer | Inno Setup | 6 |
#9. Deployment Target
| Property | Value |
|---|---|
| Platform | Windows 10 / 11 (x64) |
| Install location | %LOCALAPPDATA%\IceLegends\FrostVault\ |
| Vault data | %APPDATA%\FrostVault\vault.db |
| Privileges required | User-level only (no admin/UAC) |
| Distribution | Single .exe installer (~37 MB) |
| Runtime | Bundled (no Python install required) |