Cybersecurity-Projects/PROJECTS/foundations/password-manager
CarterPerez-dev d97662ee61 feat(foundations): add foundations tier — three teaching-first Python projects
Adds the foundations tier: three Python projects pitched at someone
who has never written Python before, ramping from a single-file hash
identifier up to the cryptographic boundary of the beginner tier.

Projects
- hash-identifier: identify ~30 hash formats by prefix, length, and
  charset (one file, ~30 tests).
- http-headers-scanner: fetch a URL and grade its security headers
  A–F (httpx + rich + respx, single-file scanner, 13 tests).
- password-manager: Argon2id + AES-256-GCM encrypted CLI vault with
  atomic durable writes and advisory file locking (the hardest
  foundations project — stepping stone into the beginner tier).

Each project ships with
- Heavily-commented source as a teaching aid (foundations-tier override
  of the no-comments rule — explanations targeted at first-time readers).
- A full learn/ folder: Overview, Concepts, Architecture, line-by-line
  Implementation walkthrough, Challenges.
- README with ASCII banner, badges (incl. tier + difficulty), demo
  sessions, command tables, learn-folder index, see-also links.
- install.sh + justfile + uv-managed dependencies.
- pytest + ruff + mypy + pylint config, all linters at 10.00/10.

Also centralizes the docs/ exclude in .gitignore (was a per-project
list of advanced/beginner docs paths) so dev-only audit reports and
plan documents stay local across the whole repo without needing
individual entries.
2026-05-13 13:57:33 -04:00
..
learn feat(foundations): add foundations tier — three teaching-first Python projects 2026-05-13 13:57:33 -04:00
src/password_manager feat(foundations): add foundations tier — three teaching-first Python projects 2026-05-13 13:57:33 -04:00
tests feat(foundations): add foundations tier — three teaching-first Python projects 2026-05-13 13:57:33 -04:00
.gitignore feat(foundations): add foundations tier — three teaching-first Python projects 2026-05-13 13:57:33 -04:00
.style.yapf feat(foundations): add foundations tier — three teaching-first Python projects 2026-05-13 13:57:33 -04:00
LICENSE feat(foundations): add foundations tier — three teaching-first Python projects 2026-05-13 13:57:33 -04:00
README.md feat(foundations): add foundations tier — three teaching-first Python projects 2026-05-13 13:57:33 -04:00
install.sh feat(foundations): add foundations tier — three teaching-first Python projects 2026-05-13 13:57:33 -04:00
justfile feat(foundations): add foundations tier — three teaching-first Python projects 2026-05-13 13:57:33 -04:00
pyproject.toml feat(foundations): add foundations tier — three teaching-first Python projects 2026-05-13 13:57:33 -04:00
uv.lock feat(foundations): add foundations tier — three teaching-first Python projects 2026-05-13 13:57:33 -04:00

README.md

██████╗  █████╗ ███████╗███████╗    ██╗   ██╗ █████╗ ██╗   ██╗██╗     ████████╗
██╔══██╗██╔══██╗██╔════╝██╔════╝    ██║   ██║██╔══██╗██║   ██║██║     ╚══██╔══╝
██████╔╝███████║███████╗███████╗    ██║   ██║███████║██║   ██║██║        ██║
██╔═══╝ ██╔══██║╚════██║╚════██║    ╚██╗ ██╔╝██╔══██║██║   ██║██║        ██║
██║     ██║  ██║███████║███████║     ╚████╔╝ ██║  ██║╚██████╔╝███████╗   ██║
╚═╝     ╚═╝  ╚═╝╚══════╝╚══════╝      ╚═══╝  ╚═╝  ╚═╝ ╚═════╝ ╚══════╝   ╚═╝

Cybersecurity Projects Tier: Foundations Difficulty: Hardest Foundation Python 3.13 Argon2id AES-256-GCM License: AGPLv3 Tests Lint

Encrypted command-line password manager — Argon2id key derivation, AES-256-GCM authenticated encryption, atomic durable writes, advisory file locking. One master password protects every credential you trust to it.

This is a quick overview — security theory, architecture, and full walkthroughs are in the learn modules.

[!NOTE] Foundations tier — the hardest of the three. This is a stepping stone into the beginner tier. It assumes no prior Python experience but ramps faster than hash-identifier and http-headers-scanner. The source is heavily commented as a teaching aid, the learn/ folder explains every cryptographic idea from zero, and every Python feature is introduced when it first appears. If "what's a @dataclass" feels like the wrong question, start with hash-identifier first.

What It Does

  • Stores credentials in a single encrypted JSON file at ~/.password-vault/vault.json (mode 0600)
  • Derives a 32-byte AES key from your master password via Argon2id (OWASP-recommended parameters, ~0.5s per derivation)
  • Encrypts vault contents with AES-256-GCM — confidentiality + tamper detection in one primitive
  • Atomic, durable, concurrent-safe writes: tmp file → fsync → atomic rename → directory fsync, with advisory fcntl lock to serialize concurrent pv invocations
  • Master password rotation that re-encrypts the entire vault under a fresh salt and key
  • Cryptographically secure password generator using secrets (never random) with a Fisher-Yates shuffle on top of secrets.randbelow
  • Stores KDF parameters in the file — old vaults remain readable when defaults change, and rotation can upgrade them transparently
  • Typed exception hierarchy (WrongPasswordError, VaultFormatError, EntryNotFoundError, …) for precise error handling
  • Rich-rendered colored panels and tables; pipe-friendly stdout/stderr separation
  • Refuses to distinguish "wrong password" from "tampered file" — both look the same cryptographically, exposing the difference helps attackers

