From df0c4a0c797a85141d8ffae8b6c950e4e9835cb4 Mon Sep 17 00:00:00 2001 From: David Senk Date: Tue, 19 Aug 2025 00:07:35 -0400 Subject: [PATCH] working multi hash --- src/hashes.rs | 197 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 196 insertions(+), 1 deletion(-) diff --git a/src/hashes.rs b/src/hashes.rs index 4466cd3..9588cfc 100644 --- a/src/hashes.rs +++ b/src/hashes.rs @@ -71,4 +71,199 @@ pub trait Hasher { fn complete(self) -> HashReturn; } -pub struct Hashes {} +#[derive(Debug, Copy, Clone)] +pub enum HashType { + CRC32(crc32::CRC32TYPE), + SHA256, + SHA3_256, + BLAKE256, + BLAKE512, + BLAKE3, + SHA1, + MD5, + Null, +} + +// Why? because static dispatch is why! +// TODO: Make the concrete types return this too +// Maybe even a macro that does the below boilerplate? idk +enum HasherHolder { + CRC32(crc32::CRC32), + SHA256(sha256::SHA256), + SHA3_256(sha3_256::SHA3_256), + BLAKE256(blake256::BLAKE256), + BLAKE512(blake512::BLAKE512), + BLAKE3(blake3::BLAKE3), + SHA1(sha1::SHA1), + MD5(md5::MD5), + NULL(null_hash::NullHash), +} + +pub struct Hashes { + hashers: Vec, +} + +impl Hashes { + pub fn new(types: &[HashType]) -> Self { + let mut hashers = Vec::with_capacity(types.len()); + for t in types { + match t { + HashType::CRC32(t) => match t { + crc32::CRC32TYPE::ISO => hashers.push(HasherHolder::CRC32(crc32::crc32_iso())), + crc32::CRC32TYPE::CKSUM => { + hashers.push(HasherHolder::CRC32(crc32::crc32_cksum())) + } + crc32::CRC32TYPE::XFER => hashers.push(HasherHolder::CRC32(crc32_xfer())), + }, + HashType::SHA256 => hashers.push(HasherHolder::SHA256(sha256::SHA256::new())), + HashType::SHA3_256 => { + hashers.push(HasherHolder::SHA3_256(sha3_256::SHA3_256::new())) + } + HashType::BLAKE256 => { + hashers.push(HasherHolder::BLAKE256(blake256::BLAKE256::new())) + } + HashType::BLAKE512 => { + hashers.push(HasherHolder::BLAKE512(blake512::BLAKE512::new())) + } + HashType::BLAKE3 => hashers.push(HasherHolder::BLAKE3(blake3::BLAKE3::new())), + HashType::SHA1 => hashers.push(HasherHolder::SHA1(sha1::SHA1::new())), + HashType::MD5 => hashers.push(HasherHolder::MD5(md5::MD5::new())), + HashType::Null => hashers.push(HasherHolder::NULL(null_hash::NullHash::new())), + } + } + + Hashes { hashers } + } + + pub fn digest(&mut self, bytes: impl AsRef<[u8]>) { + for hasher in &mut self.hashers { + match hasher { + HasherHolder::CRC32(hasher) => hasher.digest(&bytes), + HasherHolder::SHA256(hasher) => hasher.digest(&bytes), + HasherHolder::SHA3_256(hasher) => hasher.digest(&bytes), + HasherHolder::BLAKE256(hasher) => hasher.digest(&bytes), + HasherHolder::BLAKE512(hasher) => hasher.digest(&bytes), + HasherHolder::BLAKE3(hasher) => hasher.digest(&bytes), + HasherHolder::SHA1(hasher) => hasher.digest(&bytes), + HasherHolder::MD5(hasher) => hasher.digest(&bytes), + HasherHolder::NULL(hasher) => hasher.digest(&bytes), + } + } + } + + pub fn complete(mut self) -> Vec { + let mut ret = Vec::with_capacity(self.hashers.len()); + + for hasher in self.hashers { + match hasher { + HasherHolder::CRC32(hasher) => ret.push(hasher.complete()), + HasherHolder::SHA256(hasher) => ret.push(hasher.complete()), + HasherHolder::SHA3_256(hasher) => ret.push(hasher.complete()), + HasherHolder::BLAKE256(hasher) => ret.push(hasher.complete()), + HasherHolder::BLAKE512(hasher) => ret.push(hasher.complete()), + HasherHolder::BLAKE3(hasher) => ret.push(hasher.complete()), + HasherHolder::SHA1(hasher) => ret.push(hasher.complete()), + HasherHolder::MD5(hasher) => ret.push(hasher.complete()), + HasherHolder::NULL(hasher) => ret.push(hasher.complete()), + } + } + + ret + } +} + +#[cfg(test)] +mod test { + use crate::hashes::crc32::CRC32TYPE; + use crate::hashes::{HashReturn, HashType, Hashes}; + + #[test] + fn test_multi_hash() { + let hash_types = [ + HashType::CRC32(CRC32TYPE::CKSUM), + HashType::CRC32(CRC32TYPE::ISO), + HashType::CRC32(CRC32TYPE::XFER), + HashType::SHA256, + HashType::SHA3_256, + HashType::BLAKE256, + HashType::BLAKE512, + HashType::BLAKE3, + HashType::SHA1, + HashType::MD5, + //HashType::Null, + ]; + let mut hashes = Hashes::new(&hash_types); + + hashes.digest("HashStream2025"); + + let ret = hashes.complete(); + + // Not using .into_bytes() to properly check each hash type + for hash in ret { + match hash { + HashReturn::CRC32(hash, t) => { + let hash_out = + crate::hex_table::u8_array_to_lower_hex_string(&hash.to_be_bytes()) + .unwrap(); + match t { + CRC32TYPE::CKSUM => { + assert_eq!(hash_out, "5f4f456d"); + } + CRC32TYPE::XFER => { + assert_eq!(hash_out, "99e560da"); + } + CRC32TYPE::ISO => { + assert_eq!(hash_out, "66eac774"); + } + } + } + HashReturn::SHA256(hash) => { + let hash_out = crate::hex_table::u8_array_to_lower_hex_string(&hash).unwrap(); + assert_eq!( + hash_out, + "9975cc7fe9ae0c307eae57ac62a099f07749d0d7d01362e92435941ca18a635e" + ); + } + HashReturn::SHA3_256(hash) => { + let hash_out = crate::hex_table::u8_array_to_lower_hex_string(&hash).unwrap(); + assert_eq!( + hash_out, + "47eec6a5beaf06f0bf5f0ee06f0cae374fd46b968849d2b65de2d8623e907e16" + ); + } + HashReturn::BLAKE256(hash) => { + let hash_out = crate::hex_table::u8_array_to_lower_hex_string(&hash).unwrap(); + assert_eq!( + hash_out, + "850e58e95935ccde7320cc41ff99c41877b4e8956f6c5c09d0a5afd40c659519" + ); + } + HashReturn::BLAKE512(hash) => { + let hash_out = crate::hex_table::u8_array_to_lower_hex_string(&hash).unwrap(); + assert_eq!( + hash_out, + "29f9c9c6f598e570fa6d036f68bc9e22d34cab2359a1dcf738d9a32a98721a5f0b7de34f20429c814529d3fac96e68a2d37460aeea355a3712e30f601ea3e607" + ); + } + HashReturn::BLAKE3(hash) => { + let hash_out = crate::hex_table::u8_array_to_lower_hex_string(&hash).unwrap(); + assert_eq!( + hash_out, + "6323d7085cd64ef68d16b2b53ec50de2b65f0f6e9d6fc44833de8925790eb3e6" + ); + } + HashReturn::SHA1(hash) => { + let hash_out = crate::hex_table::u8_array_to_lower_hex_string(&hash).unwrap(); + assert_eq!(hash_out, "d07e867570b94d81178a644e4b0359dc354872f1"); + } + HashReturn::MD5(hash) => { + let hash_out = crate::hex_table::u8_array_to_lower_hex_string(&hash).unwrap(); + assert_eq!(hash_out, "e001d16d3d95cf04ec4a86ecb4c5ab6a"); + } + HashReturn::RAW(_) => { + todo!("Shouldn't be here for this test...") + } + } + } + } +}