# v0.4.0 — Hashing: BLAKE3, SHA-256, SHA-512
**Release date:** 2026-05-22
**Status:** pre-1.0 milestone. The public API is allowed to evolve in
breaking ways through the `0.x` series; 1.0 freezes it.
---
## What this release is
`crypt-io` now hashes. A new `crypt_io::hash` module ships three
cryptographic hash functions behind a consistent free-function API,
each paired with a matching streaming hasher for inputs that arrive
in chunks or don't fit in memory:
| BLAKE3 | `hash::blake3` | `Blake3Hasher` | 32 B | `hash-blake3` |
| BLAKE3 XOF| `hash::blake3_long` | `Blake3Hasher::finalize_xof` | N B | `hash-blake3` |
| SHA-256 | `hash::sha256` | `Sha256Hasher` | 32 B | `hash-sha2` |
| SHA-512 | `hash::sha512` | `Sha512Hasher` | 64 B | `hash-sha2` |
Every entry point is verified byte-for-byte against the spec — FIPS
180-4 vectors for SHA-2, the upstream-pinned constants for BLAKE3.
---
## Highlights
- **Three algorithms, one API shape.** The one-shot functions take
`&[u8]` and return a fixed-size byte array; the streaming hashers
share a `new` / `update` / `finalize` triad. Code that switches
algorithms changes one identifier, not a paradigm.
- **BLAKE3 XOF mode.** `hash::blake3_long(data, len)` and
`Blake3Hasher::finalize_xof(len)` give you arbitrary-length output
for key derivation, fingerprinting larger ranges, or whatever else
needs more than 32 bytes. The first 32 bytes of an extended
output equal the default digest of the same input — verified by
test.
- **Spec-pinned KATs.** SHA-256 has FIPS 180-4 B.1 (`abc`), B.2
(the 56-byte two-block input), and empty-input. SHA-512 has C.1
(`abc`), C.2 (the 112-byte two-block input), and empty-input.
BLAKE3 has empty + `"IETF"`. All eight are byte-array constants
pinned in source so any future wrapper-level mistake (wrong
endianness, wrong slicing, accidental truncation) is caught
immediately.
- **Streaming-equivalence proved.** Every algorithm has a test
that feeds the same data in three different chunk boundaries to
the streaming hasher and asserts the digest equals the one-shot
output. This catches any state-management bug in the wrapper.
---
## API at a glance
One-shot:
```rust
use crypt_io::hash;
let b = hash::blake3(b"the quick brown fox");
assert_eq!(b.len(), 32);
let s256 = hash::sha256(b"the quick brown fox");
assert_eq!(s256.len(), 32);
let s512 = hash::sha512(b"the quick brown fox");
assert_eq!(s512.len(), 64);
let big = hash::blake3_long(b"the quick brown fox", 128);
assert_eq!(big.len(), 128);
```
Streaming:
```rust
use crypt_io::hash::Blake3Hasher;
let mut h = Blake3Hasher::new();
h.update(b"first ");
h.update(b"second ");
h.update(b"third");
let digest = h.finalize();
assert_eq!(digest.len(), 32);
```
Or chained:
```rust
use crypt_io::hash::Sha256Hasher;
let mut h = Sha256Hasher::new();
h.update(b"chain").update(b"-friendly");
let digest = h.finalize();
```
---
## Choosing a hash
Both BLAKE3 and SHA-2 are safe at 256-bit cryptographic strength.
The choice is about speed and ecosystem interop, not about
strength.
| Maximum throughput, modern hardware | `BLAKE3` |
| Variable-length output (KDF, fingerprinting, MGF) | `BLAKE3` (XOF) |
| TLS / JWT / certificate fingerprint interop | `SHA-256` |
| 64-byte output for spec compliance | `SHA-512` |
| Tree-hashing for very large inputs | `BLAKE3` |
| FIPS-certified algorithm (via a downstream FIPS-validated build) | `SHA-256` / `SHA-512` |
The hardware-acceleration story differs:
- **BLAKE3** uses `AVX2` / `AVX-512` on x86 and `NEON` on ARM — the
upstream crate dispatches at runtime.
- **SHA-2** uses `SHA-NI` on supporting x86 chips and the ARMv8
crypto extensions on AArch64 — also runtime-dispatched by
upstream.
---
## What's NOT in 0.4.0
- **Keyed hashing / MACs.** `Blake3Hasher` does not expose
`with_key`, and there's no `hmac_*` here. Both belong in the
`mac` module shipping in Phase 0.5.0 — keeping "data integrity
check" (no key) and "authentication tag" (key + constant-time
verify) in separate modules is deliberate. Mixing them is the
shape of API that produces "I used SHA-256 as my MAC" bug
reports.
- **Benchmarks.** Deferred to Phase 0.8.0 alongside the AEAD
benchmark suite. The hash performance contract (BLAKE3 ≤ 500 ns
per 1 KiB, SHA-256 ≤ 2 µs per 1 KiB) gets measured there with
the dedicated criterion harness, not stubbed in this phase.
- **No-key BLAKE3 `derive_key` mode.** That's the BLAKE3-specific
key derivation API; KDF concerns live in the `kdf` module
(Phase 0.6.0).
---
## Security notes
- **Hash-only, no MAC.** Using a raw hash as a MAC is a security
mistake (`hash(key || message)` is vulnerable to length-extension
on Merkle-Damgård hashes; even on BLAKE3 it produces a weaker
authenticator than the keyed mode). The absence of a `with_key`
on the hashers is intentional and documented.
- **Constant-time discipline preserved.** SHA-2 timing is
data-dependent in the spec; BLAKE3 timing is data-independent.
Neither matters at the hash level (the input isn't a secret in
the threat model for "hash some bytes") — what would matter is
comparing two digests, and that comparison must use
`subtle::ConstantTimeEq` or equivalent. We do not expose a
`Digest == Digest` shortcut for this reason.
- **No bytes in errors.** Hash functions don't fail — they have no
`Result` return type — so there are no error variants to leak
bytes through.
---
## Compatibility & build
- **Default features extended.** `default` now includes `hash-sha2`
in addition to `hash-blake3`. A fresh `cargo add crypt-io` ships
with both. Drop `hash-sha2` (via `default-features = false` + a
custom feature list) if you want BLAKE3-only.
- **MSRV** unchanged: Rust 1.85 (edition 2024).
- **No breaking changes** to the 0.3.0 surface. `Crypt`,
`Algorithm`, `Error`, `Result` are all unchanged.
---
## Installation
```toml
[dependencies]
crypt-io = "0.4"
```
Or:
```bash
cargo add crypt-io
```
---
## Verification
| `cargo fmt --all -- --check` | clean |
| `cargo clippy --all-targets --all-features -- -D warnings` | clean |
| `cargo test --all-features` | 64 unit + 1 smoke + 12 doctest — all passing |
| SHA-256 FIPS 180-4 B.1 / B.2 / empty | byte-exact match |
| SHA-512 FIPS 180-4 C.1 / C.2 / empty | byte-exact match |
| BLAKE3 empty + `"IETF"` | byte-exact match |
| Streaming-equals-one-shot (per algorithm) | verified at multiple chunk boundaries |
| MSRV (1.85) build | clean |
---
## Acknowledgements
`crypt-io` does not implement cryptographic primitives. The math
new in 0.4.0 comes from:
- [`blake3`](https://crates.io/crates/blake3) — official BLAKE3
implementation, including SIMD dispatch.
- [`sha2`](https://crates.io/crates/sha2) — RustCrypto's SHA-2
family, including SHA-NI / ARMv8 crypto-extension dispatch.
Carried forward from 0.3.0: the `chacha20poly1305`, `aes-gcm`, and
`mod-rand` stack.
---
## What's next
Phase 0.5.0: MAC. HMAC-SHA256, HMAC-SHA512, BLAKE3 keyed mode —
the authentication-tag surface that pairs with the hashing landing
here, with constant-time verification and known-answer tests from
RFC 4231.