use crate::canonical::{CanonicalError, Result};
use sha2::{Digest, Sha256};
pub fn compute_hash<T: AsRef<[u8]>>(data: &T) -> Result<String> {
let mut hasher = Sha256::new();
hasher.update(data.as_ref());
let result = hasher.finalize();
Ok(hex::encode(result))
}
pub fn compute_hash_multi<T: AsRef<[u8]>>(data: &[T]) -> Result<String> {
let mut hasher = Sha256::new();
for item in data {
hasher.update(item.as_ref());
}
let result = hasher.finalize();
Ok(hex::encode(result))
}
pub struct HashVerifier {
expected: String,
}
impl HashVerifier {
pub fn new(expected: String) -> Self {
Self { expected }
}
pub fn verify<T: AsRef<[u8]>>(&self, data: &T) -> Result<()> {
let actual = compute_hash(data)?;
if actual == self.expected {
Ok(())
} else {
Err(CanonicalError::Hash(format!(
"Hash mismatch: expected {}, got {}",
self.expected, actual
)))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_deterministic_hash() {
let data = b"test data";
let hash1 = compute_hash(&data).unwrap();
let hash2 = compute_hash(&data).unwrap();
assert_eq!(hash1, hash2);
}
#[test]
fn test_hash_length() {
let data = b"test";
let hash = compute_hash(&data).unwrap();
assert_eq!(hash.len(), 64); }
#[test]
fn test_multi_hash_deterministic() {
let data = vec![
b"part1".as_slice(),
b"part2".as_slice(),
b"part3".as_slice(),
];
let hash1 = compute_hash_multi(&data).unwrap();
let hash2 = compute_hash_multi(&data).unwrap();
assert_eq!(hash1, hash2);
}
#[test]
fn test_hash_verifier_success() {
let data = b"test data";
let hash = compute_hash(&data).unwrap();
let verifier = HashVerifier::new(hash);
assert!(verifier.verify(&data).is_ok());
}
#[test]
fn test_hash_verifier_failure() {
let data1 = b"test data";
let data2 = b"different data";
let hash1 = compute_hash(&data1).unwrap();
let verifier = HashVerifier::new(hash1);
assert!(verifier.verify(&data2).is_err());
}
}