Skip to content

Repository files navigation

Deterministic Password Manager

A local, offline, terminal-based password manager built around deterministic password generation — your passwords are never stored anywhere. They are derived on demand from your master password, the domain name, and a counter using a cryptographic key derivation function. The vault stores only metadata.


How it works

Most password managers encrypt and store your passwords. This one doesn't. Instead, it stores the parameters needed to re-derive each password and generates the actual value at the moment you ask for it.

master password + domain + counter + policy  →  HKDF / Argon2  →  password

This means:

  • A compromised vault file reveals no passwords — only usernames, policy names, and counters
  • There is nothing to sync, backup, or leak in the traditional sense
  • Rotating a password means incrementing a counter, not generating and storing a new random string

Features

  • Deterministic generation — same inputs always produce the same output; no random secrets stored
  • AES-256-GCM encrypted vault — metadata is encrypted at rest with a key derived via Argon2id
  • Scrollback clearing — terminal scrollback buffer is purged before and after revealing a secret so it cannot be read by scrolling up
  • Master password verification — the master is verified against the vault ciphertext before any secret is derived; a wrong or blank entry is rejected (deterministic functions return output for any input, so naive implementations silently accept wrong passwords)
  • Named policies — reusable password rules (length, character sets, forbidden characters, sequence limits) stored in config.yaml
  • PIN support — separate secret type for numeric PINs with its own validation rules
  • Per-entry overrides — tweak one policy field for a specific entry without changing the shared policy; the full merged policy is validated before saving
  • Password rotation — increment the counter to move to a new password; previous counters are tracked up to a configurable history depth
  • Search — filter entries by domain or username substring
  • Clipboard support — copy username or password directly; falls back to plaintext display if pyperclip is not installed
  • ESC to cancel — every prompt respects ESC as a cancellation signal; no dead ends
  • Autosave — vault is re-encrypted and written to disk after every mutation

Security model

Threat Mitigation
Vault file stolen AES-256-GCM encryption; file contains no passwords
Wrong master accepted Master is verified via vault decrypt before use
Secret visible in terminal history Scrollback buffer purged via \033[3J before and after reveal
Master password leaked from memory Master string held in process memory only, never written to disk or derived key cache
Weak KDF Argon2id with time_cost=3, memory_cost=64MB
Idle terminal exposes secrets Secret shown on a separate screen; cleared on Enter

Limitations to be aware of:

  • tmux and screen maintain their own scrollback that ANSI sequences cannot clear — close and reopen the pane when using a multiplexer
  • Process memory is not locked (mlock); a memory dump or swap file could expose the master string in theory
  • This is a local tool — there is no sync, no cloud, no mobile app

Project structure

.
├── cli.py              # Terminal UI, all user-facing flows
├── vault_models.py     # Pydantic models: Vault, VaultEntry
├── vault_manager.py    # Vault operations: CRUD, search, counter management
├── vault_crypto.py     # Encryption layer: Argon2id KDF, AES-256-GCM, serialisation
├── policy.py           # PolicySchema, config.yaml read/write
├── generator.py        # Deterministic password derivation
├── config.yaml         # Named policies (created on first policy save)
└── vault.vault         # Encrypted vault (created on first run)

Installation

Requirements: Python 3.10+

git clone https://github.com/SiddhantGupta3112/deterministic-password-manager
cd deterministic-password-manager

python -m venv env
source env/bin/activate        # Windows: env\Scripts\activate

pip install -r requirements.txt

Optional clipboard support:

pip install pyperclip
# Linux may also need: sudo apt install xclip

Usage

python cli.py ui

On first run the vault does not exist — you will be prompted to create a master password. This password cannot be recovered. The vault is created immediately and encrypted with a key derived from it.

First time setup walkthrough

1. Run the app
2. Create a master password (min 8 characters recommended)
3. Go to Manage Policies → Create new policy
4. Give the policy a name, choose Password or PIN, set your rules
5. Go to Add new entry, pick your policy, enter the domain
6. Select the entry → Reveal password to see your generated password

Navigation

Key Action
/ Move between options
Enter Select
ESC Cancel current flow and go back

Policies

Policies are named rule sets stored in config.yaml. They define what a generated password looks like.

Field Description
secret_type password or pin
length Character count (4–128)
uppercase Include A–Z
lowercase Include a–z
digits Include 0–9
special Include special characters
special_chars Which special characters are allowed
max_alpha_sequence Max consecutive letters before rejection
max_digit_sequence Max consecutive digits before rejection
forbidden_chars Characters never included
history_depth How many previous counters to remember

Per-entry overrides

An override adjusts one policy field for a single entry without changing the shared policy. Useful when a site imposes constraints your default policy doesn't match — for example, a site that forbids % or caps length at 12.

Overrides are validated against the full merged policy before saving, so an invalid combination (e.g. all character sets disabled) is rejected at the UI level.


Example config.yaml

default_policy:
  secret_type: password
  length: 16
  uppercase: true
  lowercase: true
  digits: true
  special: true
  special_chars: '!@#$%^&*'
  max_alpha_sequence: null
  max_digit_sequence: null
  forbidden_chars: []
  history_depth: 5

banking_policy:
  secret_type: password
  length: 12
  uppercase: true
  lowercase: true
  digits: true
  special: true
  special_chars: '@#$*'
  max_alpha_sequence: 3
  max_digit_sequence: 3
  forbidden_chars: [" ", "/", "\\", "'", '"', '`']
  history_depth: 10

pin_policy:
  secret_type: pin
  length: 6
  digits: true
  uppercase: false
  lowercase: false
  special: false
  special_chars: ''
  max_digit_sequence: 2
  forbidden_chars: []
  history_depth: 5

Dependencies

Package Purpose
click CLI framework and terminal output
questionary Interactive arrow-key menus and prompts
pydantic Schema validation for vault models and policies
cryptography AES-256-GCM encryption
argon2-cffi Argon2id key derivation
pyyaml Policy config serialisation
pyperclip Clipboard support (optional)

Internals

Key derivation

Argon2id(master_password, salt, time=3, memory=64MB)  →  32-byte key

A fresh 16-byte salt is generated when the vault is created and stored in the vault file. The key is derived fresh on every unlock and never cached to disk.

Vault file format

{
  "version": 1,
  "salt": "<base64>",
  "nonce": "<base64>",
  "ciphertext": "<base64>"
}

The ciphertext is the AES-256-GCM encryption of a JSON-serialised Vault object. The GCM authentication tag means any tampering with the ciphertext causes decryption to fail — the same mechanism used to reject a wrong master password.

Password generation

generator.py takes the master, domain, counter, and resolved policy and produces a password deterministically. The same inputs on any machine always produce the same output.


Potential improvements

  • mlock to prevent master password from being swapped to disk
  • Auto-lock after configurable idle timeout
  • TOTP / 2FA code generation
  • Export to encrypted portable format
  • Delete / rename policies
  • View password history (previous counters)

About

Offline deterministic password manager using Argon2id, HMAC-SHA512, and AES-256-GCM with encrypted metadata and on-demand password generation.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages