hsh 0.0.8

Quantum-Resistant Cryptographic Hash Library for Password Encryption and Verification in Rust.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
// Copyright © 2023-2024 Hash (HSH) library. All rights reserved.
// SPDX-License-Identifier: Apache-2.0 OR MIT

#[cfg(test)]
mod tests {
    use hsh::models::hash::Hash;
    use hsh::models::hash_algorithm::HashAlgorithm;

    #[test]
    fn test_new() {
        let password = "password123";
        let salt = "somesalt";
        let algo = "argon2i";

        let hash = Hash::new(password, salt, algo).unwrap();

        assert_eq!(hash.algorithm, HashAlgorithm::Argon2i);
        assert_eq!(hash.salt, salt.as_bytes().to_vec());
    }

    #[test]
    fn test_new_with_unsupported_algo() {
        let password = "password123";
        let salt = "somesalt";
        let algo = "unsupported_algo";

        let hash = Hash::new(password, salt, algo);

        match hash {
            Ok(_) => {
                panic!("Expected an error for unsupported hash algorithm, but got Ok");
            }
            Err(e) => {
                assert_eq!(
                    e,
                    format!("Unsupported hash algorithm: {}", algo)
                );
            }
        }
    }

    #[test]
    fn test_verify() {
        let password = "password123";
        let salt = "somesalt";
        let algo = "argon2i";

        let hash = Hash::new(password, salt, algo).unwrap();

        assert!(hash.verify(password).unwrap());
        assert!(!hash.verify("wrongpassword").unwrap());
    }

    #[test]
    fn test_from_string() {
        // You'll need to provide a valid hash string here for this test
        let hash_string = "$argon2i$v=19$m=4096,t=3,p=1$c29tZXNhbHQ$RdescudvJCsgt3ub+b+dWRWJTmaaJObG";

        let hash = Hash::from_string(hash_string);

        match hash {
            Ok(hash) => {
                // Assert that the hash, salt, and algorithm are as expected
                assert_eq!(hash.algorithm, HashAlgorithm::Argon2i);
            }
            Err(e) => {
                panic!("Failed to parse hash string: {}", e);
            }
        }
    }

    #[test]
    fn test_from_string_invalid_hash_string() {
        // Provide an invalid hash string
        let invalid_hash_string = "invalid$hash$string";

        let hash = Hash::from_string(invalid_hash_string);

        // Expect an error to be returned
        assert!(hash.is_err());

        // Check the error message
        match hash {
            Err(e) => {
                assert_eq!(e, String::from("Invalid hash string"))
            }
            _ => panic!("Expected Err, got Ok"),
        }
    }

    #[test]
    fn test_generate_salt() {
        let algo = "argon2i";

        let salt = Hash::generate_salt(algo).unwrap();

        // Assert that the salt is of the correct length and format
        assert_eq!(salt.len(), 16);
    }

    #[test]
    fn test_generate_salt_invalid_algorithm() {
        let invalid_algo = "unsupported_algo";

        let salt = Hash::generate_salt(invalid_algo);

        // Expect an error to be returned
        assert!(salt.is_err());

        // Check the error message
        match salt {
            Err(e) => assert_eq!(
                e,
                format!("Unsupported hash algorithm: {}", invalid_algo)
            ),
            _ => panic!("Expected Err, got Ok"),
        }
    }

    #[test]
    fn test_generate_salt_bcrypt() {
        let algo = "bcrypt";

        let salt = Hash::generate_salt(algo).unwrap();

        // Assert that the salt is of the correct length and format
        assert_eq!(salt.len(), 24); // bcrypt salt will be longer due to base64 encoding
    }

    #[test]
    fn test_generate_salt_scrypt() {
        let algo = "scrypt";

        let salt = Hash::generate_salt(algo).unwrap();

        // Assert that the salt is of the correct length and format
        assert_eq!(salt.len(), 44); // scrypt salt will be longer due to base64 encoding
    }

    #[test]
    fn test_argon2i_hashing() {
        let password = "password123";
        let salt = "somesalt";
        let algo = "argon2i";

        let hash = Hash::new(password, salt, algo).unwrap();

        assert!(hash.verify(password).unwrap());
        assert!(!hash.verify("wrongpassword").unwrap());
    }

    #[test]
    fn test_bcrypt_hashing() {
        let password = "password123";
        let salt = Hash::generate_salt("bcrypt").unwrap();
        let algo = "bcrypt";

        let hash = Hash::new(password, &salt, algo).unwrap();

        assert!(hash.verify(password).unwrap());
        assert!(!hash.verify("wrongpassword").unwrap());
    }

