oci_image_spec/image_digest/
algorithm.rs1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3use std::io::{Error, ErrorKind};
4
5pub const SHA256: &str = "sha256";
7pub const SHA384: &str = "sha384";
9pub const SHA512: &str = "sha512";
11pub const BLAKE3: &str = "blake3";
13
14pub const CANONICAL: &str = SHA256;
18
19use digest::DynDigest;
20use sha2::{Digest, Sha256, Sha384, Sha512};
21
22pub trait CryptoHash {
24 fn available(self) -> bool;
26 fn size(self) -> isize;
28 fn string(self) -> &'static str;
30 fn set(self, _: &str) -> Result<Self, Error>
32 where
33 Self: Sized;
34 fn digester(&self) -> Box<dyn DynDigest>;
38 fn hash(self) -> Box<dyn DynDigest>;
41 fn encode(&self, _: &[u8]) -> String;
44 fn from_reader<R: std::io::Read>(&self, _: R) -> String;
46 fn from_bytes(&self, _: &[u8]) -> String;
48 fn from_string(&self, _: &str) -> String;
50 fn from_file(&self, _: &str) -> Result<String, Error>;
52 fn validate(&self, _: &str) -> bool;
54}
55
56#[derive(Serialize, Deserialize, Debug, Clone)]
57pub struct Algorithm<'a> {
58 pub name: &'a str,
59 pub bitsize: isize,
60}
61
62impl Algorithm<'_> {
63 pub fn new(name: &str, size: isize) -> Algorithm {
64 Algorithm {
65 name,
66 bitsize: size,
67 }
68 }
69}
70
71impl CryptoHash for Algorithm<'static> {
72 fn available(self) -> bool {
73 true
74 }
75
76 fn string(self) -> &'static str {
77 self.name
78 }
79
80 fn size(self) -> isize {
81 self.bitsize
82 }
83
84 fn set(self, name: &str) -> Result<Self, Error> {
85 match name {
86 SHA256 => Ok(Algorithm::new(SHA256, 256)),
87 SHA384 => Ok(Algorithm::new(SHA384, 384)),
88 SHA512 => Ok(Algorithm::new(SHA512, 512)),
89 _ => Err(Error::new(ErrorKind::Other, "Unsupported algorithm")),
90 }
91 }
92
93 fn digester(&self) -> Box<dyn DynDigest> {
94 match self.name {
95 SHA256 => Box::new(Sha256::new()),
96 SHA384 => Box::new(Sha384::new()),
97 SHA512 => Box::new(Sha512::new()),
98 _ => panic!("Unsupported algorithm"),
99 }
100 }
101
102 fn hash(self) -> Box<dyn DynDigest> {
103 self.digester()
104 }
105
106 fn encode(&self, bytes: &[u8]) -> String {
107 let mut digest: Box<dyn DynDigest>;
108 match self.name {
109 SHA256 => {
110 digest = Box::new(Sha256::new());
111 }
112 SHA384 => {
113 digest = Box::new(Sha384::new());
114 }
115 SHA512 => {
116 digest = Box::new(Sha512::new());
117 }
118 BLAKE3 => {
119 let mut digest = blake3::Hasher::new();
120 digest.update(bytes);
121 return hex::encode(digest.finalize().as_bytes());
122 }
123 _ => panic!("Unsupported algorithm"),
124 };
125 digest.update(bytes);
126 hex::encode(digest.finalize())
127 }
128
129 fn from_reader<R: std::io::Read>(&self, reader: R) -> String {
130 let mut digest: Box<dyn DynDigest>;
131 match self.name {
132 SHA256 => {
133 digest = Box::new(Sha256::new());
134 }
135 SHA384 => {
136 digest = Box::new(Sha384::new());
137 }
138 SHA512 => {
139 digest = Box::new(Sha512::new());
140 }
141 BLAKE3 => {
142 let mut digest = blake3::Hasher::new();
143 let mut reader = reader;
144 let mut buffer = [0; 1024];
145 loop {
146 let len = match reader.read(&mut buffer) {
147 Ok(len) => len,
148 Err(_) => break,
149 };
150 if len == 0 {
151 break;
152 }
153 digest.update(&buffer[..len]);
154 }
155 return hex::encode(digest.finalize().as_bytes());
156 }
157 _ => panic!("Unsupported algorithm"),
158 };
159 let mut reader = reader;
160 let mut buffer = [0; 1024];
161 loop {
162 let len = match reader.read(&mut buffer) {
163 Ok(len) => len,
164 Err(_) => break,
165 };
166 if len == 0 {
167 break;
168 }
169 digest.update(&buffer[..len]);
170 }
171 hex::encode(digest.finalize())
172 }
173
174 fn from_bytes(&self, bytes: &[u8]) -> String {
175 let mut digest: Box<dyn DynDigest>;
176 match self.name {
177 SHA256 => {
178 digest = Box::new(Sha256::new());
179 }
180 SHA384 => {
181 digest = Box::new(Sha384::new());
182 }
183 SHA512 => {
184 digest = Box::new(Sha512::new());
185 }
186 BLAKE3 => {
187 let mut digest = blake3::Hasher::new();
188 digest.update(bytes);
189 return hex::encode(digest.finalize().as_bytes());
190 }
191 _ => panic!("Unsupported algorithm"),
192 };
193 digest.update(bytes);
194 hex::encode(digest.finalize())
195 }
196
197 fn from_string(&self, str: &str) -> String {
198 let mut digest: Box<dyn DynDigest>;
199 match self.name {
200 SHA256 => {
201 digest = Box::new(Sha256::new());
202 }
203 SHA384 => {
204 digest = Box::new(Sha384::new());
205 }
206 SHA512 => {
207 digest = Box::new(Sha512::new());
208 }
209 BLAKE3 => {
210 let mut digest = blake3::Hasher::new();
211 digest.update(str.as_bytes());
212 return hex::encode(digest.finalize().as_bytes());
213 }
214 _ => panic!("Unsupported algorithm"),
215 };
216 digest.update(str.as_bytes());
217 hex::encode(digest.finalize())
218 }
219
220 fn from_file(&self, path: &str) -> Result<String, Error> {
221 let mut digest: Box<dyn DynDigest>;
222 match self.name {
223 SHA256 => {
224 digest = Box::new(Sha256::new());
225 }
226 SHA384 => {
227 digest = Box::new(Sha384::new());
228 }
229 SHA512 => {
230 digest = Box::new(Sha512::new());
231 }
232 BLAKE3 => {
233 let mut digest = blake3::Hasher::new();
234 let mut file = std::fs::File::open(path)?;
235 let mut buffer = [0; 1024];
236 loop {
237 let len = match std::io::Read::read(&mut file, &mut buffer) {
238 Ok(len) => len,
239 Err(_) => break,
240 };
241 if len == 0 {
242 break;
243 }
244 digest.update(&buffer[..len]);
245 }
246 return Ok(hex::encode(digest.finalize().as_bytes()));
247 }
248 _ => panic!("Unsupported algorithm"),
249 };
250 let mut file = std::fs::File::open(path)?;
251 let mut buffer = [0; 1024];
252 loop {
253 let len = match std::io::Read::read(&mut file, &mut buffer) {
254 Ok(len) => len,
255 Err(_) => break,
256 };
257 if len == 0 {
258 break;
259 }
260 digest.update(&buffer[..len]);
261 }
262 Ok(hex::encode(digest.finalize()))
263 }
264
265 fn validate(&self, str: &str) -> bool {
266 let re = regex::Regex::new(&format!(r"^[0-9a-f]{{{}}}$", self.bitsize / 4)).unwrap();
267 re.is_match(str)
268 }
269}
270
271pub struct Algorithms<'a> {
273 algorithms: HashMap<&'a str, isize>,
274}
275
276impl<'a> Algorithms<'a> {
277 pub fn new() -> Self {
278 let mut algs = Algorithms {
279 algorithms: HashMap::new(),
280 };
281 algs.register_algorithm(CANONICAL, 256);
282 algs.register_algorithm(SHA256, 256);
283 algs.register_algorithm(SHA384, 384);
284 algs.register_algorithm(SHA512, 512);
285 algs.register_algorithm(BLAKE3, 256);
286 algs
287 }
288
289 pub fn register_algorithm(self: &mut Self, name: &'a str, size: isize) -> bool {
291 match self.algorithms.get(name) {
292 Some(_) => false,
293 None => {
294 self.algorithms.insert(name, size);
295 true
296 }
297 }
298 }
299
300 pub fn get_algorithm(self: &Self, name: &'a str) -> Option<Algorithm<'a>> {
301 match self.algorithms.get(name) {
302 Some(size) => Some(Algorithm::new(name, *size)),
303 None => None,
304 }
305 }
306}
307
308#[cfg(test)]
309mod tests {
310 use super::{Algorithms, CryptoHash};
311
312 #[test]
313 fn encode_canonical() {
314 let algs = Algorithms::new();
315 let alg = algs.get_algorithm(super::CANONICAL).unwrap();
316 assert_eq!(
317 alg.encode(b"hello"),
318 "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
319 );
320 assert_eq!(
321 alg.encode(b"hello"),
322 "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
323 );
324 }
325
326 #[test]
327 fn encode() {
328 let algs = Algorithms::new();
329 let alg = algs.get_algorithm(super::SHA256).unwrap();
330 assert_eq!(
331 alg.encode(b"hello"),
332 "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
333 );
334 assert_eq!(
335 alg.encode(b"hello"),
336 "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
337 );
338 }
339
340 #[test]
341 fn encode_blake3() {
342 let algs = Algorithms::new();
343 let alg = algs.get_algorithm(super::BLAKE3).unwrap();
344 assert_eq!(
345 alg.encode(b"hello"),
346 "ea8f163db38682925e4491c5e58d4bb3506ef8c14eb78a86e908c5624a67200f"
347 );
348 }
349
350 #[test]
351 fn from_reader() {
352 let algs = Algorithms::new();
353 let alg = algs.get_algorithm(super::SHA256).unwrap();
354 assert_eq!(
355 alg.from_reader(b"hello".as_ref()),
356 "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
357 );
358 }
359
360 #[test]
361 fn from_bytes() {
362 let algs = Algorithms::new();
363 let alg = algs.get_algorithm(super::SHA256).unwrap();
364 assert_eq!(
365 alg.from_bytes(b"hello"),
366 "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
367 );
368 }
369
370 #[test]
371 fn from_file() {
372 let algs = Algorithms::new();
373 let alg = algs.get_algorithm(super::SHA256).unwrap();
374 assert_eq!(
375 alg.from_file(".gitignore").unwrap(),
376 "fca9125f1d755424b55a18877213a746135c1ce0087f9471a6f98cdd4600df83"
377 );
378 }
379
380 #[test]
381 fn validate() {
382 let algs = Algorithms::new();
383 let alg = algs.get_algorithm(super::SHA256).unwrap();
384 assert_eq!(
385 alg.validate("2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"),
386 true
387 );
388 }
389
390 #[test]
391 fn validate_blake3() {
392 let algs = Algorithms::new();
393 let alg = algs.get_algorithm(super::BLAKE3).unwrap();
394 assert_eq!(
395 alg.validate("2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"),
396 true
397 );
398 }
399}