nahui
Authenticated encryption (ChaCha20-Poly1305) and BLAKE3 content hashing for document attestation.
nahui is a small, focused crypto crate built on top of [chacha20poly1305]
and [blake3]. It exposes a Vault type that handles single-shot and
streaming encryption, AAD binding, and a wire format that's versioned so
breaking changes can't silently corrupt old blobs.
It runs natively and on wasm32-unknown-unknown (browser).
Features
- AEAD encryption —
ChaCha20-Poly1305, both single-shot (in-memory) and streaming (IETF STREAM construction, noSeekrequired on decrypt). - AAD on every API — bind a ciphertext to its context (filename, user id, doc version) so an attacker can't swap one valid blob for another.
- Versioned wire format — every blob starts with a version byte, and the version is bound into the AEAD tag so cross-version confusion is impossible.
- Plaintext hashing —
hash_bytes,hash_stream, and a one-passhash_and_encrypt_streamthat returns the BLAKE3 hash of the plaintext (suitable for an attestation note). - Allocation-aware —
encrypt_into/decrypt_intoreuse a caller'sVecso the hot path runs allocation-free once warmed up. - Plaintext scrubbing on failure — if AEAD verification fails, the unauthenticated keystream-XOR plaintext is zeroed before returning.
#![forbid(unsafe_code)]in the library.- Zeroization — master key and derived cipher state are wiped on drop.
Usage
use Vault;
// Generate a fresh random key. Use `Vault::new(key)` if you already have
// one (e.g. derived from a password via Argon2 or fetched from a KMS).
let vault = generate.expect;
// Single-shot, with AAD binding the ciphertext to a filename.
let ct = vault.encrypt.unwrap;
let pt = vault.decrypt.unwrap;
assert_eq!;
Streaming + plaintext hash in one pass:
use Vault;
use File;
use BufWriter;
let vault = generate.unwrap;
let mut input = open.unwrap;
let output = new;
let hash = vault
.hash_and_encrypt_stream
.unwrap;
println!;
Wire format
Single-shot:
[ version (1 B) | nonce (12 B) | ciphertext (N B) | tag (16 B) ]
Streaming:
[ version (1 B) | header_nonce (7 B) | chunk_0 | chunk_1 | ... | chunk_final ]
Each streaming chunk carries its own 16-byte Poly1305 tag, and STREAM's "last block" flag prevents truncation attacks.
Status
0.x — the API and wire format are stable enough that we KAT-test them,
but they may evolve. Expect a 1.0 once the dust settles. The current
VERSION constant is 0x01.
License
MIT OR Apache-2.0, at your option.