| Crates.io | enc_file |
| lib.rs | enc_file |
| version | 0.5.16 |
| created_at | 2020-01-04 09:48:11.428732+00 |
| updated_at | 2025-08-29 07:04:46.081093+00 |
| description | Password-based file encryption tool with a versioned header, AEAD, Argon2id KDF, and streaming mode. Library + CLI. |
| homepage | https://crates.io/crates/enc_file |
| repository | https://github.com/ArdentEmpiricist/enc_file |
| max_upload_size | |
| id | 195079 |
| size | 2,159,217 |
Password-based, authenticated file encryption with a small versioned header and Argon2id KDF. Ships as both a library and a CLI.
[!CAUTION] Security note: This project is neither audited nor reviewed. It protects data at rest but cannot defend a compromised host or advanced side channels. Use at your own risk. For important or sensitive information, use Veracrypt (or similar) instead.
chunk_size).secrecy wrappers and zeroize buffers.You can install enc-file in several ways:
cargo install enc-file
PATH.# from source
cargo build --release
# binary
target/release/enc-file --help
Add to a project as a library:
# Cargo.toml
[dependencies]
enc_file = "0.5.16"
enc-file <SUBCOMMAND>
Subcommands:
enc Encrypt a file (use --stream for large files)
dec Decrypt a file
key Manage an encrypted key map
hash Compute a file hash and print it as hex
# Simple: prompts for password and outputs secret.pdf.enc in same directory
enc-file enc --in secret.pdf
# Specify output filename, use AES-256-GCM-SIV and read password from file <PATH>
# Use shortcuts -i for --in and -o for --out
enc-file enc -i secret.pdf -o hidden.enc -a aes -p <PATH>
Options of interest:
-i, --in <file> Input file (required)
-o, --out <file> Output file
-a, --alg <algorithm> AEAD algorithm (xchacha default, aes)
--stream Enable streaming mode for large inputs
--chunk-size <bytes> Maximum frame length in streaming mode.
Default (0): adaptive sizing based on total file size:
• ≤ 1 MiB → 64 KiB
• 1 MiB–100 MiB → 1 MiB
• Files > 100 MiB → scales up (max 8 MiB)
Must be ≤ u32::MAX – 16 (32-bit length + 16 B tag).
-f, --force Overwrite output if file exists
--armor ASCII-armor output (streaming not supported)
-p, --password-file <path> Read password from file
# Use --force (or -f) to overwrite existing file
enc-file dec --in secret.enc --out secret.pdf
# Default blake3
enc-file hash README.md
# Specific algorithm (see below)
enc-file hash README.md --alg sha256
If you use the library’s key map helpers, the CLI can provide small helpers to init/save/load. Check enc-file key --help for available subcommands.
use enc_file::{encrypt_bytes, decrypt_bytes, EncryptOptions, AeadAlg};
use secrecy::SecretString;
let pw = SecretString::new("correct horse battery staple".into());
let opts = EncryptOptions {
alg: AeadAlg::XChaCha20Poly1305,
..Default::default()
};
let ct = encrypt_bytes(b"hello", pw.clone(), &opts)?;
let pt = decrypt_bytes(&ct, pw)?;
assert_eq!(pt, b"hello");
# Ok::<(), enc_file::EncFileError>(())
use enc_file::{encrypt_file, decrypt_file, EncryptOptions, AeadAlg};
use secrecy::SecretString;
use std::path::Path;
let pw = SecretString::new("pw".into());
let opts = EncryptOptions {
alg: AeadAlg::XChaCha20Poly1305, // or AeadAlg::Aes256GcmSiv
stream: false, // set true for large files
armor: false,
..Default::default()
};
let out = encrypt_file(Path::new("in.bin"), Some(Path::new("out.enc")), pw.clone(), opts)?;
let back = decrypt_file(&out, Some(Path::new("back.bin")), pw)?;
assert!(back.exists());
# Ok::<(), enc_file::EncFileError>(())
use enc_file::{encrypt_file_streaming, EncryptOptions, AeadAlg};
use secrecy::SecretString;
use std::path::Path;
let pw = SecretString::new("pw".into());
let opts = EncryptOptions {
alg: AeadAlg::XChaCha20Poly1305,
stream: true,
chunk_size: 1024 * 1024, // 1 MiB chunks (example)
..Default::default()
};
let out = encrypt_file_streaming(Path::new("big.dat"), None, pw, opts)?;
# Ok::<(), enc_file::EncFileError>(())
Chunk size:
In streaming mode,--chunk-size 0(the default) enables an adaptive helper that picks an optimal frame size based on the total file length:
- ≤ 1 MiB → 64 KiB
- 1 MiB – 100 MiB → 1 MiB
- Files larger than 100 MiB → scales up (max 8 MiB)
You can override this by passing any non-zero byte count. The absolute maximum is
u32::MAX - 16bytes (each frame encodes its length as a 32-bit ciphertext-byte count plus a 16-byte AEAD tag), and any larger value will be rejected.
Both the CLI and library support multiple hashing algorithms for files and byte slices:
| Algorithm | CLI --alg value(s) |
Output length |
|---|---|---|
| BLAKE3 | blake3 |
32 bytes |
| BLAKE2b-512 | blake2b |
64 bytes |
| SHA-256 | sha256 |
32 bytes |
| SHA-512 | sha512 |
64 bytes |
| SHA3-256 | sha3-256, sha3256, sha3_256 |
32 bytes |
| SHA3-512 | sha3-512, sha3512, sha3_512 |
64 bytes |
| XXH3-64 | xxh3-64, xxh364 |
8 bytes |
| XXH3-128 | xxh3-128, xxh3128 |
16 bytes |
| CRC32 | crc32 |
4 bytes |
[!CAUTION] XXH3 and CRC32 are non-cryptographic! Use with care.
CLI Example:
# Compute SHA3-512 hash of a file
enc-file hash --file data.bin --alg sha3-512
# Use XXH3-64 (fast, non-cryptographic)
enc-file hash --file data.bin --alg xxh3-64
Library Example:
use enc_file::{hash_file, to_hex_lower, HashAlg};
let digest = hash_file(std::path::Path::new("data.bin"), HashAlg::Sha3_512)?;
println!("{}", to_hex_lower(&digest));
# Ok::<(), enc_file::EncFileError>(())
use enc_file::{hash_bytes, hash_file, to_hex_lower, HashAlg};
let digest = hash_bytes(b"abc", HashAlg::Sha256);
assert_eq!(
to_hex_lower(&digest),
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
);
let file_digest = hash_file(std::path::Path::new("README.md"), HashAlg::Blake3)?;
println!("{}", to_hex_lower(&file_digest));
# Ok::<(), enc_file::EncFileError>(())
use enc_file::hash_bytes_keyed_blake3;
let key = [0u8; 32];
let tag = hash_bytes_keyed_blake3(b"message", &key);
assert_eq!(tag.len(), 32);
# Ok::<(), ()>(())
use enc_file::{KeyMap, load_keymap, save_keymap};
use secrecy::SecretString;
use std::path::Path;
let mut km = KeyMap::new();
km.insert("service".into(), "supersecret".into());
let pw = SecretString::new("pw".into());
let path = Path::new("keymap.enc");
save_keymap(&km, path, pw.clone())?;
let loaded = load_keymap(path, pw)?;
assert_eq!(loaded, km);
# Ok::<(), enc_file::EncFileError>(())
All fallible APIs return Result<_, EncFileError>. The error type is trait-based (thiserror::Error) and covers all expected failures without panics.
Error variants:
Io(std::io::Error): I/O failures (file read/write issues)Crypto: AEAD encryption/decryption failures (bad password, tampering)UnsupportedVersion(u16): File format version not supportedUnsupportedAead(u8): AEAD algorithm ID not supportedUnsupportedKdf(u8): Password KDF algorithm ID not supportedMalformed: Corrupt or invalid file structureInvalid(&'static str): Invalid argument or operation (e.g. streaming with keymap)Serde(serde_cbor::Error): Serialization errors (CBOR encoding/decoding)All errors are returned as Err(EncFileError); they never panic for expected failures.
See library and CLI tests for examples of error handling.
This library uses Argon2id for password-based key derivation with hardened defaults:
These parameters are enforced at the library level. The CLI uses compliant defaults automatically.
u32::MAX - 16 bytes due to 32-bit frame length + 16-byte AEAD tagThis library maintains backward compatibility for reading encrypted files across versions (beginning from 0.5). Backward-compatible format extensions (optional header fields) may be added between minor releases. Existing files remain decryptable by newer versions.
--armor when moving ciphertexts through systems that mangle binaries.--password-file over interactive prompts.Licensed under either of
at your option.
Any contribution intentionally submitted for inclusion in this work shall be dual licensed as above, without any additional terms or conditions.
Note on names
The library crate is named enc_file (snake_case), which is the name you use when importing it in Rust code:
use enc_file::{hash_file, HashAlg};
The compiled CLI binary is named enc-file (kebab-case), which is the name you use when invoking it from the shell:
enc-file hash --file test.txt
This naming separation is intentional and follows common conventions.
Feedback, bug reports, and pull requests are highly appreciated! Open an Issue or start a discussion.