1use std::{
2 fmt,
3 str::{from_utf8, FromStr},
4};
5
6#[derive(Debug)]
7pub enum HashError {
8 Undefined,
9 InvalidDigest { hex_digest: String },
10 DigestLength { raw_digest_length: String },
11 DigestLengthMissmatch { length: usize, digest: Vec<u8> },
12}
13
14#[derive(Debug)]
15pub struct HashConfig {
16 pub salt: Option<Box<Vec<u8>>>,
17}
18
19#[derive(Debug, PartialEq)]
20pub enum HashType {
21 Blake3,
22 CRC,
23 Argon2,
24}
25
26impl fmt::Display for HashType {
27 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
28 match *self {
29 HashType::Argon2 => {
30 write!(f, "{:02}", 3)
31 }
32 HashType::CRC => {
33 write!(f, "{:02}", 2)
34 }
35 _ => {
36 write!(f, "{:02}", 1)
37 }
38 }
39 }
40}
41
42#[derive(Debug)]
59pub struct DispnetHash {
60 pub hash_type: HashType,
61 pub digest_length: usize,
62 pub digest_value: Vec<u8>,
63 pub digest_encoded: u64,
64 value: String,
65}
66
67trait Hash {
68 fn equal(hash: DispnetHash) -> bool;
69 fn upgrade();
70}
71
72impl DispnetHash {
73 pub fn new(value: &[u8]) -> Self {
75 DispnetHash::create(HashType::Blake3, value, None)
76 }
77
78 pub fn create(hash_type: HashType, value: &[u8], config: Option<HashConfig>) -> Self {
92 let internal_hash = InternalDispnetHash::new(hash_type, value, config);
93 let internal_hash_value = format!("{}", internal_hash);
94 let encoded: u64 = DispnetHash::encoded_u64(&internal_hash.digest_value);
95 Self {
96 hash_type: internal_hash.hash_type,
97 digest_length: internal_hash.digest_length,
98 digest_value: internal_hash.digest_value,
99 digest_encoded: encoded,
100 value: internal_hash_value,
101 }
102 }
103
104 pub fn verify(hash: &str, value: &[u8]) -> bool {
117 let dispnet_hash = hash.parse::<DispnetHash>();
118 if let Ok(hash) = dispnet_hash {
119 return DispnetHash::verify_instance(&hash, value);
120 }
121 false
122 }
123
124 pub fn verify_instance(hash: &DispnetHash, value: &[u8]) -> bool {
137 let str_hash = from_utf8(&hash.digest_value).unwrap();
138 let matches_result = argon2::verify_encoded(str_hash, value);
139 if let Ok(matches) = matches_result {
140 return matches;
141 }
142 false
143 }
144
145 fn parse(hash_value: &str) -> Result<Self, HashError> {
146 let internal_hash_result = InternalDispnetHash::parse(hash_value);
147 if let Ok(internal_hash) = internal_hash_result {
148 let internal_hash_value = format!("{}", internal_hash);
149 let encoded: u64 = DispnetHash::encoded_u64(&internal_hash.digest_value);
150 return Ok(Self {
151 hash_type: internal_hash.hash_type,
152 digest_length: internal_hash.digest_length,
153 digest_value: internal_hash.digest_value,
154 digest_encoded: encoded,
155 value: internal_hash_value,
156 });
157 }
158 Err(internal_hash_result.err().unwrap())
159 }
160
161 pub fn hex_to_bytes(s: &str) -> Option<Vec<u8>> {
174 if s.len() % 2 == 0 {
175 (0..s.len())
176 .step_by(2)
177 .map(|i| {
178 s.get(i..i + 2)
179 .and_then(|sub| u8::from_str_radix(sub, 16).ok())
180 })
181 .collect()
182 } else {
183 None
184 }
185 }
186
187 pub fn bytes_to_hex(bytes: &[u8]) -> String {
199 bytes.iter().map(|b| format!("{:02x}", b)).collect()
200 }
201
202 pub fn encoded_u64(bytes: &[u8]) -> u64 {
216 if bytes.len() < 8 {
217 let mut b = [0; 8];
218 b[..bytes.len()].copy_from_slice(bytes);
219 return u64::from_le_bytes(b);
220 }
221 u64::from_le_bytes(bytes[(bytes.len() - 8)..].try_into().unwrap())
222 }
223}
224
225impl fmt::Display for DispnetHash {
226 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
227 write!(f, "{}", self.value)
228 }
229}
230
231impl PartialEq for DispnetHash {
232 fn eq(&self, other: &Self) -> bool {
233 self.value == other.value
234 }
235}
236
237impl PartialEq<String> for DispnetHash {
238 fn eq(&self, other: &String) -> bool {
239 self.value == *other
240 }
241}
242
243impl FromStr for DispnetHash {
244 type Err = HashError;
245
246 fn from_str(s: &str) -> Result<Self, HashError> {
247 DispnetHash::parse(s)
248 }
249}
250
251#[derive(Debug)]
252struct InternalDispnetHash {
253 pub hash_type: HashType,
254 pub digest_length: usize,
255 pub digest_value: Vec<u8>,
256}
257
258impl InternalDispnetHash {
259 fn new(hash_type: HashType, value: &[u8], config: Option<HashConfig>) -> Self {
260 let mut _hash_config: HashConfig = HashConfig { salt: None };
261 let mut config_hash_salt: Box<Vec<u8>> =
262 Box::new("A8nUz1Pkc0IZ0uJSZNnMlvdLz0T3al5Hjhg2".as_bytes().to_owned());
263 let salt: &[u8];
264
265 if let Some(_hash_config) = config {
266 if let Some(config_hash_salt_value) = _hash_config.salt {
267 config_hash_salt = config_hash_salt_value;
268 salt = &(*config_hash_salt);
269 } else {
270 salt = &(*config_hash_salt);
271 }
272 } else {
273 salt = &(*config_hash_salt);
274 }
275 match hash_type {
276 HashType::Argon2 => {
277 let argon2_config = argon2::Config::default();
278 let hash = argon2::hash_encoded(value, salt, &argon2_config).unwrap();
279 Self {
280 hash_type: HashType::Argon2,
281 digest_length: hash.len(),
282 digest_value: hash.into_bytes().to_vec(),
283 }
284 }
285 HashType::CRC => {
286 let crc32 = crc::Crc::<u32>::new(&crc::CRC_32_ISCSI);
287 let hash = crc32.checksum(value).to_string();
288 Self {
289 hash_type: HashType::CRC,
290 digest_length: hash.len(),
291 digest_value: hash.into_bytes().to_vec(),
292 }
293 }
294 _ => {
295 let hash = blake3::hash(value);
296 let hash_bytes = hash.as_bytes();
297 Self {
298 hash_type: HashType::Blake3,
299 digest_length: hash_bytes.len(),
300 digest_value: hash_bytes.to_vec(),
301 }
302 }
303 }
304 }
305
306 fn parse(hash_value: &str) -> Result<Self, HashError> {
307 let (raw_type, raw_digest_len_value) = hash_value.split_at(2);
308 let (raw_digest_len, raw_digest_value) = raw_digest_len_value.split_at(4);
309 let mut type_result = HashType::Blake3;
310 let raw_type_result = raw_type.parse::<u8>();
311 if let Ok(raw_type) = raw_type_result {
312 match raw_type {
313 3 => {
314 type_result = HashType::Argon2;
315 }
316 2 => {
317 type_result = HashType::CRC;
318 }
319 _ => {
320 type_result = HashType::Blake3;
321 }
322 }
323 } else {
324 println!(
325 "Invalid hash type raw value:{}. Use Blake3 as fallback!",
326 raw_type
327 );
328 }
329
330 let hex_result = DispnetHash::hex_to_bytes(raw_digest_value);
331 if let Some(hash_bytes) = hex_result {
332 let digest_len_result = raw_digest_len.parse::<usize>();
333 if let Ok(hash_bytes_len) = digest_len_result {
334 if hash_bytes_len == hash_bytes.len() {
335 Ok(Self {
336 hash_type: type_result,
337 digest_length: hash_bytes_len,
338 digest_value: hash_bytes,
339 })
340 } else {
341 println!(
342 "Length missmatch for digest. Length:{} Digest:{}",
343 hash_bytes_len,
344 hash_bytes.len()
345 );
346 Err(HashError::DigestLengthMissmatch {
347 length: hash_bytes_len,
348 digest: hash_bytes,
349 })
350 }
351 } else {
352 println!("Digest length is not a valid usize:{}", raw_digest_len);
353 Err(HashError::DigestLength {
354 raw_digest_length: raw_digest_len.to_owned(),
355 })
356 }
357 } else {
358 println!("Invalid digest hex value:{}", raw_digest_value);
359 Err(HashError::InvalidDigest {
360 hex_digest: raw_digest_value.to_owned(),
361 })
362 }
363 }
364}
365
366impl fmt::Display for InternalDispnetHash {
367 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
368 write!(
369 f,
370 "{}{:04}{}",
371 self.hash_type,
372 self.digest_length,
373 self.digest_value
374 .iter()
375 .map(|x| format!("{:02x}", x))
376 .collect::<String>()
377 )
378 }
379}
380
381#[cfg(test)]
382mod tests {
383 use crate::{DispnetHash, HashType, HashConfig};
384
385 #[test]
386 fn new_hash() {
387 let dispnet_hash = DispnetHash::new("test".as_bytes());
388 let display_hash = format!("{}", dispnet_hash);
389 assert_eq!(display_hash, "0100324878ca0425c739fa427f7eda20fe845f6b2e46ba5fe2a14df5b1e32f50603215");
390 }
391
392 #[test]
393 fn create_blake3_hash() {
394 let dispnet_hash = DispnetHash::create(HashType::Blake3, "test".as_bytes(), None);
395 let display_hash = format!("{}", dispnet_hash);
396 assert_eq!(display_hash, "0100324878ca0425c739fa427f7eda20fe845f6b2e46ba5fe2a14df5b1e32f50603215");
397 assert_eq!(dispnet_hash.digest_encoded, 1527389121149121013);
398 }
399
400 #[test]
401 fn create_crc32_hash() {
402 let dispnet_hash = DispnetHash::create(HashType::CRC, "test".as_bytes(), None);
403 let display_hash = format!("{}", dispnet_hash);
404 assert_eq!(display_hash, "02001032323538363632303830");
405 assert_eq!(dispnet_hash.digest_encoded, 3474580104732358709);
406 }
407
408 #[test]
409 fn create_argon2_hash() {
410 let dispnet_hash = DispnetHash::create(HashType::Argon2, "test".as_bytes(), None);
411 let display_hash = format!("{}", dispnet_hash);
412 assert_eq!(display_hash, "030121246172676f6e326924763d3139246d3d343039362c743d332c703d31245154687556586f785547746a4d456c614d48564b5531704f626b3173646d524d656a42554d3246734e5568716147637924464d4f7a6f46647754464676397a31435a485751684b7a2f63696f754c55427571494a54756a574d375338");
413 assert_eq!(dispnet_hash.digest_encoded, 4058648494509552980);
414 }
415
416 #[test]
417 fn create_argon2_salt_hash() {
418 let dispnet_hash = DispnetHash::create(HashType::Argon2, "test".as_bytes(), Some(HashConfig { salt: Some(Box::new(b"12345678".to_vec())) }));
419 let display_hash = format!("{}", dispnet_hash);
420 assert_eq!(display_hash, "030084246172676f6e326924763d3139246d3d343039362c743d332c703d31244d54497a4e4455324e7a6724686f56354d494638596a39746b39356c467365546279554a6e393336484944586754685533637065643151");
421 assert_eq!(dispnet_hash.digest_encoded, 5850567777771008853);
422 }
423
424 #[test]
425 fn parse_hash() {
426 let dispnet_hash = "0100324878ca0425c739fa427f7eda20fe845f6b2e46ba5fe2a14df5b1e32f50603215".parse::<DispnetHash>().unwrap();
427 assert_eq!(dispnet_hash.hash_type, HashType::Blake3);
428 assert_eq!(dispnet_hash.digest_length, 32);
429 assert_eq!(dispnet_hash.digest_value.len(), 32);
430 }
431
432 #[test]
433 fn parse_crc32_hash() {
434 let dispnet_hash = "02001032323538363632303830".parse::<DispnetHash>().unwrap();
435 assert_eq!(dispnet_hash.hash_type, HashType::CRC);
436 assert_eq!(dispnet_hash.digest_length, 10);
437 assert_eq!(dispnet_hash.digest_value.len(), 10);
438 }
439
440 #[test]
441 fn parse_argon2_hash() {
442 let dispnet_hash = "030121246172676f6e326924763d3139246d3d343039362c743d332c703d31245154687556586f785547746a4d456c614d48564b5531704f626b3173646d524d656a42554d3246734e5568716147637924464d4f7a6f46647754464676397a31435a485751684b7a2f63696f754c55427571494a54756a574d375338".parse::<DispnetHash>().unwrap();
443 assert_eq!(dispnet_hash.hash_type, HashType::Argon2);
444 assert_eq!(dispnet_hash.digest_length, 121);
445 assert_eq!(dispnet_hash.digest_value.len(), 121);
446 }
447
448 #[test]
449 fn parse_argon2_salt_hash() {
450 let dispnet_hash = "030084246172676f6e326924763d3139246d3d343039362c743d332c703d31244d54497a4e4455324e7a6724686f56354d494638596a39746b39356c467365546279554a6e393336484944586754685533637065643151".parse::<DispnetHash>().unwrap();
451 assert_eq!(dispnet_hash.hash_type, HashType::Argon2);
452 assert_eq!(dispnet_hash.digest_length, 84);
453 assert_eq!(dispnet_hash.digest_value.len(), 84);
454 }
455
456 #[test]
457 fn compare_hash_instances() {
458 let dispnet_hash_1 = DispnetHash::new("test".as_bytes());
459 let dispnet_hash_2 = DispnetHash::new("test".as_bytes());
460 assert_eq!(dispnet_hash_1, dispnet_hash_2);
461 }
462
463 #[test]
464 fn compare_crc32_hash_instances() {
465 let dispnet_hash_1 = DispnetHash::create(HashType::CRC, "test".as_bytes(), None);
466 let dispnet_hash_2 = DispnetHash::create(HashType::CRC, "test".as_bytes(), None);
467 assert_eq!(dispnet_hash_1, dispnet_hash_2);
468 }
469
470 #[test]
471 fn compare_argon2_hash_instances() {
472 let dispnet_hash_1 = DispnetHash::create(HashType::Argon2, "test".as_bytes(), None);
473 let dispnet_hash_2 = DispnetHash::create(HashType::Argon2, "test".as_bytes(), None);
474 assert_eq!(dispnet_hash_1, dispnet_hash_2);
475 }
476
477 #[test]
478 fn compare_argon2_salt_hash_instances() {
479 let dispnet_hash_1 = DispnetHash::create(HashType::Argon2, "test".as_bytes(), Some(HashConfig { salt: Some(Box::new(b"12345678".to_vec())) }));
480 let dispnet_hash_2 = DispnetHash::create(HashType::Argon2, "test".as_bytes(), Some(HashConfig { salt: Some(Box::new(b"12345678".to_vec())) }));
481 assert_eq!(dispnet_hash_1, dispnet_hash_2);
482 }
483
484 #[test]
485 fn compare_hash_instance_and_prase() {
486 let dispnet_hash_1 = DispnetHash::new("test".as_bytes());
487 let dispnet_hash_2 = "0100324878ca0425c739fa427f7eda20fe845f6b2e46ba5fe2a14df5b1e32f50603215".parse::<DispnetHash>().unwrap();
488 assert_eq!(dispnet_hash_1, dispnet_hash_2);
489 }
490
491 #[test]
492 fn compare_crc32_hash_instance_and_prase() {
493 let dispnet_hash_1 = DispnetHash::create(HashType::CRC, "test".as_bytes(), None);
494 let dispnet_hash_2 = "02001032323538363632303830".parse::<DispnetHash>().unwrap();
495 assert_eq!(dispnet_hash_1, dispnet_hash_2);
496 }
497
498 #[test]
499 fn compare_argon2_hash_instance_and_prase() {
500 let dispnet_hash_1 = DispnetHash::create(HashType::Argon2, "test".as_bytes(), None);
501 let dispnet_hash_2 = "030121246172676f6e326924763d3139246d3d343039362c743d332c703d31245154687556586f785547746a4d456c614d48564b5531704f626b3173646d524d656a42554d3246734e5568716147637924464d4f7a6f46647754464676397a31435a485751684b7a2f63696f754c55427571494a54756a574d375338".parse::<DispnetHash>().unwrap();
502 assert_eq!(dispnet_hash_1, dispnet_hash_2);
503 }
504
505 #[test]
506 fn compare_argon2_salt_hash_instance_and_prase() {
507 let dispnet_hash_1 = DispnetHash::create(HashType::Argon2, "test".as_bytes(), Some(HashConfig { salt: Some(Box::new(b"12345678".to_vec())) }));
508 let dispnet_hash_2 = "030084246172676f6e326924763d3139246d3d343039362c743d332c703d31244d54497a4e4455324e7a6724686f56354d494638596a39746b39356c467365546279554a6e393336484944586754685533637065643151".parse::<DispnetHash>().unwrap();
509 assert_eq!(dispnet_hash_1, dispnet_hash_2);
510 }
511
512 #[test]
513 fn compare_hash_instance_and_string() {
514 let dispnet_hash_1 = DispnetHash::new("test".as_bytes());
515 assert_eq!(dispnet_hash_1, "0100324878ca0425c739fa427f7eda20fe845f6b2e46ba5fe2a14df5b1e32f50603215".to_owned());
516 }
517
518 #[test]
519 fn compare_crc32_hash_instance_and_string() {
520 let dispnet_hash_1 = DispnetHash::create(HashType::CRC, "test".as_bytes(), None);
521 assert_eq!(dispnet_hash_1, "02001032323538363632303830".to_owned());
522 }
523
524 #[test]
525 fn verify_argon2_hash() {
526 assert!(DispnetHash::verify("030084246172676f6e326924763d3139246d3d343039362c743d332c703d31244d54497a4e4455324e7a6724686f56354d494638596a39746b39356c467365546279554a6e393336484944586754685533637065643151", "test".as_bytes()));
527 assert!(!DispnetHash::verify("030084246172676f6e326924763d3139246d3d343039362c743d332c703d31244d54497a4e4455324e7a6724686f56354d494638596a39746b39356c467365546279554a6e393336484944586754685533637065644262", "test".as_bytes()));
528 }
529
530 #[test]
531 fn hex() {
532 assert_eq!(DispnetHash::bytes_to_hex("test".as_bytes()), "74657374");
533 assert_eq!(DispnetHash::hex_to_bytes("74657374").unwrap(), "test".as_bytes());
534 }
535
536 #[test]
537 fn encoded_u64() {
538 assert_eq!(DispnetHash::encoded_u64("test".as_bytes()), 1953719668);
539 assert_eq!(DispnetHash::encoded_u64("a".as_bytes()), 97);
540 assert_eq!(DispnetHash::encoded_u64("aasdsakdljaslfhaksjhuahwiuewasdfgs4354sg".as_bytes()), 7454359211325289319);
541 }
542}