use core::fmt;
use core::ops::{Deref, DerefMut};
use hex;
use zeroize::Zeroize;
use crate::error::{Error, Result};
use crate::types::{ByteSerializable, ConstantTimeEq, FixedSize, SecureZeroingType};
#[derive(Clone, Zeroize)]
pub struct Digest<const N: usize> {
data: [u8; N],
len: usize, }
impl<const N: usize> Digest<N> {
pub fn new(data: [u8; N]) -> Self {
Self { data, len: N }
}
pub fn with_len(data: [u8; N], len: usize) -> Self {
if len > N {
panic!("Logical length {} cannot exceed buffer size {}", len, N);
}
Self { data, len }
}
pub fn from_slice(slice: &[u8]) -> Result<Self> {
if slice.len() > N {
return Err(Error::Length {
context: "Digest::from_slice",
expected: N,
actual: slice.len(),
});
}
let mut data = [0u8; N];
data[..slice.len()].copy_from_slice(slice);
Ok(Self {
data,
len: slice.len(),
})
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn to_hex(&self) -> String {
hex::encode(&self.data[..self.len])
}
pub fn from_hex(hex_str: &str) -> Result<Self> {
let bytes = hex::decode(hex_str)
.map_err(|_| Error::param("hex_str", "Invalid hexadecimal string"))?;
Self::from_slice(&bytes)
}
}
impl<const N: usize> AsRef<[u8]> for Digest<N> {
fn as_ref(&self) -> &[u8] {
&self.data[..self.len]
}
}
impl<const N: usize> AsMut<[u8]> for Digest<N> {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.data[..self.len]
}
}
impl<const N: usize> Deref for Digest<N> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.data[..self.len]
}
}
impl<const N: usize> DerefMut for Digest<N> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data[..self.len]
}
}
impl<const N: usize> PartialEq for Digest<N> {
fn eq(&self, other: &Self) -> bool {
self.len == other.len && self.data[..self.len] == other.data[..other.len]
}
}
impl<const N: usize> Eq for Digest<N> {}
impl<const N: usize> fmt::Debug for Digest<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Digest<{}>({}) [len={}]", N, self.to_hex(), self.len)
}
}
impl<const N: usize> fmt::Display for Digest<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_hex())
}
}
impl<const N: usize> ConstantTimeEq for Digest<N> {
fn ct_eq(&self, other: &Self) -> bool {
if self.len != other.len {
return false;
}
dcrypt_internal::constant_time::ct_eq(&self.data[..self.len], &other.data[..other.len])
}
}
impl<const N: usize> SecureZeroingType for Digest<N> {
fn zeroed() -> Self {
Self {
data: [0u8; N],
len: N,
}
}
}
impl<const N: usize> FixedSize for Digest<N> {
fn size() -> usize {
N
}
}
impl<const N: usize> ByteSerializable for Digest<N> {
fn to_bytes(&self) -> Vec<u8> {
self.data[..self.len].to_vec()
}
fn from_bytes(bytes: &[u8]) -> Result<Self> {
Self::from_slice(bytes)
}
}
pub trait Sha256Compatible {}
impl Sha256Compatible for Digest<32> {}
pub trait Sha512Compatible {}
impl Sha512Compatible for Digest<64> {}
pub trait Blake2bCompatible {}
impl Blake2bCompatible for Digest<32> {}
impl Blake2bCompatible for Digest<64> {}
pub trait Keccak256Compatible {}
impl Keccak256Compatible for Digest<32> {}