    #[test]
    fn test_scrypt_hashing() {
        let password = "password123";
        let salt = Hash::generate_salt("scrypt").unwrap();
        let algo = "scrypt";

        let hash = Hash::new(password, &salt, algo).unwrap();

        assert!(hash.verify(password).unwrap());
        assert!(!hash.verify("wrongpassword").unwrap());
    }

    #[test]
    fn test_set_password() {
        let password = "password123";
        let salt = "somesalt";
        let algo = "argon2i";

        let mut hash = Hash::new(password, salt, algo).unwrap();

        let new_password = "newpassword123";
        hash.set_password(new_password, salt, algo).unwrap();

        assert!(hash.verify(new_password).unwrap());
        assert!(!hash.verify(password).unwrap());
    }

    #[test]
    fn test_invalid_algorithm() {
        let password = "password123";
        let salt = "somesalt";
        let algo = "invalid_algo";

        let hash = Hash::new(password, salt, algo);

        assert!(hash.is_err());
    }

    #[test]
    fn test_short_password() {
        let password = "short";
        let salt = "somesalt";
        let algo = "argon2i";

        let hash = Hash::new(password, salt, algo);

        assert!(hash.is_err());
    }

    #[test]
    fn test_algorithm() {
        let password = "password123";
        let salt = "somesalt";
        let algo = "bcrypt";

        let hash = Hash::new(password, salt, algo).unwrap();

        assert_eq!(HashAlgorithm::Bcrypt, hash.algorithm());
    }

    #[test]
    fn test_from_hash() {
        let password = "password123";
        let salt = "somesalt";
        let algo = "scrypt";

        // Generate a hash from the password
        let original_hash = Hash::new(password, salt, algo).unwrap();

        // Get the hashed password bytes
        let hashed_password = original_hash.hash;

        // Now try to create a new Hash struct from the hashed password bytes
        let from_hash = Hash::from_hash(&hashed_password, algo);

        // Check that from_hash is Ok
        assert!(from_hash.is_ok());

        // Unwrap the Result and get the Hash struct
        let from_hash = from_hash.unwrap();

        // Check that the algorithm is correct
        assert_eq!(from_hash.algorithm(), HashAlgorithm::Scrypt);

        // Check that the hash is correct
        assert_eq!(from_hash.hash, hashed_password);

        // Check that the salt is empty (since from_hash doesn't set the salt)
        assert_eq!(from_hash.salt, Vec::<u8>::new());
    }

    #[test]
    fn test_from_hash_invalid_algorithm() {
        let password = "password123";
        let salt = "somesalt";
        let algo = "unsupported_algo";

        // Generate a hash from the password
        let original_hash =
            Hash::new(password, salt, "bcrypt").unwrap();

        // Get the hashed password bytes
        let hashed_password = original_hash.hash;

        // Now try to create a new Hash struct from the hashed password bytes
        let from_hash = Hash::from_hash(&hashed_password, algo);

        // Check that from_hash is Err
        assert!(from_hash.is_err());

        // Check the error message
        match from_hash {
            Err(e) => assert_eq!(
                e,
                format!("Unsupported hash algorithm: {}", algo)
            ),
            _ => panic!("Expected Err, got Ok"),
        }
    }

    #[test]
    fn test_hash() {
        let password = "password123";
        let salt = "somesalt";
        let algo = "bcrypt";

        // Create a new Hash
        let original_hash = Hash::new(password, salt, algo).unwrap();

        // Get the hashed password bytes
        let hashed_password = original_hash.hash.clone();

        // Test the `hash` method
        assert_eq!(original_hash.hash(), &hashed_password);
    }

    #[test]
    fn test_salt() {
        let password = "password123";
        let salt = "somesalt";
        let algo = "bcrypt";

        // Create a new Hash
        let original_hash = Hash::new(password, salt, algo).unwrap();

        // Convert the salt to bytes for comparison
        let salt_bytes = salt.as_bytes();

        // Test the `salt` method
        assert_eq!(original_hash.salt(), salt_bytes);
    }

    #[test]
    fn test_set_hash() {
        let password = "password123";
        let salt = "somesalt";
        let algo = "bcrypt";

        // Create a new Hash
        let mut original_hash =
            Hash::new(password, salt, algo).unwrap();

        // Create a new hash value
        let new_hash = vec![1, 2, 3, 4, 5];

        // Set the hash of the Hash struct to the new value
        original_hash.set_hash(&new_hash);

        // Test that the `hash` method returns the new hash value
        assert_eq!(original_hash.hash(), &new_hash);
    }

