A zero-knowledge, encrypted CLI password manager written in C++17.
Your credentials, encrypted at rest. Nothing is ever written in plaintext.
- Highlights
- Security Model
- How It Works
- Features
- Installation
- Usage
- Build from Source
- Testing
- Project Structure
- Roadmap
- Security Considerations
- Contributing
- License
- π‘οΈ Zero-knowledge by design β the vault file is a binary blob; no master password or credential plaintext ever touches disk.
- π Envelope encryption β your master password derives a Key Encryption Key (KEK) that wraps a random Data Encryption Key (DEK); the DEK encrypts every entry.
- β‘ Argon2id KDF β memory-hard password hashing (64 MiB / 3 iterations) via libsodium, GPU/ASIC-resistant.
- π XChaCha20-Poly1305 AEAD β authenticated encryption per entry with 192-bit nonces; tamper-evident.
- π§ Secure memory β secrets live in
sodium_mallocguarded pages, locked, and zeroed on release (RAII). - π² CSPRNG generation β unbiased secure random passwords with rejection sampling.
- π Clipboard hygiene β retrieved passwords auto-clear from the clipboard after 15 seconds.
- π₯οΈ Cross-platform β Windows, Linux, and macOS.
Cryptenium uses a defense-in-depth approach centered on client-side encryption:
Master Password
β
βΌ
Argon2id (crypto_pwhash) β memory-hard, slow to brute-force
β
βΌ
Key Encryption Key (KEK) βββββββ β held only in secure memory
β wraps
βΌ
Data Encryption Key (DEK) β β random 256-bit, generated at init
β β
βΌ β
Per-Entry Keys (BLAKE2b) β β unique key per entry from the DEK
β β
βΌ βΌ
XChaCha20-Poly1305 per entry β β AEAD: ciphertext + MAC + nonce
β
βββββββββββββββββββββββββββββββββ΄βββββββββββββββββββββββββββ
β vault.dat: header (KDF params Β· salt Β· 8-byte verifier β
β Β· wrapped DEK Β· entry count) + AEAD blobs β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- Nothing recoverable from disk β without the master password, the vault yields only KDF parameters, a random salt, a wrapped DEK, and ciphertext.
- Master password verification β an 8-byte BLAKE2b fingerprint of the KEK is stored and compared in constant time (
sodium_memcmp); it leaks nothing about the password (preimage-resistant). - Per-entry keys β a single compromised entry key does not compromise the rest of the vault.
initderives a KEK from your master password with Argon2id and generates a random DEK.- The DEK is wrapped (encrypted) under the KEK; only the wrapped DEK is stored.
- Each entry is encrypted under its own key derived from the DEK via BLAKE2b.
- On unlock, the client re-derives the KEK, verifies the fingerprint, unwraps the DEK, and decrypts entries β all in memory.
Because the KEK exists only for the duration of a session, changing the master password later only requires re-wrapping the DEK β the entire vault never needs re-encryption.
| Command | Description |
|---|---|
init |
Create a new encrypted vault and set your master password |
add |
Store a credential (provided or auto-generated) |
get |
Retrieve a credential; password is copied to the clipboard and cleared after 15 s |
list |
List all stored entries (service / username only β never passwords) |
delete |
Remove a credential (with confirmation prompt) |
generate |
Generate a secure random password with configurable character sets |
version / help |
Print version or usage information |
- A C++17 compiler (GCC 8+, Clang, MSVC)
- CMake β₯ 3.10
- libsodium β₯ 1.0.18
| OS | Install libsodium |
|---|---|
| MSYS2 (UCRT64) | pacman -S mingw-w64-ucrt-x86_64-libsodium |
| Debian / Ubuntu | sudo apt install libsodium-dev |
| macOS | brew install libsodium |
| Windows (vcpkg) | vcpkg install libsodium |
git clone https://github.com/<you>/cryptenium.git
cd cryptenium
mkdir build && cd build
cmake .. -G "MinGW Makefiles" # or your platform's generator (e.g. "Unix Makefiles")
cmake --build .The binary is produced at build/cryptenium (or build\cryptenium.exe on Windows).
All commands that access the vault prompt for your master password.
# Initialize your vault (sets your master password)
cryptenium init
# Store a credential
cryptenium add --service github --username alice --password secret123
# ...or generate one and store it
cryptenium add --service gmail --username bob --generate --length 20 --symbols
# List all entries
cryptenium list
# Retrieve a credential (password copied to clipboard, cleared after 15 s)
cryptenium get --service github
# Generate a standalone password
cryptenium generate --length 24 --symbolscryptenium init
cryptenium add --service <s> --username <u> [--password <p> | --generate [--length <n>] [--symbols]]
cryptenium get --service <s> [--username <u>]
cryptenium list
cryptenium delete --service <s> [--username <u>]
cryptenium generate [--length <n>] [--no-digits] [--no-lower] [--no-upper] [--symbols]
cryptenium version
cryptenium help
Note: For interactive use the master password is read with echo disabled. When piping stdin (e.g. in scripts), trailing whitespace is trimmed so piped input behaves consistently.
- Windows:
%USERPROFILE%\.cryptenium\vault.dat - Linux / macOS:
~/.cryptenium/vault.dat
An automated end-to-end suite runs 10 scenarios against an isolated test home (USERPROFILE/HOME), so your real vault is never touched:
cd tests
run_tests.cmd # Windows / cmdThe suite verifies: init, duplicate-init, add, generated-password add, list contents, wrong-password rejection, confirmed delete, cancelled delete, generation, and version output.
cryptenium/
βββ include/
β βββ cli.hpp # CLI command parsing & dispatch
β βββ vault.hpp # Vault storage interface (binary, encrypted)
β βββ password_generator.hpp # CSPRNG password generation
β βββ platform.hpp # Hidden input & clipboard helpers
βββ src/
β βββ main.cpp # Entry point
β βββ cli.cpp # Command implementations
β βββ vault.cpp # Encrypted binary vault read/write
β βββ password_generator.cpp # Secure password generation
β βββ platform.cpp # OS-specific helpers
β βββ crypto/
β βββ Crypto.h # Crypto module declarations
β βββ Crypto.cpp # Argon2id, AEAD, secure random, secure memory
βββ tests/
β βββ run_tests.cmd # End-to-end test suite
βββ CMakeLists.txt # CMake build configuration
βββ LICENSE
βββ README.md
- Phase 1 β Secure Core: encrypted vault, master password,
init/add/get/list/delete/generate - Phase 2 β Envelope encryption: Argon2id KEK, wrapped DEK, per-entry keys, constant-time verification
-
rekey(change master password β instant via DEK re-wrap) -
status/backup/export/import - Recovery codes (BIP39-style)
- Progressive delay + lockdown on failed unlocks
- Vault tamper detection (canary values, Merkle root, signatures)
- Optional zero-knowledge cloud sync
Cryptenium makes the following trade-offs you should understand:
- Irrecoverable master password β by design, losing your master password means losing access to your vault. Back up the vault file and keep your master password safe.
- Local threat model β protection focuses on data at rest and in memory. A compromised OS with active monitoring is outside the threat model.
- Audited primitives β all cryptography is provided by libsodium, a widely audited, battle-tested library. Cryptenium never implements crypto primitives itself.
- Constant-time operations β verifier comparison uses
sodium_memcmp; generation uses rejection sampling to avoid modulo bias.
Contributions are welcome! Please:
- Fork the repository and create a feature branch.
- Keep commits small and focused with clear messages.
- Run the test suite before opening a pull request.
- Discuss security-sensitive changes before implementing them.
Distributed under the MIT License. Copyright (c) 2026 Cryptenium.