Skip to content

Commit

Permalink
tiger: implement TTH hash algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
baodrate committed Jul 4, 2023
1 parent 7287da1 commit 55b0596
Show file tree
Hide file tree
Showing 3 changed files with 205 additions and 10 deletions.
21 changes: 12 additions & 9 deletions tiger/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
#![forbid(unsafe_code)]
#![warn(missing_docs, rust_2018_idioms)]

extern crate alloc;

pub use digest::{self, Digest};

use core::fmt;
Expand All @@ -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,
Expand Down Expand Up @@ -91,7 +96,7 @@ impl UpdateCore for TigerCore {
impl FixedOutputCore for TigerCore {
#[inline]
fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
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);

Expand Down Expand Up @@ -165,7 +170,7 @@ impl UpdateCore for Tiger2Core {
impl FixedOutputCore for Tiger2Core {
#[inline]
fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
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);

Expand All @@ -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,
}
}
}
Expand All @@ -208,7 +209,9 @@ impl fmt::Debug for Tiger2Core {
}
}

/// Tiger hasher state.
/// Tiger hasher.
pub type Tiger = CoreWrapper<TigerCore>;
/// Tiger2 hasher state.
/// Tiger2 hasher.
pub type Tiger2 = CoreWrapper<Tiger2Core>;
/// TTH hasher.
pub type TigerTree = CoreWrapper<TigerTreeCore>;
137 changes: 137 additions & 0 deletions tiger/src/tth.rs
Original file line number Diff line number Diff line change
@@ -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<Output<TigerCore>>,
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 / <TigerCore as BlockSizeUser>::BlockSize::USIZE;

impl HashMarker for TigerTreeCore {}

impl BlockSizeUser for TigerTreeCore {
type BlockSize = <TigerCore as BlockSizeUser>::BlockSize;
}

impl BufferKindUser for TigerTreeCore {
type BufferKind = <TigerCore as BufferKindUser>::BufferKind;
}

impl OutputSizeUser for TigerTreeCore {
type OutputSize = <TigerCore as OutputSizeUser>::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>) {
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<Self>]) {
for block in blocks {
self.update_block(*block);
}
}
}

impl FixedOutputCore for TigerTreeCore {
#[inline]
fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
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<TigerCore>]) -> Output<TigerCore> {
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<Output<TigerCore>> = 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 { ... }")
}
}
57 changes: 56 additions & 1 deletion tiger/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -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);
Expand All @@ -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")[..]
);
}

0 comments on commit 55b0596

Please sign in to comment.