    #[test]
    fn test_set_salt() {
        let password = "password123";
        let salt = "somesalt";
        let algo = "bcrypt";

        // Create a new Hash
        let mut original_hash =
            Hash::new(password, salt, algo).unwrap();

        // Create a new salt value
        let new_salt = vec![1, 2, 3, 4, 5];

        // Set the salt of the Hash struct to the new value
        original_hash.set_salt(&new_salt);

        // Test that the `salt` method returns the new salt value
        assert_eq!(original_hash.salt(), &new_salt);
    }
    #[test]
    fn test_to_string_representation() {
        let password = "password123";
        let salt = "somesalt";
        let algo = "bcrypt";

        // Create a new Hash
        let original_hash = Hash::new(password, salt, algo).unwrap();

        // Get the string representation
        let string_repr = original_hash.to_string_representation();

        // Get the expected string representation
        let expected_repr = format!(
            "{}:{}",
            salt,
            original_hash
                .hash()
                .iter()
                .map(|b| format!("{:02x}", b))
                .collect::<Vec<String>>()
                .join("")
        );

        assert_eq!(string_repr, expected_repr);
    }
    #[test]
    fn test_hash_display() {
        let password = "password123";
        let salt = "somesalt";
        let algo = "bcrypt";

        // Create a new Hash
        let original_hash = Hash::new(password, salt, algo).unwrap();

        // Test the Display implementation for Hash
        assert_eq!(
            format!("{}", original_hash),
            format!("Hash {{ hash: {:?} }}", original_hash.hash())
        );
    }

    #[test]
    fn test_hash_algorithm_display() {
        let algo = HashAlgorithm::Bcrypt;

        // Test the Display implementation for HashAlgorithm
        assert_eq!(format!("{}", algo), format!("{:?}", algo));
    }

    #[test]
    fn test_hash_algorithm_from_str() {
        let algo_str = "bcrypt";
        let expected_algo = HashAlgorithm::Bcrypt;

        // Test the FromStr implementation for HashAlgorithm
        assert_eq!(
            algo_str.parse::<HashAlgorithm>().unwrap(),
            expected_algo
        );
    }

    #[test]
    fn test_hash_algorithm_from_str_invalid() {
        let invalid_algo_str = "invalid";

        // Test the FromStr implementation for HashAlgorithm with an invalid string
        assert!(invalid_algo_str.parse::<HashAlgorithm>().is_err());
    }

    #[test]
    fn test_parse() {
        let password = "password123";
        let salt = "somesalt";
        let algo = "bcrypt";

        // Create a new Hash
        let original_hash = Hash::new(password, salt, algo).unwrap();

        // Convert the Hash to a JSON string
        let hash_json = serde_json::to_string(&original_hash).unwrap();

        // Parse the JSON string back into a Hash
        let parsed_hash = Hash::parse(&hash_json).unwrap();

        // Check that the parsed Hash is equal to the original
        assert_eq!(original_hash, parsed_hash);
    }

    #[test]
    fn test_parse_invalid() {
        let invalid_json = "invalid";

        // Try to parse the invalid JSON string
        assert!(Hash::parse(invalid_json).is_err());
    }

    #[test]
    fn test_parse_algorithm_argon2i() {
        let hash_str = "$argon2i$somehashstring";
        let algorithm = Hash::parse_algorithm(hash_str);

        assert_eq!(algorithm.unwrap(), HashAlgorithm::Argon2i);
    }

    #[test]
    fn test_parse_algorithm_bcrypt() {
        let hash_str = "$bcrypt$somehashstring";
        let algorithm = Hash::parse_algorithm(hash_str);

        assert_eq!(algorithm.unwrap(), HashAlgorithm::Bcrypt);
    }

    #[test]
    fn test_parse_algorithm_scrypt() {
        let hash_str = "$scrypt$somehashstring";
        let algorithm = Hash::parse_algorithm(hash_str);

        assert_eq!(algorithm.unwrap(), HashAlgorithm::Scrypt);
    }

    #[test]
    fn test_parse_algorithm_unsupported() {
        let hash_str = "$unsupported$somehashstring";
        let algorithm = Hash::parse_algorithm(hash_str);

        assert!(algorithm.is_err());
        assert_eq!(
            algorithm.err().unwrap(),
            "Unsupported hash algorithm: unsupported"
        );
    }

    #[test]
    fn test_parse_algorithm_invalid() {
        let hash_str = "invalidhashstring";
        let algorithm = Hash::parse_algorithm(hash_str);

        assert!(algorithm.is_err());
        assert_eq!(algorithm.err().unwrap(), "Invalid hash string");
    }
}