Skip to content
Return to Projects
Case Study

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

Python 3.12PySide6AES-256-GCMCryptographyPyInstaller

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

Interface
PySide6 Desktop UI
Application
Vault Manager
Honey Vault
Freeze Mode
Cryptography
AES-256-GCM
Key Derivation
Ice Crystal Fingerprints
Storage
Encrypted local vault file

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.

  1. 1Add an automated integrity self-test that runs on every launch and surfaces tampering visibly to the user.
  2. 2Move key derivation parameters to a versioned header so future Argon2 tuning doesn't break old vaults.
  3. 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

EmitterSignalReceiverEffect
LoginScreenlogin_successMainWindow._on_loginSwitch to dashboard
VaultDashboardlock_requestedMainWindow._on_lockLock vault, show login
GeneratorWidgetpassword_selected(str)VaultDashboard._on_generated_passwordPre-fill EntryDialog
EntryCardedit_requested(dict)VaultDashboard._on_edit_entryOpen edit dialog
EntryCarddelete_requested(int)VaultDashboard._on_delete_entryConfirm + delete
AutoLocklock_triggeredMainWindow._on_lockIdle timeout lock
FreezeModeinternalMainWindowCalculator 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

ComponentTechnologyVersion
LanguagePython3.12
GUI FrameworkPySide6 (Qt6)6.11.0
Encryptioncryptography (AES-256-GCM)≥42.0
Key Derivationargon2-cffi (Argon2id)≥23.1
Password Scoringzxcvbn≥4.4
DatabaseSQLite 3 (stdlib)
Global Hotkeyskeyboard≥0.13
BuildPyInstaller≥6.0
InstallerInno Setup6

#9. Deployment Target

PropertyValue
PlatformWindows 10 / 11 (x64)
Install location%LOCALAPPDATA%\IceLegends\FrostVault\
Vault data%APPDATA%\FrostVault\vault.db
Privileges requiredUser-level only (no admin/UAC)
DistributionSingle .exe installer (~37 MB)
RuntimeBundled (no Python install required)
~ End of Document ~