rust-hmac-sha256
A small, self-contained SHA-256, HMAC-SHA256, and HKDF-SHA256 implementation in pure Rust with no_std support.
Repository Health
Technical Analysis
hmac-sha256 is a minimal, dependency-free Rust crate that implements SHA-256, HMAC-SHA256, and HKDF-SHA256 from scratch in a single source file. It targets no_std environments — embedded systems, WASM targets, and other constrained runtimes — where pulling in the broader RustCrypto dependency tree isn’t practical. Both one-shot and streaming APIs are provided for computing hashes and MACs, along with constant-time verification helpers built specifically to resist timing-attack based key or data recovery.
The crate optionally implements the Digest trait from the digest crate (supporting versions 0.9.0, 0.10.7, and 0.11.0 simultaneously via separate feature flags) so it can be used as a drop-in hasher wherever the RustCrypto trait ecosystem is expected, while staying dependency-free by default. An opt_size feature further shrinks the compiled .text section by roughly 75% at a documented ~16% runtime cost, aimed at size-constrained embedded targets.
What You Get
- One-shot and streaming APIs for SHA-256 hashing (
Hash::hash,Hash::new/update/finalize) - One-shot and streaming HMAC-SHA256 computation (
HMAC::mac,HMAC::new/update/finalize) - RFC 5869 HKDF-SHA256 key derivation via
HKDF::extractandHKDF::expand - Constant-time verification helpers (
finalize_verify,verify) to prevent timing side-channels - Optional
Digesttrait implementations fordigest0.9, 0.10, and 0.11 behind feature flags - An
opt_sizefeature for reducing binary size on embedded targets
Common Use Cases
- Computing SHA-256 checksums or HMAC-SHA256 signatures in no_std embedded firmware
- Verifying webhook signatures or signed API requests in size-sensitive WASM builds
- Deriving session or encryption keys from a shared secret via HKDF-SHA256
- Providing a lightweight Digest-trait-compatible hasher for crates that expect the RustCrypto interface without pulling in the full sha2 dependency tree
Under The Hood
Architecture
The crate is organized as a single lib.rs implementing a layered Merkle–Damgård construction: low-level State/W structs perform 64-byte block compression, the public Hash struct wraps that state machine with a streaming update/finalize API, HMAC composes two Hash instances around inner/outer key padding per the standard HMAC construction, and HKDF is built entirely on top of HMAC::mac for its extract/expand steps. Three feature-gated submodules (digest_trait09, digest_trait010, digest_trait011) each implement the RustCrypto Digest trait family against the same underlying Hash type, letting one implementation serve three incompatible major versions of that ecosystem’s trait simultaneously without duplicating core logic.
Tech Stack
Built against the 2021 Rust edition with #![no_std] and zero mandatory dependencies. The only dependencies (digest09, digest010, digest011, all optional and each pinned to one digest crate release) are gated behind matching traits09/traits010/traits011 feature flags for RustCrypto interop; the default build pulls in nothing external. There is no build script, no async runtime, and no I/O — the entire crate is pure computation over byte slices, published to crates.io as hmac-sha256.
Code Quality
A single #[test] fn main() embedded in lib.rs exercises HMAC, HKDF, and verification paths against hardcoded known-answer test vectors; a separate tests/test_digest011.rs file covers the Digest trait implementation (chaining, reset, one-shot digest). There are no typed errors — HKDF::expand panics via assert! if the requested output exceeds 255×32 bytes, which is standard for a low-level no_std primitive but does mean misuse surfaces as a panic rather than a Result. No CI workflow is present in the repository (only a Dependabot config for dependency updates), so tests are not automatically run on every change; naming is consistent and inline doc comments are used throughout the public API, with #![allow(...)] clippy lint overrides suggesting clippy is run informally during development.
API Design
The public surface is intentionally narrow and consistent: Hash, HMAC, and HKDF each expose a matching one-shot convenience function alongside an incremental new/update/finalize pair, so callers can pick whichever shape fits their control flow without learning different idioms per primitive. Every public method carries a doc comment with a runnable example, and impl AsRef<[u8]> parameters let callers pass byte slices, arrays, or Vec<u8> interchangeably without explicit conversions. The tradeoff for that simplicity is standard-spec functionality only — SHA-256/HMAC-SHA256/HKDF-SHA256 exactly as defined by NIST and RFC 5869, with no algorithm agility, but a genuinely low-boilerplate onramp for the one job it does.