# encryptman
[](https://crates.io/crates/encryptman)
[](https://docs.rs/encryptman)
[](#license)
AES-256-GCM encryption for application settings with HKDF key derivation.
## Features
- **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
## Installation
Add this to your `Cargo.toml`:
```toml
[dependencies]
encryptman = "0.4.0"
```
## Quick Start
```rust
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(())
}
```
## Usage
### Basic encrypt/decrypt
```rust
use encryptman::{encrypt, decrypt, MasterKey};
let key = MasterKey::generate()?;
let ct = encrypt(&key, "secret")?;
let pt = decrypt(&key, &ct)?;
```
### Multiple contexts with one key
```rust
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)?;
```
### URL-safe encoding (JWTs, cookies, URLs)
```rust
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");
```
### Binding a ciphertext to a record (AAD)
```rust
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());
```
### Rotating the master key
```rust
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://...");
```
### Storing and restoring the master key
```rust
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);
```
## How It Works
```text
master_key ──► HKDF-SHA256(context) ──► AES-256 key
│
- **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.
## Security Considerations
- **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**: `MasterKey` implements `Zeroize` to scrub key bytes from RAM when dropped.
## When NOT to use this crate
This crate is designed for encrypting small strings (passwords, API keys, tokens). It is **not** suitable for:
- **Password hashing** — use [argon2](https://crates.io/crates/argon2) or [bcrypt](https://crates.io/crates/bcrypt) instead.
- **File encryption** — use a streaming AEAD like [ChaCha20Poly1305](https://crates.io/crates/chacha20poly1305) with proper chunking.
- **Database-at-rest encryption** — use your database's built-in encryption (e.g., PostgreSQL `pgcrypto`, MySQL `AES_ENCRYPT`).
- **Large data** — this crate allocates the entire plaintext/ciphertext in memory.
## Minimum Supported Rust Version
MSRV: **1.85** (edition 2024)
## License
Licensed under either of:
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
at your option.