From 55b05960a1d642d1f3eb582cf7ee3fbb5afb81e1 Mon Sep 17 00:00:00 2001 From: Bao Trinh Date: Mon, 3 Jul 2023 05:59:12 -0500 Subject: [PATCH] tiger: implement TTH hash algorithm --- tiger/src/lib.rs | 21 ++++--- tiger/src/tth.rs | 137 +++++++++++++++++++++++++++++++++++++++++++++ tiger/tests/mod.rs | 57 ++++++++++++++++++- 3 files changed, 205 insertions(+), 10 deletions(-) create mode 100644 tiger/src/tth.rs diff --git a/tiger/src/lib.rs b/tiger/src/lib.rs index a5f1f8d80..97c84cc80 100644 --- a/tiger/src/lib.rs +++ b/tiger/src/lib.rs @@ -33,6 +33,8 @@ #![forbid(unsafe_code)] #![warn(missing_docs, rust_2018_idioms)] +extern crate alloc; + pub use digest::{self, Digest}; use core::fmt; @@ -50,6 +52,9 @@ mod compress; mod tables; use compress::compress; +mod tth; +use tth::TigerTreeCore; + type State = [u64; 3]; const S0: State = [ 0x0123_4567_89AB_CDEF, @@ -91,7 +96,7 @@ impl UpdateCore for TigerCore { impl FixedOutputCore for TigerCore { #[inline] fn finalize_fixed_core(&mut self, buffer: &mut Buffer, out: &mut Output) { - let bs = Self::BlockSize::U64 as u64; + let bs = Self::BlockSize::U64; let pos = buffer.get_pos() as u64; let bit_len = 8 * (pos + bs * self.block_len); @@ -165,7 +170,7 @@ impl UpdateCore for Tiger2Core { impl FixedOutputCore for Tiger2Core { #[inline] fn finalize_fixed_core(&mut self, buffer: &mut Buffer, out: &mut Output) { - let bs = Self::BlockSize::U64 as u64; + let bs = Self::BlockSize::U64; let pos = buffer.get_pos() as u64; let bit_len = 8 * (pos + bs * self.block_len); @@ -180,11 +185,7 @@ impl Default for Tiger2Core { fn default() -> Self { Self { block_len: 0, - state: [ - 0x0123_4567_89AB_CDEF, - 0xFEDC_BA98_7654_3210, - 0xF096_A5B4_C3B2_E187, - ], + state: S0, } } } @@ -208,7 +209,9 @@ impl fmt::Debug for Tiger2Core { } } -/// Tiger hasher state. +/// Tiger hasher. pub type Tiger = CoreWrapper; -/// Tiger2 hasher state. +/// Tiger2 hasher. pub type Tiger2 = CoreWrapper; +/// TTH hasher. +pub type TigerTree = CoreWrapper; diff --git a/tiger/src/tth.rs b/tiger/src/tth.rs new file mode 100644 index 000000000..5805454c7 --- /dev/null +++ b/tiger/src/tth.rs @@ -0,0 +1,137 @@ +use crate::{Digest, Tiger, TigerCore}; +use alloc::vec::Vec; +use core::fmt; +use digest::{ + core_api::{ + AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, FixedOutputCore, + OutputSizeUser, Reset, UpdateCore, + }, + typenum::Unsigned, + typenum::U1024, + HashMarker, Output, +}; + +/// Core Tiger hasher state. +#[derive(Clone)] +pub struct TigerTreeCore { + leaves: Vec>, + hasher: Tiger, + blocks_processed: usize, +} + +impl Default for TigerTreeCore { + fn default() -> Self { + Self { + leaves: Vec::default(), + hasher: Tiger::new_with_prefix([LEAF_SIG]), + blocks_processed: 0, + } + } +} + +type DataBlockSize = U1024; +const LEAF_SIG: u8 = 0u8; +const NODE_SIG: u8 = 1u8; +/// The number of TigerCore blocks in a TigerTree data block +const LEAF_BLOCKS: usize = DataBlockSize::USIZE / ::BlockSize::USIZE; + +impl HashMarker for TigerTreeCore {} + +impl BlockSizeUser for TigerTreeCore { + type BlockSize = ::BlockSize; +} + +impl BufferKindUser for TigerTreeCore { + type BufferKind = ::BufferKind; +} + +impl OutputSizeUser for TigerTreeCore { + type OutputSize = ::OutputSize; +} + +impl TigerTreeCore { + #[inline] + fn finalize_blocks(&mut self) { + let hasher = core::mem::replace(&mut self.hasher, Tiger::new_with_prefix([LEAF_SIG])); + let hash = hasher.finalize(); + self.leaves.push(hash); + self.blocks_processed = 0; + } + + #[inline] + fn update_block(&mut self, block: Block) { + self.hasher.update(block); + self.blocks_processed += 1; + if self.blocks_processed == LEAF_BLOCKS { + self.finalize_blocks(); + } + } +} + +impl UpdateCore for TigerTreeCore { + #[inline] + fn update_blocks(&mut self, blocks: &[Block]) { + for block in blocks { + self.update_block(*block); + } + } +} + +impl FixedOutputCore for TigerTreeCore { + #[inline] + fn finalize_fixed_core(&mut self, buffer: &mut Buffer, out: &mut Output) { + if buffer.get_pos() > 0 { + self.hasher.update(buffer.get_data()); + self.blocks_processed += 1; + } + + if self.blocks_processed > 0 { + self.finalize_blocks() + } + + let result = hash_nodes(self.leaves.as_slice()); + out.copy_from_slice(&result); + } +} + +#[inline] +fn hash_nodes(hashes: &[Output]) -> Output { + match hashes.len() { + 0 => hash_nodes(&[Tiger::digest([LEAF_SIG])]), + 1 => hashes[0], + _ => { + let left_hashes = hashes.iter().step_by(2); + + let right_hashes = hashes.iter().map(Some).skip(1).chain([None]).step_by(2); + + let next_level_hashes: Vec> = left_hashes + .zip(right_hashes) + .map(|(left, right)| match right { + Some(right) => Tiger::digest([&[NODE_SIG][..], left, right].concat()), + None => *left, + }) + .collect(); + + hash_nodes(next_level_hashes.as_slice()) + } + } +} + +impl Reset for TigerTreeCore { + #[inline] + fn reset(&mut self) { + *self = Default::default(); + } +} + +impl AlgorithmName for TigerTreeCore { + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("TigerTree") + } +} + +impl fmt::Debug for TigerTreeCore { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("TigerTreeCore { ... }") + } +} diff --git a/tiger/tests/mod.rs b/tiger/tests/mod.rs index 09e78d6c3..a903c38b1 100644 --- a/tiger/tests/mod.rs +++ b/tiger/tests/mod.rs @@ -1,7 +1,7 @@ use digest::dev::{feed_rand_16mib, fixed_reset_test}; use digest::new_test; use hex_literal::hex; -use tiger::{Digest, Tiger, Tiger2}; +use tiger::{Digest, Tiger, Tiger2, TigerTree}; new_test!(tiger, "tiger", tiger::Tiger, fixed_reset_test); new_test!(tiger2, "tiger2", tiger::Tiger2, fixed_reset_test); @@ -25,3 +25,58 @@ fn tiger2_rand() { hex!("1bb7a80144c97f831fdefb635477776dd6c164048ce5895d")[..] ); } + +#[test] +fn tiger_empty() { + let mut h = Tiger::new(); + h.update(b""); + assert_eq!( + h.finalize()[..], + hex!("3293ac630c13f0245f92bbb1766e16167a4e58492dde73f3")[..] + ); +} + +#[test] +fn tth_empty() { + let mut h = TigerTree::new(); + h.update(b""); + assert_eq!( + h.finalize()[..], + hex!("5d9ed00a030e638bdb753a6a24fb900e5a63b8e73e6c25b6")[..] + ); +} + +#[test] +fn tth_one_block() { + let mut h = TigerTree::new(); + let content = hex!("deadbeef"); + h.update(content); + assert_eq!( + h.finalize()[..], + hex!("f527ab1ddbc4cced8572e0cf7a968604ebccb9414eb438dc")[..] + ); +} + +#[test] +fn tth_two_blocks() { + let content = hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); + assert!((1025..2049).contains(&content.len())); + let mut h = TigerTree::new(); + h.update(content); + assert_eq!( + h.finalize()[..], + hex!("db67d9e452c3af6c329fb664737bc2378aa364b74c3468bb")[..] + ); +} + +#[test] +fn tth_three_blocks() { + let content = hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef8badf00d"); + assert!((2049..3073).contains(&content.len())); + let mut h = TigerTree::new(); + h.update(content); + assert_eq!( + h.finalize()[..], + hex!("f3a63c92019cffb7527e9adfa57a1ecb783728f67b06fed0")[..] + ); +}