dsfb_densor_runtime/
seal.rs1use sha2::{Digest, Sha256};
9
10pub struct CanonicalHasher {
12 inner: Sha256,
13}
14
15impl Default for CanonicalHasher {
16 fn default() -> Self {
17 Self::new()
18 }
19}
20
21impl CanonicalHasher {
22 pub fn new() -> Self {
23 CanonicalHasher {
24 inner: Sha256::new(),
25 }
26 }
27
28 pub fn field(&mut self, label: &str, bytes: &[u8]) -> &mut Self {
31 self.inner.update((label.len() as u64).to_le_bytes());
32 self.inner.update(label.as_bytes());
33 self.inner.update((bytes.len() as u64).to_le_bytes());
34 self.inner.update(bytes);
35 self
36 }
37
38 pub fn u64(&mut self, label: &str, v: u64) -> &mut Self {
40 self.field(label, &v.to_le_bytes())
41 }
42
43 pub fn hash32(&mut self, label: &str, h: &[u8; 32]) -> &mut Self {
45 self.field(label, h)
46 }
47
48 pub fn finalize(self) -> [u8; 32] {
50 self.inner.finalize().into()
51 }
52
53 pub fn finalize_hex(self) -> String {
55 to_hex(&self.finalize())
56 }
57}
58
59pub fn sha256(bytes: &[u8]) -> [u8; 32] {
61 let mut h = Sha256::new();
62 h.update(bytes);
63 h.finalize().into()
64}
65
66pub fn to_hex(h: &[u8; 32]) -> String {
68 let mut s = String::with_capacity(64);
69 for b in h {
70 s.push_str(&format!("{b:02x}"));
71 }
72 s
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn field_order_and_length_prefix_prevent_collisions() {
81 let mut a = CanonicalHasher::new();
83 a.field("ab", b"c");
84 let mut b = CanonicalHasher::new();
85 b.field("a", b"bc");
86 assert_ne!(a.finalize(), b.finalize());
87 }
88
89 #[test]
90 fn seal_is_deterministic() {
91 let mk = || {
92 let mut h = CanonicalHasher::new();
93 h.field("schema", b"x")
94 .u64("n", 3)
95 .hash32("auth", &sha256(b"policy"));
96 h.finalize()
97 };
98 assert_eq!(mk(), mk());
99 assert_eq!(to_hex(&mk()).len(), 64);
100 }
101}