pax_global_header00006660000000000000000000000064146450103000014503gustar00rootroot0000000000000052 comment=9bf06a79aef177f97150e242d01c9fad6aa2d811 ocaml-pbkdf-2.0.0/000077500000000000000000000000001464501030000136615ustar00rootroot00000000000000ocaml-pbkdf-2.0.0/.gitignore000066400000000000000000000000341464501030000156460ustar00rootroot00000000000000_build *.install **/*.merlinocaml-pbkdf-2.0.0/.travis.yml000066400000000000000000000005761464501030000160020ustar00rootroot00000000000000language: c install: wget https://raw.githubusercontent.com/ocaml/ocaml-ci-scripts/master/.travis-docker.sh script: bash -ex .travis-docker.sh sudo: false services: - docker env: global: - PACKAGE="pbkdf" - DISTRO=alpine - TESTS=true matrix: - OCAML_VERSION=4.07 - OCAML_VERSION=4.08 - OCAML_VERSION=4.09 - OCAML_VERSION=4.10 notifications: email: false ocaml-pbkdf-2.0.0/CHANGES.md000066400000000000000000000006761464501030000152640ustar00rootroot00000000000000# 2.0.0 (2024-06-29) * Update to mirage-crypto 1.0.0 (#13 @dinosaure) # 1.2.0 (2020-08-03) * Upgrade to Cstruct 6.0.0 # 1.1.0 (2020-03-31) * Port to mirage-crypto (thanks to @hannesm) # 1.0.0 (2019-04-12) * Move to dune * Upgrade to opam 2.0 * Reimplement `cdiv`, no longer available in `nocrypto`. # 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 ocaml-pbkdf-2.0.0/LICENSE.md000066400000000000000000000024431464501030000152700ustar00rootroot00000000000000Copyright (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. ocaml-pbkdf-2.0.0/README.md000066400000000000000000000007001464501030000151350ustar00rootroot00000000000000[![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 [mirage-crypto](https://github.com/mirage/mirage-crypto)ocaml-pbkdf-2.0.0/dune-project000066400000000000000000000000351464501030000162010ustar00rootroot00000000000000(lang dune 1.8) (name pbkdf) ocaml-pbkdf-2.0.0/pbkdf.opam000066400000000000000000000017321464501030000156300ustar00rootroot00000000000000opam-version: "2.0" synopsis: "Password based key derivation functions (PBKDF) from PKCS#5" description: """ An implementation of PBKDF 1 and 2 as defined by [PKCS#5](https://tools.ietf.org/html/rfc2898) using [mirage-crypto](https://github.com/mirage/mirage-crypto) """ maintainer: ["Alfredo Beaumont "] authors: ["Alfredo Beaumont " "Sonia Meruelo "] license: "BSD-2-Clause" homepage: "https://github.com/abeaumont/ocaml-pbkdf" bug-reports: "https://github.com/abeaumont/ocaml-pbkdf/issues" dev-repo: "git+https://github.com/abeaumont/ocaml-pbkdf.git" doc: "https://abeaumont.github.io/ocaml-pbkdf/" depends: [ "ocaml" {>= "4.07.0"} "dune" {>= "1.8.0"} "mirage-crypto" {>= "1.0.0"} "digestif" "alcotest" {with-test & >= "0.8.1"} "ohex" {with-test} ] build: [ [ "dune" "subst" ] {dev} [ "dune" "build" "-j" jobs "-p" name "@install" ] [ "dune" "runtest" "-p" name "-j" jobs ] {with-test} ] ocaml-pbkdf-2.0.0/src/000077500000000000000000000000001464501030000144505ustar00rootroot00000000000000ocaml-pbkdf-2.0.0/src/dune000066400000000000000000000001211464501030000153200ustar00rootroot00000000000000(library (name pbkdf) (public_name pbkdf) (libraries digestif mirage-crypto)) ocaml-pbkdf-2.0.0/src/pbkdf.ml000066400000000000000000000047771464501030000161070ustar00rootroot00000000000000module type S = sig val pbkdf1 : password:string -> salt:string -> count:int -> dk_len:int -> string val pbkdf2 : password:string -> salt:string -> count:int -> dk_len:int32 -> string end let cdiv x y = (* This is lifted from Nocrypto.Uncommon.(//) (formerly known as [cdiv]). It is part of the documented, publically exposed _internal_ utility library not for public consumption, hence the API break that prompted this copy-pasted function. *) if y < 1 then raise Division_by_zero else if x > 0 then 1 + ((x - 1) / y) else 0 [@@inline] module Make (H: Digestif.S) : S = struct let pbkdf1 ~password ~salt ~count ~dk_len = if String.length 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.(to_raw_string (digest_string t)) (i - 1) in String.sub (loop (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.(to_raw_string (hmac_string ~key:password u)) in f u (Mirage_crypto.Uncommon.xor xor u) (j - 1) in let int_i = Bytes.create 4 in Bytes.set_int32_be int_i 0 (Int32.of_int i); let u_1 = H.hmac_string ~key:password (salt ^ Bytes.unsafe_to_string int_i) in let u_1 = H.to_raw_string u_1 in f u_1 u_1 (count - 1) in let rec loop blocks = function 0 -> blocks | i -> loop ((block i)::blocks) (i - 1) in String.concat "" (loop [String.sub (block l) 0 r] (l - 1)) end let pbkdf1 ~hash ~password ~salt ~count ~dk_len = let module H = (val (Digestif.module_of_hash' 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 (Digestif.module_of_hash' prf)) in let module PBKDF = Make (H) in PBKDF.pbkdf2 ~password ~salt ~count ~dk_len ocaml-pbkdf-2.0.0/src/pbkdf.mli000066400000000000000000000025201464501030000162400ustar00rootroot00000000000000(** {{: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:string -> salt:string -> count:int -> dk_len:int -> string (** [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:string -> salt:string -> count:int -> dk_len:int32 -> string end (** Given a Hash/pseudorandom function, get the PBKDF *) module Make (H: Digestif.S) : S (** convenience [pbkdf1 hash password salt count dk_len] where the [hash] has to be provided explicitly *) val pbkdf1 : hash:Digestif.hash' -> password:string -> salt:string -> count:int -> dk_len:int -> string (** convenience [pbkdf2 prf password salt count dk_len] where the [prf] has to be provided explicitly *) val pbkdf2 : prf:Digestif.hash' -> password:string -> salt:string -> count:int -> dk_len:int32 -> string ocaml-pbkdf-2.0.0/test/000077500000000000000000000000001464501030000146405ustar00rootroot00000000000000ocaml-pbkdf-2.0.0/test/dune000066400000000000000000000000741464501030000155170ustar00rootroot00000000000000(test (name pbkdf_tests) (libraries ohex pbkdf alcotest)) ocaml-pbkdf-2.0.0/test/pbkdf_tests.ml000066400000000000000000000166711464501030000175150ustar00rootroot00000000000000let () = Printexc.record_backtrace true (* PBKDF1 *) let test_pbkdf1 ~hash ~password ~salt ~count ~dk_len ~dk = let salt = Ohex.decode salt and dk = Ohex.decode dk in (fun () -> let edk = Pbkdf.pbkdf1 ~hash ~password ~salt ~count ~dk_len in Alcotest.check Alcotest.string "PBKDF1 test" edk dk) let test_pbkdf1_invalid_arg ~hash ~password ~salt ~count ~dk_len ~msg = let salt = Ohex.decode salt 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 salt = Ohex.decode salt and dk = Ohex.decode dk in (fun () -> let edk = Pbkdf.pbkdf2 ~prf ~password ~salt ~count ~dk_len in Alcotest.check Alcotest.string "PBKDF2 test" edk dk) let test_pbkdf2_invalid_arg ~prf ~password ~salt ~count ~dk_len ~msg () = let salt = Ohex.decode salt 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; ]