use crate::base64::{Base64, Base64Url};
use crate::types::{EncodingFormat, SHAFamily};
use sha1::Sha1;
use sha2::{Digest, Sha256, Sha384, Sha512};
pub struct Hash {
algorithm: SHAFamily,
encoding: Option<EncodingFormat>,
}
impl Hash {
pub fn new(algorithm: SHAFamily, encoding: Option<EncodingFormat>) -> Self {
Self {
algorithm,
encoding,
}
}
pub async fn digest(
&self,
input: impl AsRef<[u8]>,
) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let data = input.as_ref();
let hash = match self.algorithm {
SHAFamily::SHA1 => {
let mut hasher = Sha1::new();
hasher.update(data);
hasher.finalize().to_vec()
}
SHAFamily::SHA256 => {
let mut hasher = Sha256::new();
hasher.update(data);
hasher.finalize().to_vec()
}
SHAFamily::SHA384 => {
let mut hasher = Sha384::new();
hasher.update(data);
hasher.finalize().to_vec()
}
SHAFamily::SHA512 => {
let mut hasher = Sha512::new();
hasher.update(data);
hasher.finalize().to_vec()
}
};
match self.encoding {
Some(EncodingFormat::Hex) => Ok(hash
.iter()
.map(|b| format!("{:02x}", b))
.collect::<String>()
.into_bytes()),
Some(EncodingFormat::Base64) => Ok(Base64::encode(&hash, None).into_bytes()),
Some(EncodingFormat::Base64Url) => {
Ok(Base64Url::encode(&hash, Some(true)).into_bytes())
}
Some(EncodingFormat::Base64UrlNoPad) => {
Ok(Base64Url::encode(&hash, Some(false)).into_bytes())
}
Some(EncodingFormat::None) | None => Ok(hash),
}
}
pub async fn digest_string(
&self,
input: impl AsRef<str>,
) -> Result<String, Box<dyn std::error::Error>> {
let bytes = self.digest(input.as_ref().as_bytes()).await?;
Ok(String::from_utf8(bytes)?)
}
}
pub fn create_hash(algorithm: SHAFamily, encoding: Option<EncodingFormat>) -> Hash {
Hash::new(algorithm, encoding)
}