Quick Start

./install.sh
just run -- init
just run -- add github
just run -- get github
$ pv init
New master password: ************
Confirm master password: ************
Vault created at /home/you/.password-vault/vault.json

$ pv add github
Username for github: alice
Password for github (hidden): ************
URL (optional, press Enter to skip): https://github.com
Notes (optional, press Enter to skip):
Added entry: github

$ pv get github
╭────────────────── github ──────────────────╮
│ username   alice                           │
│ password   hunter2-but-better              │
│ url        https://github.com              │
│ created    2026-05-13T14:22:10+00:00       │
│ updated    2026-05-13T14:22:10+00:00       │
╰────────────────────────────────────────────╯

[!TIP] This project uses just as a command runner. Type just to see all available recipes.

Install: curl -sSf https://just.systems/install.sh | bash -s -- --to ~/.local/bin

Commands

Command What it does
pv init Create a new empty vault. Prompts for the master password twice.
pv add <name> Add an entry. Prompts for username, password, optional URL and notes. --generate / -g to use a random password.
pv get <name> Show every field of one entry in a colored panel.
pv list Print every entry name as a table (no passwords shown).
pv delete <name> Remove an entry by name.
pv gen [length] Generate a strong random password and print it to stdout. No vault required.
pv change-password Rotate the master password — re-encrypts the entire vault under a fresh salt and key.

Every command takes --vault PATH (or $PV_VAULT) to point at an alternate vault file.

Demo: pipe-friendly generation

# Generate and copy to clipboard (macOS)
just run -- gen 32 | pbcopy

# Generate and copy to clipboard (Linux)
just run -- gen 32 | xclip -selection clipboard

# Generate into a shell variable
PASSWORD=$(just run -- gen 32)

# Letters + digits only, no symbols
just run -- gen 24 --no-symbols

[!IMPORTANT] pv never accepts the master password as a CLI flag. Passwords passed as flags leak into shell history (history command) and process listings (ps aux). Every prompt uses getpass.getpass() — same primitive sudo uses — so the password is never echoed and never logged.

Cryptographic guarantees

Concern Mitigation
Vault file stolen Argon2id with 64 MiB / 3 passes / 4 lanes makes each guess ~0.5s; a billion guesses ≈ 15 years
Vault file tampered AES-GCM authentication tag refuses to decrypt; same error as "wrong password" by design
Power loss mid-save Atomic write: tmp → fsync → os.replace → parent-dir fsync. Always old-or-new, never half
Two pv processes racing Advisory fcntl.LOCK_EX on sidecar .lock file (POSIX; NTFS atomic-rename on Windows)
Vault tmp world-readable os.open with mode 0o600 at the very first syscall — no chmod race window
Predictable random output secrets module everywhere — for salts, nonces, passwords, and the Fisher-Yates shuffle
Aging KDF parameters Parameters stored in the vault file; change-password can upgrade them transparently
KDF parameter corruption Validated against Argon2's algorithmic floors on load; clean VaultFormatError instead of library crash
Forward-incompatible format Top-level version field; future versions can refuse or migrate

What this project does not defend against — and why — is documented honestly in learn/01-CONCEPTS.md §12.

Tooling

just            # list available recipes
just test       # run pytest (60+ tests across crypto, vault, generator)
just test-cov   # tests + coverage report
just lint       # ruff + mypy + pylint
just format     # yapf
just run -- <cmd> [args]

Requirements

  • Python 3.13+ — the install script will check.
  • uv — modern Python package manager (auto-installed by ./install.sh).
  • just — command runner (auto-installed by ./install.sh).
  • Linux, macOS, or WSL2 strongly recommended over native Windows — file locking and directory fsync paths are POSIX-flavored. NTFS gives atomic os.replace regardless, so native Windows works with reduced concurrency guarantees.

No compilers or system libraries beyond what argon2-cffi and cryptography install through uv. No network access required at runtime.

Learn

This project includes step-by-step learning materials covering the security theory, architecture, and implementation — written for someone who has never touched Python or cryptography before. Read them in order.

Module Topic
00 - Overview Quick start, prerequisites, project layout, common problems
01 - Concepts What encryption is, KDFs, Argon2id, salts, AES-GCM, nonces, the threat model, real breaches
02 - Architecture Five-file layout, on-disk format, per-command flow diagrams, the atomic-write pipeline
03 - Implementation Line-by-line walkthrough of every source file — every Python feature explained when first encountered
04 - Challenges Fifteen extension ideas across four tiers, from a search command to porting the vault format to another language

See Also

License

AGPL 3.0