cargo / blake2 / audit
cargo : blake2 @ 0.10.6
PE Patrick Elsen signed 2026-05-27 published 2026-05-27

Claims

crypto-impl-correctcrypto-impl-safecrypto-impl-testedcrypto-safehas-binarieshas-build-exechas-fuzz-testshas-install-exechas-integration-testshas-property-testshas-unit-testsimpl-algorithmimpl-concurrencyimpl-cryptoimpl-datastructureimpl-interpreterimpl-jitimpl-parserimpl-protocolis-benignunsafe-documentedunsafe-minimalunsafe-safeunsafe-testeduses-concurrencyuses-cryptouses-environmentuses-execuses-filesystemuses-interpreteruses-jituses-networkuses-unsafe

Summary

blake2 0.10.6 implements BLAKE2b and BLAKE2s per RFC 7693, including keyed MAC variants; two low-severity findings: unsafe blocks lack safety documentation, and MAC key material is not zeroized on drop.

Report

Subject

blake2 0.10.6 is the RustCrypto project's pure-Rust implementation of the BLAKE2 hash function family. It implements BLAKE2b (512-bit internal state, up to 64-byte output) and BLAKE2s (256-bit internal state, up to 32-byte output), including fixed-length, variable-length, and keyed MAC variants per RFC 7693. Public types include Blake2b512, Blake2s256, Blake2bVar, Blake2sVar, Blake2bMac, and Blake2sMac, all generic over output size and integrating with the RustCrypto digest framework. Optional features expose SIMD-accelerated compression and an assembly-level NEON path for 32-bit ARM. The single runtime dependency is digest 0.10.3.

Methodology

The audit used openvet 0.6.0 and diff. All 1304 lines of source in contents/src/ were read in full. The VCS checkout at vcs/ differs from the published crate only in Cargo.toml (cargo normalization); there are no source-file divergences. The vcs/ directory does not contain a .git directory because openvet clones only the tree for the tagged commit; the VCS integrity check relies on the byte-exact diff against contents. The SIGMA permutation table and IV constants in src/consts.rs were verified by inspection against RFC 7693 Appendix A and C. The binary test-vector files (blake2b/mac.blb, blake2s/mac.blb) derive from the official BLAKE2 test vectors. The digest::new_test! and digest::new_mac_test! macros run these vectors against the crate's output. No fuzzer or MIRI run was performed as part of this audit.

Results

The published crate is byte-for-byte identical to the tagged VCS tree (1f727ce37ff40fa0cce84eb8543a45bdd3ca4a4e, path blake2/) apart from the cargo-normalized Cargo.toml. No binary assets are present (has-binaries). There is no build script and no proc macro (has-build-exec, has-install-exec). The crate makes no network, filesystem, environment, or concurrency calls (uses-network, uses-filesystem, uses-environment, uses-concurrency, uses-exec, uses-jit, uses-interpreter, uses-concurrency).

The implementation is #![no_std] with an optional std feature. The compression function in src/macros.rs implements the BLAKE2 G mixing function with the SIGMA permutation table, 10 rounds for Blake2s and 12 rounds for Blake2b, and correct counter XOR at finalization, all matching RFC 7693. The parameter block construction in new_with_params correctly encodes key size, output size, salt, and persona in little-endian format. Endianness is handled by combining from_ne_bytes for message loading with Vector4::from_le() byte-swapping in quarter_round, which is correct on both architectures (a prior big-endian bug was fixed in 0.10.4). The MAC key block setup pads the key to a full block and submits it as the first message block, per RFC 7693 section 2.6. The integration tests run the official BLAKE2 test vectors for keyed and unkeyed modes, and two additional persona/salt test cases. Justifies impl-crypto, uses-crypto, crypto-impl-correct, crypto-impl-tested, has-unit-tests, has-integration-tests, has-fuzz-tests, has-property-tests, is-benign.

