1use std::io::{self, Read};
10
11pub type Hash = [u8; 32];
13
14pub const HASH_BUFFER_SIZE: usize = 64 * 1024;
16
17pub fn hash(data: &[u8]) -> Hash {
19 *blake3::hash(data).as_bytes()
20}
21
22pub fn hash_multi(chunks: &[&[u8]]) -> Hash {
24 let mut hasher = blake3::Hasher::new();
25 for chunk in chunks {
26 hasher.update(chunk);
27 }
28 *hasher.finalize().as_bytes()
29}
30
31pub fn verify_hash(data: &[u8], expected: &Hash) -> bool {
33 &hash(data) == expected
34}
35
36pub fn keyed_hash(key: &[u8; 32], data: &[u8]) -> Hash {
38 *blake3::keyed_hash(key, data).as_bytes()
39}
40
41pub struct IncrementalHasher {
57 inner: blake3::Hasher,
58 bytes_processed: u64,
59}
60
61impl Default for IncrementalHasher {
62 fn default() -> Self {
63 Self::new()
64 }
65}
66
67impl IncrementalHasher {
68 pub fn new() -> Self {
70 Self {
71 inner: blake3::Hasher::new(),
72 bytes_processed: 0,
73 }
74 }
75
76 pub fn new_keyed(key: &[u8; 32]) -> Self {
78 Self {
79 inner: blake3::Hasher::new_keyed(key),
80 bytes_processed: 0,
81 }
82 }
83
84 pub fn update(&mut self, data: &[u8]) {
86 self.inner.update(data);
87 self.bytes_processed += data.len() as u64;
88 }
89
90 pub fn bytes_processed(&self) -> u64 {
92 self.bytes_processed
93 }
94
95 pub fn finalize(self) -> Hash {
97 *self.inner.finalize().as_bytes()
98 }
99
100 pub fn finalize_reset(&mut self) -> Hash {
102 let hash = *self.inner.finalize().as_bytes();
103 self.inner.reset();
104 self.bytes_processed = 0;
105 hash
106 }
107
108 pub fn update_reader<R: Read>(&mut self, reader: &mut R) -> io::Result<u64> {
113 let mut buffer = [0u8; HASH_BUFFER_SIZE];
114 let mut total = 0u64;
115
116 loop {
117 let bytes_read = reader.read(&mut buffer)?;
118 if bytes_read == 0 {
119 break;
120 }
121 self.update(&buffer[..bytes_read]);
122 total += bytes_read as u64;
123 }
124
125 Ok(total)
126 }
127}
128
129pub fn hash_reader<R: Read>(reader: &mut R) -> io::Result<Hash> {
131 let mut hasher = IncrementalHasher::new();
132 hasher.update_reader(reader)?;
133 Ok(hasher.finalize())
134}
135
136#[derive(Debug, Clone)]
138pub struct HashResult {
139 pub hash: Hash,
141 pub bytes_processed: u64,
143}
144
145pub struct ChunkHasher {
150 chunk_hashes: Vec<Hash>,
151 root_hasher: IncrementalHasher,
152}
153
154impl Default for ChunkHasher {
155 fn default() -> Self {
156 Self::new()
157 }
158}
159
160impl ChunkHasher {
161 pub fn new() -> Self {
163 Self {
164 chunk_hashes: Vec::new(),
165 root_hasher: IncrementalHasher::new(),
166 }
167 }
168
169 pub fn add_chunk(&mut self, chunk: &[u8]) -> Hash {
171 let chunk_hash = hash(chunk);
172 self.chunk_hashes.push(chunk_hash);
173 self.root_hasher.update(&chunk_hash);
174 chunk_hash
175 }
176
177 pub fn chunk_count(&self) -> usize {
179 self.chunk_hashes.len()
180 }
181
182 pub fn chunk_hashes(&self) -> &[Hash] {
184 &self.chunk_hashes
185 }
186
187 pub fn finalize(self) -> ChunkHashResult {
189 ChunkHashResult {
190 chunk_hashes: self.chunk_hashes,
191 root_hash: self.root_hasher.finalize(),
192 }
193 }
194}
195
196#[derive(Debug, Clone)]
198pub struct ChunkHashResult {
199 pub chunk_hashes: Vec<Hash>,
201 pub root_hash: Hash,
203}
204
205impl ChunkHashResult {
206 pub fn verify_chunk(&self, index: usize, chunk: &[u8]) -> bool {
208 if index >= self.chunk_hashes.len() {
209 return false;
210 }
211 hash(chunk) == self.chunk_hashes[index]
212 }
213
214 pub fn chunk_count(&self) -> usize {
216 self.chunk_hashes.len()
217 }
218}
219
220pub fn hash_chunked<R: Read>(reader: &mut R, chunk_size: usize) -> io::Result<ChunkHashResult> {
222 let mut chunk_hasher = ChunkHasher::new();
223 let mut buffer = vec![0u8; chunk_size];
224
225 loop {
226 let mut total_read = 0;
227
228 while total_read < chunk_size {
230 let bytes_read = reader.read(&mut buffer[total_read..])?;
231 if bytes_read == 0 {
232 break;
233 }
234 total_read += bytes_read;
235 }
236
237 if total_read == 0 {
238 break;
239 }
240
241 chunk_hasher.add_chunk(&buffer[..total_read]);
242
243 if total_read < chunk_size {
244 break;
246 }
247 }
248
249 Ok(chunk_hasher.finalize())
250}
251
252#[cfg(test)]
253mod tests {
254 use super::*;
255 use std::io::Cursor;
256
257 #[test]
258 fn test_hash() {
259 let data = b"Hello, CHIE Protocol!";
260 let h = hash(data);
261
262 assert!(verify_hash(data, &h));
263 assert!(!verify_hash(b"Different data", &h));
264 }
265
266 #[test]
267 fn test_hash_multi() {
268 let chunk1 = b"Hello, ";
269 let chunk2 = b"CHIE Protocol!";
270 let combined = b"Hello, CHIE Protocol!";
271
272 let h_multi = hash_multi(&[chunk1, chunk2]);
273 let h_combined = hash(combined);
274
275 assert_eq!(h_multi, h_combined);
276 }
277
278 #[test]
279 fn test_incremental_hasher() {
280 let data = b"Hello, CHIE Protocol!";
281
282 let h1 = hash(data);
284
285 let mut hasher = IncrementalHasher::new();
287 hasher.update(b"Hello, ");
288 hasher.update(b"CHIE ");
289 hasher.update(b"Protocol!");
290 let h2 = hasher.finalize();
291
292 assert_eq!(h1, h2);
293 }
294
295 #[test]
296 fn test_hash_reader() {
297 let data = b"Hello, CHIE Protocol!";
298 let mut cursor = Cursor::new(data);
299
300 let h1 = hash(data);
301 let h2 = hash_reader(&mut cursor).unwrap();
302
303 assert_eq!(h1, h2);
304 }
305
306 #[test]
307 fn test_chunk_hasher() {
308 let chunk1 = b"Chunk 1 data";
309 let chunk2 = b"Chunk 2 data";
310 let chunk3 = b"Chunk 3 data";
311
312 let mut hasher = ChunkHasher::new();
313 hasher.add_chunk(chunk1);
314 hasher.add_chunk(chunk2);
315 hasher.add_chunk(chunk3);
316
317 let result = hasher.finalize();
318
319 assert_eq!(result.chunk_count(), 3);
320 assert!(result.verify_chunk(0, chunk1));
321 assert!(result.verify_chunk(1, chunk2));
322 assert!(result.verify_chunk(2, chunk3));
323 assert!(!result.verify_chunk(0, chunk2));
324 }
325
326 #[test]
327 fn test_hash_chunked() {
328 let data = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
329 let mut cursor = Cursor::new(data);
330
331 let result = hash_chunked(&mut cursor, 10).unwrap();
332
333 assert_eq!(result.chunk_count(), 3);
335 assert!(result.verify_chunk(0, b"ABCDEFGHIJ"));
336 assert!(result.verify_chunk(1, b"KLMNOPQRST"));
337 assert!(result.verify_chunk(2, b"UVWXYZ"));
338 }
339
340 #[test]
341 fn test_keyed_hash() {
342 let key = [0u8; 32];
343 let data = b"Hello, CHIE Protocol!";
344
345 let h1 = keyed_hash(&key, data);
346 let h2 = keyed_hash(&key, data);
347 let h3 = keyed_hash(&[1u8; 32], data);
348
349 assert_eq!(h1, h2);
350 assert_ne!(h1, h3); }
352}