upstream: github.com/robur-coop/kdf
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Squashed 'kdf/' content from commit 28d0c76 git-subtree-split: 28d0c7692846985deaee006d93ebf3ed905ad98a

+1010
+7
.gitignore
··· 1 + _build/ 2 + _tests/ 3 + coverage/ 4 + *.install 5 + **/*.merlin 6 + *.byte 7 + *.native
+1
.ocamlformat
··· 1 + version = 0.28.1
+98
CHANGES.md
··· 1 + # v1.0.0 (2024-08-28) 2 + 3 + * Migrate scrypt from Cstruct.t to string 4 + * Merge ocaml-pbkdf (from https://github.com/abeaumont/ocaml-pbkdf), 5 + hkdf (from https://github.com/hannesm/ocaml-hkdf), and scrypt (from 6 + https://github.com/abeaumont/ocaml-scrypt-kdf) into a single repository 7 + and opam package (with three subpackages, kdf.pbkdf. kdf.hkdf, and 8 + kdf.scrypt). 9 + * Disable a failing testcase for architectures with integers no longer than 31 10 + bits (thanks to @kit-ty-kate) 11 + 12 + # pbkdf 2.0.0 (2024-06-29) 13 + 14 + * Update to mirage-crypto 1.0.0 (#13 @dinosaure) 15 + 16 + # hkdf v2.0.0 (2024-06-29) 17 + 18 + * use digestif instead of mirage-crypto (@dinosaure @hannesm) 19 + 20 + # scrypt-kdf 1.2.0 (2021-08-03) 21 + 22 + * Upgrade to Cstruct 6.0.0 23 + 24 + # pbkdf 1.2.0 (2020-08-03) 25 + 26 + * Upgrade to Cstruct 6.0.0 27 + 28 + # pbkdf 1.1.0 (2020-03-31) 29 + 30 + * Port to mirage-crypto (thanks to @hannesm) 31 + 32 + # scrypt-kdf 1.1.0 (2020-03-31) 33 + 34 + * Port to mirage-crypto (thanks to @hannesm) 35 + 36 + # hkdf v1.0.4 (2020-03-11) 37 + 38 + * use mirage-crypto instead of nocrypto 39 + 40 + # scrypt-kdf 1.0.0 (2019-04-12) 41 + 42 + * Move to dune 43 + * Upgrade to opam 2.0 44 + 45 + # pbkdf 1.0.0 (2019-04-12) 46 + 47 + * Move to dune 48 + * Upgrade to opam 2.0 49 + * Reimplement `cdiv`, no longer available in `nocrypto`. 50 + 51 + # hkdf 1.0.3 (2019-02-15) 52 + 53 + * move to dune 54 + 55 + # pbkdf 0.3.0 (2018-02-16) 56 + 57 + * Build: switch to jbuilder 58 + 59 + # scrypt-kdf 0.4.0 (2017-03-09) 60 + 61 + * Removed Makefile, unneeded with topkg 62 + * Made pkg.ml executable 63 + * Added salsa20-core as a dependency and remove related code 64 + 65 + # scrypt-kdf 0.3.0 (2017-02-21) 66 + 67 + * Replaced underscores by dashes in library names 68 + * Exported Salsa20_core module 69 + 70 + # pbkdf 0.2.0 (2016-10-31) 71 + 72 + * Added topkg dependency 73 + 74 + # scrypt-kdf 0.2.0 (2016-10-31) 75 + 76 + * Added topkg dependency 77 + * Optimized inner loop in salsa_core to improve performance 78 + * Replaced custom clone function by Nocrypto's implementation 79 + 80 + # hkdf 1.0.2 (2016-07-18) 81 + 82 + * move to topkg 83 + 84 + # scrypt-kdf 0.1.0 (2016-03-18) 85 + 86 + * Initial release 87 + 88 + # pbkdf 0.1.0 (2016-03-14) 89 + 90 + * Initial release 91 + 92 + # hkdf 1.0.1 (2015-12-20) 93 + 94 + * move from oasis to topkg 95 + 96 + # hkdf 1.0.0 (2015-11-30) 97 + 98 + * initial release
+25
LICENSE.md
··· 1 + Copyright (c) 2014, Hannes Mehnert 2 + Copyright (c) 2016, Alfredo Beaumont, Sonia Meruelo 3 + All rights reserved. 4 + 5 + Redistribution and use in source and binary forms, with or without 6 + modification, are permitted provided that the following conditions are met: 7 + 8 + * Redistributions of source code must retain the above copyright notice, this 9 + list of conditions and the following disclaimer. 10 + 11 + * Redistributions in binary form must reproduce the above copyright notice, 12 + this list of conditions and the following disclaimer in the documentation 13 + and/or other materials provided with the distribution. 14 + 15 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 +
+17
README.md
··· 1 + # kdf - Key Derivation Functions 2 + 3 + This repository provides multiple already specified key derivation functions in 4 + and for OCaml: 5 + 6 + - [scrypt](https://tools.ietf.org/html/rfc7914), 7 + - [PBKDF 1 and 2 as defined by PKCS#5](https://tools.ietf.org/html/rfc2898), 8 + - and [HKDF](https://tools.ietf.org/html/rfc5869). 9 + 10 + ## Documentation 11 + 12 + [API Documentation](https://robur-coop.github.io/kdf/doc) 13 + 14 + ## Installation 15 + 16 + `opam install kdf` will install the latest released version. 17 +
+2
dune-project
··· 1 + (lang dune 1.8) 2 + (name kdf)
+5
hkdf/dune
··· 1 + (library 2 + (name hkdf) 3 + (public_name kdf.hkdf) 4 + (modules hkdf) 5 + (libraries digestif))
+42
hkdf/hkdf.ml
··· 1 + 2 + module type S = sig 3 + val extract : ?salt:string -> string -> string 4 + val expand : prk:string -> ?info:string -> int -> string 5 + end 6 + 7 + module Make (H : Digestif.S) : S = struct 8 + let extract ?salt ikm = 9 + let key = match salt with 10 + | None -> String.make H.digest_size '\x00' 11 + | Some x -> x 12 + in 13 + H.(to_raw_string (hmac_string ~key ikm)) 14 + 15 + let expand ~prk ?info len = 16 + let info = match info with 17 + | None -> "" 18 + | Some x -> x 19 + in 20 + let t n last = 21 + let nc = String.make 1 (Char.unsafe_chr n) in 22 + H.(to_raw_string (hmac_string ~key:prk (String.concat "" [last ; info ; nc]))) 23 + in 24 + let n = succ (len / H.digest_size) in 25 + let rec compute acc count = match count, acc with 26 + | c, xs when c > n -> String.concat "" (List.rev xs) 27 + | c, x::_ -> compute (t c x :: acc) (succ c) 28 + | _, [] -> invalid_arg "can not happen" 29 + in 30 + let buf = compute [""] 1 in 31 + String.sub buf 0 len 32 + end 33 + 34 + let extract ~hash ?salt ikm = 35 + let module H = (val (Digestif.module_of_hash' hash)) in 36 + let module HKDF = Make (H) in 37 + HKDF.extract ?salt ikm 38 + 39 + let expand ~hash ~prk ?info len = 40 + let module H = (val (Digestif.module_of_hash' hash)) in 41 + let module HKDF = Make (H) in 42 + HKDF.expand ~prk ?info len
+27
hkdf/hkdf.mli
··· 1 + 2 + (** {{:https://tools.ietf.org/html/rfc5869}RFC 5869} specifies a HMAC-based 3 + Extract-and-Expand Key Derivation Function (HKDF), which is abstracted over 4 + a specific hash function. *) 5 + 6 + module type S = sig 7 + 8 + (** [extract salt ikm] is [prk], the pseudorandom key of hash length octets. 9 + The [salt] is an optional non-secret random value, [ikm] the input key 10 + material. *) 11 + val extract : ?salt:string -> string -> string 12 + 13 + (** [expand prk info length] is [okm], the output keying material. Given the 14 + pseudorandom key of hash length (usually output of [!extract] step), and an 15 + optional context and application specific information [info], the [okm] is 16 + generated. *) 17 + val expand : prk:string -> ?info:string -> int -> string 18 + end 19 + 20 + (** Given a Hash function, get the HKDF *) 21 + module Make (H : Digestif.S) : S 22 + 23 + (** convenience [extract hash salt ikm] where the [hash] has to be provided explicitly *) 24 + val extract : hash:Digestif.hash' -> ?salt:string -> string -> string 25 + 26 + (** convenience [expand hash prk info len] where the [hash] has to be provided explicitly *) 27 + val expand : hash:Digestif.hash' -> prk:string -> ?info:string -> int -> string
+5
hkdf/tests/dune
··· 1 + (test 2 + (name rfctests) 3 + (modules rfctests) 4 + (modes native) 5 + (libraries alcotest kdf.hkdf ohex))
+149
hkdf/tests/rfctests.ml
··· 1 + 2 + let test ~hash ~ikm ?salt ?info ~l ~prk ~okm () = 3 + let ikm = Ohex.decode ikm 4 + and salt = match salt with None -> None | Some x -> Some (Ohex.decode x) 5 + and info = match info with None -> None | Some x -> Some (Ohex.decode x) 6 + and prk = Ohex.decode prk 7 + and okm = Ohex.decode okm 8 + in 9 + (fun () -> 10 + let cprk = Hkdf.extract ~hash ?salt ikm in 11 + Alcotest.check Alcotest.string "PRK matches" prk cprk ; 12 + let cokm = Hkdf.expand ~hash ~prk:cprk ?info l in 13 + Alcotest.check Alcotest.string "OKM matches" okm cokm) 14 + 15 + let test1 = 16 + test 17 + ~hash:`SHA256 18 + ~ikm:"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b" 19 + ~salt:"000102030405060708090a0b0c" 20 + ~info:"f0f1f2f3f4f5f6f7f8f9" 21 + ~l:42 22 + ~prk:"077709362c2e32df0ddc3f0dc47bba63 \ 23 + 90b6c73bb50f9c3122ec844ad7c2b3e5" 24 + ~okm:"3cb25f25faacd57a90434f64d0362f2a \ 25 + 2d2d0a90cf1a5a4c5db02d56ecc4c5bf \ 26 + 34007208d5b887185865" 27 + () 28 + 29 + and test2 = 30 + test 31 + ~hash:`SHA256 32 + ~ikm:"000102030405060708090a0b0c0d0e0f \ 33 + 101112131415161718191a1b1c1d1e1f \ 34 + 202122232425262728292a2b2c2d2e2f \ 35 + 303132333435363738393a3b3c3d3e3f \ 36 + 404142434445464748494a4b4c4d4e4f" 37 + ~salt:"606162636465666768696a6b6c6d6e6f \ 38 + 707172737475767778797a7b7c7d7e7f \ 39 + 808182838485868788898a8b8c8d8e8f \ 40 + 909192939495969798999a9b9c9d9e9f \ 41 + a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" 42 + ~info:"b0b1b2b3b4b5b6b7b8b9babbbcbdbebf \ 43 + c0c1c2c3c4c5c6c7c8c9cacbcccdcecf \ 44 + d0d1d2d3d4d5d6d7d8d9dadbdcdddedf \ 45 + e0e1e2e3e4e5e6e7e8e9eaebecedeeef \ 46 + f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" 47 + ~l:82 48 + ~prk:"06a6b88c5853361a06104c9ceb35b45c \ 49 + ef760014904671014a193f40c15fc244" 50 + ~okm:"b11e398dc80327a1c8e7f78c596a4934 \ 51 + 4f012eda2d4efad8a050cc4c19afa97c \ 52 + 59045a99cac7827271cb41c65e590e09 \ 53 + da3275600c2f09b8367793a9aca3db71 \ 54 + cc30c58179ec3e87c14c01d5c1f3434f \ 55 + 1d87" 56 + () 57 + 58 + and test3 = 59 + test 60 + ~hash:`SHA256 61 + ~ikm:"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b" 62 + ~salt:"" 63 + (* info = (0 octets) *) 64 + ~l:42 65 + ~prk:"19ef24a32c717b167f33a91d6f648bdf \ 66 + 96596776afdb6377ac434c1c293ccb04" 67 + ~okm:"8da4e775a563c18f715f802a063c5a31 \ 68 + b8a11f5c5ee1879ec3454e5f3c738d2d \ 69 + 9d201395faa4b61a96c8" 70 + () 71 + 72 + and test4 = 73 + test 74 + ~hash:`SHA1 75 + ~ikm:"0b0b0b0b0b0b0b0b0b0b0b" 76 + ~salt:"000102030405060708090a0b0c" 77 + ~info:"f0f1f2f3f4f5f6f7f8f9" 78 + ~l:42 79 + ~prk:"9b6c18c432a7bf8f0e71c8eb88f4b30baa2ba243" 80 + ~okm:"085a01ea1b10f36933068b56efa5ad81 \ 81 + a4f14b822f5b091568a9cdd4f155fda2 \ 82 + c22e422478d305f3f896" 83 + () 84 + 85 + and test5 = 86 + test 87 + ~hash:`SHA1 88 + ~ikm:"000102030405060708090a0b0c0d0e0f \ 89 + 101112131415161718191a1b1c1d1e1f \ 90 + 202122232425262728292a2b2c2d2e2f \ 91 + 303132333435363738393a3b3c3d3e3f \ 92 + 404142434445464748494a4b4c4d4e4f" 93 + ~salt:"606162636465666768696a6b6c6d6e6f \ 94 + 707172737475767778797a7b7c7d7e7f \ 95 + 808182838485868788898a8b8c8d8e8f \ 96 + 909192939495969798999a9b9c9d9e9f \ 97 + a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" 98 + ~info:"b0b1b2b3b4b5b6b7b8b9babbbcbdbebf \ 99 + c0c1c2c3c4c5c6c7c8c9cacbcccdcecf \ 100 + d0d1d2d3d4d5d6d7d8d9dadbdcdddedf \ 101 + e0e1e2e3e4e5e6e7e8e9eaebecedeeef \ 102 + f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" 103 + ~l:82 104 + ~prk:"8adae09a2a307059478d309b26c4115a224cfaf6" 105 + ~okm:"0bd770a74d1160f7c9f12cd5912a06eb \ 106 + ff6adcae899d92191fe4305673ba2ffe \ 107 + 8fa3f1a4e5ad79f3f334b3b202b2173c \ 108 + 486ea37ce3d397ed034c7f9dfeb15c5e \ 109 + 927336d0441f4c4300e2cff0d0900b52 \ 110 + d3b4" 111 + () 112 + 113 + and test6 = 114 + test 115 + ~hash:`SHA1 116 + ~ikm:"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b" 117 + ~salt:"" 118 + (* info = (0 octets) *) 119 + ~l:42 120 + ~prk:"da8c8a73c7fa77288ec6f5e7c297786aa0d32d01" 121 + ~okm:"0ac1af7002b3d761d1e55298da9d0506 \ 122 + b9ae52057220a306e07b6b87e8df21d0 \ 123 + ea00033de03984d34918" 124 + () 125 + 126 + and test7 = 127 + test 128 + ~hash:`SHA1 129 + ~ikm:"0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c" 130 + (* salt = not provided (defaults to HashLen zero octets) *) 131 + (* info = (0 octets) *) 132 + ~l:42 133 + ~prk:"2adccada18779e7c2077ad2eb19d3f3e731385dd" 134 + ~okm:"2c91117204d745f3500d636a62f64f0a \ 135 + b3bae548aa53d423b0d1f27ebba6f5e5 \ 136 + 673a081d70cce7acfc48" 137 + () 138 + 139 + let tests = [ 140 + "RFC 5869 Test Case 1", `Quick, test1 ; 141 + "RFC 5869 Test Case 2", `Quick, test2 ; 142 + "RFC 5869 Test Case 3", `Quick, test3 ; 143 + "RFC 5869 Test Case 4", `Quick, test4 ; 144 + "RFC 5869 Test Case 5", `Quick, test5 ; 145 + "RFC 5869 Test Case 6", `Quick, test6 ; 146 + "RFC 5869 Test Case 7", `Quick, test7 ; 147 + ] 148 + 149 + let () = Alcotest.run "HKDF Tests" [ "RFC 5869", tests ]
+29
kdf.opam
··· 1 + opam-version: "2.0" 2 + name: "kdf" 3 + maintainer: ["Alfredo Beaumont <alfredo.beaumont@gmail.com>" "Hannes Mehnert <hannes@mehnert.org>"] 4 + authors: ["Alfredo Beaumont <alfredo.beaumont@gmail.com>" "Sonia Meruelo <smeruelo@gmail.com>" "Hannes Mehnert <hannes@mehnert.org>"] 5 + license: "BSD-2-Clause" 6 + homepage: "https://github.com/robur-coop/kdf" 7 + doc: "https://robur-coop.github.io/kdf/doc" 8 + bug-reports: "https://github.com/robur-coop/kdf/issues" 9 + depends: [ 10 + "ocaml" {>= "4.13.0"} 11 + "dune" {>= "1.8.0"} 12 + "digestif" {>= "1.2.0"} 13 + "crypto" {>= "1.0.0"} 14 + "alcotest" {with-test & >= "0.8.1"} 15 + "ohex" {with-test & >= "0.2.0"} 16 + ] 17 + build: [ 18 + ["dune" "subst"] {dev} 19 + ["dune" "build" "-p" name "-j" jobs] 20 + ["dune" "runtest" "-p" name "-j" jobs] {with-test} 21 + ] 22 + dev-repo: "git+https://github.com/robur-coop/kdf.git" 23 + synopsis: "Key Derivation Functions: HKDF RFC 5869, PBKDF RFC 2898, SCRYPT RFC 7914" 24 + description: """ 25 + A pure OCaml implementation of [scrypt](https://tools.ietf.org/html/rfc7914), 26 + [PBKDF 1 and 2 as defined by PKCS#5](https://tools.ietf.org/html/rfc2898), 27 + and [HKDF](https://tools.ietf.org/html/rfc5869). 28 + """ 29 + x-maintenance-intent: [ "(latest)" ]
+4
pbkdf/dune
··· 1 + (library 2 + (name pbkdf) 3 + (public_name kdf.pbkdf) 4 + (libraries digestif crypto))
+63
pbkdf/pbkdf.ml
··· 1 + module type S = sig 2 + val pbkdf1 : password:string -> salt:string -> count:int -> dk_len:int -> string 3 + val pbkdf2 : password:string -> salt:string -> count:int -> dk_len:int32 -> string 4 + end 5 + 6 + let cdiv x y = 7 + (* This is lifted from Nocrypto.Uncommon.(//) 8 + (formerly known as [cdiv]). It is part of the documented, publically 9 + exposed _internal_ utility library not for public consumption, hence 10 + the API break that prompted this copy-pasted function. *) 11 + if y < 1 then raise Division_by_zero else 12 + if x > 0 then 1 + ((x - 1) / y) else 0 [@@inline] 13 + 14 + module Make (H: Digestif.S) : S = struct 15 + let pbkdf1 ~password ~salt ~count ~dk_len = 16 + if String.length salt <> 8 then invalid_arg "salt should be 8 bytes" 17 + else if count <= 0 then invalid_arg "count must be a positive integer" 18 + else if dk_len <= 0 then invalid_arg "derived key length must be a positive integer" 19 + else if dk_len > H.digest_size then invalid_arg "derived key too long" 20 + else 21 + let rec loop t = function 22 + 0 -> t 23 + | i -> loop H.(to_raw_string (digest_string t)) (i - 1) 24 + in 25 + String.sub (loop (password ^ salt) count) 0 dk_len 26 + 27 + let pbkdf2 ~password ~salt ~count ~dk_len = 28 + if count <= 0 then invalid_arg "count must be a positive integer" 29 + else if dk_len <= 0l then invalid_arg "derived key length must be a positive integer" 30 + else 31 + let h_len = H.digest_size 32 + and dk_len = Int32.to_int dk_len in 33 + let l = cdiv dk_len h_len in 34 + let r = dk_len - (l - 1) * h_len in 35 + let block i = 36 + let rec f u xor = function 37 + | 0 -> xor 38 + | j -> 39 + let u = H.(to_raw_string (hmac_string ~key:password u)) in 40 + f u (Crypto.Uncommon.xor xor u) (j - 1) 41 + in 42 + let int_i = Bytes.create 4 in 43 + Bytes.set_int32_be int_i 0 (Int32.of_int i); 44 + let u_1 = H.hmac_string ~key:password (salt ^ Bytes.unsafe_to_string int_i) in 45 + let u_1 = H.to_raw_string u_1 in 46 + f u_1 u_1 (count - 1) 47 + in 48 + let rec loop blocks = function 49 + | 0 -> blocks 50 + | i -> loop (block i :: blocks) (i - 1) 51 + in 52 + String.concat "" (loop [String.sub (block l) 0 r] (l - 1)) 53 + end 54 + 55 + let pbkdf1 ~hash ~password ~salt ~count ~dk_len = 56 + let module H = (val (Digestif.module_of_hash' hash)) in 57 + let module PBKDF = Make (H) in 58 + PBKDF.pbkdf1 ~password ~salt ~count ~dk_len 59 + 60 + let pbkdf2 ~prf ~password ~salt ~count ~dk_len = 61 + let module H = (val (Digestif.module_of_hash' prf)) in 62 + let module PBKDF = Make (H) in 63 + PBKDF.pbkdf2 ~password ~salt ~count ~dk_len
+23
pbkdf/pbkdf.mli
··· 1 + (** {{:https://tools.ietf.org/html/rfc2898}RFC 2898} specifies two password-based 2 + key derivation functions (PBKDF1 and PBKDF2), which are abstracted over 3 + a specific hash/pseudorandom function. *) 4 + module type S = sig 5 + (** [pbkdf1 password salt count dk_len] is [dk], the derived key of [dk_len] octets. 6 + The [salt] must be eight octets, [count] the iteration count. 7 + @raise Invalid_argument when either [salt] is not eight octets long or either 8 + [count] or [dk_len] are not valid. *) 9 + val pbkdf1 : password:string -> salt:string -> count:int -> dk_len:int -> string 10 + 11 + (** [pbkdf2 password salt count dk_len] is [dk], the derived key of [dk_len] octets. 12 + @raise Invalid_argument when either [count] or [dk_len] are not valid *) 13 + val pbkdf2 : password:string -> salt:string -> count:int -> dk_len:int32 -> string 14 + end 15 + 16 + (** Given a Hash/pseudorandom function, get the PBKDF *) 17 + module Make (H: Digestif.S) : S 18 + 19 + (** convenience [pbkdf1 hash password salt count dk_len] where the [hash] has to be provided explicitly *) 20 + val pbkdf1 : hash:Digestif.hash' -> password:string -> salt:string -> count:int -> dk_len:int -> string 21 + 22 + (** convenience [pbkdf2 prf password salt count dk_len] where the [prf] has to be provided explicitly *) 23 + val pbkdf2 : prf:Digestif.hash' -> password:string -> salt:string -> count:int -> dk_len:int32 -> string
+4
pbkdf/test/dune
··· 1 + (test 2 + (name pbkdf_tests) 3 + (modes native) 4 + (libraries ohex kdf.pbkdf alcotest))
+287
pbkdf/test/pbkdf_tests.ml
··· 1 + let () = Printexc.record_backtrace true 2 + 3 + (* PBKDF1 *) 4 + let test_pbkdf1 ~hash ~password ~salt ~count ~dk_len ~dk = 5 + let salt = Ohex.decode salt 6 + and dk = Ohex.decode dk in 7 + (fun () -> 8 + let edk = Pbkdf.pbkdf1 ~hash ~password ~salt ~count ~dk_len in 9 + Alcotest.check Alcotest.string "PBKDF1 test" edk dk) 10 + 11 + let test_pbkdf1_invalid_arg ~hash ~password ~salt ~count ~dk_len ~msg = 12 + let salt = Ohex.decode salt in 13 + (fun () -> 14 + Alcotest.check_raises 15 + msg 16 + (Invalid_argument msg) 17 + (fun () -> ignore (Pbkdf.pbkdf1 ~hash ~password ~salt ~count ~dk_len))) 18 + 19 + (* Taken from http://www.di-mgt.com.au/cryptoKDFs.html *) 20 + let pbkdf1_test1 = 21 + test_pbkdf1 22 + ~hash:`SHA1 23 + ~password:"password" 24 + ~salt:"78578e5a5d63cb06" 25 + ~count:1000 26 + ~dk_len:16 27 + ~dk:"dc19847e05c64d2faf10ebfb4a3d2a20" 28 + 29 + let pbkdf1_test2 = 30 + test_pbkdf1_invalid_arg 31 + ~hash:`SHA1 32 + ~password:"password" 33 + ~salt:"78578e5a5d63cb" 34 + ~count:1000 35 + ~dk_len:16 36 + ~msg:"salt should be 8 bytes" 37 + 38 + let pbkdf1_test3 = 39 + test_pbkdf1_invalid_arg 40 + ~hash:`SHA1 41 + ~password:"password" 42 + ~salt:"78578e5a5d63cb0600" 43 + ~count:1000 44 + ~dk_len:16 45 + ~msg:"salt should be 8 bytes" 46 + 47 + let pbkdf1_test4 = 48 + test_pbkdf1_invalid_arg 49 + ~hash:`SHA1 50 + ~password:"password" 51 + ~salt:"78578e5a5d63cb06" 52 + ~count:(-1) 53 + ~dk_len:16 54 + ~msg:"count must be a positive integer" 55 + 56 + let pbkdf1_test5 = 57 + test_pbkdf1_invalid_arg 58 + ~hash:`SHA1 59 + ~password:"password" 60 + ~salt:"78578e5a5d63cb06" 61 + ~count:0 62 + ~dk_len:16 63 + ~msg:"count must be a positive integer" 64 + 65 + let pbkdf1_test6 = 66 + test_pbkdf1_invalid_arg 67 + ~hash:`SHA1 68 + ~password:"password" 69 + ~salt:"78578e5a5d63cb06" 70 + ~count:1000 71 + ~dk_len:24 72 + ~msg:"derived key too long" 73 + 74 + let pbkdf1_test7 = 75 + test_pbkdf1_invalid_arg 76 + ~hash:`SHA1 77 + ~password:"password" 78 + ~salt:"78578e5a5d63cb06" 79 + ~count:1000 80 + ~dk_len:0 81 + ~msg:"derived key length must be a positive integer" 82 + 83 + let pbkdf1_tests = [ 84 + "Test Case 1", `Quick, pbkdf1_test1; 85 + "Test Case 2", `Quick, pbkdf1_test2; 86 + "Test Case 3", `Quick, pbkdf1_test3; 87 + "Test Case 4", `Quick, pbkdf1_test4; 88 + "Test Case 5", `Quick, pbkdf1_test5; 89 + "Test Case 6", `Quick, pbkdf1_test6; 90 + "Test Case 7", `Quick, pbkdf1_test7; 91 + ] 92 + 93 + 94 + (* PBKDF2 *) 95 + let test_pbkdf2 ~prf ~password ~salt ~count ~dk_len ~dk = 96 + let salt = Ohex.decode salt 97 + and dk = Ohex.decode dk in 98 + (fun () -> 99 + let edk = Pbkdf.pbkdf2 ~prf ~password ~salt ~count ~dk_len in 100 + Alcotest.check Alcotest.string "PBKDF2 test" edk dk) 101 + 102 + let test_pbkdf2_invalid_arg ~prf ~password ~salt ~count ~dk_len ~msg () = 103 + let salt = Ohex.decode salt in 104 + Alcotest.check_raises 105 + msg 106 + (Invalid_argument msg) 107 + (fun () -> ignore (Pbkdf.pbkdf2 ~prf ~password ~salt ~count ~dk_len)) 108 + 109 + (* Taken from https://github.com/randombit/botan/blob/master/src/tests/data/pbkdf/pbkdf2.vec *) 110 + let pbkdf2_test1 = 111 + test_pbkdf2 112 + ~prf:`SHA1 113 + ~password:"" 114 + ~salt:"0001020304050607" 115 + ~count:10000 116 + ~dk_len:32l 117 + ~dk:"59b2b1143b4cb1059ec58d9722fb1c72471e0d85c6f7543ba5228526375b0127" 118 + 119 + let pbkdf2_test2 = 120 + test_pbkdf2 121 + ~prf:`SHA1 122 + ~password:"jyueqgxrscgglpxdykcf" 123 + ~salt:"9b56e55328a4c97a250738f8dba1b992e8a1b508" 124 + ~count:10000 125 + ~dk_len:14l 126 + ~dk:"df6d9d72872404bf73e708cf3b7d" 127 + 128 + let pbkdf2_test3 = 129 + test_pbkdf2 130 + ~prf:`SHA1 131 + ~password:"aqrqsznzvvzgtksammgo" 132 + ~salt:"57487813cdd2220dfc485d932a2979ee8769ea8b" 133 + ~count:101 134 + ~dk_len:40l 135 + ~dk:"fa13f40af1ade2a30f2fffd66fc8a659ef95e6388c1682fc0fe4d15a70109517a32942e39c371440" 136 + 137 + let pbkdf2_test4 = 138 + test_pbkdf2 139 + ~prf:`SHA1 140 + ~password:"ltexmfeyylmlbrsyikaw" 141 + ~salt:"ed1f39a0a7f3889aaf7e60743b3bc1cc2c738e60" 142 + ~count:1000 143 + ~dk_len:10l 144 + ~dk:"027afadd48f4be8dcc4f" 145 + 146 + let pbkdf2_test5 = 147 + test_pbkdf2 148 + ~prf:`SHA1 149 + ~password:"cxgnyrcgrvllylolsjpo" 150 + ~salt:"94ac88200743fb0f6ac51be62166cbef08d94c15" 151 + ~count:1 152 + ~dk_len:32l 153 + ~dk:"7c0d009fc91b48cb6d19bafbfccff3e2ccabfe725eaa234e56bde1d551c132f2" 154 + 155 + let pbkdf2_test6 = 156 + test_pbkdf2 157 + ~prf:`SHA1 158 + ~password:"xqyfhrxehiedlhewnvbj" 159 + ~salt:"24a1a50b17d63ee8394b69fc70887f4f94883d68" 160 + ~count:5 161 + ~dk_len:32l 162 + ~dk:"4661301d3517ca4443a6a607b32b2a63f69996299df75db75f1e0b98dd0eb7d8" 163 + 164 + let pbkdf2_test7 = 165 + test_pbkdf2 166 + ~prf:`SHA1 167 + ~password:"andaqkpjwabvcfnpnjkl" 168 + ~salt:"9316c80801623cc2734af74bec42cf4dbaa3f6d5" 169 + ~count:100 170 + ~dk_len:30l 171 + ~dk:"82fb44a521448d5aac94b5158ead1e4dcd7363081a747b9f7626752bda2d" 172 + 173 + let pbkdf2_test8 = 174 + test_pbkdf2 175 + ~prf:`SHA1 176 + ~password:"hsavvyvocloyuztlsniu" 177 + ~salt:"612cc61df3cf2bdb36e10c4d8c9d73192bddee05" 178 + ~count:100 179 + ~dk_len:30l 180 + ~dk:"f8ec2b0ac817896ac8189d787c6424ed24a6d881436687a4629802c0ecce" 181 + 182 + let pbkdf2_test9 = 183 + test_pbkdf2 184 + ~prf:`SHA1 185 + ~password:"eaimrbzpcopbusaqtkmw" 186 + ~salt:"45248f9d0cebcb86a18243e76c972a1f3b36772a" 187 + ~count:100 188 + ~dk_len:34l 189 + ~dk:"c9a0b2622f13916036e29e7462e206e8ba5b50ce9212752eb8ea2a4aa7b40a4cc1bf" 190 + 191 + let pbkdf2_test10 = 192 + test_pbkdf2 193 + ~prf:`SHA1 194 + ~password:"gwrxpqxumsdsmbmhfhmfdcvlcvngzkig" 195 + ~salt:"a39b76c6eec8374a11493ad08c246a3e40dfae5064f4ee3489c273646178" 196 + ~count:1000 197 + ~dk_len:64l 198 + ~dk:"4c9db7ba24955225d5b845f65ef24ef1b0c6e86f2e39c8ddaa4b8abd26082d1f350381fadeaeb560dc447afc68a6b47e6ea1e7412f6cf7b2d82342fccd11d3b4" 199 + 200 + let pbkdf2_test11 = 201 + test_pbkdf2 202 + ~prf:`SHA256 203 + ~password:"xyz" 204 + ~salt:"0001020304050607" 205 + ~count: 10000 206 + ~dk_len:48l 207 + ~dk:"defd2987fa26a4672f4d16d98398432ad95e896bf619f6a6b8d4ed1faf98e8b531b39ffb66966d0e115a6cd8e70b72d0" 208 + 209 + let pbkdf2_test12 = 210 + test_pbkdf2 211 + ~prf:`SHA384 212 + ~password:"xyz" 213 + ~salt:"0001020304050607" 214 + ~count:10000 215 + ~dk_len:48l 216 + ~dk:"47a3ae920b24edaa2bb53155808554b13fab58df62b81f043d9812e9f2881164df20bbffa54e5ee2489fa183b6718a74" 217 + 218 + let pbkdf2_test13 = 219 + test_pbkdf2 220 + ~prf:`SHA512 221 + ~password:"xyz" 222 + ~salt:"0001020304050607" 223 + ~count:10000 224 + ~dk_len:48l 225 + ~dk:"daf8a734327745eb63d19054dbd4018a682cef11086a1bfb63fdbc16158c2f8b0742802f36aef1b1df92accbea5d31a5" 226 + 227 + let pbkdf2_test14 = 228 + test_pbkdf2_invalid_arg 229 + ~prf:`SHA1 230 + ~password:"password" 231 + ~salt:"0001020304050607" 232 + ~count:(-1) 233 + ~dk_len:48l 234 + ~msg:"count must be a positive integer" 235 + 236 + let pbkdf2_test15 = 237 + test_pbkdf2_invalid_arg 238 + ~prf:`SHA1 239 + ~password:"password" 240 + ~salt:"0001020304050607" 241 + ~count:0 242 + ~dk_len:48l 243 + ~msg:"count must be a positive integer" 244 + 245 + let pbkdf2_test16 = 246 + test_pbkdf2_invalid_arg 247 + ~prf:`SHA1 248 + ~password:"password" 249 + ~salt:"0001020304050607" 250 + ~count:1000 251 + ~dk_len:(-1l) 252 + ~msg:"derived key length must be a positive integer" 253 + 254 + let pbkdf2_test17 = 255 + test_pbkdf2_invalid_arg 256 + ~prf:`SHA1 257 + ~password:"password" 258 + ~salt:"0001020304050607" 259 + ~count:1000 260 + ~dk_len:0l 261 + ~msg:"derived key length must be a positive integer" 262 + 263 + let pbkdf2_tests = [ 264 + "Test Case 1", `Quick, pbkdf2_test1; 265 + "Test Case 2", `Quick, pbkdf2_test2; 266 + "Test Case 3", `Quick, pbkdf2_test3; 267 + "Test Case 4", `Quick, pbkdf2_test4; 268 + "Test Case 5", `Quick, pbkdf2_test5; 269 + "Test Case 6", `Quick, pbkdf2_test6; 270 + "Test Case 7", `Quick, pbkdf2_test7; 271 + "Test Case 8", `Quick, pbkdf2_test8; 272 + "Test Case 9", `Quick, pbkdf2_test9; 273 + "Test Case 10", `Quick, pbkdf2_test10; 274 + "Test Case 11", `Quick, pbkdf2_test11; 275 + "Test Case 12", `Quick, pbkdf2_test12; 276 + "Test Case 13", `Quick, pbkdf2_test13; 277 + "Test Case 14", `Quick, pbkdf2_test14; 278 + "Test Case 15", `Quick, pbkdf2_test15; 279 + "Test Case 16", `Quick, pbkdf2_test16; 280 + "Test Case 17", `Quick, pbkdf2_test17; 281 + ] 282 + 283 + let () = 284 + Alcotest.run "PBKDF Tests" [ 285 + "PBKDF1 tests", pbkdf1_tests; 286 + "PBKDF2 tests", pbkdf2_tests; 287 + ]
+7
scrypt/dune
··· 1 + (library 2 + (name scrypt) 3 + (public_name kdf.scrypt) 4 + (modules scrypt) 5 + (libraries crypto kdf.pbkdf) 6 + (c_names salsa-core) 7 + (c_flags (:standard --std=c99 -Wall -Wextra -O3)))
+65
scrypt/salsa-core.c
··· 1 + #include <stdint.h> 2 + 3 + #define CAML_NAME_SPACE 4 + #include <caml/mlvalues.h> 5 + #include <caml/bigarray.h> 6 + 7 + static inline uint32_t r(uint32_t a, int b) { 8 + int rs = 32 - b; 9 + return (a << b) | (a >> rs); 10 + } 11 + 12 + static inline uint32_t combine(uint32_t y0, uint32_t y1, uint32_t y2, int shift) { 13 + return r(y1 + y2, shift) ^ y0; 14 + } 15 + 16 + static inline void quarterround(uint32_t *x, int y0, int y1, int y2, int y3) { 17 + x[y1] = combine(x[y1], x[y0], x[y3], 7); 18 + x[y2] = combine(x[y2], x[y1], x[y0], 9); 19 + x[y3] = combine(x[y3], x[y2], x[y1], 13); 20 + x[y0] = combine(x[y0], x[y3], x[y2], 18); 21 + } 22 + 23 + static inline uint32_t get_u32_le(const uint8_t *input, int offset) { 24 + return input[offset] 25 + | (input[offset + 1] << 8) 26 + | (input[offset + 2] << 16) 27 + | (input[offset + 3] << 24); 28 + } 29 + 30 + static inline void set_u32_le(uint8_t *input, int offset, uint32_t value) { 31 + input[offset] = (uint8_t) value; 32 + input[offset + 1] = (uint8_t) (value >> 8); 33 + input[offset + 2] = (uint8_t) (value >> 16); 34 + input[offset + 3] = (uint8_t) (value >> 24); 35 + } 36 + 37 + static void salsa_core(int count, const uint8_t *src, uint8_t *dst) { 38 + uint32_t x[16]; 39 + for (int i = 0; i < 16; i++) { 40 + x[i] = get_u32_le(src, i * 4); 41 + } 42 + for (int i = 0; i < count; i++) { 43 + quarterround(x, 0, 4, 8, 12); 44 + quarterround(x, 5, 9, 13, 1); 45 + quarterround(x, 10, 14, 2, 6); 46 + quarterround(x, 15, 3, 7, 11); 47 + 48 + quarterround(x, 0, 1, 2, 3); 49 + quarterround(x, 5, 6, 7, 4); 50 + quarterround(x, 10, 11, 8, 9); 51 + quarterround(x, 15, 12, 13, 14); 52 + } 53 + for (int i = 0; i < 16; i++) { 54 + uint32_t xi = x[i]; 55 + uint32_t hj = get_u32_le(src, i * 4); 56 + set_u32_le(dst, i * 4, xi + hj); 57 + } 58 + } 59 + 60 + CAMLprim value 61 + caml_salsa_core(value count, value src, value dst) 62 + { 63 + salsa_core(Int_val(count), (const uint8_t*)(String_val(src)), Bytes_val(dst)); 64 + return Val_unit; 65 + }
+66
scrypt/scrypt.ml
··· 1 + external salsa_core : int -> string -> bytes -> unit = "caml_salsa_core" [@@noalloc] 2 + 3 + let salsa20_core count i = 4 + let l = 64 in 5 + if String.length i <> l then invalid_arg "input must be 16 blocks of 32 bits" 6 + else 7 + let o = Bytes.create l in 8 + salsa_core count i o; 9 + Bytes.unsafe_to_string o 10 + 11 + let salsa20_8_core i = 12 + salsa20_core 4 i 13 + 14 + let scrypt_block_mix b r = 15 + let b' = Bytes.create (String.length b) in 16 + let x = Bytes.create 64 in 17 + Bytes.unsafe_blit_string b ((2 * r - 1) * 64) x 0 64; 18 + for i = 0 to 2 * r - 1 do 19 + let b_i = Bytes.unsafe_of_string (String.sub b (i * 64) 64) in 20 + Crypto.Uncommon.unsafe_xor_into (Bytes.unsafe_to_string x) ~src_off:0 b_i ~dst_off:0 64; 21 + Bytes.unsafe_blit_string (salsa20_8_core (Bytes.unsafe_to_string b_i)) 0 x 0 64; 22 + let offset = (i mod 2) lsl (max 0 (r / 2 - 1)) + i / 2 in 23 + Bytes.blit x 0 b' (offset * 64) 64 24 + done; 25 + b' 26 + 27 + let scrypt_ro_mix b ~r ~n = 28 + let blen = r * 128 in 29 + let x = ref (Bytes.copy b) in 30 + let v = Bytes.create (blen * n) in 31 + for i = 0 to n - 1 do 32 + Bytes.unsafe_blit !x 0 v (blen * i) blen; 33 + x := scrypt_block_mix (Bytes.unsafe_to_string !x) r 34 + done; 35 + for _ = 0 to n - 1 do 36 + let integerify x = 37 + let k = Bytes.get_int32_le x (128 * r - 64) in 38 + let n' = n - 1 in 39 + Int32.(to_int (logand k (of_int n'))) 40 + in 41 + let j = integerify !x in 42 + Crypto.Uncommon.unsafe_xor_into (Bytes.unsafe_to_string v) ~src_off:(blen * j) !x ~dst_off:0 blen; 43 + x := scrypt_block_mix (Bytes.unsafe_to_string !x) r; 44 + done; 45 + !x 46 + 47 + let scrypt ~password ~salt ~n ~r ~p ~dk_len = 48 + let is_power_of_2 x = (x land (x - 1)) = 0 in 49 + if n <= 1 then invalid_arg "n must be larger than 1" 50 + else if not (is_power_of_2 n) then invalid_arg "n must be a power of 2" 51 + else if p <= 0 then invalid_arg "p must be a positive integer" 52 + else if p > (Int64.to_int (Int64.div 0xffffffffL 4L) / r) then invalid_arg "p too big" 53 + else if dk_len <= 0l then invalid_arg "derived key length must be a positive integer"; 54 + let rec partition b blocks = function 55 + | 0 -> blocks 56 + | i -> 57 + let off = (i - 1) * r * 128 in 58 + let block = Bytes.unsafe_of_string (String.sub b off (r * 128)) in 59 + partition b (block :: blocks) (i - 1) 60 + in 61 + let blen = Int32.of_int (128 * r * p) in 62 + let dk = Pbkdf.pbkdf2 ~prf:`SHA256 ~password ~salt ~count:1 ~dk_len:blen in 63 + let b = partition dk [] p in 64 + let b' = List.map (scrypt_ro_mix ~r ~n) b in 65 + let salt = String.concat "" (List.map Bytes.unsafe_to_string b') in 66 + Pbkdf.pbkdf2 ~prf:`SHA256 ~password ~salt ~count:1 ~dk_len
+15
scrypt/scrypt.mli
··· 1 + (** {{:https://tools.ietf.org/html/rfc7914} 2 + The scrypt Password-Based Key Derivation Function} 3 + specifies the password-based key derivation function scrypt. The 4 + function derives one or more secret keys from a secret string. 5 + It is based on memory-hard functions which offer added protection 6 + against attacks using custom hardware. *) 7 + 8 + (** [scrypt_kdf password salt n r p dk_len] is [dk], the derived key 9 + of [dk_len] octets. 10 + [n], the cost parameter, must be larger than 1 and a power of 2. 11 + [p], the parallelization parameter, must be a possitive integer 12 + and less than or equal to 2^32 - 1 / (4 * r) 13 + @raise Invalid_argument when either [n], [p] or [dk_len] are not 14 + valid *) 15 + val scrypt : password:string -> salt:string -> n:int -> r:int -> p:int -> dk_len:int32 -> string
+5
scrypt/tests/dune
··· 1 + (test 2 + (name scrypt_kdf_tests) 3 + (modules scrypt_kdf_tests) 4 + (modes native) 5 + (libraries kdf.scrypt ohex alcotest))
+64
scrypt/tests/scrypt_kdf_tests.ml
··· 1 + let test_scrypt_kdf ~password ~salt ~n ~r ~p ~dk_len ~dk = 2 + let dk = Ohex.decode dk in 3 + (fun () -> 4 + let edk = Scrypt.scrypt ~password ~salt ~n ~r ~p ~dk_len in 5 + Alcotest.check Alcotest.string "Scrypt test" edk dk) 6 + 7 + let scrypt_kdf_test1 = 8 + test_scrypt_kdf 9 + ~password:"" 10 + ~salt:"" 11 + ~n:16 12 + ~r:1 13 + ~p:1 14 + ~dk_len:64l 15 + ~dk:"77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906" 16 + 17 + let scrypt_kdf_test2 = 18 + test_scrypt_kdf 19 + ~password:"password" 20 + ~salt:"NaCl" 21 + ~n:1024 22 + ~r:8 23 + ~p:16 24 + ~dk_len:64l 25 + ~dk:"fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640" 26 + 27 + let scrypt_kdf_test3 = 28 + test_scrypt_kdf 29 + ~password:"pleaseletmein" 30 + ~salt:"SodiumChloride" 31 + ~n:16384 32 + ~r:8 33 + ~p:1 34 + ~dk_len:64l 35 + ~dk:"7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887" 36 + 37 + let scrypt_kdf_test4 = 38 + test_scrypt_kdf 39 + ~password:"pleaseletmein" 40 + ~salt:"SodiumChloride" 41 + ~n:1048576 42 + ~r:8 43 + ~p:1 44 + ~dk_len:64l 45 + ~dk:"2101cb9b6a511aaeaddbbe09cf70f881ec568d574a2ffd4dabe5ee9820adaa478e56fd8f4ba5d09ffa1c6d927c40f4c337304049e8a952fbcbf45c6fa77a41a4" 46 + 47 + let scrypt_kdf_tests () = 48 + let tests = [ 49 + "Test Case 1", `Quick, scrypt_kdf_test1; 50 + "Test Case 2", `Quick, scrypt_kdf_test2; 51 + ] in 52 + (* Skip test case 3 and 4 for architectures with 31 bit sizes or less, as it requires a buffer larger than Int.max_size in those cases *) 53 + if Sys.int_size <= 31 then 54 + tests 55 + else 56 + tests @ [ 57 + "Test Case 3", `Quick, scrypt_kdf_test3; 58 + "Test Case 4", `Slow, scrypt_kdf_test4; 59 + ] 60 + 61 + let () = 62 + Alcotest.run "Scrypt kdf Tests" [ 63 + "Scrypt kdf tests", scrypt_kdf_tests (); 64 + ]