1 | //! Helper module to compute a CRC32 checksum |
2 | |
3 | use std::io; |
4 | use std::io::prelude::*; |
5 | |
6 | use crc32fast::Hasher; |
7 | |
8 | /// Reader that validates the CRC32 when it reaches the EOF. |
9 | pub struct Crc32Reader<R> { |
10 | inner: R, |
11 | hasher: Hasher, |
12 | check: u32, |
13 | /// Signals if `inner` stores aes encrypted data. |
14 | /// AE-2 encrypted data doesn't use crc and sets the value to 0. |
15 | ae2_encrypted: bool, |
16 | } |
17 | |
18 | impl<R> Crc32Reader<R> { |
19 | /// Get a new Crc32Reader which checks the inner reader against checksum. |
20 | /// The check is disabled if `ae2_encrypted == true`. |
21 | pub(crate) fn new(inner: R, checksum: u32, ae2_encrypted: bool) -> Crc32Reader<R> { |
22 | Crc32Reader { |
23 | inner, |
24 | hasher: Hasher::new(), |
25 | check: checksum, |
26 | ae2_encrypted, |
27 | } |
28 | } |
29 | |
30 | fn check_matches(&self) -> bool { |
31 | self.check == self.hasher.clone().finalize() |
32 | } |
33 | |
34 | pub fn into_inner(self) -> R { |
35 | self.inner |
36 | } |
37 | } |
38 | |
39 | impl<R: Read> Read for Crc32Reader<R> { |
40 | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { |
41 | let invalid_check: bool = !buf.is_empty() && !self.check_matches() && !self.ae2_encrypted; |
42 | |
43 | let count: usize = match self.inner.read(buf) { |
44 | Ok(0) if invalid_check => { |
45 | return Err(io::Error::new(kind:io::ErrorKind::Other, error:"Invalid checksum" )) |
46 | } |
47 | Ok(n: usize) => n, |
48 | Err(e: Error) => return Err(e), |
49 | }; |
50 | self.hasher.update(&buf[0..count]); |
51 | Ok(count) |
52 | } |
53 | } |
54 | |
55 | #[cfg (test)] |
56 | mod test { |
57 | use super::*; |
58 | use std::io::Read; |
59 | |
60 | #[test ] |
61 | fn test_empty_reader() { |
62 | let data: &[u8] = b"" ; |
63 | let mut buf = [0; 1]; |
64 | |
65 | let mut reader = Crc32Reader::new(data, 0, false); |
66 | assert_eq!(reader.read(&mut buf).unwrap(), 0); |
67 | |
68 | let mut reader = Crc32Reader::new(data, 1, false); |
69 | assert!(reader |
70 | .read(&mut buf) |
71 | .unwrap_err() |
72 | .to_string() |
73 | .contains("Invalid checksum" )); |
74 | } |
75 | |
76 | #[test ] |
77 | fn test_byte_by_byte() { |
78 | let data: &[u8] = b"1234" ; |
79 | let mut buf = [0; 1]; |
80 | |
81 | let mut reader = Crc32Reader::new(data, 0x9be3e0a3, false); |
82 | assert_eq!(reader.read(&mut buf).unwrap(), 1); |
83 | assert_eq!(reader.read(&mut buf).unwrap(), 1); |
84 | assert_eq!(reader.read(&mut buf).unwrap(), 1); |
85 | assert_eq!(reader.read(&mut buf).unwrap(), 1); |
86 | assert_eq!(reader.read(&mut buf).unwrap(), 0); |
87 | // Can keep reading 0 bytes after the end |
88 | assert_eq!(reader.read(&mut buf).unwrap(), 0); |
89 | } |
90 | |
91 | #[test ] |
92 | fn test_zero_read() { |
93 | let data: &[u8] = b"1234" ; |
94 | let mut buf = [0; 5]; |
95 | |
96 | let mut reader = Crc32Reader::new(data, 0x9be3e0a3, false); |
97 | assert_eq!(reader.read(&mut buf[..0]).unwrap(), 0); |
98 | assert_eq!(reader.read(&mut buf).unwrap(), 4); |
99 | } |
100 | } |
101 | |