enc_file 0.5.14

Password-based file encryption tool with a versioned header, AEAD, Argon2id KDF, and streaming mode. Library + CLI.
Documentation

Crates.io Clippy Deploy Documentation Crates.io Crates.io

enc_file

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.

Features

  • Argon2id password KDF (per-file salt + stored parameters).
  • AEAD: XChaCha20-Poly1305 (default) or AES-256-GCM-SIV.
  • Compact binary header (magic, version, alg, KDF kind/params, salt, nonce, length).
  • Optional ASCII armor for transport.
  • Streaming mode for large files (constant memory; configurable chunk_size).
  • Use secrecy wrappers and zeroize buffers.
  • Compute a file hash and print it as hex.
  • Usable as library and CLI.

Install

You can install enc-file in several ways:

From crates.io (requires Rust toolchain)

cargo install enc-file

From GitHub Releases (prebuilt binaries)

  1. Visit the Releases page.
  2. Download the binary for your platform.
  3. Place it in a directory in your PATH.

From source

# 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.14"

CLI Usage

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

Encrypt

# 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:

  • --in / -i specify input file (required)
  • --out / -o specify output file
  • --alg / -a AEAD algorithm: xchacha (default), aes
  • --stream stream mode for large inputs
  • --chunk-size <bytes> set chunk size in streaming mode (default from library is 1 MiB)
  • --armor ASCII-armor output, attention: armored streaming is not available
  • --force / -f overwrite output if file exists
  • --password-file / -p <PATH> read password from a file

Decrypt

# Use --force (or -f) to overwrite existing file
enc-file dec --in secret.enc --out secret.pdf

Hash

# Default blake3
enc-file hash README.md
# Specific algorithm (see below)
enc-file hash README.md --alg sha256

Key map (optional)

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.


Library Usage

Encrypt / Decrypt bytes

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>(())

Encrypt / Decrypt files

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>(())

Streaming encryption

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 uses the default (1 MiB). The maximum allowed is u32::MAX - 16 bytes, because each frame’s length is a 32-bit count of ciphertext bytes and the AEAD adds a 16-byte tag.

Hash helpers

Supported Hash Algorithms

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>(())

Keyed BLAKE3 (MAC-style)

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::<(), ()>(())

Key map helpers

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>(())

Error handling

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 supported
  • UnsupportedAead(u8): AEAD algorithm ID not supported
  • UnsupportedKdf(u8): Password KDF algorithm ID not supported
  • Malformed: Corrupt or invalid file structure
  • Invalid(&'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.


KDF defaults and bounds

This library uses Argon2id for password-based key derivation with hardened defaults:

  • Time cost: 3 iterations (minimum)
  • Memory cost: 64 MiB (minimum)
  • Parallelism: min(4, number of CPU cores)

These parameters are enforced at the library level. The CLI uses compliant defaults automatically.

Streaming and armor

  • Streaming mode provides constant memory usage for large files using chunked framing
  • ASCII armor is ignored in streaming mode - only non-streaming payloads can be armored
  • Maximum chunk size is u32::MAX - 16 bytes due to 32-bit frame length + 16-byte AEAD tag

Compatibility policy

This 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.


Tips

  • Use streaming for large files to keep memory predictable.
  • Consider --armor when moving ciphertexts through systems that mangle binaries.
  • For CLI automation, prefer --password-file over interactive prompts.

License

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 & Issues

Feedback, bug reports, and pull requests are highly appreciated! Open an Issue or start a discussion.