View raw

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