Vendors: add ocaml-pbkdf

This commit is contained in:
Vincent Bernardoff 2018-02-16 14:33:10 +01:00 committed by Benjamin Canou
parent bb0fa86d91
commit 77bb186611
11 changed files with 493 additions and 34 deletions

View File

@ -271,162 +271,167 @@ opam:11:tezos-base:
variables:
package: tezos-base
opam:12:ocplib-resto-cohttp:
opam:12:pbkdf:
<<: *opam_definition
variables:
package: pbkdf
opam:13:ocplib-resto-cohttp:
<<: *opam_definition
variables:
package: ocplib-resto-cohttp
opam:13:irmin-leveldb:
opam:14:irmin-leveldb:
<<: *opam_definition
variables:
package: irmin-leveldb
opam:14:tezos-stdlib-unix:
opam:15:tezos-stdlib-unix:
<<: *opam_definition
variables:
package: tezos-stdlib-unix
opam:15:bip39:
opam:16:bip39:
<<: *opam_definition
variables:
package: bip39
opam:16:tezos-rpc-http:
opam:17:tezos-rpc-http:
<<: *opam_definition
variables:
package: tezos-rpc-http
opam:17:tezos-shell-services:
opam:18:tezos-shell-services:
<<: *opam_definition
variables:
package: tezos-shell-services
opam:18:tezos-storage:
opam:19:tezos-storage:
<<: *opam_definition
variables:
package: tezos-storage
opam:19:tezos-protocol-compiler:
opam:20:tezos-protocol-compiler:
<<: *opam_definition
variables:
package: tezos-protocol-compiler
opam:20:tezos-client-base:
opam:21:tezos-client-base:
<<: *opam_definition
variables:
package: tezos-client-base
opam:21:tezos-protocol-alpha:
opam:22:tezos-protocol-alpha:
<<: *opam_definition
variables:
package: tezos-protocol-alpha
opam:22:tezos-protocol-environment-client:
opam:23:tezos-protocol-environment-client:
<<: *opam_definition
variables:
package: tezos-protocol-environment-client
opam:23:tezos-client-alpha:
opam:24:tezos-client-alpha:
<<: *opam_definition
variables:
package: tezos-client-alpha
opam:24:tezos-client-commands:
opam:25:tezos-client-commands:
<<: *opam_definition
variables:
package: tezos-client-commands
opam:25:tezos-baking-alpha:
opam:26:tezos-baking-alpha:
<<: *opam_definition
variables:
package: tezos-baking-alpha
opam:26:tezos-protocol-genesis:
opam:27:tezos-protocol-genesis:
<<: *opam_definition
variables:
package: tezos-protocol-genesis
opam:27:tezos-protocol-updater:
opam:28:tezos-protocol-updater:
<<: *opam_definition
variables:
package: tezos-protocol-updater
opam:28:tezos-p2p:
opam:29:tezos-p2p:
<<: *opam_definition
variables:
package: tezos-p2p
opam:29:ocplib-resto-json:
opam:30:ocplib-resto-json:
<<: *opam_definition
variables:
package: ocplib-resto-json
opam:30:tezos-baking-alpha-commands:
opam:31:tezos-baking-alpha-commands:
<<: *opam_definition
variables:
package: tezos-baking-alpha-commands
opam:31:tezos-client-alpha-commands:
opam:32:tezos-client-alpha-commands:
<<: *opam_definition
variables:
package: tezos-client-alpha-commands
opam:32:tezos-client-base-unix:
opam:33:tezos-client-base-unix:
<<: *opam_definition
variables:
package: tezos-client-base-unix
opam:33:tezos-client-genesis:
opam:34:tezos-client-genesis:
<<: *opam_definition
variables:
package: tezos-client-genesis
opam:34:tezos-embedded-protocol-alpha:
opam:35:tezos-embedded-protocol-alpha:
<<: *opam_definition
variables:
package: tezos-embedded-protocol-alpha
opam:35:tezos-embedded-protocol-demo:
opam:36:tezos-embedded-protocol-demo:
<<: *opam_definition
variables:
package: tezos-embedded-protocol-demo
opam:36:tezos-embedded-protocol-genesis:
opam:37:tezos-embedded-protocol-genesis:
<<: *opam_definition
variables:
package: tezos-embedded-protocol-genesis
opam:37:tezos-shell:
opam:38:tezos-shell:
<<: *opam_definition
variables:
package: tezos-shell
opam:38:ocplib-ezresto:
opam:39:ocplib-ezresto:
<<: *opam_definition
variables:
package: ocplib-ezresto
opam:39:tezos-client:
opam:40:tezos-client:
<<: *opam_definition
variables:
package: tezos-client
opam:40:tezos-node:
opam:41:tezos-node:
<<: *opam_definition
variables:
package: tezos-node
opam:41:ocplib-ezresto-directory:
opam:42:ocplib-ezresto-directory:
<<: *opam_definition
variables:
package: ocplib-ezresto-directory
opam:42:tezos-baker-alpha:
opam:43:tezos-baker-alpha:
<<: *opam_definition
variables:
package: tezos-baker-alpha
opam:43:tezos-protocol-demo:
opam:44:tezos-protocol-demo:
<<: *opam_definition
variables:
package: tezos-protocol-demo

