A high-quality Rust library for encrypted vaults and single-file encryption with full metadata protection - built on AES-256-GCM and Argon2id.
See SECURITY for the threat model, design decisions and known limitations.
See ROADMAP for the upcoming planned work.
If you are interested in contributing to the project, contribution rules are mentioned in CONTRIBUTING.md.
use krypton::{encrypt_file, decrypt_file, Vault};
use std::path::Path;
// Single file: filename is encrypted inside the container.
let out = encrypt_file("correct horse", Path::new("document.pdf"), None)?;
let original_name = decrypt_file("correct horse", &out, Path::new("restored.pdf"))?;
assert_eq!(original_name, "document.pdf");
// Multi-file encrypted vault.
let mut vault = Vault::new("myvault".into());
vault.init("correct horse")?;
vault.unlock("correct horse")?;
vault.add(Path::new("secret.pdf"), None)?;
vault.add(Path::new("photos"), Some("photos"))?; // whole trees
let entries = vault.list()?;
vault.extract("photos", Path::new("./restored"))?;
vault.change_password("correct horse", "new password")?; // no re-encryption needed
vault.lock();| Module | Contents |
|---|---|
krypton::* |
encrypt_file / decrypt_file (.krf containers), Vault, EntryInfo, IntegrityReport |
krypton::crypto |
Low-level primitives: Key, seal/open (AAD), derive_key, HKDF subkeys, constant-time compare |
krypton::kdf |
KdfParams — Argon2id parameters with validation bounds |
krypton::sanitize |
Entry-name validation / traversal defense |
krypton::error |
Error, Result |
Run cargo doc --open for full documentation with examples on every public item.
- AES-256-GCM authenticated encryption for every byte of ciphertext
- Argon2id key derivation (64 MiB, t=4) with parameters stored in each container, so defaults can evolve without breaking old files
- Per-object subkeys via HKDF-SHA256 — every file gets an independent content key
- Deterministic counter nonces per chunk — eliminates random-nonce birthday collisions on large files and makes reordering detectable
- AAD everywhere — chunk index and record context are bound into each AEAD operation; truncation, splicing and extension are all detected
- Streaming — 64 KiB chunks with constant memory use; files of any size
- Metadata privacy — original filenames never appear unencrypted;
.krfoutputs get random hashed names - Hardened memory — keys live in zeroizing buffers, scrubbed on drop;
#![forbid(unsafe_code)] - Crash safety — configs, manifests and plaintext outputs are written via temp file + rename; interrupted operations never leave half-written data or bricked vaults
- Traversal defense — names recovered from containers are sanitized before touching the filesystem
- One universal container format —
.krffiles, vault blobs and manifests share a single envelope; new object types are new payload types, never new formats
Every object is a krypton container:
"KRYPTON\0" magic (8 bytes, never changes)
[u16 LE] envelope version (currently 1)
[u8 kind] key derivation:
0 = HKDF(master key, object salt[32]) — vault objects
1 = Argon2id(password, salt[32], params[12])
then HKDF with object salt[32] — .krf files
[len u32][ct‖tag] … chunk records (64 KiB plaintext each)
[00000000] end-of-chunks marker
[len u32][ct‖tag] authenticated trailer: {type: file|directory|manifest,
name?, size, chunk_count}
Header bytes are bound to key derivation; the payload type is bound into every record's AAD, so records cannot be transplanted between containers.
vault/
├── vault.config JSON v2: wrapped master key + KDF params (atomic writes)
└── d/
├── .manifest.enc encrypted index incl. directory child lists
└── <hash>/<hash>.enc per-entry blobs
Entry paths are derived from SHA256(master_key ‖ path); blob contents use per-entry subkeys. Directory removal deletes the entire subtree — blobs and manifest records alike.
Password ──Argon2id(salt) -> KEK ──AES-GCM-wrap -> vault.config holds Master Key -> SHA256(master ‖ path) → blob location
│
HKDF(master, per-file salt) → content key
│
counter nonces + AAD(index) → AES-GCM chunks
Changing the password only re-wraps the master key — stored data is never re-encrypted.
cargo test # 55 tests: unit, integration, doc examples
cargo clippy # cleanLicensed under Apache-2.0.