From 697680399a373b4af4761c69acf16a2d795e0e93 Mon Sep 17 00:00:00 2001 From: Devin Smith Date: Mon, 6 Jan 2025 23:31:44 -0800 Subject: [PATCH] Add keyed and derive_key variants for guts --- src/guts.rs | 159 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 128 insertions(+), 31 deletions(-) diff --git a/src/guts.rs b/src/guts.rs index ecde32618..c3e8f67aa 100644 --- a/src/guts.rs +++ b/src/guts.rs @@ -9,19 +9,84 @@ pub const BLOCK_LEN: usize = 64; pub const CHUNK_LEN: usize = 1024; +#[derive(Clone, Copy, Debug)] +pub struct Guts { + cv: crate::CVWords, + flags: u8, + platform: crate::platform::Platform, +} + +impl Guts { + fn new_internal(key: &crate::CVWords, flags: u8) -> Self { + Self { + cv: *key, + flags, + platform: crate::platform::Platform::detect(), + } + } + + pub fn new() -> Self { + Self::new_internal(crate::IV, 0) + } + + pub fn new_keyed(key: &[u8; crate::KEY_LEN]) -> Self { + let key_words = crate::platform::words_from_le_bytes_32(key); + Self::new_internal(&key_words, crate::KEYED_HASH) + } + + pub fn new_derive_key(context: &str) -> Self { + let context_key = crate::hash_all_at_once::( + context.as_bytes(), + crate::IV, + crate::DERIVE_KEY_CONTEXT, + ) + .root_hash(); + let context_key_words = crate::platform::words_from_le_bytes_32(context_key.as_bytes()); + Self::new_internal(&context_key_words, crate::DERIVE_KEY_MATERIAL) + } + + pub fn chunk_state(&self, chunk_counter: u64) -> ChunkState { + ChunkState(crate::ChunkState::new( + &self.cv, + chunk_counter, + self.flags, + self.platform, + )) + } + + pub fn parent_cv( + &self, + left_child: &crate::Hash, + right_child: &crate::Hash, + is_root: bool, + ) -> crate::Hash { + let output = crate::parent_node_output( + left_child.as_bytes(), + right_child.as_bytes(), + &self.cv, + self.flags, + self.platform, + ); + if is_root { + output.root_hash() + } else { + output.chaining_value().into() + } + } +} + +impl Default for Guts { + fn default() -> Self { + Self::new() + } +} + #[derive(Clone, Debug)] pub struct ChunkState(crate::ChunkState); impl ChunkState { - // Currently this type only supports the regular hash mode. If an - // incremental user needs keyed_hash or derive_key, we can add that. pub fn new(chunk_counter: u64) -> Self { - Self(crate::ChunkState::new( - crate::IV, - chunk_counter, - 0, - crate::platform::Platform::detect(), - )) + Guts::new().chunk_state(chunk_counter) } #[inline] @@ -45,25 +110,12 @@ impl ChunkState { } } -// As above, this currently assumes the regular hash mode. If an incremental -// user needs keyed_hash or derive_key, we can add that. pub fn parent_cv( left_child: &crate::Hash, right_child: &crate::Hash, is_root: bool, ) -> crate::Hash { - let output = crate::parent_node_output( - left_child.as_bytes(), - right_child.as_bytes(), - crate::IV, - 0, - crate::platform::Platform::detect(), - ); - if is_root { - output.root_hash() - } else { - output.chaining_value().into() - } + Guts::new().parent_cv(left_child, right_child, is_root) } #[cfg(test)] @@ -72,30 +124,75 @@ mod test { #[test] fn test_chunk() { + let input = b"foo"; assert_eq!( - crate::hash(b"foo"), - ChunkState::new(0).update(b"foo").finalize(true) + crate::hash(input), + Guts::new().chunk_state(0).update(input).finalize(true) + ); + } + + #[test] + fn test_keyed_chunk() { + let key = &[42u8; crate::KEY_LEN]; + let input = b"foo"; + assert_eq!( + crate::keyed_hash(&key, input), + Guts::new_keyed(&key) + .chunk_state(0) + .update(input) + .finalize(true) + ); + } + + #[test] + fn test_derive_key() { + let context = "bar"; + let key_material = b"key material, not a password"; + assert_eq!( + crate::derive_key(&context, key_material), + Guts::new_derive_key(&context) + .chunk_state(0) + .update(key_material) + .finalize(true) + .0 ); } #[test] fn test_parents() { - let mut hasher = crate::Hasher::new(); - let mut buf = [0; crate::CHUNK_LEN]; + do_updates(&Guts::new(), &mut crate::Hasher::new()); + } + + #[test] + fn test_keyed_parents() { + let key = &[42u8; crate::KEY_LEN]; + do_updates(&Guts::new_keyed(key), &mut crate::Hasher::new_keyed(key)); + } + + #[test] + fn test_derive_key_parents() { + let context = "bar"; + do_updates( + &Guts::new_derive_key(&context), + &mut crate::Hasher::new_derive_key(&context), + ); + } + fn do_updates(guts: &Guts, hasher: &mut crate::Hasher) { + let mut buf = [0; CHUNK_LEN]; buf[0] = 'a' as u8; hasher.update(&buf); - let chunk0_cv = ChunkState::new(0).update(&buf).finalize(false); + let chunk0_cv = guts.chunk_state(0).update(&buf).finalize(false); buf[0] = 'b' as u8; hasher.update(&buf); - let chunk1_cv = ChunkState::new(1).update(&buf).finalize(false); + let chunk1_cv = guts.chunk_state(1).update(&buf).finalize(false); hasher.update(b"c"); - let chunk2_cv = ChunkState::new(2).update(b"c").finalize(false); + let chunk2_cv = guts.chunk_state(2).update(b"c").finalize(false); - let parent = parent_cv(&chunk0_cv, &chunk1_cv, false); - let root = parent_cv(&parent, &chunk2_cv, true); + let parent = guts.parent_cv(&chunk0_cv, &chunk1_cv, false); + let root = guts.parent_cv(&parent, &chunk2_cv, true); assert_eq!(hasher.finalize(), root); } }