use sha2::{Digest, Sha256};
pub fn blake3_hash(value: impl AsRef<[u8]>) -> String {
let digest = blake3::hash(value.as_ref());
format!("blake3:{}", digest.to_hex())
}
#[deprecated(
since = "1.1.0",
note = "Use `blake3_hash` per ec ADR-0003 (hash convergence). SHA-256 \
output is retained for Phase 1 RunEvidence shape compatibility \
only; scanner fingerprints are BLAKE3 in Phase 3+."
)]
pub fn sha256_hash(value: impl AsRef<[u8]>) -> String {
let digest = Sha256::digest(value.as_ref());
let mut out = String::with_capacity(7 + 64);
out.push_str("sha256:");
for byte in digest.iter() {
out.push_str(&format!("{byte:02x}"));
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn blake3_is_deterministic_and_distinct() {
assert_eq!(blake3_hash("same"), blake3_hash("same"));
assert_ne!(blake3_hash("same"), blake3_hash("different"));
}
#[test]
fn blake3_has_prefix_and_64_hex_chars() {
let h = blake3_hash("payload");
let hex = h.strip_prefix("blake3:").expect("blake3: prefix present");
assert_eq!(hex.len(), 64);
assert!(hex.chars().all(|c| c.is_ascii_hexdigit()));
}
#[test]
#[allow(deprecated)]
fn sha256_still_available_for_compat() {
let h = super::sha256_hash("payload");
let hex = h.strip_prefix("sha256:").expect("sha256: prefix present");
assert_eq!(hex.len(), 64);
assert!(hex.chars().all(|c| c.is_ascii_hexdigit()));
}
#[test]
fn blake3_and_sha256_produce_different_outputs_for_same_input() {
#[allow(deprecated)]
let s = super::sha256_hash("same-input");
let b = blake3_hash("same-input");
assert_ne!(s, b);
assert!(s.starts_with("sha256:"));
assert!(b.starts_with("blake3:"));
}
}