1use hex::FromHex;
5use md5::{digest::generic_array::GenericArray, Digest, Md5};
6use sha1::Sha1;
7use sha2::{Sha256, Sha512};
8use std::{io, path::Path};
9use thiserror::Error;
10
11use crate::request::RequestChecksum;
12
13#[derive(Debug, Error)]
14pub enum ChecksumError {
15 #[error("checksum invalid: {0}")]
16 InvalidInput(String),
17
18 #[error("unable to open the file to validate")]
19 FileOpen(#[source] io::Error),
20
21 #[error(
22 "file does not match expected size: found {} KiB but expected {} KiB",
23 found,
24 expected
25 )]
26 InvalidSize { found: u64, expected: u64 },
27
28 #[error("error during read of file")]
29 FileRead(#[source] io::Error),
30
31 #[error("checksum mismatch")]
32 Mismatch,
33}
34
35pub fn compare_hash(
36 path: &Path,
37 expected_size: u64,
38 expected_hash: &RequestChecksum,
39) -> Result<(), ChecksumError> {
40 use std::io::Read;
41
42 let mut file = std::fs::File::open(path).map_err(ChecksumError::FileOpen)?;
43
44 let file_size = file.metadata().unwrap().len();
45 if file_size != expected_size {
46 return Err(ChecksumError::InvalidSize {
47 found: file_size / 1024,
48 expected: expected_size / 1024,
49 });
50 }
51
52 match expected_hash {
53 RequestChecksum::Md5(sum) => {
54 let expected = <[u8; 16]>::from_hex(sum)
55 .map(GenericArray::from)
56 .map_err(|_| ChecksumError::InvalidInput(format!("MD5 {}", sum)))?;
57
58 let mut buffer = vec![0u8; 8 * 1024];
59 let mut hasher = Md5::new();
60
61 loop {
62 match file.read(&mut buffer) {
63 Ok(0) => break,
64 Ok(bytes) => hasher.update(&buffer[..bytes]),
65 Err(why) => return Err(ChecksumError::FileRead(why)),
66 }
67 }
68
69 let hash = &*hasher.finalize();
70
71 if &*expected == hash {
72 Ok(())
73 } else {
74 Err(ChecksumError::Mismatch)
75 }
76 }
77 RequestChecksum::Sha1(sum) => {
78 let expected = <[u8; 20]>::from_hex(sum)
79 .map(GenericArray::from)
80 .map_err(|_| ChecksumError::InvalidInput(format!("SHA1 {}", sum)))?;
81
82 let mut buffer = vec![0u8; 8 * 1024];
83 let mut hasher = Sha1::new();
84
85 loop {
86 match file.read(&mut buffer) {
87 Ok(0) => break,
88 Ok(bytes) => hasher.update(&buffer[..bytes]),
89 Err(why) => return Err(ChecksumError::FileRead(why)),
90 }
91 }
92
93 let hash = &*hasher.finalize();
94
95 if &*expected == hash {
96 Ok(())
97 } else {
98 Err(ChecksumError::Mismatch)
99 }
100 }
101 RequestChecksum::Sha256(sum) => {
102 let expected = <[u8; 32]>::from_hex(sum)
103 .map(GenericArray::from)
104 .map_err(|_| ChecksumError::InvalidInput(format!("SHA256 {}", sum)))?;
105
106 let mut buffer = vec![0u8; 8 * 1024];
107 let mut hasher = Sha256::new();
108
109 loop {
110 match file.read(&mut buffer) {
111 Ok(0) => break,
112 Ok(bytes) => hasher.update(&buffer[..bytes]),
113 Err(why) => return Err(ChecksumError::FileRead(why)),
114 }
115 }
116
117 let hash = &*hasher.finalize();
118
119 if &*expected == hash {
120 Ok(())
121 } else {
122 Err(ChecksumError::Mismatch)
123 }
124 }
125 RequestChecksum::Sha512(sum) => {
126 let expected = <[u8; 64]>::from_hex(sum)
127 .map(GenericArray::from)
128 .map_err(|_| ChecksumError::InvalidInput(format!("SHA512 {}", sum)))?;
129
130 let mut buffer = vec![0u8; 8 * 1024];
131 let mut hasher = Sha512::new();
132
133 loop {
134 match file.read(&mut buffer) {
135 Ok(0) => break,
136 Ok(bytes) => hasher.update(&buffer[..bytes]),
137 Err(why) => return Err(ChecksumError::FileRead(why)),
138 }
139 }
140
141 let hash = &*hasher.finalize();
142
143 if &*expected == hash {
144 Ok(())
145 } else {
146 Err(ChecksumError::Mismatch)
147 }
148 }
149 }
150}