#![forbid(unsafe_code)]
#![cfg_attr(
test,
allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)
)]
#[cfg(not(any(
feature = "sha2",
feature = "sha3",
feature = "blake3"
)))]
compile_error!(
"hsh-digest requires at least one algorithm feature: `sha2`, `sha3`, or `blake3`."
);
pub mod error;
pub use error::DigestError;
#[cfg(any(feature = "sha2", feature = "sha3"))]
use digest::Digest as _;
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[non_exhaustive]
pub enum Algorithm {
#[cfg(feature = "sha2")]
Sha256,
#[cfg(feature = "sha2")]
Sha384,
#[cfg(feature = "sha2")]
Sha512,
#[cfg(feature = "sha3")]
Sha3_256,
#[cfg(feature = "sha3")]
Sha3_384,
#[cfg(feature = "sha3")]
Sha3_512,
#[cfg(feature = "blake3")]
Blake3,
}
impl Algorithm {
#[must_use]
pub const fn output_len(self) -> usize {
match self {
#[cfg(feature = "sha2")]
Self::Sha256 => 32,
#[cfg(feature = "sha2")]
Self::Sha384 => 48,
#[cfg(feature = "sha2")]
Self::Sha512 => 64,
#[cfg(feature = "sha3")]
Self::Sha3_256 => 32,
#[cfg(feature = "sha3")]
Self::Sha3_384 => 48,
#[cfg(feature = "sha3")]
Self::Sha3_512 => 64,
#[cfg(feature = "blake3")]
Self::Blake3 => 32,
}
}
#[must_use]
pub const fn id(self) -> &'static str {
match self {
#[cfg(feature = "sha2")]
Self::Sha256 => "sha256",
#[cfg(feature = "sha2")]
Self::Sha384 => "sha384",
#[cfg(feature = "sha2")]
Self::Sha512 => "sha512",
#[cfg(feature = "sha3")]
Self::Sha3_256 => "sha3-256",
#[cfg(feature = "sha3")]
Self::Sha3_384 => "sha3-384",
#[cfg(feature = "sha3")]
Self::Sha3_512 => "sha3-512",
#[cfg(feature = "blake3")]
Self::Blake3 => "blake3",
}
}
}
pub fn hash(
algorithm: Algorithm,
data: &[u8],
) -> Result<Vec<u8>, DigestError> {
let mut h = Hasher::new(algorithm)?;
h.update(data);
Ok(h.finalize())
}
pub struct Hasher {
inner: HasherInner,
}
impl std::fmt::Debug for Hasher {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Hasher")
.field("algorithm", &self.algorithm())
.finish()
}
}
#[allow(clippy::large_enum_variant)]
enum HasherInner {
#[cfg(feature = "sha2")]
Sha256(sha2::Sha256),
#[cfg(feature = "sha2")]
Sha384(sha2::Sha384),
#[cfg(feature = "sha2")]
Sha512(sha2::Sha512),
#[cfg(feature = "sha3")]
Sha3_256(sha3::Sha3_256),
#[cfg(feature = "sha3")]
Sha3_384(sha3::Sha3_384),
#[cfg(feature = "sha3")]
Sha3_512(sha3::Sha3_512),
#[cfg(feature = "blake3")]
Blake3(blake3::Hasher),
}
impl Hasher {
pub fn new(algorithm: Algorithm) -> Result<Self, DigestError> {
let inner = match algorithm {
#[cfg(feature = "sha2")]
Algorithm::Sha256 => {
HasherInner::Sha256(sha2::Sha256::new())
}
#[cfg(feature = "sha2")]
Algorithm::Sha384 => {
HasherInner::Sha384(sha2::Sha384::new())
}
#[cfg(feature = "sha2")]
Algorithm::Sha512 => {
HasherInner::Sha512(sha2::Sha512::new())
}
#[cfg(feature = "sha3")]
Algorithm::Sha3_256 => {
HasherInner::Sha3_256(sha3::Sha3_256::new())
}
#[cfg(feature = "sha3")]
Algorithm::Sha3_384 => {
HasherInner::Sha3_384(sha3::Sha3_384::new())
}
#[cfg(feature = "sha3")]
Algorithm::Sha3_512 => {
HasherInner::Sha3_512(sha3::Sha3_512::new())
}
#[cfg(feature = "blake3")]
Algorithm::Blake3 => {
HasherInner::Blake3(blake3::Hasher::new())
}
};
Ok(Self { inner })
}
#[must_use]
pub fn algorithm(&self) -> Algorithm {
match &self.inner {
#[cfg(feature = "sha2")]
HasherInner::Sha256(_) => Algorithm::Sha256,
#[cfg(feature = "sha2")]
HasherInner::Sha384(_) => Algorithm::Sha384,
#[cfg(feature = "sha2")]
HasherInner::Sha512(_) => Algorithm::Sha512,
#[cfg(feature = "sha3")]
HasherInner::Sha3_256(_) => Algorithm::Sha3_256,
#[cfg(feature = "sha3")]
HasherInner::Sha3_384(_) => Algorithm::Sha3_384,
#[cfg(feature = "sha3")]
HasherInner::Sha3_512(_) => Algorithm::Sha3_512,
#[cfg(feature = "blake3")]
HasherInner::Blake3(_) => Algorithm::Blake3,
}
}
pub fn update(&mut self, bytes: &[u8]) {
match &mut self.inner {
#[cfg(feature = "sha2")]
HasherInner::Sha256(h) => h.update(bytes),
#[cfg(feature = "sha2")]
HasherInner::Sha384(h) => h.update(bytes),
#[cfg(feature = "sha2")]
HasherInner::Sha512(h) => h.update(bytes),
#[cfg(feature = "sha3")]
HasherInner::Sha3_256(h) => h.update(bytes),
#[cfg(feature = "sha3")]
HasherInner::Sha3_384(h) => h.update(bytes),
#[cfg(feature = "sha3")]
HasherInner::Sha3_512(h) => h.update(bytes),
#[cfg(feature = "blake3")]
HasherInner::Blake3(h) => {
let _ = h.update(bytes);
}
}
}
#[must_use]
pub fn finalize(self) -> Vec<u8> {
match self.inner {
#[cfg(feature = "sha2")]
HasherInner::Sha256(h) => h.finalize().to_vec(),
#[cfg(feature = "sha2")]
HasherInner::Sha384(h) => h.finalize().to_vec(),
#[cfg(feature = "sha2")]
HasherInner::Sha512(h) => h.finalize().to_vec(),
#[cfg(feature = "sha3")]
HasherInner::Sha3_256(h) => h.finalize().to_vec(),
#[cfg(feature = "sha3")]
HasherInner::Sha3_384(h) => h.finalize().to_vec(),
#[cfg(feature = "sha3")]
HasherInner::Sha3_512(h) => h.finalize().to_vec(),
#[cfg(feature = "blake3")]
HasherInner::Blake3(h) => h.finalize().as_bytes().to_vec(),
}
}
}
#[must_use]
pub fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
use subtle::ConstantTimeEq;
if a.len() != b.len() {
return false;
}
bool::from(a.ct_eq(b))
}