1use sha2::Digest;
6use std::fs::File;
7use std::io::Read;
8use std::path::Path;
9
10use super::error::Error;
11
12#[inline]
13pub fn crc32<P: AsRef<Path>>(file: P) -> Result<String, Error> {
14 let mut reader = File::open(&file)?;
15 let mut hasher = crc32fast::Hasher::new();
16
17 let mut buffer = Vec::with_capacity(16 * 1024);
18 loop {
19 let n_read = reader.read_to_end(&mut buffer)?;
20 if n_read == 0 {
21 break;
22 }
23 hasher.update(&buffer[..n_read]);
24 }
25 let checksum = hasher.finalize();
26 Ok(checksum.to_string())
27}
28
29#[inline]
30pub fn b2sum<P: AsRef<Path>>(file: P, options: &Options) -> Result<String, Error> {
31 checksum(file, HashAlgo::B2, options)
32}
33
34#[inline]
35pub fn md5sum<P: AsRef<Path>>(file: P, options: &Options) -> Result<String, Error> {
36 checksum(file, HashAlgo::Md5, options)
37}
38
39#[inline]
40pub fn sha1sum<P: AsRef<Path>>(file: P, options: &Options) -> Result<String, Error> {
41 checksum(file, HashAlgo::Sha1, options)
42}
43
44#[inline]
45pub fn sha224sum<P: AsRef<Path>>(file: P, options: &Options) -> Result<String, Error> {
46 checksum(file, HashAlgo::Sha224, options)
47}
48
49#[inline]
50pub fn sha256sum<P: AsRef<Path>>(file: P, options: &Options) -> Result<String, Error> {
51 checksum(file, HashAlgo::Sha256, options)
52}
53
54#[inline]
55pub fn sha384sum<P: AsRef<Path>>(file: P, options: &Options) -> Result<String, Error> {
56 checksum(file, HashAlgo::Sha384, options)
57}
58
59#[inline]
60pub fn sha512sum<P: AsRef<Path>>(file: P, options: &Options) -> Result<String, Error> {
61 checksum(file, HashAlgo::Sha512, options)
62}
63
64#[derive(Debug)]
65pub struct Options {
66 pub binary_mode: bool,
68}
69
70#[inline]
71fn default_binary_mode() -> bool {
72 cfg!(windows)
73}
74
75impl Default for Options {
76 fn default() -> Self {
77 Options {
78 binary_mode: default_binary_mode(),
79 }
80 }
81}
82
83#[derive(Debug)]
84pub struct CheckOptions {
85 pub binary_mode: bool,
87
88 pub status: bool,
89 pub quiet: bool,
90 pub strict: bool,
91 pub warn: bool,
92}
93
94impl Default for CheckOptions {
95 fn default() -> Self {
96 CheckOptions {
97 binary_mode: default_binary_mode(),
98 status: false,
99 quiet: false,
100 strict: false,
101 warn: false,
102 }
103 }
104}
105
106pub fn md5sum_check<P: AsRef<Path>>(_file: P, _option: &CheckOptions) -> Result<bool, Error> {
108 Ok(false)
109}
110
111#[derive(Debug)]
112enum HashAlgo {
113 B2,
114 Md5,
115 Sha1,
116 Sha224,
117 Sha256,
118 Sha384,
119 Sha512,
120}
121
122trait FusionDigest {
123 fn input(&mut self, input: &[u8]);
124 fn result(&mut self, out: &mut [u8]);
125 fn output_bits(&self) -> usize;
126 fn output_bytes(&self) -> usize {
127 (self.output_bits() + 7) / 8
128 }
129 fn result_str(&mut self) -> String {
130 let mut buf: Vec<u8> = vec![0; self.output_bytes()];
131 self.result(&mut buf);
132 hex::encode(&buf)
133 }
134}
135
136impl FusionDigest for blake2b_simd::State {
137 fn input(&mut self, input: &[u8]) {
138 self.update(input);
139 }
140
141 fn result(&mut self, output: &mut [u8]) {
142 output.copy_from_slice(self.finalize().as_bytes());
143 }
144
145 fn output_bits(&self) -> usize {
146 512
147 }
148}
149
150impl FusionDigest for md5::Context {
151 fn input(&mut self, input: &[u8]) {
152 self.consume(input);
153 }
154
155 fn result(&mut self, output: &mut [u8]) {
156 output.copy_from_slice(&*self.clone().compute());
157 }
158
159 fn output_bits(&self) -> usize {
160 128
161 }
162}
163
164impl FusionDigest for sha1::Sha1 {
165 fn input(&mut self, input: &[u8]) {
166 self.update(input);
167 }
168
169 fn result(&mut self, output: &mut [u8]) {
170 output.copy_from_slice(&self.finalize_reset());
171 }
172
173 fn output_bits(&self) -> usize {
174 160
175 }
176}
177
178macro_rules! impl_digest_for_sha2 {
179 ($type: ty, $bits: expr) => {
180 impl FusionDigest for $type {
181 fn input(&mut self, input: &[u8]) {
182 self.update(input);
183 }
184
185 fn result(&mut self, output: &mut [u8]) {
186 output.copy_from_slice(&self.clone().finalize());
187 }
188
189 fn output_bits(&self) -> usize {
190 $bits
191 }
192 }
193 };
194}
195
196impl_digest_for_sha2!(sha2::Sha224, 224);
197impl_digest_for_sha2!(sha2::Sha256, 256);
198impl_digest_for_sha2!(sha2::Sha384, 384);
199impl_digest_for_sha2!(sha2::Sha512, 512);
200
201fn digest_for_algo(algo: HashAlgo) -> Box<dyn FusionDigest> {
202 match algo {
203 HashAlgo::B2 => Box::new(blake2b_simd::State::new()),
204 HashAlgo::Md5 => Box::new(md5::Context::new()),
205 HashAlgo::Sha1 => Box::new(sha1::Sha1::new()),
206 HashAlgo::Sha224 => Box::new(sha2::Sha224::new()),
207 HashAlgo::Sha256 => Box::new(sha2::Sha256::new()),
208 HashAlgo::Sha384 => Box::new(sha2::Sha384::new()),
209 HashAlgo::Sha512 => Box::new(sha2::Sha512::new()),
210 }
211}
212
213fn checksum<P: AsRef<Path>>(file: P, algo: HashAlgo, _options: &Options) -> Result<String, Error> {
214 let mut digest = digest_for_algo(algo);
215 let mut reader = File::open(&file)?;
216
217 let mut buffer = Vec::with_capacity(16 * 1024);
218 loop {
219 let n_read = reader.read_to_end(&mut buffer)?;
220 if n_read == 0 {
221 break;
222 }
223 digest.input(&buffer[..n_read]);
224 }
225
226 Ok(digest.result_str())
227}
228
229#[cfg(test)]
230mod tests {
231 use super::*;
232
233 const RUST_WIKIPEDIA: &str = "tests/Rust_Wikipedia.pdf";
234
235 #[test]
236 fn test_crc32() {
237 let ret = crc32(RUST_WIKIPEDIA);
238 assert!(ret.is_ok());
239 let hex_str = ret.unwrap();
240 assert_eq!(&hex_str, "2751965731");
241 }
242
243 #[test]
244 fn test_b2sum() {
245 let ret = b2sum(RUST_WIKIPEDIA, &Options::default());
246 assert!(ret.is_ok());
247 let hex_str = ret.unwrap();
248 assert_eq!(&hex_str, "e5bd6bbb61edd4cc800ed4982a8226cf0f9582b717702ee5ff79fa286b0f6c70b0533d1f63661b50fa5e739dd78e74616955d7008d6f5c18715ee52235ef32a3");
249 }
250
251 #[test]
252 fn test_md5sum() {
253 let ret = md5sum(RUST_WIKIPEDIA, &Options::default());
254 assert!(ret.is_ok());
255 let hex_str = ret.unwrap();
256 assert_eq!(&hex_str, "c7f5281f3a03cdd3a247966869cf0ba3");
257 }
258
259 #[test]
260 fn test_sha1sum() {
261 let ret = sha1sum(RUST_WIKIPEDIA, &Options::default());
262 assert!(ret.is_ok());
263 let hex_str = ret.unwrap();
264 assert_eq!(&hex_str, "671c0b590385ac473ec3bd4d1c196363252d5d2b");
265 }
266
267 #[test]
268 fn test_sha224sum() {
269 let ret = sha224sum(RUST_WIKIPEDIA, &Options::default());
270 assert!(ret.is_ok());
271 let hex_str = ret.unwrap();
272 assert_eq!(
273 &hex_str,
274 "2cbb67e54546408c512cef08964bb49a3071452347ce1e0ced931a0d"
275 );
276 }
277
278 #[test]
279 fn test_sha256sum() {
280 let ret = sha256sum(RUST_WIKIPEDIA, &Options::default());
281 assert!(ret.is_ok());
282 let hex_str = ret.unwrap();
283 assert_eq!(
284 &hex_str,
285 "79e1819c944cdbe4c02b92647d63a9f9d18f6cc6f3795dcedecc685a8a4d476b"
286 );
287 }
288
289 #[test]
290 fn test_sha384sum() {
291 let ret = sha384sum(RUST_WIKIPEDIA, &Options::default());
292 assert!(ret.is_ok());
293 let hex_str = ret.unwrap();
294 assert_eq!(
295 &hex_str,
296 "e45720b17f461732d4787edd37e23f62149ffb05482fbf2201b885ea6d4f79afcbbd1134112e56d83a479b8fdc02d6cf"
297 );
298 }
299
300 #[test]
301 fn test_sha512sum() {
302 let ret = sha512sum(RUST_WIKIPEDIA, &Options::default());
303 assert!(ret.is_ok());
304 let hex_str = ret.unwrap();
305 assert_eq!(
306 &hex_str,
307 "91a731cb4e6d2f3e63eaff98a99ea0dbd7d3ad70c2e671f533dd98271d270f63356bf8dd154a73d539548a0aed1dcfc69f499ed72d91655f451bb029bbd26c82"
308 );
309 }
310}