View File

@ -17,6 +17,6 @@ depends: [
"stdio" {build & >= "v0.10.0"}
"nocrypto" {>= "0.5.4"}
"pbkdf" {>= "0.2.0"}
"hex" { "test" }
"alcotest" { "test" }
"hex" {test & >= "1.2.0"}
"alcotest" {test & >= "0.8.1"}
]

11
vendors/ocaml-pbkdf/CHANGES.md vendored Normal file
View File

@ -0,0 +1,11 @@
# 0.3.0 (2018-02-16)
* Build: switch to jbuilder
# 0.2.0 (2016-10-31)
* Added topkg dependency
# 0.1.0 (2016-03-14)
* Initial release

24
vendors/ocaml-pbkdf/LICENSE.md vendored Normal file
View File

@ -0,0 +1,24 @@
Copyright (c) 2016, Alfredo Beaumont, Sonia Meruelo
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

6
vendors/ocaml-pbkdf/README.md vendored Normal file
View File

@ -0,0 +1,6 @@
[![docs](https://img.shields.io/badge/doc-online-blue.svg)](https://abeaumont.github.io/ocaml-pbkdf)
[![Build Status](https://travis-ci.org/abeaumont/ocaml-pbkdf.svg?branch=master)](https://travis-ci.org/abeaumont/ocaml-pbkdf)
# Password based key derivation functions (PBKDF) from PKCS#5
An implementation of PBKDF 1 and 2 as defined by [PKCS#5](https://tools.ietf.org/html/rfc2898) using [nocrypto](https://github.com/mirleft/ocaml-nocrypto)

17
vendors/ocaml-pbkdf/pbkdf.opam vendored Normal file
View File

@ -0,0 +1,17 @@
opam-version: "1.2"
name: "pbkdf"
version: "0.3.0"
homepage: "https://github.com/abeaumont/ocaml-pbkdf"
dev-repo: "https://github.com/abeaumont/ocaml-pbkdf.git"
bug-reports: "https://github.com/abeaumont/ocaml-pbkdf/issues"
authors: ["Alfredo Beaumont <alfredo.beaumont@gmail.com>" "Sonia Meruelo <smeruelo@gmail.com>"]
maintainer: ["Alfredo Beaumont <alfredo.beaumont@gmail.com>"]
license: "BSD2"
build: [ "jbuilder" "build" "-j" jobs "-p" name "@install" ]
build-test: [ "jbuilder" "runtest" "-p" name "-j" jobs ]
depends: [
"jbuilder" {build & >= "1.0+beta17"}
"nocrypto" {>= "0.5.4"}
"alcotest" {test & >= "0.8.1"}
]

7
vendors/ocaml-pbkdf/src/jbuild vendored Normal file
View File

@ -0,0 +1,7 @@
(jbuild_version 1)
(library
((name pbkdf)
(public_name pbkdf)
(synopsis "")
(libraries (nocrypto))))

56
vendors/ocaml-pbkdf/src/pbkdf.ml vendored Normal file
View File

@ -0,0 +1,56 @@
open Nocrypto
open Uncommon
module type S = sig
val pbkdf1 : password:Cstruct.t -> salt:Cstruct.t -> count:int -> dk_len:int -> Cstruct.t
val pbkdf2 : password:Cstruct.t -> salt:Cstruct.t -> count:int -> dk_len:int32 -> Cstruct.t
end
module Make (H: Hash.S) : S = struct
let pbkdf1 ~password ~salt ~count ~dk_len =
if Cstruct.len salt <> 8 then invalid_arg "salt should be 8 bytes"
else if count <= 0 then invalid_arg "count must be a positive integer"
else if dk_len <= 0 then invalid_arg "derived key length must be a positive integer"
else if dk_len > H.digest_size then invalid_arg "derived key too long"
else
let rec loop t = function
0 -> t
| i -> loop (H.digest t) (i - 1)
in
Cstruct.sub (loop (Cstruct.append password salt) count) 0 dk_len
let pbkdf2 ~password ~salt ~count ~dk_len =
if count <= 0 then invalid_arg "count must be a positive integer"
else if dk_len <= 0l then invalid_arg "derived key length must be a positive integer"
else
let h_len = H.digest_size
and dk_len = Int32.to_int dk_len in
let l = cdiv dk_len h_len in
let r = dk_len - (l - 1) * h_len in
let block i =
let rec f u xor = function
0 -> xor
| j -> let u = H.hmac ~key:password u in
f u (Cs.xor xor u) (j - 1)
in
let int_i = Cstruct.create 4 in
Cstruct.BE.set_uint32 int_i 0 (Int32.of_int i);
let u_1 = H.hmac ~key:password (Cstruct.append salt int_i) in
f u_1 u_1 (count - 1)
in
let rec loop blocks = function
0 -> blocks
| i -> loop ((block i)::blocks) (i - 1)
in
Cstruct.concat (loop [Cstruct.sub (block l) 0 r] (l - 1))
end
let pbkdf1 ~hash ~password ~salt ~count ~dk_len =
let module H = (val (Hash.module_of hash)) in
let module PBKDF = Make (H) in
PBKDF.pbkdf1 ~password ~salt ~count ~dk_len
let pbkdf2 ~prf ~password ~salt ~count ~dk_len =
let module H = (val (Hash.module_of prf)) in
let module PBKDF = Make (H) in
PBKDF.pbkdf2 ~password ~salt ~count ~dk_len

23
vendors/ocaml-pbkdf/src/pbkdf.mli vendored Normal file
View File

@ -0,0 +1,23 @@
(** {{:https://tools.ietf.org/html/rfc2898}RFC 2898} specifies two password-based
key derivation functions (PBKDF1 and PBKDF2), which are abstracted over
a specific hash/pseudorandom function. *)
module type S = sig
(** [pbkdf1 password salt count dk_len] is [dk], the derived key of [dk_len] octets.
The [salt] must be eight octets, [count] the iteration count.
@raise Invalid_argument when either [salt] is not eight octets long or either
[count] or [dk_len] are not valid. *)
val pbkdf1 : password:Cstruct.t -> salt:Cstruct.t -> count:int -> dk_len:int -> Cstruct.t
(** [pbkdf2 password salt count dk_len] is [dk], the derived key of [dk_len] octets.
@raise Invalid_argument when either [count] or [dk_len] are not valid *)
val pbkdf2 : password:Cstruct.t -> salt:Cstruct.t -> count:int -> dk_len:int32 -> Cstruct.t
end
(** Given a Hash/pseudorandom function, get the PBKDF *)
module Make (H: Nocrypto.Hash.S) : S
(** convenience [pbkdf1 hash password salt count dk_len] where the [hash] has to be provided explicitly *)
val pbkdf1 : hash:[`MD5 | `SHA1] -> password:Cstruct.t -> salt:Cstruct.t -> count:int -> dk_len:int -> Cstruct.t
(** convenience [pbkdf2 prf password salt count dk_len] where the [prf] has to be provided explicitly *)
val pbkdf2 : prf:Nocrypto.Hash.hash -> password:Cstruct.t -> salt:Cstruct.t -> count:int -> dk_len:int32 -> Cstruct.t

10
vendors/ocaml-pbkdf/test/jbuild vendored Normal file
View File

@ -0,0 +1,10 @@
(jbuild_version 1)
(executable
((name pbkdf_tests)
(libraries (pbkdf alcotest))))
(alias
((name runtest-pbkdf)
(deps (pbkdf_tests.exe))
(action (run ${<}))))

300
vendors/ocaml-pbkdf/test/pbkdf_tests.ml vendored Normal file
View File

@ -0,0 +1,300 @@
(* PBKDF1 *)
let test_pbkdf1 ~hash ~password ~salt ~count ~dk_len ~dk =
let open Nocrypto.Uncommon.Cs in
let salt = of_hex salt
and dk = of_hex dk
and password = Cstruct.of_string password in
(fun () ->
let edk = Pbkdf.pbkdf1 ~hash ~password ~salt ~count ~dk_len in
let sedk = Cstruct.to_string edk
and sdk = Cstruct.to_string dk
in
Alcotest.check Alcotest.string "PBKDF1 test" sedk sdk)
let test_pbkdf1_invalid_arg ~hash ~password ~salt ~count ~dk_len ~msg =
let open Nocrypto.Uncommon.Cs in
let salt = of_hex salt
and password = Cstruct.of_string password in
(fun () ->
Alcotest.check_raises
msg
(Invalid_argument msg)
(fun () -> ignore (Pbkdf.pbkdf1 ~hash ~password ~salt ~count ~dk_len)))
(* Taken from http://www.di-mgt.com.au/cryptoKDFs.html *)
let pbkdf1_test1 =
test_pbkdf1
~hash:`SHA1
~password:"password"
~salt:"78578e5a5d63cb06"
~count:1000
~dk_len:16
~dk:"dc19847e05c64d2faf10ebfb4a3d2a20"
let pbkdf1_test2 =
test_pbkdf1_invalid_arg
~hash:`SHA1
~password:"password"
~salt:"78578e5a5d63cb"
~count:1000
~dk_len:16
~msg:"salt should be 8 bytes"
let pbkdf1_test3 =
test_pbkdf1_invalid_arg
~hash:`SHA1
~password:"password"
~salt:"78578e5a5d63cb0600"
~count:1000
~dk_len:16
~msg:"salt should be 8 bytes"
let pbkdf1_test4 =
test_pbkdf1_invalid_arg
~hash:`SHA1
~password:"password"
~salt:"78578e5a5d63cb06"
~count:(-1)
~dk_len:16
~msg:"count must be a positive integer"
let pbkdf1_test5 =
test_pbkdf1_invalid_arg
~hash:`SHA1
~password:"password"
~salt:"78578e5a5d63cb06"
~count:0
~dk_len:16
~msg:"count must be a positive integer"
let pbkdf1_test6 =
test_pbkdf1_invalid_arg
~hash:`SHA1
~password:"password"
~salt:"78578e5a5d63cb06"
~count:1000
~dk_len:24
~msg:"derived key too long"
let pbkdf1_test7 =
test_pbkdf1_invalid_arg
~hash:`SHA1
~password:"password"
~salt:"78578e5a5d63cb06"
~count:1000
~dk_len:0
~msg:"derived key length must be a positive integer"
let pbkdf1_tests = [
"Test Case 1", `Quick, pbkdf1_test1;
"Test Case 2", `Quick, pbkdf1_test2;
"Test Case 3", `Quick, pbkdf1_test3;
"Test Case 4", `Quick, pbkdf1_test4;
"Test Case 5", `Quick, pbkdf1_test5;
"Test Case 6", `Quick, pbkdf1_test6;
"Test Case 7", `Quick, pbkdf1_test7;
]
(* PBKDF2 *)
let test_pbkdf2 ~prf ~password ~salt ~count ~dk_len ~dk =
let open Nocrypto.Uncommon.Cs in
let salt = of_hex salt
and dk = of_hex dk
and password = Cstruct.of_string password
in
(fun () ->
let edk = Pbkdf.pbkdf2 ~prf ~password ~salt ~count ~dk_len in
let sedk = Cstruct.to_string edk
and sdk = Cstruct.to_string dk
in
Alcotest.check Alcotest.string "PBKDF2 test" sedk sdk)
let test_pbkdf2_invalid_arg ~prf ~password ~salt ~count ~dk_len ~msg () =
let salt = Nocrypto.Uncommon.Cs.of_hex salt
and password = Cstruct.of_string password
in
Alcotest.check_raises
msg
(Invalid_argument msg)
(fun () -> ignore (Pbkdf.pbkdf2 ~prf ~password ~salt ~count ~dk_len))
(* Taken from https://github.com/randombit/botan/blob/master/src/tests/data/pbkdf/pbkdf2.vec *)
let pbkdf2_test1 =
test_pbkdf2
~prf:`SHA1
~password:""
~salt:"0001020304050607"
~count:10000
~dk_len:32l
~dk:"59b2b1143b4cb1059ec58d9722fb1c72471e0d85c6f7543ba5228526375b0127"
let pbkdf2_test2 =
test_pbkdf2
~prf:`SHA1
~password:"jyueqgxrscgglpxdykcf"
~salt:"9b56e55328a4c97a250738f8dba1b992e8a1b508"
~count:10000
~dk_len:14l
~dk:"df6d9d72872404bf73e708cf3b7d"
let pbkdf2_test3 =
test_pbkdf2
~prf:`SHA1
~password:"aqrqsznzvvzgtksammgo"
~salt:"57487813cdd2220dfc485d932a2979ee8769ea8b"
~count:101
~dk_len:40l
~dk:"fa13f40af1ade2a30f2fffd66fc8a659ef95e6388c1682fc0fe4d15a70109517a32942e39c371440"
let pbkdf2_test4 =
test_pbkdf2
~prf:`SHA1
~password:"ltexmfeyylmlbrsyikaw"
~salt:"ed1f39a0a7f3889aaf7e60743b3bc1cc2c738e60"
~count:1000
~dk_len:10l
~dk:"027afadd48f4be8dcc4f"
let pbkdf2_test5 =
test_pbkdf2
~prf:`SHA1
~password:"cxgnyrcgrvllylolsjpo"
~salt:"94ac88200743fb0f6ac51be62166cbef08d94c15"
~count:1
~dk_len:32l
~dk:"7c0d009fc91b48cb6d19bafbfccff3e2ccabfe725eaa234e56bde1d551c132f2"
let pbkdf2_test6 =
test_pbkdf2
~prf:`SHA1
~password:"xqyfhrxehiedlhewnvbj"
~salt:"24a1a50b17d63ee8394b69fc70887f4f94883d68"
~count:5
~dk_len:32l
~dk:"4661301d3517ca4443a6a607b32b2a63f69996299df75db75f1e0b98dd0eb7d8"
let pbkdf2_test7 =
test_pbkdf2
~prf:`SHA1
~password:"andaqkpjwabvcfnpnjkl"
~salt:"9316c80801623cc2734af74bec42cf4dbaa3f6d5"
~count:100
~dk_len:30l
~dk:"82fb44a521448d5aac94b5158ead1e4dcd7363081a747b9f7626752bda2d"
let pbkdf2_test8 =
test_pbkdf2
~prf:`SHA1
~password:"hsavvyvocloyuztlsniu"
~salt:"612cc61df3cf2bdb36e10c4d8c9d73192bddee05"
~count:100
~dk_len:30l
~dk:"f8ec2b0ac817896ac8189d787c6424ed24a6d881436687a4629802c0ecce"
let pbkdf2_test9 =
test_pbkdf2
~prf:`SHA1
~password:"eaimrbzpcopbusaqtkmw"
~salt:"45248f9d0cebcb86a18243e76c972a1f3b36772a"
~count:100
~dk_len:34l
~dk:"c9a0b2622f13916036e29e7462e206e8ba5b50ce9212752eb8ea2a4aa7b40a4cc1bf"
let pbkdf2_test10 =
test_pbkdf2
~prf:`SHA1
~password:"gwrxpqxumsdsmbmhfhmfdcvlcvngzkig"
~salt:"a39b76c6eec8374a11493ad08c246a3e40dfae5064f4ee3489c273646178"
~count:1000
~dk_len:64l
~dk:"4c9db7ba24955225d5b845f65ef24ef1b0c6e86f2e39c8ddaa4b8abd26082d1f350381fadeaeb560dc447afc68a6b47e6ea1e7412f6cf7b2d82342fccd11d3b4"
let pbkdf2_test11 =
test_pbkdf2
~prf:`SHA256
~password:"xyz"
~salt:"0001020304050607"
~count: 10000
~dk_len:48l
~dk:"defd2987fa26a4672f4d16d98398432ad95e896bf619f6a6b8d4ed1faf98e8b531b39ffb66966d0e115a6cd8e70b72d0"
let pbkdf2_test12 =
test_pbkdf2
~prf:`SHA384
~password:"xyz"
~salt:"0001020304050607"
~count:10000
~dk_len:48l
~dk:"47a3ae920b24edaa2bb53155808554b13fab58df62b81f043d9812e9f2881164df20bbffa54e5ee2489fa183b6718a74"
let pbkdf2_test13 =
test_pbkdf2
~prf:`SHA512
~password:"xyz"
~salt:"0001020304050607"
~count:10000
~dk_len:48l
~dk:"daf8a734327745eb63d19054dbd4018a682cef11086a1bfb63fdbc16158c2f8b0742802f36aef1b1df92accbea5d31a5"
let pbkdf2_test14 =
test_pbkdf2_invalid_arg
~prf:`SHA1
~password:"password"
~salt:"0001020304050607"
~count:(-1)
~dk_len:48l
~msg:"count must be a positive integer"
let pbkdf2_test15 =
test_pbkdf2_invalid_arg
~prf:`SHA1
~password:"password"
~salt:"0001020304050607"
~count:0
~dk_len:48l
~msg:"count must be a positive integer"
let pbkdf2_test16 =
test_pbkdf2_invalid_arg
~prf:`SHA1
~password:"password"
~salt:"0001020304050607"
~count:1000
~dk_len:(-1l)
~msg:"derived key length must be a positive integer"
let pbkdf2_test17 =
test_pbkdf2_invalid_arg
~prf:`SHA1
~password:"password"
~salt:"0001020304050607"
~count:1000
~dk_len:0l
~msg:"derived key length must be a positive integer"
let pbkdf2_tests = [
"Test Case 1", `Quick, pbkdf2_test1;
"Test Case 2", `Quick, pbkdf2_test2;
"Test Case 3", `Quick, pbkdf2_test3;
"Test Case 4", `Quick, pbkdf2_test4;
"Test Case 5", `Quick, pbkdf2_test5;
"Test Case 6", `Quick, pbkdf2_test6;
"Test Case 7", `Quick, pbkdf2_test7;
"Test Case 8", `Quick, pbkdf2_test8;
"Test Case 9", `Quick, pbkdf2_test9;
"Test Case 10", `Quick, pbkdf2_test10;
"Test Case 11", `Quick, pbkdf2_test11;
"Test Case 12", `Quick, pbkdf2_test12;
"Test Case 13", `Quick, pbkdf2_test13;
"Test Case 14", `Quick, pbkdf2_test14;
"Test Case 15", `Quick, pbkdf2_test15;
"Test Case 16", `Quick, pbkdf2_test16;
"Test Case 17", `Quick, pbkdf2_test17;
]
let () =
Alcotest.run "PBKDF Tests" [
"PBKDF1 tests", pbkdf1_tests;
"PBKDF2 tests", pbkdf2_tests;
]