type _ s = Bytes : Bytes.t s | String : String.t s | Bigstring : bigstring s and bigstring = (char, Bigarray.int8_unsigned_elt, Bigarray.c_layout) Bigarray.Array1.t let title : type a k. [ `HMAC | `HMAC_feed | `Digest ] -> k Digestif.hash -> a s -> string = fun computation hash input -> let pp_computation ppf = function | `HMAC -> Fmt.string ppf "hmac" | `HMAC_feed -> Fmt.string ppf "hmac_feed" | `Digest -> Fmt.string ppf "digest" in let pp_hash : type k. k Digestif.hash Fmt.t = fun ppf -> function | Digestif.MD5 -> Fmt.string ppf "md5" | Digestif.SHA1 -> Fmt.string ppf "sha1" | Digestif.RMD160 -> Fmt.string ppf "rmd160" | Digestif.SHA224 -> Fmt.string ppf "sha224" | Digestif.SHA256 -> Fmt.string ppf "sha256" | Digestif.SHA384 -> Fmt.string ppf "sha384" | Digestif.SHA512 -> Fmt.string ppf "sha512" | Digestif.SHA3_224 -> Fmt.string ppf "sha3_224" | Digestif.SHA3_256 -> Fmt.string ppf "sha3_256" | Digestif.KECCAK_256 -> Fmt.string ppf "keccak_256" | Digestif.SHA3_384 -> Fmt.string ppf "sha3_384" | Digestif.SHA3_512 -> Fmt.string ppf "sha3_512" | Digestif.WHIRLPOOL -> Fmt.string ppf "whirlpool" | Digestif.BLAKE2B -> Fmt.string ppf "blake2b" | Digestif.BLAKE2S -> Fmt.string ppf "blake2s" in let pp_input : type a. a s Fmt.t = fun ppf -> function | Bytes -> Fmt.string ppf "bytes" | String -> Fmt.string ppf "string" | Bigstring -> Fmt.string ppf "bigstring" in Fmt.str "%a:%a:%a" pp_computation computation pp_hash hash pp_input input let bytes = Bytes let string = String let bigstring = Bigstring let test_hmac : type k a. a s -> k Digestif.hash -> string -> a -> k Digestif.t -> unit = fun kind hash key input expect -> let title = title `HMAC hash kind in let test_hash = Alcotest.testable (Digestif.pp hash) (Digestif.equal hash) in match kind with | Bytes -> let result = Digestif.hmaci_bytes hash ~key (fun f -> f input) in Alcotest.(check test_hash) title expect result | String -> let result = Digestif.hmaci_string hash ~key (fun f -> f input) in Alcotest.(check test_hash) title expect result | Bigstring -> let result = Digestif.hmaci_bigstring hash ~key (fun f -> f input) in Alcotest.(check test_hash) title expect result let test_hmac_feed : type k a. a s -> k Digestif.hash -> string -> a -> k -> unit = fun kind hash key input expect -> let title = title `HMAC_feed hash kind in let module H = (val Digestif.module_of hash) in let test_hash = Alcotest.testable H.pp H.equal in let hmac_ctx = H.hmac_init ~key in let total_len = match kind with | Bytes -> Bytes.length input | String -> String.length input | Bigstring -> Bigarray.Array1.dim input in let rec loop hmac_ctx off = if off = total_len then hmac_ctx else let len = min (total_len - off) 16 in let hmac_ctx = match kind with | Bytes -> H.hmac_feed_bytes hmac_ctx ~off ~len input | String -> H.hmac_feed_string hmac_ctx ~off ~len input | Bigstring -> H.hmac_feed_bigstring hmac_ctx ~off ~len input in loop hmac_ctx (off + len) in Alcotest.check test_hash title expect (H.hmac_get (loop hmac_ctx 0)) let test_digest : type k a. a s -> k Digestif.hash -> a -> k Digestif.t -> unit = fun kind hash input expect -> let title = title `Digest hash kind in let test_hash = Alcotest.testable (Digestif.pp hash) (Digestif.equal hash) in match kind with | Bytes -> let result = Digestif.digesti_bytes hash (fun f -> f input) in Alcotest.(check test_hash) title expect result | String -> let result = Digestif.digesti_string hash (fun f -> f input) in Alcotest.(check test_hash) title expect result | Bigstring -> let result = Digestif.digesti_bigstring hash (fun f -> f input) in Alcotest.(check test_hash) title expect result let make_hmac : type a k. name:string -> a s -> k Digestif.hash -> string -> a -> k Digestif.t -> unit Alcotest.test_case = fun ~name kind hash key input expect -> (name, `Quick, fun () -> test_hmac kind hash key input expect) let make_hmac_feed : type a k. name:string -> a s -> k Digestif.hash -> string -> a -> k -> unit Alcotest.test_case = fun ~name kind hash key input expect -> (name, `Quick, fun () -> test_hmac_feed kind hash key input expect) let make_digest : type a k. name:string -> a s -> k Digestif.hash -> a -> k Digestif.t -> unit Alcotest.test_case = fun ~name kind hash input expect -> (name, `Quick, fun () -> test_digest kind hash input expect) let combine a b c = let rec aux r a b c = match (a, b, c) with | xa :: ra, xb :: rb, xc :: rc -> aux ((xa, xb, xc) :: r) ra rb rc | [], [], [] -> List.rev r | _ -> raise (Invalid_argument "combine") in aux [] a b c let makes ~name kind hash keys inputs expects = List.map (fun (key, input, expect) -> make_hmac ~name kind hash key input expect) (combine keys inputs expects) let makes' ~name kind hash keys inputs expects = List.map (fun (key, input, expect) -> make_hmac_feed ~name kind hash key input expect) (combine keys inputs expects) let to_bigstring s = let ln = Bytes.length s in let bi = Bigarray.Array1.create Bigarray.Char Bigarray.c_layout ln in for i = 0 to ln - 1 do bi.{i} <- Bytes.get s i done ; bi let split3 lst = let rec go (ax, ay, az) = function | (x, y, z) :: r -> go (x :: ax, y :: ay, z :: az) r | [] -> (List.rev ax, List.rev ay, List.rev az) in go ([], [], []) lst let keys_by, keys_st, keys_bi = [ "Salut"; "Jefe"; "Lorenzo"; "Le son qui fait plaiz'"; "La c'est un peu chaud en vrai"; ] |> List.map (fun s -> (Bytes.unsafe_of_string s, s, to_bigstring (Bytes.unsafe_of_string s))) |> split3 let inputs_by, inputs_st, inputs_bi = [ "Hi There"; "what do ya want for nothing?"; "C'est Lolo je bois de l'Ice Tea quand j'suis fonsde"; "Mes pecs dansent le flamenco, Lolo l'empereur du sale, dans le deal on \ m'surnomme Joe La Crapule"; "Y'a un pack de douze a cote du cadavre dans le coffre. Pourquoi t'etais \ Charlie mais t'etais pas Jean-Pierre Coffe. Ca sniffe tellement la coke, \ mes crottes de nez c'est d'la MD. J'deteste juste les keufs, j'aime bien \ les obeses et les pedes. Mamene finira dans le dico'. J'ai qu'un reuf: le \ poto Rico. Ca rotte-ca l'argent des clodos. C'est moi qu'ecrit tous les \ pornos. Cite-moi en controle de philo'. Toutes les miss grimpent aux \ rideaux."; ] |> List.map (fun s -> (Bytes.unsafe_of_string s, s, to_bigstring (Bytes.unsafe_of_string s))) |> split3 let results_md5 = [ "689e721d493b6eeea482947be736c808"; "750c783e6ab0b503eaa86e310a5db738"; "1cdd24eef6163afee7adc7c53dd6c9df"; "0316ebcad933675e84a81850e24d55b2"; "9ee938a2659d546ccc2e5993601964eb"; ] |> List.map (Digestif.of_hex Digestif.md5) let results_sha1 = [ "b0a6490a6fcb9479a7aa2306ecb56730d6225dba"; "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79"; "d80589525b1cc9f5e5ffd48ffd73d710ac89a3f1"; "0a5212b295e11a1de5c71873e70ce54f45119516"; "deaf6465e5945a0d04cba439c628ee9f47b95aef"; ] |> List.map (Digestif.of_hex Digestif.sha1) let results_sha224 = [ "9a26f1380aae8c580441676891765c8a647ddf16a7d12fa427090901"; "a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44"; "b94a09654fc749ae6cb21c7765bf4938ff9af03e13d83fbf23342ce7"; "7c66e4c7297a22ca80e2e1db9774afea64b1e086be366d2da3e6bc83"; "438dc3311243cd54cc7ee24c9aac8528a1750abc595f06e68a331d2a"; ] |> List.map (Digestif.of_hex Digestif.sha224) let results_sha256, results_sha256' = let raw_results_sha256 = [ "2178f5f21b4311607bf9347bcde5f6552edb9ec5aa13b954d53de2fbfd8b75de"; "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"; "aa36cd61caddefe26b07ba1d3d07ea978ed575c9d1f921837dff9f73e019713e"; "a7c8b53d68678a8e6e4d403c6b97cf0f82c4ef7b835c41039c0a73aa4d627d05"; "b2a83b628f7e0da71c3879b81075775072d0d35935c62cc6c5a79b337ccccca1"; ] in ( List.map (Digestif.of_hex Digestif.sha256) raw_results_sha256, List.map Digestif.SHA256.of_hex raw_results_sha256 ) let results_sha384 = [ "43e75797c1d875c5e5e7e90d0525061703d6b95b6137461566c2d067304458e62c144bbe12c0b741dcfaa38f7d41575e"; "af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e8e2240ca5e69e2c78b3239ecfab21649"; "bd3b5c82edcd0f206aadff7aa89dbbc3a7655844ffc9f8f9fa17c90eb36b13ec7828fba7252c3f5d90cff666ea44d557"; "16461c2a44877c69fb38e4dce2edc822d68517917fc84d252de64132bd43c7cbe3310b7e8661741b7728000e8abf51e0"; "2c3751d1dc792344514928fad94672a256cf2f66344e4df96b0cc4cc3f6800aa5a628e9becf5f65672e1acf013284893"; ] |> List.map (Digestif.of_hex Digestif.sha384) let results_sha512 = [ "5f26752be4a1282646ed8c6a611d4c621e22e3fa96e9e6bc9e19a86deaacf0315151c46f779c3184632ab5793e2ddcb2ff87ca11cc886130f033364b08aef4e2"; "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737"; "c2f2077f538171d7c6cbee0c94948f82987117a50229fb0b48a534e3c63553a9a9704cdb460c597c8b46b631e49c22a9d2d46bded40f8a77652f754ec725e351"; "89d7284e89642ec195f7a8ef098ef4e411fa3df17a07724cf13033bc6b7863968aad449cee973df9b92800d803ba3e14244231a86253cfacd1de882a542e945f"; "f6ecfca37d2abcff4b362f1919629e784c4b618af77e1061bb992c11d7f518716f5df5978b0a1455d68ceeb10ced9251306d2f26181407be76a219d48c36b592"; ] |> List.map (Digestif.of_hex Digestif.sha512) let results_sha3_224 = [ "27d199d761adfa5530313acdf7e1680fbdea09236ac6395b43c4a0e6"; "7fdb8dd88bd2f60d1b798634ad386811c2cfc85bfaf5d52bbace5e66"; "179895b711ca2bebf420a2e7255564d4cb2217ea3ac8b2d45f29d127"; "5bc718d440729ba7d857543eed04cbec3374eb835da33e99f8e0561f"; "0f44044cd2cb5a02ec3b7dff4367c54a1ace6cb7d602e005684aee7c"; ] |> List.map (Digestif.of_hex Digestif.sha3_224) let results_sha3_256 = [ "bb25b6f7672dab6734313c8c63aab800b2c451c81833509c1afdb986be9bdea3"; "c7d4072e788877ae3596bbb0da73b887c9171f93095b294ae857fbe2645e1ba5"; "f58a4c9641f87ead6c16525906857f5fce149bb822c4fe7a2abcaebe823d9e0f"; "1dcc5f9bcfb9fa35349d51c40672b2bd971afc32f9cf5e478ec442d6d90be4ce"; "8d1de07fd2312402f94d061a88b02dc1e0173e9d89750284b78d2bb004e9d3c1"; ] |> List.map (Digestif.of_hex Digestif.sha3_256) let results_keccak_256 = [ "0dbf49d1c2d4625f87592309b3c7ceb2c1a2194dc866bb21be7ac6abb733f0f1"; "aa9aed448c7abc8b5e326ffa6a01cdedf7b4b831881468c044ba8dd4566369a1"; "7fce3f69adac930d657ce6998d6ad5ee102b5e7560e6690b4ca855e5d4c268a0"; "c6cf40deda9a1028823641235499c9b1891c6e2ab7d2bfa9db06890ce8bc855e"; "8af5e3a5ebc1a9927d43765c85ca455de007e357ea250ae3ed65b55765d3252a"; ] |> List.map (Digestif.of_hex Digestif.keccak_256) let results_sha3_384 = [ "fddae4c273e970a5f530cc737b15c1f0546caf0900e29fdf0ce57512a4c6898ca38931d1d3d9827cf16712c52da814e6"; "f1101f8cbf9766fd6764d2ed61903f21ca9b18f57cf3e1a23ca13508a93243ce48c045dc007f26a21b3f5e0e9df4c20a"; "ba546a5edbd7cdf49f2669553241e9867af842eb508432e8191d64282a9bb6e856311be49c8e673d72f212d446d0bee9"; "f8d65fe91fe24a009263e9aee0267c48cafbe422b899a76763eb7ec095b6f0293033a504925a345ec70a3d984f98540d"; "2485a07f2b1585572d492db2dcfffcc30a35e019ad6490af3bef94e514b66f90913fb11a9a365e42d2d03e3cad28b847"; ] |> List.map (Digestif.of_hex Digestif.sha3_384) let results_sha3_512 = [ "c2f4417c4dbc86cea2054beb755029c29c8dbed7781595fc9d5222214538a6975afc23f2f9e96683d33f547ea0df897bd1ca766fbb2c4ea674b9b9484e9e782a"; "5a4bfeab6166427c7a3647b747292b8384537cdb89afb3bf5665e4c5e709350b287baec921fd7ca0ee7a0c31d022a95e1fc92ba9d77df883960275beb4e62024"; "967c75d948f8b1efc263c4581287186500bf38daecda304fe68f34dacd622f299218ad47a4a112db5eedd5c8a30b03fefa17d20ddc3a735848f08fdc2d7ae592"; "ba3d37e455183ac5a9af109512d97bcc5e34daa5e10796625db8661519a4027b2cf89d282302bd8a620b8813ee98f781a9388e4f479e189899d820c1dcd50b8c"; "ce4a9d6e2b98b7fbb9ea668cd21b18c361d1d929fc6914192069b8c2672682a36ece8a6de07b17d4448afbc701b460264994ae9c79f26cfdd14a8fdc108d62a1"; ] |> List.map (Digestif.of_hex Digestif.sha3_512) let results_whirlpool = [ "1174a4781245c2c78435b68bd0eb5e462f66a455ccfde94f61be594f9db841e7f4e85ba740f31dfd89186724f953cbd454451e987c608958dc9b563fd9594776"; "3d595ccd1d4f4cfd045af53ba7d5c8283fee6ded6eaf1269071b6b4ea64800056b5077c6a942cfa1221bd4e5aed791276e5dd46a407d2b8007163d3e7cd1de66"; "7af46cc6bb193d7958bd55a91509c99570cbd233d48a8fbf05207017040e27671024a21fad3877ecd2a309fc13c403ea8e83c6423ab8d695b654dbf6a1d2e8ee"; "a8646f7e371a1f9de1169d21de9a59ff2a32c73617c9b73708a226081b9316e81442e793e094c41a89e79705f1832c22e0cd3ac93d3b68a6842ddf35169908ae"; "b80dc14932e92fda0ba7f09e1db20d514633d15c2b89ad96a96198f4f751f2acf34e4fe0c9e2d13c4efaf7082c0871584b8dde7a367703d6fdf4f400a52f9432"; ] |> List.map (Digestif.of_hex Digestif.whirlpool) let results_blake2b = [ "aba2eef053923ba3a671b54244580ca7c8dfa9c487431c3437e1a8504e166ed894778045a5c6a314fadee110a5254f6f370e9db1d3093a62e0448a5e91b1d4c6"; "6ff884f8ddc2a6586b3c98a4cd6ebdf14ec10204b6710073eb5865ade37a2643b8807c1335d107ecdb9ffeaeb6828c4625ba172c66379efcd222c2de11727ab4"; "42aadab231ff4edbdad29a18262bbb6ba74cf0850f40b64a92dc62a92608a65f06af850aa1988cd1e379cf9cc9a8f64d61125d7b3def292ae57e537bc202e812"; "4abf562dc64f4062ea59ae9b4e2061a7a6c1a75af74b3663fd05aa4437420b8deea657e395a7dbac02aef7b7d70dc8b8a8db99aa8db028961a5ee66bac22b0f0"; "69f9e4236cd0c50204e4f8b86dc1751d37cc195835e9db25c9b366f41e1d86cdeec6a8702dfed1bc0ed0d6a1e2c5af275c331ec91f884c979021fb64021915de"; ] |> List.map (Digestif.of_hex Digestif.blake2b) let results_rmd160 = [ "65b3cb3360881842a0d454bd6e7bc1bfe838b384"; "dda6c0213a485a9e24f4742064a7f033b43c4069"; "f071dcd2514fd89de78a5a2db1128dfa3e54d503"; "bda5511e63389385218a8d902a70f2d8dc4dc074"; "6c2486f169432281b6d71ae5b6765239c3cc1ea6"; ] |> List.map (Digestif.of_hex Digestif.rmd160) let results_blake2s = [ "5bb23bbe41678b23e6d38881d2515fdf5df253dd2e9a80075ea759c93e1bca3a"; "90b6281e2f3038c9056af0b4a7e763cae6fe5d9eb4386a0ec95237890c104ff0"; "5d0064cb2848ab5dc948876a6be3e5685301a744735c25858c0bd283a7940eb7"; "6903efd2383b13adaa985d00ca271ccb420ab8f953841081c9c15a2dfebf866c"; "b8e167de23a5f136dc26bf06da0d724ebf7310903c2f702403b66810a230d622"; ] |> List.map (Digestif.of_hex Digestif.blake2s) module BLAKE2 = struct let input_blake2b_file = "../blake2b.test" let input_blake2s_file = "../blake2s.test" let fold_s f a s = let r = ref a in String.iter (fun x -> r := f !r x) s ; !r let of_hex len hex = let code x = match x with | '0' .. '9' -> Char.code x - 48 | 'A' .. 'F' -> Char.code x - 55 | 'a' .. 'z' -> Char.code x - 87 | _ -> raise (Invalid_argument "of_hex") in let wsp = function ' ' | '\t' | '\r' | '\n' -> true | _ -> false in fold_s (fun (res, i, acc) -> function | chr when wsp chr -> (res, i, acc) | chr -> match (acc, code chr) with | None, x -> (res, i, Some (x lsl 4)) | Some y, x -> Bytes.set res i (Char.unsafe_chr (x lor y)) ; (res, succ i, None)) (Bytes.create len, 0, None) hex |> (function | _, _, Some _ -> invalid_arg "of_hex" | res, i, _ -> if i = len then res else ( for i = i to len - 1 do Bytes.set res i '\000' done ; res)) |> Bytes.unsafe_to_string let parse kind ic = ignore @@ input_line ic ; ignore @@ input_line ic ; let rec loop state acc = match (state, input_line ic) with | `In, line -> let i = ref "" in Scanf.sscanf line "in:\t%s" (fun v -> i := of_hex (String.length v / 2) v) ; loop (`Key !i) acc | `Key i, line -> ( let k = ref None in Scanf.sscanf line "key:\t%s" (fun v -> k := Some (Digestif.to_raw_string kind (Digestif.of_hex kind v))) ; match !k with | Some k -> loop (`Hash (i, (k :> string))) acc | None -> loop `In acc) | `Hash (i, k), line -> ( let h = ref None in Scanf.sscanf line "hash:\t%s" (fun v -> h := Some (Digestif.of_hex kind v)) ; match !h with | Some h -> loop (`Res (i, k, h)) acc | None -> loop `In acc) | `Res v, "" -> loop `In (v :: acc) | `Res v, _ -> (* avoid malformed line *) loop (`Res v) acc | exception End_of_file -> List.rev acc in loop `In [] let test_mac : type k a. a s -> k Digestif.hash -> (module Digestif.MAC) -> string -> a -> k Digestif.t -> unit = fun kind hash (module Mac) key input expect -> let title = title `HMAC hash kind in let check (result : Mac.t) = Alcotest.(check string) title (Digestif.to_raw_string hash expect) (Obj.magic result) (* XXX(dinosaure): ok, this is really bad but I'm lazy to keep type equality on [Mac] - extend interface and play with [with type t = t] anywhere. *) in match kind with | Bytes -> check @@ Mac.maci_bytes ~key (fun f -> f input) | String -> check @@ Mac.maci_string ~key (fun f -> f input) | Bigstring -> check @@ Mac.maci_bigstring ~key (fun f -> f input) let make_keyed_blake m ~name kind hash key input expect = (name, `Quick, fun () -> test_mac kind hash m key input expect) let tests m kind filename = let ic = open_in filename in let tests = parse kind ic in close_in ic ; List.map (fun (input, key, expect) -> make_keyed_blake m ~name:"blake2{b,s}" string kind key input expect) tests let tests_blake2s = tests (module Digestif.BLAKE2S.Keyed) Digestif.blake2s input_blake2s_file let tests_blake2b = tests (module Digestif.BLAKE2B.Keyed) Digestif.blake2b input_blake2b_file end module RMD160 = struct let inputs = [ ""; "a"; "abc"; "message digest"; "abcdefghijklmnopqrstuvwxyz"; "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; "12345678901234567890123456789012345678901234567890123456789012345678901234567890"; ] let expects = [ "9c1185a5c5e9fc54612808977ee8f548b2258d31"; "0bdc9d2d256b3ee9daae347be6f4dc835a467ffe"; "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"; "5d0689ef49d2fae572b881b123a85ffa21595f36"; "f71c27109c692c1b56bbdceb5b9d2865b3708dbc"; "12a053384a9c0c88e405a06c27dcf49ada62eb2b"; "b0e20b6e3116640286ed3a87a5713079b21f5189"; "9b752e45573d4b39f4dbd3323cab82bf63326bfb"; ] let million : expect:Digestif.RMD160.t Digestif.t -> unit Alcotest.test_case = fun ~expect -> let iter n f = let rec go = function | 0 -> () | n -> f "a" ; go (n - 1) in go n in let result = Digestif.digesti_string Digestif.rmd160 (iter 1_000_000) in let test_hash = Alcotest.testable Digestif.(pp rmd160) Digestif.(equal rmd160) in ( "give me a million", `Slow, fun () -> Alcotest.(check test_hash) "rmd160" expect result ) let tests = let expect_million = Digestif.of_hex Digestif.rmd160 "52783243c1697bdbe16d37f97f68f08325dc1528" in List.map (fun (input, expect) -> make_digest ~name:"rmd160" string Digestif.rmd160 input expect) (List.combine inputs (List.map Digestif.(of_hex rmd160) expects)) @ [ million ~expect:expect_million ] end let str = Alcotest.testable (fun ppf -> Fmt.pf ppf "%S") String.equal let blake2s_spe digest_size = Alcotest.test_case (Fmt.str "BLAKE2S (digest-size: %d)" digest_size) `Quick @@ fun () -> let module Hash = Digestif.Make_BLAKE2S (struct let digest_size = digest_size end) in Fmt.epr ">>> Use digest_string\n%!" ; let hash0 = Hash.digest_string "" in Fmt.epr ">>> Use feed_string\n%!" ; let hash1 = Hash.get (Hash.feed_string Hash.empty "") in let raw_hash0 = Hash.to_raw_string hash0 in let raw_hash1 = Hash.to_raw_string hash1 in Alcotest.(check int) "raw length" digest_size (String.length raw_hash0) ; Alcotest.(check int) "raw length" digest_size (String.length raw_hash1) ; let hash = Alcotest.testable Hash.pp Hash.equal in Alcotest.(check hash) "hash" hash0 hash1 ; Alcotest.(check str) "raw hash" raw_hash0 raw_hash1 let blake2b_spe digest_size = Alcotest.test_case (Fmt.str "BLAKE2B (digest-size: %d)" digest_size) `Quick @@ fun () -> let module Hash = Digestif.Make_BLAKE2B (struct let digest_size = digest_size end) in let hash0 = Hash.digest_string "" in let hash1 = Hash.get (Hash.feed_string Hash.empty "") in let raw_hash0 = Hash.to_raw_string hash0 in let raw_hash1 = Hash.to_raw_string hash1 in Alcotest.(check int) "raw length" digest_size (String.length raw_hash0) ; Alcotest.(check int) "raw length" digest_size (String.length raw_hash1) ; let hash = Alcotest.testable Hash.pp Hash.equal in Alcotest.(check hash) "hash" hash0 hash1 ; Alcotest.(check str) "raw hash" raw_hash0 raw_hash1 type kind = V : 'a Digestif.hash -> kind let ( <.> ) f g x = f (g x) let code x = match x with | '0' .. '9' -> Char.code x - Char.code '0' | 'A' .. 'F' -> Char.code x - Char.code 'A' + 10 | 'a' .. 'f' -> Char.code x - Char.code 'a' + 10 | _ -> Fmt.invalid_arg "of_hex: %02X" (Char.code x) let decode chr1 chr2 = Char.chr ((code chr1 lsl 4) lor code chr2) let of_hex hex = let offset = ref 0 in let rec go have_first idx = if !offset + idx >= String.length hex then '\x00' else match hex.[!offset + idx] with | ' ' | '\t' | '\r' | '\n' -> incr offset ; go have_first idx | chr2 when have_first -> chr2 | chr1 -> incr offset ; let chr2 = go true idx in if chr2 <> '\x00' then decode chr1 chr2 else invalid_arg "of_hex: odd number of hex characters" in String.init (String.length hex / 2) (go false) let sha3_of_name str = match Astring.String.cut ~sep:":" str with | None -> Fmt.invalid_arg "Invalid line: %S" str | Some (_name, value) -> ( let value = Astring.String.trim value in match value with | "SHA3-224" -> V Digestif.sha3_224 | "SHA3-256" -> V Digestif.sha3_256 | "SHA3-384" -> V Digestif.sha3_384 | "SHA3-512" -> V Digestif.sha3_512 | v -> Fmt.invalid_arg "Invalid kind of hash: %s" v) let parse_field str = match Astring.String.cut ~sep:":" str with | Some (_key, v) -> Astring.String.trim v | None -> Fmt.invalid_arg "Invalid line: %S" str let empty = "\"\"" let sha3_vector_tests filename = Alcotest.test_case filename `Quick @@ fun () -> let ic = open_in filename in let _algorithm_type = input_line ic in let _source = input_line ic in let (V hash) = sha3_of_name (input_line ic) in let rec go () = try let comment = parse_field (input_line ic) in let message = parse_field (input_line ic) in Fmt.epr ">>> %S.\n%!" comment ; Fmt.epr ">>> %S.\n%!" (if message = empty then "" else of_hex message) ; let digest = (Digestif.of_hex hash <.> parse_field <.> input_line) ic in let _verify = input_line ic in let result = if message = empty then Digestif.digest_string hash "" else Digestif.digest_string hash (of_hex message) in Alcotest.(check (testable (Digestif.pp hash) (Digestif.equal hash))) comment digest result ; go () with End_of_file -> () in go () ; close_in ic let keccak_vector_tests filename = Alcotest.test_case filename `Quick @@ fun () -> let ic = open_in filename in let _algorithm_type = input_line ic in let _name = input_line ic in let hash = Digestif.keccak_256 in let rec go () = try let comment = parse_field (input_line ic) in let message = parse_field (input_line ic) in let digest = (Digestif.of_hex hash <.> parse_field <.> input_line) ic in let _verify = input_line ic in let result = if message = empty then Digestif.digest_string hash "" else Digestif.digest_string hash (of_hex message) in Alcotest.(check (testable (Digestif.pp hash) (Digestif.equal hash))) comment digest result ; go () with End_of_file -> () in go () ; close_in ic let tests () = Alcotest.run "digestif" [ ("md5", makes ~name:"md5" bytes Digestif.md5 keys_st inputs_by results_md5); ( "md5 (bigstring)", makes ~name:"md5" bigstring Digestif.md5 keys_st inputs_bi results_md5 ); ( "sha1", makes ~name:"sha1" bytes Digestif.sha1 keys_st inputs_by results_sha1 ); ( "sha1 (bigstring)", makes ~name:"sha1" bigstring Digestif.sha1 keys_st inputs_bi results_sha1 ); ( "sha224", makes ~name:"sha224" bytes Digestif.sha224 keys_st inputs_by results_sha224 ); ( "sha224 (bigstring)", makes ~name:"sha224" bigstring Digestif.sha224 keys_st inputs_bi results_sha224 ); ( "sha256", makes ~name:"sha256" bytes Digestif.sha256 keys_st inputs_by results_sha256 ); ( "sha256 (bigstring)", makes ~name:"sha256" bigstring Digestif.sha256 keys_st inputs_bi results_sha256 ); ( "sha256 (feed bytes)", makes' ~name:"sha256" bytes Digestif.sha256 keys_st inputs_by results_sha256' ); ( "sha384", makes ~name:"sha384" bytes Digestif.sha384 keys_st inputs_by results_sha384 ); ( "sha384 (bigstring)", makes ~name:"sha384" bigstring Digestif.sha384 keys_st inputs_bi results_sha384 ); ( "sha512", makes ~name:"sha512" bytes Digestif.sha512 keys_st inputs_by results_sha512 ); ( "sha512 (bigstring)", makes ~name:"sha512" bigstring Digestif.sha512 keys_st inputs_bi results_sha512 ); ( "sha3_224", makes ~name:"sha3_224" bytes Digestif.sha3_224 keys_st inputs_by results_sha3_224 ); ( "sha3_224 (bigstring)", makes ~name:"sha3_224" bigstring Digestif.sha3_224 keys_st inputs_bi results_sha3_224 ); ( "sha3_256", makes ~name:"sha3_256" bytes Digestif.sha3_256 keys_st inputs_by results_sha3_256 ); ( "sha3_256 (bigstring)", makes ~name:"sha3_256" bigstring Digestif.sha3_256 keys_st inputs_bi results_sha3_256 ); ( "keccak_256", makes ~name:"keccak_256" bytes Digestif.keccak_256 keys_st inputs_by results_keccak_256 ); ( "keccak_256 (bigstring)", makes ~name:"keccak_256" bigstring Digestif.keccak_256 keys_st inputs_bi results_keccak_256 ); ( "sha3_384", makes ~name:"sha3_384" bytes Digestif.sha3_384 keys_st inputs_by results_sha3_384 ); ( "sha3_384 (bigstring)", makes ~name:"sha3_384" bigstring Digestif.sha3_384 keys_st inputs_bi results_sha3_384 ); ( "sha3_512", makes ~name:"sha3_512" bytes Digestif.sha3_512 keys_st inputs_by results_sha3_512 ); ( "sha3_512 (bigstring)", makes ~name:"sha3_512" bigstring Digestif.sha3_512 keys_st inputs_bi results_sha3_512 ); ( "whirlpool", makes ~name:"whirlpool" bytes Digestif.whirlpool keys_st inputs_by results_whirlpool ); ( "whirlpool (bigstring)", makes ~name:"whirlpool" bigstring Digestif.whirlpool keys_st inputs_bi results_whirlpool ); ( "blake2b", makes ~name:"blake2b" bytes Digestif.blake2b keys_st inputs_by results_blake2b ); ( "blake2b (bigstring)", makes ~name:"blake2b" bigstring Digestif.blake2b keys_st inputs_bi results_blake2b ); ( "rmd160", makes ~name:"rmd160" bytes Digestif.rmd160 keys_st inputs_by results_rmd160 ); ( "rmd160 (bigstring)", makes ~name:"rmd160" bigstring Digestif.rmd160 keys_st inputs_bi results_rmd160 ); ( "blake2s", makes ~name:"blake2s" bytes Digestif.blake2s keys_st inputs_by results_blake2s ); ( "blake2s (bigstring)", makes ~name:"blake2s" bigstring Digestif.blake2s keys_st inputs_bi results_blake2s ); ("blake2s (keyed, input file)", BLAKE2.tests_blake2s); ("blake2b (keyed, input file)", BLAKE2.tests_blake2b); ( "blake2s (specialization)", [ blake2s_spe 32; blake2s_spe 8; blake2s_spe 16 ] ); ( "blake2b (specialization)", [ blake2b_spe 32; blake2b_spe 64; blake2b_spe 16 ] ); ("ripemd160", RMD160.tests); ( "sha3 (vector tests)", [ sha3_vector_tests "../sha3_224_fips_202.txt"; sha3_vector_tests "../sha3_256_fips_202.txt"; sha3_vector_tests "../sha3_384_fips_202.txt"; sha3_vector_tests "../sha3_512_fips_202.txt"; keccak_vector_tests "../keccak_256.txt"; ] ); ] let () = tests ()