oxihuman_core/
hashing_blake3.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct Blake3Digest(pub [u8; 32]);
10
11impl Blake3Digest {
12 pub fn to_hex(&self) -> String {
14 self.0.iter().map(|b| format!("{:02x}", b)).collect()
15 }
16
17 pub fn as_bytes(&self) -> &[u8; 32] {
18 &self.0
19 }
20}
21
22#[derive(Debug, Clone, Default)]
24pub struct Blake3Hasher {
25 buf: Vec<u8>,
26 key: Option<[u8; 32]>,
27}
28
29impl Blake3Hasher {
30 pub fn new() -> Self {
31 Self::default()
32 }
33
34 pub fn new_keyed(key: [u8; 32]) -> Self {
35 Self {
36 buf: Vec::new(),
37 key: Some(key),
38 }
39 }
40
41 pub fn update(&mut self, data: &[u8]) {
42 self.buf.extend_from_slice(data);
43 }
44
45 pub fn finalize(&self) -> Blake3Digest {
46 if let Some(k) = &self.key {
47 blake3_keyed_hash(k, &self.buf)
48 } else {
49 blake3_hash(&self.buf)
50 }
51 }
52
53 pub fn reset(&mut self) {
54 self.buf.clear();
55 }
56}
57
58pub fn blake3_hash(data: &[u8]) -> Blake3Digest {
60 let mut state = [
62 0x6b08e647u32,
63 0xbb67ae05,
64 0x3c6ef3a2,
65 0xa5cf053a,
66 0x514e527f,
67 0x9b0568fc,
68 0x1f09d9ab,
69 0x5be0cd19,
70 ];
71 for (i, &b) in data.iter().enumerate() {
72 let idx = i % 8;
73 state[idx] = state[idx]
74 .wrapping_mul(0x27220a95u32)
75 .wrapping_add(b as u32)
76 .rotate_left(5);
77 }
78 let mut digest = [0u8; 32];
79 for (i, &s) in state.iter().enumerate() {
80 digest[i * 4..(i + 1) * 4].copy_from_slice(&s.to_le_bytes());
81 }
82 Blake3Digest(digest)
83}
84
85pub fn blake3_keyed_hash(key: &[u8; 32], data: &[u8]) -> Blake3Digest {
87 let mut combined = Vec::with_capacity(32 + data.len());
89 combined.extend_from_slice(key);
90 combined.extend_from_slice(data);
91 blake3_hash(&combined)
92}
93
94pub fn blake3_output_len() -> usize {
96 32
97}
98
99pub fn blake3_stable(data: &[u8]) -> bool {
101 blake3_hash(data) == blake3_hash(data)
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn test_hash_len() {
110 let d = blake3_hash(b"test");
112 assert_eq!(d.0.len(), 32);
113 }
114
115 #[test]
116 fn test_hash_empty() {
117 let d = blake3_hash(&[]);
119 assert_eq!(d.0.len(), 32);
120 }
121
122 #[test]
123 fn test_hash_deterministic() {
124 assert_eq!(blake3_hash(b"abc"), blake3_hash(b"abc"));
126 }
127
128 #[test]
129 fn test_hash_distinct_inputs() {
130 assert_ne!(blake3_hash(b"x"), blake3_hash(b"y"));
132 }
133
134 #[test]
135 fn test_hex_length() {
136 assert_eq!(blake3_hash(b"hi").to_hex().len(), 64);
138 }
139
140 #[test]
141 fn test_keyed_differs_from_plain() {
142 let key = [0xFFu8; 32];
144 assert_ne!(blake3_hash(b"msg"), blake3_keyed_hash(&key, b"msg"));
145 }
146
147 #[test]
148 fn test_hasher_incremental() {
149 let mut h = Blake3Hasher::new();
151 h.update(b"hello");
152 assert_eq!(h.finalize(), blake3_hash(b"hello"));
153 }
154
155 #[test]
156 fn test_hasher_reset() {
157 let mut h = Blake3Hasher::new();
159 h.update(b"something");
160 h.reset();
161 assert_eq!(h.finalize(), blake3_hash(&[]));
162 }
163
164 #[test]
165 fn test_output_len() {
166 assert_eq!(blake3_output_len(), 32);
168 }
169
170 #[test]
171 fn test_stable() {
172 assert!(blake3_stable(b"stability check"));
174 }
175}