| 1 | //! This undocumented and unstable module is for use cases like the `bao` crate, |
| 2 | //! which need to traverse the BLAKE3 Merkle tree and work with chunk and parent |
| 3 | //! chaining values directly. There might be breaking changes to this module |
| 4 | //! between patch versions. |
| 5 | //! |
| 6 | //! We could stabilize something like this module in the future. If you have a |
| 7 | //! use case for it, please let us know by filing a GitHub issue. |
| 8 | |
| 9 | pub const BLOCK_LEN: usize = 64; |
| 10 | pub const CHUNK_LEN: usize = 1024; |
| 11 | |
| 12 | #[derive (Clone, Debug)] |
| 13 | pub struct ChunkState(crate::ChunkState); |
| 14 | |
| 15 | impl ChunkState { |
| 16 | // Currently this type only supports the regular hash mode. If an |
| 17 | // incremental user needs keyed_hash or derive_key, we can add that. |
| 18 | pub fn new(chunk_counter: u64) -> Self { |
| 19 | Self(crate::ChunkState::new( |
| 20 | crate::IV, |
| 21 | chunk_counter, |
| 22 | 0, |
| 23 | crate::platform::Platform::detect(), |
| 24 | )) |
| 25 | } |
| 26 | |
| 27 | #[inline ] |
| 28 | pub fn len(&self) -> usize { |
| 29 | self.0.len() |
| 30 | } |
| 31 | |
| 32 | #[inline ] |
| 33 | pub fn update(&mut self, input: &[u8]) -> &mut Self { |
| 34 | self.0.update(input); |
| 35 | self |
| 36 | } |
| 37 | |
| 38 | pub fn finalize(&self, is_root: bool) -> crate::Hash { |
| 39 | let output = self.0.output(); |
| 40 | if is_root { |
| 41 | output.root_hash() |
| 42 | } else { |
| 43 | output.chaining_value().into() |
| 44 | } |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | // As above, this currently assumes the regular hash mode. If an incremental |
| 49 | // user needs keyed_hash or derive_key, we can add that. |
| 50 | pub fn parent_cv( |
| 51 | left_child: &crate::Hash, |
| 52 | right_child: &crate::Hash, |
| 53 | is_root: bool, |
| 54 | ) -> crate::Hash { |
| 55 | let output: Output = crate::parent_node_output( |
| 56 | left_child.as_bytes(), |
| 57 | right_child.as_bytes(), |
| 58 | key:crate::IV, |
| 59 | flags:0, |
| 60 | crate::platform::Platform::detect(), |
| 61 | ); |
| 62 | if is_root { |
| 63 | output.root_hash() |
| 64 | } else { |
| 65 | output.chaining_value().into() |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | #[cfg (test)] |
| 70 | mod test { |
| 71 | use super::*; |
| 72 | |
| 73 | #[test ] |
| 74 | fn test_chunk() { |
| 75 | assert_eq!( |
| 76 | crate::hash(b"foo" ), |
| 77 | ChunkState::new(0).update(b"foo" ).finalize(true) |
| 78 | ); |
| 79 | } |
| 80 | |
| 81 | #[test ] |
| 82 | fn test_parents() { |
| 83 | let mut hasher = crate::Hasher::new(); |
| 84 | let mut buf = [0; crate::CHUNK_LEN]; |
| 85 | |
| 86 | buf[0] = 'a' as u8; |
| 87 | hasher.update(&buf); |
| 88 | let chunk0_cv = ChunkState::new(0).update(&buf).finalize(false); |
| 89 | |
| 90 | buf[0] = 'b' as u8; |
| 91 | hasher.update(&buf); |
| 92 | let chunk1_cv = ChunkState::new(1).update(&buf).finalize(false); |
| 93 | |
| 94 | hasher.update(b"c" ); |
| 95 | let chunk2_cv = ChunkState::new(2).update(b"c" ).finalize(false); |
| 96 | |
| 97 | let parent = parent_cv(&chunk0_cv, &chunk1_cv, false); |
| 98 | let root = parent_cv(&parent, &chunk2_cv, true); |
| 99 | assert_eq!(hasher.finalize(), root); |
| 100 | } |
| 101 | } |
| 102 | |