AES-256-GCM encryption for application settings with HKDF key derivation.
- AES-256-GCM authenticated encryption - guarantees confidentiality + integrity
- HKDF-SHA256 key derivation - derive purpose-specific keys safely from one master key
- Random nonces - encrypting the same plaintext twice yields distinct ciphertexts
- Zeroize on drop - master key memory is zeroed out automatically on drop
- Context isolation - prevents cross-domain ciphertext substitution attacks
- AAD binding - optionally bind a ciphertext to the record it belongs to (user id, field name)
- Key rotation -
reencrypt()moves a ciphertext to a new master key without exposing plaintext
Add this to your Cargo.toml:
[dependencies]
encryptman = "0.4.0"use encryptman::{encrypt, decrypt, generate_master_key};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let master_key = generate_master_key()?;
let encrypted = encrypt(&master_key, "my_database_password")?;
let decrypted = decrypt(&master_key, &encrypted)?;
assert_eq!(decrypted, "my_database_password");
Ok(())
}use encryptman::{encrypt, decrypt, MasterKey};
let key = MasterKey::generate()?;
let ct = encrypt(&key, "secret")?;
let pt = decrypt(&key, &ct)?;use encryptman::{encrypt_with_context, decrypt_with_context, MasterKey};
let key = MasterKey::generate()?;
// Database passwords
let db_ct = encrypt_with_context(&key, "database", "postgres://...")?;
// API keys
let api_ct = encrypt_with_context(&key, "api-keys", "sk-12345")?;
// Same plaintext, different contexts → different ciphertext
assert_ne!(db_ct, api_ct);
// Decrypt with the matching context
let db_pt = decrypt_with_context(&key, "database", &db_ct)?;use encryptman::{encrypt_with_encoding, decrypt_with_encoding, generate_master_key, Encoding};
let key = generate_master_key()?;
let encrypted = encrypt_with_encoding(&key, "jwt", "token", Encoding::UrlSafeNoPad)?;
let decrypted = decrypt_with_encoding(&key, "jwt", &encrypted, Encoding::UrlSafeNoPad)?;
assert_eq!(decrypted, "token");use encryptman::{encrypt_with_aad, decrypt_with_aad, generate_master_key};
let key = generate_master_key()?;
// AAD is public (a user id, a field name) and is authenticated, not stored.
let encrypted = encrypt_with_aad(&key, "settings", "s3cret", b"user:42")?;
// The same AAD is required to decrypt; a different record fails.
let decrypted = decrypt_with_aad(&key, "settings", &encrypted, b"user:42")?;
assert_eq!(decrypted, "s3cret");
assert!(decrypt_with_aad(&key, "settings", &encrypted, b"user:43").is_err());use encryptman::{encrypt_with_context, reencrypt, decrypt_with_context, MasterKey};
let old_key = MasterKey::generate()?;
let new_key = MasterKey::generate()?;
let before = encrypt_with_context(&old_key, "database", "postgres://...")?;
// The intermediate plaintext is zeroized inside reencrypt().
let after = reencrypt(&old_key, &new_key, "database", &before)?;
assert_eq!(decrypt_with_context(&new_key, "database", &after)?, "postgres://...");use encryptman::MasterKey;
// Generate a new key
let key = MasterKey::generate()?;
// Export raw bytes (e.g., to store in OS keychain / Vault)
let bytes = *key.as_bytes();
// Reconstruct key later
let restored = MasterKey::from_bytes(bytes);encryptman builds for both WASI and the browser/worker world:
# wasm32-wasip1 (WASI preview 1): no configuration needed
cargo build --target wasm32-wasip1
# wasm32-unknown-unknown (browsers, workers): enable the `wasm_js`
# feature so getrandom uses the Web Crypto API
cargo build --target wasm32-unknown-unknown --features wasm_jsThe host must provide crypto.getRandomValues (browsers and workers
do). Both targets are compiled on every CI run.
master_key ──► HKDF-SHA256(context) ──► AES-256 key
│
plaintext + random 12-byte nonce ─────────────┴──► AES-256-GCM ──► version || nonce || ciphertext ──► Base64
- Key Derivation: HKDF-SHA256 generates domain-separated subkeys from a single master key.
- Authenticated Encryption: AES-256-GCM detects any payload tampering during decryption.
- Fresh Nonce: 12 random bytes generated per encryption call to maintain security guarantees.
- Version Byte: Reserved for future algorithm migrations.
- Secure Storage: Always store the master key in a key management service (KMS), OS keychain, or encrypted secret store.
- Context Separation: Use distinct context strings for different application domains (e.g.,
"db","oauth") to prevent cross-context substitution. - Integrity Validation: Decryption will fail with an error if the payload has been tampered with or corrupted.
- Memory Security:
MasterKeyimplementsZeroizeto scrub key bytes from RAM when dropped.
This crate is designed for encrypting small strings (passwords, API keys, tokens). It is not suitable for:
- Password hashing - use argon2 or bcrypt instead.
- File encryption - use a streaming AEAD like ChaCha20Poly1305 with proper chunking.
- Database-at-rest encryption - use your database's built-in encryption (e.g., PostgreSQL
pgcrypto, MySQLAES_ENCRYPT). - Large data - this crate allocates the entire plaintext/ciphertext in memory.
MSRV: 1.85 (edition 2024)
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.