|
stewie
398cbc02ce
Initial commit: minimal reproducible password manager
Slint dark-theme GUI with an anonymous anti-keylogger on-screen keyboard (arrow-key navigation, selection re-randomised after every character). Crypto: - Argon2id (2 GiB) -> 1536-bit key split 1024+256+256 - per-subkey hash diversification: Whirlpool x2 / BLAKE3 / SHA3-256 - triple cascade: Serpent-256 -> Threefish-1024 -> AES-256-GCM (AEAD) - single thread-safe Hash_DRBG CSPRNG (SHA-512), SHA3-512 seed - X25519 sealed-box writes while locked, drained on unlock - memory hardening: secrecy/zeroize, mlockall, no core dumps, no ptrace Reproducible build: rust:1.92-alpine pinned by digest, Cargo.lock + cargo vendor (--offline --locked), fixed clock/locale, scripts/build.sh --verify. vendor/ is gitignored (~770 MB) and regenerated deterministically from Cargo.lock. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .cargo | Initial commit: minimal reproducible password manager | |
| scripts | Initial commit: minimal reproducible password manager | |
| src | Initial commit: minimal reproducible password manager | |
| ui | Initial commit: minimal reproducible password manager | |
| .dockerignore | Initial commit: minimal reproducible password manager | |
| .gitignore | Initial commit: minimal reproducible password manager | |
| build.rs | Initial commit: minimal reproducible password manager | |
| Cargo.lock | Initial commit: minimal reproducible password manager | |
| Cargo.toml | Initial commit: minimal reproducible password manager | |
| Dockerfile | Initial commit: minimal reproducible password manager | |
| LICENSE | Initial commit: minimal reproducible password manager | |
| README.md | Initial commit: minimal reproducible password manager | |
| REQUIREMENTS.md | Initial commit: minimal reproducible password manager | |
| rust-toolchain.toml | Initial commit: minimal reproducible password manager | |
passwords
License: AGPL-3.0-or-later Rust 1.92 UI: Slint Reproducible build Offline Vendored deps Crypto
A minimal, paranoid, fully reproducible password manager. Local only — no network, no telemetry, no plugins. A single encrypted SQLite database, a dark Slint GUI, and an optional anonymous on-screen keyboard that makes a keylogger useless even while you type the whole master password on the physical keyboard.
The authoritative specification is REQUIREMENTS.md; if the
code and that document disagree, one of them is a bug. Reproducibility follows
the org-wide standard in ../REPRODUCIBLE-BUILDS.md.
What makes it paranoid
- One auditable source of randomness. A single, thread-safe
Hash_DRBG-style CSPRNG over SHA-512 (seeded once from the OS, or from--seed <STRING>) produces every random byte: the KDF salt, all IVs/nonces, every ephemeral key, and the on-screen keyboard selection. Output is a one-way hash of the secret state, so observed output never reveals past or future values. - Anonymous on-screen keyboard (anti-keylogger). You navigate a grid with the arrow keys only and commit with Space — never typing the actual letters. The highlighted cell starts at a random position and jumps to a new random cell after every character, so the recorded arrow-key sequence has no fixed relation to your password. No mouse, no Enter, no F-keys on the grid.
- Strong KDF. Master password → Argon2id with a 2 GiB memory cost → a 1536-bit key, split 1024 + 256 + 256.
- Triple-layer cipher cascade. The payload is encrypted inner→outer with Serpent-256 (CBC) → Threefish-1024 (CBC) → AES-256-GCM (AEAD). The outer GCM authenticates everything (and the file header), and is the wrong-password / tamper detector.
- Write-while-locked. Each database has an X25519 keypair. Locked, the master key and private key are wiped from RAM, existing entries are hidden, but you can still add entries — they are sealed to the public key and drained back in on unlock.
- Memory hygiene. Secrets live in
secrecy/zeroizetypes (auto-wiped, never logged). At startup the process doesmlockall(no swap to SSD),RLIMIT_CORE = 0andPR_SET_DUMPABLE = 0(no core dumps, noptrace).
The decrypted database lives only in RAM; the on-disk file is only the encrypted container — there is no plaintext SQLite header to fingerprint.
Using it
passwords [--db <PATH>] [--seed <STRING>]
- Create a database: enter a path, leave Anonyme Bildschirmtastatur ticked (or not), click Erstellen, then enter the master password twice.
- Open: same, click Öffnen, enter the master password once.
- Anonymous keyboard: move with ↑ ↓ ← →, press Space to take the highlighted character, Backspace to delete. ⇧ = next char uppercase, ⇪ = caps lock. Click Öffnen/Erstellen when done.
- In the list: User/Pass copy to the clipboard, ✕ deletes, Sperren 🔒 locks.
mlockallmay need a higher limit for the full no-swap guarantee: run withulimit -l unlimited(or grantCAP_IPC_LOCK). The app warns in the status line if it could not lock memory, and continues.
Reproducible build
Built only inside the pinned Docker image — Rust 1.92 on Alpine, pinned by
content digest — strictly offline against the vendored sources in vendor/.
scripts/build.sh # build once -> ./out/{passwords,SHA256SUMS}
scripts/build.sh --verify # build twice and prove the hashes match
What pins the bytes: the base image digest, rust-toolchain.toml (1.92.0),
Cargo.lock + vendor/ (--offline --locked), fixed SOURCE_DATE_EPOCH/
TZ/LC_ALL, --remap-path-prefix, and a fixed release profile
(codegen-units=1, lto, strip, panic=abort). See the
house standard.
Dependency version policy
We take the newest mutually-compatible version of every dependency, then
freeze it via Cargo.lock + vendor/. One deliberate constraint: the cipher
cascade (Serpent/Threefish/CBC) is held to the cipher 0.4 generation
because the AES-256-GCM authenticator (aes-gcm) is published stable only on
that generation; jumping the block ciphers to cipher 0.5 would mean dropping
GCM authentication. Bumping is always a deliberate re-vendor commit, never
silent. To re-vendor:
cargo update && cargo vendor # paste the printed [source.*] block into .cargo/config.toml
Threat model & honest limits
The anonymous keyboard defeats keyloggers, not screen recorders — a tool
that captures the screen sees the highlighted cell. mlock/no-core-dump/no-
ptrace raise the cost for spyware and crash forensics but do not stop an
attacker who is already root. The triple cascade is defence-in-depth, not a
proof; its security is at least that of AES-256-GCM. The custom CSPRNG follows a
published construction (Hash_DRBG) and is kept small specifically so it can be
audited (see src/csprng.rs and
REQUIREMENTS.md §4).
Layout
| Path | What |
|---|---|
REQUIREMENTS.md |
authoritative spec |
src/csprng.rs |
central thread-safe Hash_DRBG CSPRNG (§4) |
src/crypto.rs |
Argon2id KDF, triple cascade, sealed box (§6–8) |
src/keyboard.rs |
anti-keylogger on-screen keyboard (§5) |
src/db.rs |
in-RAM SQLite, encrypted container, lock/unlock (§7–9) |
src/hardening.rs |
mlock / no-core-dump / no-ptrace (§8a) |
src/ui.rs + ui/app.slint |
dark Slint GUI |
Dockerfile, scripts/build.sh |
reproducible build |
Licence
AGPL-3.0-or-later — see LICENSE.