The crate uses unsafe in 14 places (uses-unsafe). All unsafe is confined to the SIMD acceleration path (src/simd/, only active under the nightly-only simd feature) and the AsBytes byte-reinterpretation utility (src/as_bytes.rs). The unsafe is narrowly scoped and not gratuitous (unsafe-minimal). However, none of the unsafe blocks carry // SAFETY: comments, and the Safe trait suppresses clippy's missing_safety_doc lint with an #[allow] attribute. The ARM NEON inline-assembly path in src/simd/simd_opt/u64x4.rs:110-134 has no documented ABI invariants. Because the invariants are not stated, unsafe-safe and unsafe-documented are both false. The test suite does not include MIRI or sanitizer runs for the unsafe code paths (unsafe-tested).

The MAC types Blake2bMac and Blake2sMac store the key in a LazyBuffer and, when the reset feature is enabled, in a separate key_block field. Neither field is zeroized on drop. This means key material persists in memory after the MAC object is dropped, a known gap for contexts where key confidentiality after deallocation matters. This is why crypto-impl-safe is false. The crate's direct use of the digest framework's MAC interface is otherwise correct (crypto-safe). No network, filesystem, or environment access occurs; the crate implements no concurrency primitives and no algorithms outside of the cryptographic hash (impl-algorithm, impl-datastructure, impl-concurrency, impl-parser, impl-interpreter, impl-jit, impl-protocol are all false).

Conclusion

blake2 0.10.6 implements BLAKE2b and BLAKE2s per RFC 7693, with keyed MAC, variable-output, and SIMD-accelerated variants. Two findings were recorded: a low-severity quality finding that the 14 unsafe blocks carry no // SAFETY: comments, and a low-severity security finding that MAC key material is not zeroized on drop. The SIGMA permutation table and IVs were verified against the RFC. The integration test suite covers keyed and unkeyed modes with official BLAKE2 test vectors.

Findings(2)

FINDING-1 quality low

Unsafe blocks lack safety documentation

The crate contains 14 unsafe blocks across src/as_bytes.rs, src/simd.rs, src/simd/simd_opt.rs, src/simd/simd_opt/u64x4.rs, and src/simd/simdop.rs. None carries a // SAFETY: comment documenting the invariant being relied upon.

Concretely:

  • src/as_bytes.rs:22-24 and src/as_bytes.rs:29-34: slice::from_raw_parts and slice::from_raw_parts_mut on self.as_ptr(). The invariants (alignment, non-null pointer, valid byte range) hold because T: Safe is restricted to integer primitives with no padding, and the pointer comes from a valid Rust slice. These invariants are implied by the Safe bound but are not stated in a comment. Additionally, the file has #[allow(clippy::missing_safety_doc)] on the Safe trait, which suppresses clippy's lint for the missing documentation.

  • src/simd/simdop.rs:22, 43, 64, 85: calls to simdint::simd_add, simd_xor, simd_shl, simd_shr. These are platform intrinsics valid for the #[repr(simd)] types they operate on. The invariants are not documented.

  • src/simd.rs:101, 115, 129: calls to simd_shuffle4 with constant index arrays. The shuffle indices are within bounds (0-3) for a 4-lane vector, but this is not stated.

  • src/simd/simd_opt.rs:12-23 (transmute_shuffle! macro): transmute between SIMD types of equal size. The size equality holds structurally but is not documented.

  • src/simd/simd_opt/u64x4.rs:115-132 (vext_u64 and rotate_right_vext): inline assembly using asm! on ARM/NEON. No safety comment documents the ABI constraints or register allocation correctness.

The absence of safety documentation makes it harder to verify correctness during future maintenance and means the current state cannot confirm invariants hold. Justifies unsafe-documented and unsafe-safe being false.

FINDING-2 security low

MAC key material not zeroized on drop

