diff --git a/Cargo.lock b/Cargo.lock index 78a17c4c4..9fda127e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "blake2" version = "0.10.6" @@ -276,6 +282,7 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" name = "tiger" version = "0.2.1" dependencies = [ + "arrayvec", "digest", "hex-literal", ] diff --git a/tiger/Cargo.toml b/tiger/Cargo.toml index a8f59a4c7..c2531cb1c 100644 --- a/tiger/Cargo.toml +++ b/tiger/Cargo.toml @@ -12,6 +12,7 @@ keywords = ["crypto", "hash", "tiger", "digest"] categories = ["cryptography", "no-std"] [dependencies] +arrayvec = { version = "0.7.4", default-features = false, optional = true } digest = "0.10.7" [dev-dependencies] @@ -19,5 +20,6 @@ digest = { version = "0.10.7", features = ["dev"] } hex-literal = "0.2.2" [features] -default = ["std"] -std = ["digest/std"] +default = ["std", "tth"] +std = ["digest/std", "arrayvec/std"] +tth = ["arrayvec"] diff --git a/tiger/src/lib.rs b/tiger/src/lib.rs index a5f1f8d80..7f50c9709 100644 --- a/tiger/src/lib.rs +++ b/tiger/src/lib.rs @@ -50,6 +50,11 @@ mod compress; mod tables; use compress::compress; +#[cfg(feature = "tth")] +mod tth; +#[cfg(feature = "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,10 @@ 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. +#[cfg(feature = "tth")] +pub type TigerTree = CoreWrapper; diff --git a/tiger/src/tth.rs b/tiger/src/tth.rs new file mode 100644 index 000000000..6e19208e8 --- /dev/null +++ b/tiger/src/tth.rs @@ -0,0 +1,143 @@ +use crate::{Digest, Tiger, TigerCore}; +use arrayvec::ArrayVec; +use core::fmt; +use digest::{ + core_api::{ + AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, FixedOutputCore, + OutputSizeUser, Reset, UpdateCore, + }, + typenum::Unsigned, + typenum::U1024, + HashMarker, Output, +}; + +#[derive(Clone)] +struct TigerTreeNode { + level: u32, + hash: Output, +} + +/// Core Tiger hasher state. +#[derive(Clone)] +pub struct TigerTreeCore { + nodes: ArrayVec, + hasher: Tiger, + blocks_processed: usize, +} + +impl TigerTreeCore { + fn add_node(&mut self, level: u32, hash: Output) { + match self.nodes.last() { + Some(last) if last.level == level => { + let new_hash = Tiger::digest([&[NODE_SIG][..], &last.hash, &hash].concat()); + self.nodes.pop(); + self.add_node(level + 1, new_hash); + } + _ => self.nodes.push(TigerTreeNode { level, hash }), + } + } +} + +impl Default for TigerTreeCore { + fn default() -> Self { + Self { + nodes: ArrayVec::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 maximum height of a TigerTree based on the maximum addressable data length +// max height = log2(usize::MAX / DataBlockSize) = log2(2^usize::BITS) - log2(DataBlockSize) = usize::BITS - log2(2^10) +const MAX_TREE_HEIGHT: usize = (usize::BITS - 10 + 1) as usize; +/// 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.add_node(0, 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 mut hash = self + .nodes + .pop() + .map(|n| n.hash) + .unwrap_or_else(|| Tiger::digest([LEAF_SIG])); + while let Some(left) = self.nodes.pop() { + hash = Tiger::digest([&[NODE_SIG][..], &left.hash, &hash].concat()); + } + out.copy_from_slice(hash.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")[..] + ); +}