use crate::Hasher;
#[cfg(not(feature = "std"))]
use alloc::vec;
use bytes::{Buf, BufMut};
use commonware_codec::{DecodeExt, Error as CodecError, FixedSize, Read, ReadExt, Write};
use commonware_utils::{hex, Array, Span};
use core::{
fmt::{Debug, Display},
ops::Deref,
};
use rand_core::CryptoRngCore;
use sha2::{Digest as _, Sha256 as ISha256};
use zeroize::Zeroize;
pub type CoreSha256 = ISha256;
const DIGEST_LENGTH: usize = 32;
#[derive(Debug, Default)]
pub struct Sha256 {
hasher: ISha256,
}
impl Clone for Sha256 {
fn clone(&self) -> Self {
Self::default()
}
}
impl Sha256 {
pub fn fill(b: u8) -> <Self as Hasher>::Digest {
<Self as Hasher>::Digest::decode(vec![b; DIGEST_LENGTH].as_ref()).unwrap()
}
}
impl Hasher for Sha256 {
type Digest = Digest;
fn update(&mut self, message: &[u8]) -> &mut Self {
self.hasher.update(message);
self
}
fn finalize(&mut self) -> Self::Digest {
let finalized = self.hasher.finalize_reset();
let array: [u8; DIGEST_LENGTH] = finalized.into();
Self::Digest::from(array)
}
fn reset(&mut self) -> &mut Self {
self.hasher = ISha256::new();
self
}
fn empty() -> Self::Digest {
Self::new().finalize()
}
}
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct Digest(pub [u8; DIGEST_LENGTH]);
impl Write for Digest {
fn write(&self, buf: &mut impl BufMut) {
self.0.write(buf);
}
}
impl Read for Digest {
type Cfg = ();
fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
let array = <[u8; DIGEST_LENGTH]>::read(buf)?;
Ok(Self(array))
}
}
impl FixedSize for Digest {
const SIZE: usize = DIGEST_LENGTH;
}
impl Span for Digest {}
impl Array for Digest {}
impl From<[u8; DIGEST_LENGTH]> for Digest {
fn from(value: [u8; DIGEST_LENGTH]) -> Self {
Self(value)
}
}
impl AsRef<[u8]> for Digest {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl Deref for Digest {
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.0
}
}
impl Debug for Digest {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", hex(&self.0))
}
}
impl Display for Digest {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", hex(&self.0))
}
}
impl crate::Digest for Digest {
fn random<R: CryptoRngCore>(rng: &mut R) -> Self {
let mut array = [0u8; DIGEST_LENGTH];
rng.fill_bytes(&mut array);
Self(array)
}
}
impl Zeroize for Digest {
fn zeroize(&mut self) {
self.0.zeroize();
}
}
#[cfg(test)]
mod tests {
use super::*;
use commonware_codec::{DecodeExt, Encode};
use commonware_utils::hex;
const HELLO_DIGEST: [u8; DIGEST_LENGTH] =
hex!("b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9");
const EMPTY_DIGEST: [u8; DIGEST_LENGTH] =
hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
#[test]
fn test_sha256() {
let msg = b"hello world";
let mut hasher = Sha256::new();
hasher.update(msg);
let digest = hasher.finalize();
assert!(Digest::decode(digest.as_ref()).is_ok());
assert_eq!(digest.as_ref(), HELLO_DIGEST);
hasher.update(msg);
let digest = hasher.finalize();
assert!(Digest::decode(digest.as_ref()).is_ok());
assert_eq!(digest.as_ref(), HELLO_DIGEST);
let hash = Sha256::hash(msg);
assert_eq!(hash.as_ref(), HELLO_DIGEST);
}
#[test]
fn test_sha256_len() {
assert_eq!(Digest::SIZE, DIGEST_LENGTH);
}
#[test]
fn test_hash_empty() {
let digest1 = Sha256::empty();
let digest2 = Sha256::empty();
assert_eq!(digest1, digest2);
}
#[test]
fn test_sha256_empty() {
let empty_digest = Sha256::empty();
let expected_digest = Digest::from(EMPTY_DIGEST);
assert_eq!(empty_digest, expected_digest);
}
#[test]
fn test_codec() {
let msg = b"hello world";
let mut hasher = Sha256::new();
hasher.update(msg);
let digest = hasher.finalize();
let encoded = digest.encode();
assert_eq!(encoded.len(), DIGEST_LENGTH);
assert_eq!(encoded, digest.as_ref());
let decoded = Digest::decode(encoded).unwrap();
assert_eq!(digest, decoded);
}
}