The MAC types Blake2bMac and Blake2sMac store the secret key in two locations in memory:

  1. The padded key block is written into a LazyBuffer (buffer field, src/macros.rs:298,338), which is block-sized (U128 for Blake2b, U64 for Blake2s).
  2. When the reset feature is enabled, the key is additionally stored verbatim in key_block: Key<Self> (src/macros.rs:300-303, 340-343), so that the MAC state can be re-initialised.

Neither location implements Drop with key erasure (zeroize). When the struct goes out of scope the key material remains in the heap or stack until the allocator reuses the backing memory. The digest dependency does not impose a ZeroizeOnDrop bound on these types.

For a MAC, the key is a secret: an adversary with access to process memory after drop (e.g., via a use-after-free in calling code, a core dump, or a cold-boot-style attack) can recover the key and forge MAC values for arbitrary messages.

The absence of zeroization is a known trade-off for the RustCrypto ecosystem: crates that require it can depend on the zeroize crate and opt in explicitly. However, the omission is not documented in the blake2 crate's API or README, and callers relying on key secrecy after the MAC object is dropped may not be aware of this behaviour. Justifies crypto-impl-safe being false.

Annotations(4)

src/as_bytes.rs

Defines the Safe marker trait (unsafe) and implements AsBytes via slice::from_raw_parts and slice::from_raw_parts_mut. The Safe bound is restricted to integer primitives (u8, u16, u32, u64, i8, i16, i32, i64) and SIMD wrappers of those, ensuring no padding bytes or invalid bit patterns. The slice::from_raw_parts call is sound: the pointer comes from an existing valid Rust slice, so it is non-null, aligned, and the byte count (len * size_of::<T>()) is within the original allocation. No // SAFETY: comments are present; clippy's missing_safety_doc lint is suppressed with #[allow]. Justifies uses-unsafe, unsafe-safe, unsafe-documented, unsafe-minimal.

src/consts.rs

Defines the BLAKE2b and BLAKE2s initialization vectors (IVs) and the 12-row SIGMA permutation table used in the compression function. The BLAKE2b IVs are the same as SHA-512 IVs (fractional parts of square roots of the first eight primes), and the BLAKE2s IVs are the SHA-256 IVs, as specified in RFC 7693. The SIGMA rows are the 10 message-schedule permutations from RFC 7693 plus two additional rows (repeating rows 0 and 1) used only in the 12-round Blake2b variant. Values match RFC 7693 Appendix A and C. Justifies crypto-impl-correct.

src/macros.rs

Core implementation macros. blake2_impl! expands into the hash state struct and trait impls for both Blake2b and Blake2s variants. The compress function implements the BLAKE2 G mixing function and 10/12 rounds with the SIGMA permutation table, matching RFC 7693. Message words are read with from_ne_bytes and then byte-swapped via Vector4::from_le() in quarter_round, which is correct on both little-endian and big-endian targets (the big-endian fix was introduced in 0.10.4). Finalization XORs the !0 flag into the counter register as specified. blake2_mac_impl! wraps the hash core with key-block initialization: the key is zero-padded to a full block and fed as the first message block per RFC 7693 section 2.6. No zeroize-on-drop is implemented for the key material. Justifies impl-crypto, crypto-impl-correct, crypto-impl-safe.

tests

Integration test suite. tests/mod.rs runs the digest crate's new_test! macro against binary test-vector files in tests/data/ for Blake2b-512 (fixed output), Blake2b (variable output), and Blake2s (variable output). tests/mac.rs tests Blake2bMac512 and Blake2sMac256 against binary test vectors covering keyed hashing, plus a manual round-trip test for new vs new_from_slice. tests/persona.rs includes two hardcoded test cases for salt and persona parameters. The binary test-vector files (blake2b/mac.blb, blake2s/mac.blb) contain 310 and 282 test cases respectively in the digest binary format; blake2b/fixed.blb and blake2b/variable.blb are present as binary data files. Justifies has-integration-tests, crypto-impl-correct, crypto-impl-tested.