use std::boxed::Box;
use digest::Digest;
use md5::Md5;
use ripemd::Ripemd160;
use sha1::Sha1;
use crate::errors::Result;
#[derive(Debug, PartialEq, Eq, Copy, Clone, FromPrimitive)]
#[repr(u8)]
pub enum HashAlgorithm {
None = 0,
MD5 = 1,
SHA1 = 2,
RIPEMD160 = 3,
SHA2_256 = 8,
SHA2_384 = 9,
SHA2_512 = 10,
SHA2_224 = 11,
SHA3_256 = 12,
SHA3_512 = 14,
Private10 = 110,
}
impl zeroize::DefaultIsZeroes for HashAlgorithm {}
impl Default for HashAlgorithm {
fn default() -> Self {
HashAlgorithm::SHA2_256
}
}
pub trait Hasher: std::io::Write {
fn update(&mut self, _: &[u8]);
fn finish(self: Box<Self>) -> Vec<u8>;
}
macro_rules! derive_hasher {
($name:ident, $struct:path) => {
#[derive(Clone, Default)]
pub struct $name {
inner: $struct,
}
impl Hasher for $name {
fn update(&mut self, data: &[u8]) {
self.inner.update(data);
}
fn finish(self: Box<Self>) -> Vec<u8> {
self.inner.finalize().as_slice().to_vec()
}
}
impl std::io::Write for $name {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.update(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
};
}
derive_hasher!(Md5Hasher, Md5);
derive_hasher!(Sha1Hasher, Sha1);
derive_hasher!(Ripemd160Hasher, Ripemd160);
derive_hasher!(Sha2_256Hasher, sha2::Sha256);
derive_hasher!(Sha2_384Hasher, sha2::Sha384);
derive_hasher!(Sha2_512Hasher, sha2::Sha512);
derive_hasher!(Sha2_224Hasher, sha2::Sha224);
derive_hasher!(Sha3_256Hasher, sha3::Sha3_256);
derive_hasher!(Sha3_512Hasher, sha3::Sha3_512);
impl HashAlgorithm {
pub fn new_hasher(self) -> Result<Box<dyn Hasher>> {
match self {
HashAlgorithm::MD5 => Ok(Box::new(Md5Hasher::default())),
HashAlgorithm::SHA1 => Ok(Box::new(Sha1Hasher::default())),
HashAlgorithm::RIPEMD160 => Ok(Box::new(Ripemd160Hasher::default())),
HashAlgorithm::SHA2_256 => Ok(Box::new(Sha2_256Hasher::default())),
HashAlgorithm::SHA2_384 => Ok(Box::new(Sha2_384Hasher::default())),
HashAlgorithm::SHA2_512 => Ok(Box::new(Sha2_512Hasher::default())),
HashAlgorithm::SHA2_224 => Ok(Box::new(Sha2_224Hasher::default())),
HashAlgorithm::SHA3_256 => Ok(Box::new(Sha3_256Hasher::default())),
HashAlgorithm::SHA3_512 => Ok(Box::new(Sha3_512Hasher::default())),
_ => unimplemented_err!("hasher {:?}", self),
}
}
pub fn digest(self, data: &[u8]) -> Result<Vec<u8>> {
Ok(match self {
HashAlgorithm::MD5 => Md5::digest(data).to_vec(),
HashAlgorithm::SHA1 => Sha1::digest(data).to_vec(),
HashAlgorithm::RIPEMD160 => Ripemd160::digest(data).to_vec(),
HashAlgorithm::SHA2_256 => sha2::Sha256::digest(data).to_vec(),
HashAlgorithm::SHA2_384 => sha2::Sha384::digest(data).to_vec(),
HashAlgorithm::SHA2_512 => sha2::Sha512::digest(data).to_vec(),
HashAlgorithm::SHA2_224 => sha2::Sha224::digest(data).to_vec(),
HashAlgorithm::SHA3_256 => sha3::Sha3_256::digest(data).to_vec(),
HashAlgorithm::SHA3_512 => sha3::Sha3_512::digest(data).to_vec(),
HashAlgorithm::Private10 => unsupported_err!("Private10 should not be used"),
_ => unimplemented_err!("hasher: {:?}", self),
})
}
pub fn digest_size(self) -> usize {
match self {
HashAlgorithm::MD5 => Md5::output_size(),
HashAlgorithm::SHA1 => Sha1::output_size(),
HashAlgorithm::RIPEMD160 => Ripemd160::output_size(),
HashAlgorithm::SHA2_256 => sha2::Sha256::output_size(),
HashAlgorithm::SHA2_384 => sha2::Sha384::output_size(),
HashAlgorithm::SHA2_512 => sha2::Sha512::output_size(),
HashAlgorithm::SHA2_224 => sha2::Sha224::output_size(),
HashAlgorithm::SHA3_256 => sha3::Sha3_256::output_size(),
HashAlgorithm::SHA3_512 => sha3::Sha3_512::output_size(),
_ => 0,
}
}
}