crypt-io 1.0.0

AEAD encryption (ChaCha20-Poly1305, AES-256-GCM), hashing (BLAKE3, SHA-2), MAC (HMAC, BLAKE3 keyed), and KDF (HKDF, Argon2id) for Rust. Algorithm-agile. RustCrypto-backed primitives with REPS discipline. Simple API. Sub-microsecond throughput.
Documentation

Installation

[dependencies]
crypt-io = "1"

Or:

cargo add crypt-io

MSRV: Rust 1.85 (edition 2024). Older toolchains will not build.

Quick start

AEAD round-trip

use crypt_io::Crypt;

let key = [0u8; 32];                  // your 256-bit key
let crypt = Crypt::new();             // ChaCha20-Poly1305 by default

let ciphertext = crypt.encrypt(&key, b"plaintext data")?;
let recovered  = crypt.decrypt(&key, &ciphertext)?;
assert_eq!(&*recovered, b"plaintext data");
# Ok::<(), crypt_io::Error>(())

AES-256-GCM (when you want hardware acceleration)

use crypt_io::Crypt;

let key = [0u8; 32];
let crypt = Crypt::aes_256_gcm();     // requires `aead-aes-gcm` (default-on)

let ciphertext = crypt.encrypt(&key, b"hello AES")?;
let recovered  = crypt.decrypt(&key, &ciphertext)?;
# Ok::<(), crypt_io::Error>(())

Hashing

use crypt_io::hash;

let digest = hash::blake3(b"the quick brown fox");   // [u8; 32]
let sha256 = hash::sha256(b"the quick brown fox");   // [u8; 32]
let sha512 = hash::sha512(b"the quick brown fox");   // [u8; 64]
let xof    = hash::blake3_long(b"input", 128);       // Vec<u8>, 128 bytes

MAC with constant-time verify

use crypt_io::mac;

let key  = b"shared secret";
let data = b"message to authenticate";

let tag = mac::hmac_sha256(key, data)?;
assert!(mac::hmac_sha256_verify(key, data, &tag)?);
// Never `tag == expected_tag` against a secret — use the `*_verify` path.
# Ok::<(), crypt_io::Error>(())

BLAKE3 keyed mode — typed key, infallible:

use crypt_io::mac;

let key = [0x42u8; 32];
let tag = mac::blake3_keyed(&key, b"message");
assert!(mac::blake3_keyed_verify(&key, b"message", &tag));

Streaming (large or chunked inputs)

use crypt_io::hash::Blake3Hasher;

let mut h = Blake3Hasher::new();
h.update(b"first chunk ");
h.update(b"second chunk");
let digest = h.finalize();

Key derivation

Deriving a subkey from a master:

use crypt_io::kdf;

let master = [0x42u8; 32];
let session_key = kdf::hkdf_sha256(&master, Some(b"salt"), b"app:session:v1", 32)?;
assert_eq!(session_key.len(), 32);
# Ok::<(), crypt_io::Error>(())

Hashing a password (Argon2id, OWASP-recommended defaults):

use crypt_io::kdf;

let phc = kdf::argon2_hash(b"correct horse battery staple")?;
assert!(kdf::argon2_verify(&phc, b"correct horse battery staple")?);
# Ok::<(), crypt_io::Error>(())

Encrypt a file

use crypt_io::Algorithm;
use crypt_io::stream;

let key = [0u8; 32];
stream::encrypt_file("input.bin", "output.enc", &key, Algorithm::ChaCha20Poly1305)?;
stream::decrypt_file("output.enc", "decrypted.bin", &key)?;
# Ok::<(), crypt_io::Error>(())

Chunked AEAD with the STREAM construction — works for files of any size, detects tampering / truncation / reordering. For in-memory streaming (network sockets, buffered I/O), use StreamEncryptor / StreamDecryptor directly.

See docs/API.md for the full reference.

Design philosophy

crypt-io is intentionally focused:

  • One job: symmetric crypto. Done well.
  • No reinvention. Primitives come from RustCrypto and BLAKE3 (battle-tested, widely audited).
  • Simple API. Encrypt in two lines. Hash in one. The easy path is the secure path.
  • Algorithm agility. ChaCha20-Poly1305 by default, AES-256-GCM when you want hardware acceleration. Same Crypt API either way.
  • Constant-time discipline. MAC verification uses upstream constant-time comparators, never ==. Documented in module overviews.
  • Hash ≠ MAC. Blake3Hasher has no with_key. The only way to produce a keyed tag is through the mac module. This separation is deliberate.
  • Redaction-clean errors. No variant of Error ever contains key material, plaintext, ciphertext, nonces, or tag bytes.
  • REPS-disciplined. Every commit passes cargo fmt --check, cargo clippy --all-targets --all-features -- -D warnings, cargo test --all-features, and cargo doc with -D warnings.

What we explicitly do NOT do:

  • Implement crypto primitives from scratch (use battle-tested upstreams)
  • Asymmetric crypto (RSA, ECDSA, Ed25519) — different problem, separate crate
  • PGP/GPG (use sequoia-openpgp)
  • TLS (use rustls)
  • Random number generation (use mod-rand)
  • UUID generation (use id-forge)
  • Key storage (use key-vault)

When to use crypt-io

Good fit:

  • Encrypting data for storage (databases, file systems, caches)
  • Encrypting API tokens or session data
  • Authenticating messages, audit logs, signed records
  • Hashing for integrity checks, fingerprinting, content-addressed storage
  • HMAC signatures for outgoing requests (AWS SigV4, JWT HS256/HS512, webhooks)
  • Composing with key-vault for in-memory key handling

Wrong fit:

Performance

Measured on a reference machine (AMD Ryzen 9 9950X3D, AES-NI + SHA-NI + AVX-512, WSL2 Ubuntu, Rust 1.85.0). Full methodology + per-suite tables in docs/PERFORMANCE.md.

Operation Target Measured Status
ChaCha20-Poly1305 encrypt, 1 KiB < 2 µs 1.72 µs
AES-256-GCM encrypt, 1 KiB (HW accel) < 1 µs 944 ns
BLAKE3 hash, 1 KiB < 500 ns 1.07 µs ⚠️ revised
BLAKE3 hash, 64 KiB 11.24 GiB/s
SHA-256 hash, 1 KiB (SHA-NI) < 2 µs 426 ns
HMAC-SHA256, 1 KiB < 3 µs 565 ns
HKDF-SHA256, 32-byte output < 5 µs 304 ns
Argon2id, default params ~100 ms ~9 ms (Zen 5 too fast — tune t_cost) ⚠️
Stream encrypt, 1 MiB plaintext > 1 GiB/s 932-999 MiB/s ⚠️ marginal

Reproduce: cargo bench --all-features (numbers vary by hardware — see PERFORMANCE.md for the portable analysis).

Documentation

Standards

  • REPS (Rust Efficiency & Performance Standards) governs every decision. See REPS.md.
  • MSRV: Rust 1.85.
  • Edition: 2024.
  • Cross-platform: Linux, macOS, Windows (CI matrix on stable + MSRV).

License

Dual-licensed under either of:

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.