From dd6e7512326807ff3997ebcf4898cb29dc74be7d Mon Sep 17 00:00:00 2001 From: SymmetricChaos <42520289+SymmetricChaos@users.noreply.github.com> Date: Sun, 5 Jan 2025 00:17:46 -0500 Subject: [PATCH] balloon outline --- hashers/src/balloon.rs | 136 +++++++++++++++++++++++ hashers/src/ids/hasher_descriptions.json | 2 +- hashers/src/lib.rs | 3 +- hashers/src/sha/sha256.rs | 26 +++++ hashers/src/sha/sha512.rs | 26 +++++ 5 files changed, 191 insertions(+), 2 deletions(-) create mode 100644 hashers/src/balloon.rs diff --git a/hashers/src/balloon.rs b/hashers/src/balloon.rs new file mode 100644 index 00000000..4e989dc6 --- /dev/null +++ b/hashers/src/balloon.rs @@ -0,0 +1,136 @@ +// https://eprint.iacr.org/2016/027.pdf + +use std::ops::Range; + +use crate::{ + sha::{Sha2_256, Sha2_512}, + traits::StatefulHasher, +}; + +// pub enum BalloonVariant { +// Sha256, +// Sha512, +// } + +// impl BalloonVariant { +// pub fn blocksize(&self) -> usize { +// match self { +// BalloonVariant::Sha256 => 32, +// BalloonVariant::Sha512 => 64, +// } +// } + +// pub fn init(&self) -> Box { +// match self { +// BalloonVariant::Sha256 => Box::new(Sha2_256::init()), +// BalloonVariant::Sha512 => Box::new(Sha2_512::init()), +// } +// } +// } + +pub struct Balloon { + // variant: BalloonVariant, + m_cost: u64, + t_cost: u64, + salt: Vec, + buffer: Vec, +} + +impl Balloon { + const BLOCKSIZE: usize = 32; // for SHA256 + const DELTA: u64 = 3; + + fn extract_block(n: usize, blocks: &[u8]) -> &[u8] { + &blocks[n * Self::BLOCKSIZE..][..Self::BLOCKSIZE] + } + + fn extract_block_mut(n: usize, blocks: &mut [u8]) -> &mut [u8] { + &mut blocks[n * Self::BLOCKSIZE..][..Self::BLOCKSIZE] + } + + fn extract_range(n: usize) -> Range { + (n * Self::BLOCKSIZE)..(n * Self::BLOCKSIZE + Self::BLOCKSIZE) + } + + pub fn init(t_cost: u64, m_cost: u64, salt: &[u8]) -> Self { + Self { + // variant, + m_cost, + t_cost, + salt: salt.to_vec(), + buffer: Vec::new(), + } + } + + fn total_memory(&self) -> usize { + self.m_cost as usize * Self::BLOCKSIZE + } +} + +impl StatefulHasher for Balloon { + fn update(&mut self, bytes: &[u8]) { + self.buffer.extend_from_slice(bytes); + } + + fn finalize(self) -> Vec { + let mut ctr: u64 = 0; + let mut blocks: Vec = Vec::with_capacity(self.total_memory()); + // Step 1. Expand input into buffer + let mut h = Sha2_256::init(); + h.update(&ctr.to_le_bytes()); + ctr += 1; + h.update(&self.buffer); + h.update(&self.salt); + blocks.extend(h.finalize_and_reset()); + + for i in 1..self.m_cost as usize { + h.update(&ctr.to_le_bytes()); + ctr += 1; + h.update(&Self::extract_block(i, &blocks)); + blocks.extend(h.finalize_and_reset()); + } + + // Step 2. Mix buffer contents + for _t in 0..self.t_cost as usize { + for m in 0..self.m_cost as usize { + // Step 2a. Hash last and current blocks + let p = if m == 0 { + (self.m_cost - 1) as usize + } else { + m - 1 as usize + }; + h.update(&ctr.to_le_bytes()); + ctr += 1; + h.update(&blocks[Self::extract_range(p)]); + h.update(&blocks[Self::extract_range(m)]); + blocks[Self::extract_range(m)].copy_from_slice(&h.finalize_and_reset()); + + // Step 2b. Hash in pseudorandom blocks + for i in 0..Self::DELTA { + h.update(&self.t_cost.to_le_bytes()); + h.update(&(m as u64).to_le_bytes()); + h.update(&i.to_le_bytes()); + let idx_block = h.finalize_and_reset(); + + h.update(&ctr.to_le_bytes()); + ctr += 1; + h.update(&self.salt); + h.update(&idx_block); + let other = h.finalize_and_reset(); // convert to an integer + let other: usize = todo!(); + + h.update(&ctr.to_le_bytes()); + ctr += 1; + h.update(&blocks[Self::extract_range(m)]); + h.update(&blocks[Self::extract_range(other)]); + blocks[Self::extract_range(m)].copy_from_slice(&h.finalize_and_reset()); + } + } + } + + // Step 3. Extract output + blocks[Self::extract_range((self.m_cost - 1) as usize)].to_vec() + } + + crate::stateful_hash_helpers!(); +} diff --git a/hashers/src/ids/hasher_descriptions.json b/hashers/src/ids/hasher_descriptions.json index b368bcba..c7912a58 100644 --- a/hashers/src/ids/hasher_descriptions.json +++ b/hashers/src/ids/hasher_descriptions.json @@ -1,6 +1,6 @@ { "Adler-32": { - "Description": "Adler-32 is a very fast an simple hash, usually classified as a checksum. It produces a 32-bit hash by concatenating two 16-bit counters.", + "Description": "Adler-32 is a very fast a simple hash, usually classified as a checksum. It produces a 32-bit hash by concatenating two 16-bit counters.", "Authors": "Mark Adler", "Publication": "1995", "Hash Length": 32, diff --git a/hashers/src/lib.rs b/hashers/src/lib.rs index 11d35373..3c6b667a 100644 --- a/hashers/src/lib.rs +++ b/hashers/src/lib.rs @@ -2,6 +2,7 @@ pub mod adler; pub mod argon2; pub mod ascon; pub mod auxiliary; +pub mod balloon; pub mod bcrypt; pub mod belt; pub mod blake; @@ -29,6 +30,7 @@ pub mod pbkdf1; pub mod pbkdf2; pub mod pearson; pub mod poly1305; +pub mod poseidon; pub mod radio_gatun; pub mod ripemd; pub mod scrypt; @@ -40,7 +42,6 @@ pub mod sm3; pub mod tiger; pub mod traits; pub mod vsh; -pub mod poseidon; // Template // use utils::byte_formatting::ByteFormat; diff --git a/hashers/src/sha/sha256.rs b/hashers/src/sha/sha256.rs index 354ed367..f4261d77 100644 --- a/hashers/src/sha/sha256.rs +++ b/hashers/src/sha/sha256.rs @@ -112,6 +112,32 @@ macro_rules! sha2_256 { pub fn init() -> Self { Self::default() } + + pub fn finalize_and_reset(&mut self) -> Vec { + self.bits_taken += self.buffer.len() as u64 * 8; + self.buffer.push(0x80); + while (self.buffer.len() % 64) != 56 { + self.buffer.push(0x00) + } + self.buffer.extend(self.bits_taken.to_be_bytes()); + + // There can be multiple final blocks after padding + for chunk in self.buffer.chunks_exact(64) { + compress(&mut self.state, &chunk); + } + + let mut out = self + .state + .iter() + .map(|x| x.to_be_bytes()) + .flatten() + .collect_vec(); + out.truncate($output_len); + + *self = Self::init(); + + out + } } impl StatefulHasher for $name { diff --git a/hashers/src/sha/sha512.rs b/hashers/src/sha/sha512.rs index c50aec18..ddee1568 100644 --- a/hashers/src/sha/sha512.rs +++ b/hashers/src/sha/sha512.rs @@ -125,6 +125,32 @@ macro_rules! sha2_512 { pub fn init() -> Self { Self::default() } + + pub fn finalize_and_reset(&mut self) -> Vec { + self.bits_taken += self.buffer.len() as u128 * 8; + self.buffer.push(0x80); + while (self.buffer.len() % 128) != 112 { + self.buffer.push(0x00) + } + self.buffer.extend(self.bits_taken.to_be_bytes()); + + // There can be multiple final blocks after padding + for chunk in self.buffer.chunks_exact(128) { + compress(&mut self.state, &chunk); + } + + let mut out = self + .state + .iter() + .map(|x| x.to_be_bytes()) + .flatten() + .collect_vec(); + out.truncate($output_len); + + *self = Self::init(); + + out + } } impl StatefulHasher for $name {