Subject
block-buffer 0.12.0 is a small no_std crate that provides fixed-size buffers for block-oriented data processing. It exposes two flavours: EagerBuffer<BS> (yields a full block to the caller as soon as one is filled, position stays strictly below BS) and LazyBuffer<BS> (keeps the last full block in the buffer, position can equal BS), plus a read-side ReadBuffer<BS> used by XOFs. The whole point of the crate is to avoid the cost of zero-initialising the buffer on every reset — it stores its byte buffer as MaybeUninit<Array<u8, BS>> and tracks how much of it is initialised through a kind-specific cursor. Every RustCrypto hash and MAC crate depends on this primitive.
Methodology
The three source files (src/lib.rs, src/sealed.rs, src/read.rs, ~710 lines) and the integration test (tests/mod.rs, 346 lines) were read in full and compared against the upstream RustCrypto/utils workspace at the commit recorded in .cargo_vcs_info.json; sources match exactly. The auto-generated Cargo.toml was diffed against Cargo.toml.orig. Each of the roughly twenty unsafe blocks was traced against the buffer-kind invariant it relies on (the eager invariant strictly bounds the cursor below BS-1 so the user's data never overlaps the cursor byte; the lazy invariant stores the cursor separately as u8). The Eager::get_pos/set_pos pair — which encodes the cursor in the last byte of the otherwise MaybeUninit buffer — was followed through default, try_new, deserialize, and digest_blocks to confirm that set_pos always precedes get_pos. The CI workflow vcs-root/.github/workflows/block-buffer.yml was inspected: it exercises cargo test and cargo test --all-features on stable and MSRV plus a cargo build matrix over thumbv7em-none-eabi and wasm32-unknown-unknown. No miri, sanitizer, or fuzz step is configured.
Results
The published source files match the upstream tree at the recorded commit byte-for-byte; differences against VCS are limited to cargo's Cargo.toml normalisation, an auto-generated Cargo.lock, and packaging artefacts.
The crate declares build = false and ships no procedural macros, no install hooks, and no binary artefacts (justifying has-binaries, has-build-exec, has-install-exec). It is #![no_std], performs no I/O of any kind (justifying uses-filesystem, uses-network, uses-environment, uses-exec, uses-jit, uses-interpreter, uses-concurrency), implements no cryptographic algorithm itself (justifying uses-crypto, impl-crypto, impl-parser, impl-interpreter, impl-jit, impl-protocol, impl-algorithm, impl-concurrency) — the buffer simply ferries bytes between the caller and a downstream block-level compress function.
The crate's primary contribution is an in-place block buffer data structure (justifying impl-datastructure). The implementation uses unsafe to optimise the buffer-default path (avoiding zero-initialisation), to advance the cursor without bounds checks, and to call compress on a MaybeUninit::assume_init_ref reference once enough bytes have been written. Each block was traced against the documented kind invariant and found sound (justifying uses-unsafe, unsafe-safe, datastructure-impl-safe). The unsafe is used only where strictly necessary for the optimisation goal of the crate (justifying unsafe-minimal).
Two low-severity quality findings were recorded:
- FINDING-1: the crate carries a top-level
#![allow(clippy::undocumented_unsafe_blocks)] // TODO(tarcieri): document all unsafe blocks lint allow. Most blocks DO carry // SAFETY: comments, but a few (e.g. src/lib.rs:347 in BlockBuffer::deserialize) rely on contextual reasoning that is not recorded inline (justifying unsafe-documented = false).
- FINDING-2: CI runs only
cargo test and cargo test --all-features; there is no miri, sanitizer, or fuzz step (justifying has-fuzz-tests = false, has-property-tests = false). The integration tests in tests/mod.rs exercise the public API (eager/lazy digest_blocks, pad_with_zeros, digest_pad, len64_padding_*, len128_padding_*, try_new, serialize/deserialize with rejection of invalid positions and non-zero "garbage" bytes), which validates output bytes byte-for-byte but cannot detect UB in the unsafe interior (justifying datastructure-impl-tested for happy-path correctness, unsafe-tested = false for the missing adversarial coverage). The crate also has no in-source #[test] blocks beyond the integration test (justifying has-unit-tests = false).
The data structure satisfies its documented invariants. BlockBuffer::deserialize validates both the cursor position against the kind invariant and that any bytes beyond the cursor are zero, so neither serialize nor deserialize can be used to construct an in-memory representation that breaks the invariant from safe code (justifying datastructure-impl-correct, datastructure-impl-bounds).
No malicious behaviour, obfuscation, or supply-chain anomaly was observed (justifying is-benign).
Conclusion
block-buffer 0.12.0 is a small, performance-oriented unsafe-using data-structure crate that backs every RustCrypto hash and MAC. The unsafe code is concentrated, well-bounded, and sound under the documented invariants. Both findings are quality issues: a sweep to attach the remaining // SAFETY: comments and a miri job in CI would close the audit gap. No memory-safety incident was demonstrated; safe to use.