1use std::io::{self, Read};
2
3use crate::{Hasher, Result, VerifyError};
4
5pub struct VerifiedReader<R, H> {
8 reader: R,
9 hasher: H,
10 bytes_processed: u64,
11}
12
13impl<R, H> VerifiedReader<R, H> {
14 pub fn new(reader: R, hasher: H) -> Self {
16 Self {
17 reader,
18 hasher,
19 bytes_processed: 0,
20 }
21 }
22}
23
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct VerificationReceipt {
26 pub expected_digest: Vec<u8>,
28 pub actual_digest: Vec<u8>,
30 pub bytes_processed: u64,
32}
33
34impl<R: Read, H: Hasher> VerifiedReader<R, H> {
35 pub fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
38 let n = self.reader.read(buf)?;
39 if n > 0 {
40 self.hasher.update(&buf[..n]);
41 self.bytes_processed += n as u64;
42 }
43 Ok(n)
44 }
45
46 pub fn bytes_processed(&self) -> u64 {
47 self.bytes_processed
48 }
49
50 pub fn finish(self, expected: &[u8]) -> Result<()> {
53 self.finish_with_constraints(expected, None)?;
54 Ok(())
55 }
56
57 pub fn finish_with_constraints(
65 self,
66 expected: &[u8],
67 expected_bytes: Option<u64>,
68 ) -> Result<VerificationReceipt> {
69 let actual = self.hasher.finalize();
70 if actual != expected {
71 return Err(VerifyError::HashMismatch {
72 expected: expected.to_vec(),
73 actual,
74 });
75 }
76
77 if let Some(expected_bytes) = expected_bytes
78 && self.bytes_processed != expected_bytes
79 {
80 return Err(VerifyError::SizeMismatch {
81 expected: expected_bytes,
82 actual: self.bytes_processed,
83 });
84 }
85
86 Ok(VerificationReceipt {
87 expected_digest: expected.to_vec(),
88 actual_digest: actual,
89 bytes_processed: self.bytes_processed,
90 })
91 }
92}
93
94pub fn verify_stream<R: Read, H: Hasher>(
102 reader: R,
103 hasher: H,
104 expected: &[u8],
105 expected_bytes: Option<u64>,
106) -> Result<VerificationReceipt> {
107 let mut verified = VerifiedReader::new(reader, hasher);
108 let mut buffer = [0_u8; 8192];
109 loop {
110 let read = verified.read(&mut buffer)?;
111 if read == 0 {
112 break;
113 }
114 }
115 verified.finish_with_constraints(expected, expected_bytes)
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121 use std::io::Cursor;
122
123 #[cfg(feature = "sha256")]
124 use crate::Sha256Hasher;
125
126 #[cfg(feature = "sha256")]
127 #[test]
128 fn test_sha256_hasher() {
129 let mut hasher = Sha256Hasher::new();
130 hasher.update(b"hello world");
131 let hash = hasher.finalize();
132
133 let expected =
135 hex::decode("b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9")
136 .unwrap();
137 assert_eq!(hash, expected);
138 }
139
140 #[cfg(feature = "sha256")]
141 #[test]
142 fn test_verified_reader_success() {
143 let data = b"test data for verification";
144
145 let mut hasher = Sha256Hasher::new();
147 hasher.update(data);
148 let expected = hasher.finalize();
149
150 let reader = Cursor::new(data);
152 let hasher = Sha256Hasher::new();
153 let mut verified = VerifiedReader::new(reader, hasher);
154
155 let mut buffer = [0; 32];
156 verified.read(&mut buffer).unwrap();
157
158 let receipt = verified
160 .finish_with_constraints(&expected, Some(data.len() as u64))
161 .unwrap();
162 assert_eq!(receipt.bytes_processed, data.len() as u64);
163 }
164
165 #[cfg(feature = "sha256")]
166 #[test]
167 fn test_verified_reader_hash_mismatch() {
168 let data = b"test data";
169 let reader = Cursor::new(data);
170 let hasher = Sha256Hasher::new();
171 let mut verified = VerifiedReader::new(reader, hasher);
172
173 let mut buffer = [0; 32];
174 verified.read(&mut buffer).unwrap();
175
176 let wrong_hash = vec![0; 32];
178 let result = verified.finish(&wrong_hash);
179 assert!(result.is_err());
180
181 if let Err(VerifyError::HashMismatch { expected, actual }) = result {
182 assert_eq!(expected, vec![0; 32]);
183 assert_ne!(actual, vec![0; 32]);
184 } else {
185 panic!("Expected HashMismatch error");
186 }
187 }
188
189 #[cfg(feature = "sha256")]
190 #[test]
191 fn test_verified_reader_size_mismatch() {
192 let data = b"test data";
193
194 let mut expected_hasher = Sha256Hasher::new();
195 expected_hasher.update(data);
196 let expected = expected_hasher.finalize();
197
198 let reader = Cursor::new(data);
199 let hasher = Sha256Hasher::new();
200 let mut verified = VerifiedReader::new(reader, hasher);
201
202 let mut buffer = [0; 32];
203 verified.read(&mut buffer).unwrap();
204
205 let result = verified.finish_with_constraints(&expected, Some((data.len() as u64) + 1));
206 assert!(matches!(
207 result,
208 Err(VerifyError::SizeMismatch {
209 expected,
210 actual
211 }) if expected == (data.len() as u64) + 1 && actual == data.len() as u64
212 ));
213 }
214
215 #[cfg(feature = "sha256")]
216 #[test]
217 fn test_verify_stream_consumes_full_reader() {
218 let data = b"stream verify content";
219 let mut expected_hasher = Sha256Hasher::new();
220 expected_hasher.update(data);
221 let expected = expected_hasher.finalize();
222
223 let receipt = verify_stream(
224 Cursor::new(data),
225 Sha256Hasher::new(),
226 &expected,
227 Some(data.len() as u64),
228 )
229 .unwrap();
230
231 assert_eq!(receipt.bytes_processed, data.len() as u64);
232 }
233}