What changed, and why it matters
This commit only adds new test code. It does not change any production hashing logic, fix a bug, or introduce a vulnerability. The new tests verify that feeding data to hash engines in three chunks of varying sizes produces the same result as hashing the same data all at once, which helps catch streaming bugs in the future but is not itself a security change.
No security action required. This is a test-only addition. Reviewers may optionally confirm the tests pass in release mode and that the block sizes listed match each hash algorithm's specification.
Security signals we found
No strong security signals were identified.
Evidence from the diff
Commit 8e76922 adds hashes/tests/chunk_combinations.rs, a new Rust test file that performs combinatorial chunking tests for several hash algorithms (SHA1, SHA256, SHA3-256, SHA384, SHA512, SHA512_256, RIPEMD160). For each algorithm with block size B, it generates a deterministic 3*(B+1)-byte input and iterates all (i, j, k) in 0..=B, feeding the input in three engine.input() calls and comparing the result to a one-shot hash. The tests are gated with #[cfg(not(debug_assertions))] so they run only in release mode due to O(B^3) cost. No production code is modified.
Changed components
hashes/tests/chunk_combinations.rsInspect captured patch +56 / −0
diff --git a/hashes/tests/chunk_combinations.rs b/hashes/tests/chunk_combinations.rs
new file mode 100644
index 00000000..f002ad63
--- /dev/null
+++ b/hashes/tests/chunk_combinations.rs
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: CC0-1.0
+
+//! For a hash with block size `B`, this checks all `(i, j, k)` in `0..=B` and
+//! verifies that feeding the same bytes via three `engine.input()` calls
+//! matches one-shot hashing.
+//!
+//! This catches bugs that byte-by-byte incremetal tests in `nist_cavp.rs` don't catch.
+//! especially block-boundary transitions, empty chunks or buffering bugs.
+//!
+//! Inspired by `ring` `test_i_u_f` tests:
+//! <https://github.com/briansmith/ring/commit/5daff2c0e1bb8ef00e44e15b0531dda0b69d0ec5>
+//!
+//! These tests are slow, so they only run in release mode
+
+#[cfg(not(debug_assertions))]
+use bitcoin_hashes::HashEngine as _;
+
+macro_rules! chunk_combination_test {
+ ($test_name:ident, $hash_type:ty, $block_size:expr) => {
+ #[cfg(not(debug_assertions))]
+ #[test]
+ fn $test_name() {
+ let max = $block_size + 1;
+ let input: Vec<u8> = (0..max * 3).map(|i| (i & 0xff) as u8).collect();
+
+ for i in 0..max {
+ for j in 0..max {
+ for k in 0..max {
+ let total = i + j + k;
+ let part1 = &input[..i];
+ let part2 = &input[i..i + j];
+ let part3 = &input[i + j..total];
+
+ let mut engine = <$hash_type>::engine();
+ engine.input(part1);
+ engine.input(part2);
+ engine.input(part3);
+ let chunked = bitcoin_hashes::HashEngine::finalize(engine);
+
+ let oneshot = <$hash_type>::hash(&input[..total]);
+
+ assert_eq!(chunked.to_byte_array(), oneshot.to_byte_array());
+ }
+ }
+ }
+ }
+ };
+}
+
+chunk_combination_test!(sha1, bitcoin_hashes::sha1::Hash, 64);
+chunk_combination_test!(sha256, bitcoin_hashes::sha256::Hash, 64);
+chunk_combination_test!(sha3_256, bitcoin_hashes::sha3_256::Hash, 136);
+chunk_combination_test!(sha384, bitcoin_hashes::sha384::Hash, 128);
+chunk_combination_test!(sha512, bitcoin_hashes::sha512::Hash, 128);
+chunk_combination_test!(sha512_256, bitcoin_hashes::sha512_256::Hash, 128);
+chunk_combination_test!(ripemd160, bitcoin_hashes::ripemd160::Hash, 64);
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.