use std::{fmt::Display, str::FromStr};
use digest::Digest;
use md5::Md5;
use num_enum::{FromPrimitive, IntoPrimitive};
use ripemd::Ripemd160;
use sha1_checked::{CollisionResult, Sha1};
use crate::errors::{Error, Result};
#[derive(Debug, PartialEq, Eq, Copy, Clone, FromPrimitive, IntoPrimitive, Hash)]
#[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,
#[num_enum(catch_all)]
Other(u8),
}
impl Default for HashAlgorithm {
fn default() -> Self {
Self::SHA2_256
}
}
impl HashAlgorithm {
pub const fn salt_len(&self) -> Option<usize> {
match self {
Self::SHA2_224 => Some(16),
Self::SHA2_256 => Some(16),
Self::SHA2_384 => Some(24),
Self::SHA2_512 => Some(32),
Self::SHA3_256 => Some(16),
Self::SHA3_512 => Some(32),
_ => None,
}
}
}
impl FromStr for HashAlgorithm {
type Err = Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s {
"None" => Ok(Self::None),
"MD5" => Ok(Self::MD5),
"SHA1" => Ok(Self::SHA1),
"RIPEMD160" => Ok(Self::RIPEMD160),
"SHA256" => Ok(Self::SHA2_256),
"SHA384" => Ok(Self::SHA2_384),
"SHA512" => Ok(Self::SHA2_512),
"SHA224" => Ok(Self::SHA2_224),
"SHA3-256" => Ok(Self::SHA3_256),
"SHA3-512" => Ok(Self::SHA3_512),
"Private10" => Ok(Self::Private10),
_ => bail!("unknown hash"),
}
}
}
impl Display for HashAlgorithm {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
Self::MD5 => "MD5",
Self::SHA1 => "SHA1",
Self::RIPEMD160 => "RIPEMD160",
Self::SHA2_256 => "SHA256",
Self::SHA2_384 => "SHA384",
Self::SHA2_512 => "SHA512",
Self::SHA2_224 => "SHA224",
Self::SHA3_256 => "SHA3-256",
Self::SHA3_512 => "SHA3-512",
Self::Private10 => "Private10",
Self::Other(v) => return write!(f, "Other({})", v),
Self::None => "None",
};
write!(f, "{}", s)
}
}
impl zeroize::DefaultIsZeroes for HashAlgorithm {}
pub trait Hasher: std::io::Write {
fn update(&mut self, _: &[u8]);
fn finish(self: Box<Self>) -> Vec<u8>;
fn finish_reset_into(&mut self, out: &mut [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()
}
fn finish_reset_into(&mut self, out: &mut [u8]) {
let res = self.inner.finalize_reset();
out.copy_from_slice(&res.as_slice()[..out.len()]);
}
}
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::<Md5Hasher>::default()),
HashAlgorithm::SHA1 => Ok(Box::<Sha1Hasher>::default()),
HashAlgorithm::RIPEMD160 => Ok(Box::<Ripemd160Hasher>::default()),
HashAlgorithm::SHA2_256 => Ok(Box::<Sha2_256Hasher>::default()),
HashAlgorithm::SHA2_384 => Ok(Box::<Sha2_384Hasher>::default()),
HashAlgorithm::SHA2_512 => Ok(Box::<Sha2_512Hasher>::default()),
HashAlgorithm::SHA2_224 => Ok(Box::<Sha2_224Hasher>::default()),
HashAlgorithm::SHA3_256 => Ok(Box::<Sha3_256Hasher>::default()),
HashAlgorithm::SHA3_512 => Ok(Box::<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 => match Sha1::try_digest(data) {
CollisionResult::Ok(output) => output.to_vec(),
CollisionResult::Collision(_) | CollisionResult::Mitigated(_) => {
return Err(Error::Sha1HashCollision)
}
},
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) -> Option<usize> {
let size = 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(),
_ => return None,
};
Some(size)
}
}