[OCaml] High Intensity Training Online
1
(** Tests for {!Hito_persistence.Bcrypt_hash} — the hashing adapter behind the
2
{!Hito_app.Password_hash.S} port. *)
3
4
open Hito_persistence
5
6
let hash_tests =
7
[
8
( "a hashed password verifies against itself",
9
`Quick,
10
fun () ->
11
let hash = Bcrypt_hash.hash "correct horse battery staple" in
12
Alcotest.(check bool)
13
"verifies" true
14
(Bcrypt_hash.verify ~hash "correct horse battery staple") );
15
( "a wrong password does not verify",
16
`Quick,
17
fun () ->
18
let hash = Bcrypt_hash.hash "correct horse battery staple" in
19
Alcotest.(check bool)
20
"rejects" false
21
(Bcrypt_hash.verify ~hash "Tr0ub4dor&3") );
22
( "hashing is salted: the same password hashes differently",
23
`Quick,
24
fun () ->
25
let a = Bcrypt_hash.hash "same password" in
26
let b = Bcrypt_hash.hash "same password" in
27
Alcotest.(check bool) "distinct hashes" false (String.equal a b);
28
Alcotest.(check bool)
29
"both still verify" true
30
(Bcrypt_hash.verify ~hash:a "same password"
31
&& Bcrypt_hash.verify ~hash:b "same password") );
32
( "a malformed stored hash never verifies",
33
`Quick,
34
fun () ->
35
Alcotest.(check bool)
36
"rejects garbage" false
37
(Bcrypt_hash.verify ~hash:"not a bcrypt hash" "anything") );
38
( "the production work factor is pinned high",
39
`Quick,
40
fun () ->
41
(* A bcrypt hash records its own cost in the prefix, $2y$NN$. safepass
42
defaults to 6, far too cheap for a stored verifier; production pins
43
12. Reading it back from a fresh hash proves the pin took effect. *)
44
let hash = Bcrypt_hash.hash "any password" in
45
Alcotest.(check bool)
46
"hash carries the pinned cost" true
47
(String.length hash > 7 && String.sub hash 4 2 = "12") );
48
]
49
50
let suite = [ ("bcrypt_hash", hash_tests) ]
51