This commit is contained in:
parent
aa2ff7b2f0
commit
2f3113f55d
11742 changed files with 1223940 additions and 0 deletions
7
unikernel/duniverse/kdf/.gitignore
vendored
Normal file
7
unikernel/duniverse/kdf/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
_build/
|
||||
_tests/
|
||||
coverage/
|
||||
*.install
|
||||
**/*.merlin
|
||||
*.byte
|
||||
*.native
|
||||
98
unikernel/duniverse/kdf/CHANGES.md
Normal file
98
unikernel/duniverse/kdf/CHANGES.md
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
# v1.0.0 (2024-08-28)
|
||||
|
||||
* Migrate scrypt from Cstruct.t to string
|
||||
* Merge ocaml-pbkdf (from https://github.com/abeaumont/ocaml-pbkdf),
|
||||
hkdf (from https://github.com/hannesm/ocaml-hkdf), and scrypt (from
|
||||
https://github.com/abeaumont/ocaml-scrypt-kdf) into a single repository
|
||||
and opam package (with three subpackages, kdf.pbkdf. kdf.hkdf, and
|
||||
kdf.scrypt).
|
||||
* Disable a failing testcase for architectures with integers no longer than 31
|
||||
bits (thanks to @kit-ty-kate)
|
||||
|
||||
# pbkdf 2.0.0 (2024-06-29)
|
||||
|
||||
* Update to mirage-crypto 1.0.0 (#13 @dinosaure)
|
||||
|
||||
# hkdf v2.0.0 (2024-06-29)
|
||||
|
||||
* use digestif instead of mirage-crypto (@dinosaure @hannesm)
|
||||
|
||||
# scrypt-kdf 1.2.0 (2021-08-03)
|
||||
|
||||
* Upgrade to Cstruct 6.0.0
|
||||
|
||||
# pbkdf 1.2.0 (2020-08-03)
|
||||
|
||||
* Upgrade to Cstruct 6.0.0
|
||||
|
||||
# pbkdf 1.1.0 (2020-03-31)
|
||||
|
||||
* Port to mirage-crypto (thanks to @hannesm)
|
||||
|
||||
# scrypt-kdf 1.1.0 (2020-03-31)
|
||||
|
||||
* Port to mirage-crypto (thanks to @hannesm)
|
||||
|
||||
# hkdf v1.0.4 (2020-03-11)
|
||||
|
||||
* use mirage-crypto instead of nocrypto
|
||||
|
||||
# scrypt-kdf 1.0.0 (2019-04-12)
|
||||
|
||||
* Move to dune
|
||||
* Upgrade to opam 2.0
|
||||
|
||||
# pbkdf 1.0.0 (2019-04-12)
|
||||
|
||||
* Move to dune
|
||||
* Upgrade to opam 2.0
|
||||
* Reimplement `cdiv`, no longer available in `nocrypto`.
|
||||
|
||||
# hkdf 1.0.3 (2019-02-15)
|
||||
|
||||
* move to dune
|
||||
|
||||
# pbkdf 0.3.0 (2018-02-16)
|
||||
|
||||
* Build: switch to jbuilder
|
||||
|
||||
# scrypt-kdf 0.4.0 (2017-03-09)
|
||||
|
||||
* Removed Makefile, unneeded with topkg
|
||||
* Made pkg.ml executable
|
||||
* Added salsa20-core as a dependency and remove related code
|
||||
|
||||
# scrypt-kdf 0.3.0 (2017-02-21)
|
||||
|
||||
* Replaced underscores by dashes in library names
|
||||
* Exported Salsa20_core module
|
||||
|
||||
# pbkdf 0.2.0 (2016-10-31)
|
||||
|
||||
* Added topkg dependency
|
||||
|
||||
# scrypt-kdf 0.2.0 (2016-10-31)
|
||||
|
||||
* Added topkg dependency
|
||||
* Optimized inner loop in salsa_core to improve performance
|
||||
* Replaced custom clone function by Nocrypto's implementation
|
||||
|
||||
# hkdf 1.0.2 (2016-07-18)
|
||||
|
||||
* move to topkg
|
||||
|
||||
# scrypt-kdf 0.1.0 (2016-03-18)
|
||||
|
||||
* Initial release
|
||||
|
||||
# pbkdf 0.1.0 (2016-03-14)
|
||||
|
||||
* Initial release
|
||||
|
||||
# hkdf 1.0.1 (2015-12-20)
|
||||
|
||||
* move from oasis to topkg
|
||||
|
||||
# hkdf 1.0.0 (2015-11-30)
|
||||
|
||||
* initial release
|
||||
25
unikernel/duniverse/kdf/LICENSE.md
Normal file
25
unikernel/duniverse/kdf/LICENSE.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
Copyright (c) 2014, Hannes Mehnert
|
||||
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.
|
||||
|
||||
17
unikernel/duniverse/kdf/README.md
Normal file
17
unikernel/duniverse/kdf/README.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# kdf - Key Derivation Functions
|
||||
|
||||
This repository provides multiple already specified key derivation functions in
|
||||
and for OCaml:
|
||||
|
||||
- [scrypt](https://tools.ietf.org/html/rfc7914),
|
||||
- [PBKDF 1 and 2 as defined by PKCS#5](https://tools.ietf.org/html/rfc2898),
|
||||
- and [HKDF](https://tools.ietf.org/html/rfc5869).
|
||||
|
||||
## Documentation
|
||||
|
||||
[API Documentation](https://robur-coop.github.io/kdf/doc)
|
||||
|
||||
## Installation
|
||||
|
||||
`opam install kdf` will install the latest released version.
|
||||
|
||||
3
unikernel/duniverse/kdf/dune-project
Normal file
3
unikernel/duniverse/kdf/dune-project
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
(lang dune 1.8)
|
||||
(name kdf)
|
||||
(version v1.0.0)
|
||||
5
unikernel/duniverse/kdf/hkdf/dune
Normal file
5
unikernel/duniverse/kdf/hkdf/dune
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
(library
|
||||
(name hkdf)
|
||||
(public_name kdf.hkdf)
|
||||
(modules hkdf)
|
||||
(libraries digestif))
|
||||
42
unikernel/duniverse/kdf/hkdf/hkdf.ml
Normal file
42
unikernel/duniverse/kdf/hkdf/hkdf.ml
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
|
||||
module type S = sig
|
||||
val extract : ?salt:string -> string -> string
|
||||
val expand : prk:string -> ?info:string -> int -> string
|
||||
end
|
||||
|
||||
module Make (H : Digestif.S) : S = struct
|
||||
let extract ?salt ikm =
|
||||
let key = match salt with
|
||||
| None -> String.make H.digest_size '\x00'
|
||||
| Some x -> x
|
||||
in
|
||||
H.(to_raw_string (hmac_string ~key ikm))
|
||||
|
||||
let expand ~prk ?info len =
|
||||
let info = match info with
|
||||
| None -> ""
|
||||
| Some x -> x
|
||||
in
|
||||
let t n last =
|
||||
let nc = String.make 1 (Char.unsafe_chr n) in
|
||||
H.(to_raw_string (hmac_string ~key:prk (String.concat "" [last ; info ; nc])))
|
||||
in
|
||||
let n = succ (len / H.digest_size) in
|
||||
let rec compute acc count = match count, acc with
|
||||
| c, xs when c > n -> String.concat "" (List.rev xs)
|
||||
| c, x::_ -> compute (t c x :: acc) (succ c)
|
||||
| _, [] -> invalid_arg "can not happen"
|
||||
in
|
||||
let buf = compute [""] 1 in
|
||||
String.sub buf 0 len
|
||||
end
|
||||
|
||||
let extract ~hash ?salt ikm =
|
||||
let module H = (val (Digestif.module_of_hash' hash)) in
|
||||
let module HKDF = Make (H) in
|
||||
HKDF.extract ?salt ikm
|
||||
|
||||
let expand ~hash ~prk ?info len =
|
||||
let module H = (val (Digestif.module_of_hash' hash)) in
|
||||
let module HKDF = Make (H) in
|
||||
HKDF.expand ~prk ?info len
|
||||
27
unikernel/duniverse/kdf/hkdf/hkdf.mli
Normal file
27
unikernel/duniverse/kdf/hkdf/hkdf.mli
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
|
||||
(** {{:https://tools.ietf.org/html/rfc5869}RFC 5869} specifies a HMAC-based
|
||||
Extract-and-Expand Key Derivation Function (HKDF), which is abstracted over
|
||||
a specific hash function. *)
|
||||
|
||||
module type S = sig
|
||||
|
||||
(** [extract salt ikm] is [prk], the pseudorandom key of hash length octets.
|
||||
The [salt] is an optional non-secret random value, [ikm] the input key
|
||||
material. *)
|
||||
val extract : ?salt:string -> string -> string
|
||||
|
||||
(** [extract prk info length] is [okm], the output keying material. Given the
|
||||
pseudorandom key of hash length (usually output of [!extract] step), and an
|
||||
optional context and application specific information [info], the [okm] is
|
||||
generated. *)
|
||||
val expand : prk:string -> ?info:string -> int -> string
|
||||
end
|
||||
|
||||
(** Given a Hash function, get the HKDF *)
|
||||
module Make (H : Digestif.S) : S
|
||||
|
||||
(** convenience [extract hash salt ikm] where the [hash] has to be provided explicitly *)
|
||||
val extract : hash:Digestif.hash' -> ?salt:string -> string -> string
|
||||
|
||||
(** convenience [expand hash prk info len] where the [hash] has to be provided explicitly *)
|
||||
val expand : hash:Digestif.hash' -> prk:string -> ?info:string -> int -> string
|
||||
4
unikernel/duniverse/kdf/hkdf/tests/dune
Normal file
4
unikernel/duniverse/kdf/hkdf/tests/dune
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
(test
|
||||
(name rfctests)
|
||||
(modules rfctests)
|
||||
(libraries alcotest kdf.hkdf ohex))
|
||||
149
unikernel/duniverse/kdf/hkdf/tests/rfctests.ml
Normal file
149
unikernel/duniverse/kdf/hkdf/tests/rfctests.ml
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
|
||||
let test ~hash ~ikm ?salt ?info ~l ~prk ~okm () =
|
||||
let ikm = Ohex.decode ikm
|
||||
and salt = match salt with None -> None | Some x -> Some (Ohex.decode x)
|
||||
and info = match info with None -> None | Some x -> Some (Ohex.decode x)
|
||||
and prk = Ohex.decode prk
|
||||
and okm = Ohex.decode okm
|
||||
in
|
||||
(fun () ->
|
||||
let cprk = Hkdf.extract ~hash ?salt ikm in
|
||||
Alcotest.check Alcotest.string "PRK matches" prk cprk ;
|
||||
let cokm = Hkdf.expand ~hash ~prk:cprk ?info l in
|
||||
Alcotest.check Alcotest.string "OKM matches" okm cokm)
|
||||
|
||||
let test1 =
|
||||
test
|
||||
~hash:`SHA256
|
||||
~ikm:"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"
|
||||
~salt:"000102030405060708090a0b0c"
|
||||
~info:"f0f1f2f3f4f5f6f7f8f9"
|
||||
~l:42
|
||||
~prk:"077709362c2e32df0ddc3f0dc47bba63 \
|
||||
90b6c73bb50f9c3122ec844ad7c2b3e5"
|
||||
~okm:"3cb25f25faacd57a90434f64d0362f2a \
|
||||
2d2d0a90cf1a5a4c5db02d56ecc4c5bf \
|
||||
34007208d5b887185865"
|
||||
()
|
||||
|
||||
and test2 =
|
||||
test
|
||||
~hash:`SHA256
|
||||
~ikm:"000102030405060708090a0b0c0d0e0f \
|
||||
101112131415161718191a1b1c1d1e1f \
|
||||
202122232425262728292a2b2c2d2e2f \
|
||||
303132333435363738393a3b3c3d3e3f \
|
||||
404142434445464748494a4b4c4d4e4f"
|
||||
~salt:"606162636465666768696a6b6c6d6e6f \
|
||||
707172737475767778797a7b7c7d7e7f \
|
||||
808182838485868788898a8b8c8d8e8f \
|
||||
909192939495969798999a9b9c9d9e9f \
|
||||
a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
|
||||
~info:"b0b1b2b3b4b5b6b7b8b9babbbcbdbebf \
|
||||
c0c1c2c3c4c5c6c7c8c9cacbcccdcecf \
|
||||
d0d1d2d3d4d5d6d7d8d9dadbdcdddedf \
|
||||
e0e1e2e3e4e5e6e7e8e9eaebecedeeef \
|
||||
f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"
|
||||
~l:82
|
||||
~prk:"06a6b88c5853361a06104c9ceb35b45c \
|
||||
ef760014904671014a193f40c15fc244"
|
||||
~okm:"b11e398dc80327a1c8e7f78c596a4934 \
|
||||
4f012eda2d4efad8a050cc4c19afa97c \
|
||||
59045a99cac7827271cb41c65e590e09 \
|
||||
da3275600c2f09b8367793a9aca3db71 \
|
||||
cc30c58179ec3e87c14c01d5c1f3434f \
|
||||
1d87"
|
||||
()
|
||||
|
||||
and test3 =
|
||||
test
|
||||
~hash:`SHA256
|
||||
~ikm:"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"
|
||||
~salt:""
|
||||
(* info = (0 octets) *)
|
||||
~l:42
|
||||
~prk:"19ef24a32c717b167f33a91d6f648bdf \
|
||||
96596776afdb6377ac434c1c293ccb04"
|
||||
~okm:"8da4e775a563c18f715f802a063c5a31 \
|
||||
b8a11f5c5ee1879ec3454e5f3c738d2d \
|
||||
9d201395faa4b61a96c8"
|
||||
()
|
||||
|
||||
and test4 =
|
||||
test
|
||||
~hash:`SHA1
|
||||
~ikm:"0b0b0b0b0b0b0b0b0b0b0b"
|
||||
~salt:"000102030405060708090a0b0c"
|
||||
~info:"f0f1f2f3f4f5f6f7f8f9"
|
||||
~l:42
|
||||
~prk:"9b6c18c432a7bf8f0e71c8eb88f4b30baa2ba243"
|
||||
~okm:"085a01ea1b10f36933068b56efa5ad81 \
|
||||
a4f14b822f5b091568a9cdd4f155fda2 \
|
||||
c22e422478d305f3f896"
|
||||
()
|
||||
|
||||
and test5 =
|
||||
test
|
||||
~hash:`SHA1
|
||||
~ikm:"000102030405060708090a0b0c0d0e0f \
|
||||
101112131415161718191a1b1c1d1e1f \
|
||||
202122232425262728292a2b2c2d2e2f \
|
||||
303132333435363738393a3b3c3d3e3f \
|
||||
404142434445464748494a4b4c4d4e4f"
|
||||
~salt:"606162636465666768696a6b6c6d6e6f \
|
||||
707172737475767778797a7b7c7d7e7f \
|
||||
808182838485868788898a8b8c8d8e8f \
|
||||
909192939495969798999a9b9c9d9e9f \
|
||||
a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
|
||||
~info:"b0b1b2b3b4b5b6b7b8b9babbbcbdbebf \
|
||||
c0c1c2c3c4c5c6c7c8c9cacbcccdcecf \
|
||||
d0d1d2d3d4d5d6d7d8d9dadbdcdddedf \
|
||||
e0e1e2e3e4e5e6e7e8e9eaebecedeeef \
|
||||
f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"
|
||||
~l:82
|
||||
~prk:"8adae09a2a307059478d309b26c4115a224cfaf6"
|
||||
~okm:"0bd770a74d1160f7c9f12cd5912a06eb \
|
||||
ff6adcae899d92191fe4305673ba2ffe \
|
||||
8fa3f1a4e5ad79f3f334b3b202b2173c \
|
||||
486ea37ce3d397ed034c7f9dfeb15c5e \
|
||||
927336d0441f4c4300e2cff0d0900b52 \
|
||||
d3b4"
|
||||
()
|
||||
|
||||
and test6 =
|
||||
test
|
||||
~hash:`SHA1
|
||||
~ikm:"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"
|
||||
~salt:""
|
||||
(* info = (0 octets) *)
|
||||
~l:42
|
||||
~prk:"da8c8a73c7fa77288ec6f5e7c297786aa0d32d01"
|
||||
~okm:"0ac1af7002b3d761d1e55298da9d0506 \
|
||||
b9ae52057220a306e07b6b87e8df21d0 \
|
||||
ea00033de03984d34918"
|
||||
()
|
||||
|
||||
and test7 =
|
||||
test
|
||||
~hash:`SHA1
|
||||
~ikm:"0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"
|
||||
(* salt = not provided (defaults to HashLen zero octets) *)
|
||||
(* info = (0 octets) *)
|
||||
~l:42
|
||||
~prk:"2adccada18779e7c2077ad2eb19d3f3e731385dd"
|
||||
~okm:"2c91117204d745f3500d636a62f64f0a \
|
||||
b3bae548aa53d423b0d1f27ebba6f5e5 \
|
||||
673a081d70cce7acfc48"
|
||||
()
|
||||
|
||||
let tests = [
|
||||
"RFC 5869 Test Case 1", `Quick, test1 ;
|
||||
"RFC 5869 Test Case 2", `Quick, test2 ;
|
||||
"RFC 5869 Test Case 3", `Quick, test3 ;
|
||||
"RFC 5869 Test Case 4", `Quick, test4 ;
|
||||
"RFC 5869 Test Case 5", `Quick, test5 ;
|
||||
"RFC 5869 Test Case 6", `Quick, test6 ;
|
||||
"RFC 5869 Test Case 7", `Quick, test7 ;
|
||||
]
|
||||
|
||||
let () = Alcotest.run "HKDF Tests" [ "RFC 5869", tests ]
|
||||
29
unikernel/duniverse/kdf/kdf.opam
Normal file
29
unikernel/duniverse/kdf/kdf.opam
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
version: "1.0.0"
|
||||
opam-version: "2.0"
|
||||
name: "kdf"
|
||||
maintainer: ["Alfredo Beaumont <alfredo.beaumont@gmail.com>" "Hannes Mehnert <hannes@mehnert.org>"]
|
||||
authors: ["Alfredo Beaumont <alfredo.beaumont@gmail.com>" "Sonia Meruelo <smeruelo@gmail.com>" "Hannes Mehnert <hannes@mehnert.org>"]
|
||||
license: "BSD-2-Clause"
|
||||
homepage: "https://github.com/robur-coop/kdf"
|
||||
doc: "https://robur-coop.github.io/kdf/doc"
|
||||
bug-reports: "https://github.com/robur-coop/kdf/issues"
|
||||
depends: [
|
||||
"ocaml" {>= "4.13.0"}
|
||||
"dune" {>= "1.8.0"}
|
||||
"digestif" {>= "1.2.0"}
|
||||
"mirage-crypto" {>= "1.0.0"}
|
||||
"alcotest" {with-test & >= "0.8.1"}
|
||||
"ohex" {with-test & >= "0.2.0"}
|
||||
]
|
||||
build: [
|
||||
["dune" "subst"] {dev}
|
||||
["dune" "build" "-p" name "-j" jobs]
|
||||
["dune" "runtest" "-p" name "-j" jobs] {with-test}
|
||||
]
|
||||
dev-repo: "git+https://github.com/robur-coop/kdf.git"
|
||||
synopsis: "Key Derivation Functions: HKDF RFC 5869, PBKDF RFC 2898, SCRYPT RFC 7914"
|
||||
description: """
|
||||
A pure OCaml implementation of [scrypt](https://tools.ietf.org/html/rfc7914),
|
||||
[PBKDF 1 and 2 as defined by PKCS#5](https://tools.ietf.org/html/rfc2898),
|
||||
and [HKDF](https://tools.ietf.org/html/rfc5869).
|
||||
"""
|
||||
4
unikernel/duniverse/kdf/pbkdf/dune
Normal file
4
unikernel/duniverse/kdf/pbkdf/dune
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
(library
|
||||
(name pbkdf)
|
||||
(public_name kdf.pbkdf)
|
||||
(libraries digestif mirage-crypto))
|
||||
63
unikernel/duniverse/kdf/pbkdf/pbkdf.ml
Normal file
63
unikernel/duniverse/kdf/pbkdf/pbkdf.ml
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
module 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
|
||||
23
unikernel/duniverse/kdf/pbkdf/pbkdf.mli
Normal file
23
unikernel/duniverse/kdf/pbkdf/pbkdf.mli
Normal 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: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
|
||||
3
unikernel/duniverse/kdf/pbkdf/test/dune
Normal file
3
unikernel/duniverse/kdf/pbkdf/test/dune
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
(test
|
||||
(name pbkdf_tests)
|
||||
(libraries ohex kdf.pbkdf alcotest))
|
||||
287
unikernel/duniverse/kdf/pbkdf/test/pbkdf_tests.ml
Normal file
287
unikernel/duniverse/kdf/pbkdf/test/pbkdf_tests.ml
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
let () = 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;
|
||||
]
|
||||
7
unikernel/duniverse/kdf/scrypt/dune
Normal file
7
unikernel/duniverse/kdf/scrypt/dune
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
(library
|
||||
(name scrypt)
|
||||
(public_name kdf.scrypt)
|
||||
(modules scrypt)
|
||||
(libraries mirage-crypto kdf.pbkdf)
|
||||
(c_names salsa-core)
|
||||
(c_flags (:standard --std=c99 -Wall -Wextra -O3)))
|
||||
65
unikernel/duniverse/kdf/scrypt/salsa-core.c
Normal file
65
unikernel/duniverse/kdf/scrypt/salsa-core.c
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#define CAML_NAME_SPACE
|
||||
#include <caml/mlvalues.h>
|
||||
#include <caml/bigarray.h>
|
||||
|
||||
static inline uint32_t r(uint32_t a, int b) {
|
||||
int rs = 32 - b;
|
||||
return (a << b) | (a >> rs);
|
||||
}
|
||||
|
||||
static inline uint32_t combine(uint32_t y0, uint32_t y1, uint32_t y2, int shift) {
|
||||
return r(y1 + y2, shift) ^ y0;
|
||||
}
|
||||
|
||||
static inline void quarterround(uint32_t *x, int y0, int y1, int y2, int y3) {
|
||||
x[y1] = combine(x[y1], x[y0], x[y3], 7);
|
||||
x[y2] = combine(x[y2], x[y1], x[y0], 9);
|
||||
x[y3] = combine(x[y3], x[y2], x[y1], 13);
|
||||
x[y0] = combine(x[y0], x[y3], x[y2], 18);
|
||||
}
|
||||
|
||||
static inline uint32_t get_u32_le(const uint8_t *input, int offset) {
|
||||
return input[offset]
|
||||
| (input[offset + 1] << 8)
|
||||
| (input[offset + 2] << 16)
|
||||
| (input[offset + 3] << 24);
|
||||
}
|
||||
|
||||
static inline void set_u32_le(uint8_t *input, int offset, uint32_t value) {
|
||||
input[offset] = (uint8_t) value;
|
||||
input[offset + 1] = (uint8_t) (value >> 8);
|
||||
input[offset + 2] = (uint8_t) (value >> 16);
|
||||
input[offset + 3] = (uint8_t) (value >> 24);
|
||||
}
|
||||
|
||||
static void salsa_core(int count, const uint8_t *src, uint8_t *dst) {
|
||||
uint32_t x[16];
|
||||
for (int i = 0; i < 16; i++) {
|
||||
x[i] = get_u32_le(src, i * 4);
|
||||
}
|
||||
for (int i = 0; i < count; i++) {
|
||||
quarterround(x, 0, 4, 8, 12);
|
||||
quarterround(x, 5, 9, 13, 1);
|
||||
quarterround(x, 10, 14, 2, 6);
|
||||
quarterround(x, 15, 3, 7, 11);
|
||||
|
||||
quarterround(x, 0, 1, 2, 3);
|
||||
quarterround(x, 5, 6, 7, 4);
|
||||
quarterround(x, 10, 11, 8, 9);
|
||||
quarterround(x, 15, 12, 13, 14);
|
||||
}
|
||||
for (int i = 0; i < 16; i++) {
|
||||
uint32_t xi = x[i];
|
||||
uint32_t hj = get_u32_le(src, i * 4);
|
||||
set_u32_le(dst, i * 4, xi + hj);
|
||||
}
|
||||
}
|
||||
|
||||
CAMLprim value
|
||||
caml_salsa_core(value count, value src, value dst)
|
||||
{
|
||||
salsa_core(Int_val(count), (const uint8_t*)(String_val(src)), Bytes_val(dst));
|
||||
return Val_unit;
|
||||
}
|
||||
66
unikernel/duniverse/kdf/scrypt/scrypt.ml
Normal file
66
unikernel/duniverse/kdf/scrypt/scrypt.ml
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
external salsa_core : int -> string -> bytes -> unit = "caml_salsa_core" [@@noalloc]
|
||||
|
||||
let salsa20_core count i =
|
||||
let l = 64 in
|
||||
if String.length i <> l then invalid_arg "input must be 16 blocks of 32 bits"
|
||||
else
|
||||
let o = Bytes.create l in
|
||||
salsa_core count i o;
|
||||
Bytes.unsafe_to_string o
|
||||
|
||||
let salsa20_8_core i =
|
||||
salsa20_core 4 i
|
||||
|
||||
let scrypt_block_mix b r =
|
||||
let b' = Bytes.create (String.length b) in
|
||||
let x = Bytes.create 64 in
|
||||
Bytes.unsafe_blit_string b ((2 * r - 1) * 64) x 0 64;
|
||||
for i = 0 to 2 * r - 1 do
|
||||
let b_i = Bytes.unsafe_of_string (String.sub b (i * 64) 64) in
|
||||
Mirage_crypto.Uncommon.unsafe_xor_into (Bytes.unsafe_to_string x) ~src_off:0 b_i ~dst_off:0 64;
|
||||
Bytes.unsafe_blit_string (salsa20_8_core (Bytes.unsafe_to_string b_i)) 0 x 0 64;
|
||||
let offset = (i mod 2) lsl (max 0 (r / 2 - 1)) + i / 2 in
|
||||
Bytes.blit x 0 b' (offset * 64) 64
|
||||
done;
|
||||
b'
|
||||
|
||||
let scrypt_ro_mix b ~r ~n =
|
||||
let blen = r * 128 in
|
||||
let x = ref (Bytes.copy b) in
|
||||
let v = Bytes.create (blen * n) in
|
||||
for i = 0 to n - 1 do
|
||||
Bytes.unsafe_blit !x 0 v (blen * i) blen;
|
||||
x := scrypt_block_mix (Bytes.unsafe_to_string !x) r
|
||||
done;
|
||||
for _ = 0 to n - 1 do
|
||||
let integerify x =
|
||||
let k = Bytes.get_int32_le x (128 * r - 64) in
|
||||
let n' = n - 1 in
|
||||
Int32.(to_int (logand k (of_int n')))
|
||||
in
|
||||
let j = integerify !x in
|
||||
Mirage_crypto.Uncommon.unsafe_xor_into (Bytes.unsafe_to_string v) ~src_off:(blen * j) !x ~dst_off:0 blen;
|
||||
x := scrypt_block_mix (Bytes.unsafe_to_string !x) r;
|
||||
done;
|
||||
!x
|
||||
|
||||
let scrypt ~password ~salt ~n ~r ~p ~dk_len =
|
||||
let is_power_of_2 x = (x land (x - 1)) = 0 in
|
||||
if n <= 1 then invalid_arg "n must be larger than 1"
|
||||
else if not (is_power_of_2 n) then invalid_arg "n must be a power of 2"
|
||||
else if p <= 0 then invalid_arg "p must be a positive integer"
|
||||
else if p > (Int64.to_int (Int64.div 0xffffffffL 4L) / r) then invalid_arg "p too big"
|
||||
else if dk_len <= 0l then invalid_arg "derived key length must be a positive integer";
|
||||
let rec partition b blocks = function
|
||||
| 0 -> blocks
|
||||
| i ->
|
||||
let off = (i - 1) * r * 128 in
|
||||
let block = Bytes.unsafe_of_string (String.sub b off (r * 128)) in
|
||||
partition b (block :: blocks) (i - 1)
|
||||
in
|
||||
let blen = Int32.of_int (128 * r * p) in
|
||||
let dk = Pbkdf.pbkdf2 ~prf:`SHA256 ~password ~salt ~count:1 ~dk_len:blen in
|
||||
let b = partition dk [] p in
|
||||
let b' = List.map (scrypt_ro_mix ~r ~n) b in
|
||||
let salt = String.concat "" (List.map Bytes.unsafe_to_string b') in
|
||||
Pbkdf.pbkdf2 ~prf:`SHA256 ~password ~salt ~count:1 ~dk_len
|
||||
15
unikernel/duniverse/kdf/scrypt/scrypt.mli
Normal file
15
unikernel/duniverse/kdf/scrypt/scrypt.mli
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
(** {{:https://tools.ietf.org/html/rfc7914}
|
||||
The scrypt Password-Based Key Derivation Function}
|
||||
specifies the password-based key derivation function scrypt. The
|
||||
function derives one or more secret keys from a secret string.
|
||||
It is based on memory-hard functions which offer added protection
|
||||
against attacks using custom hardware. *)
|
||||
|
||||
(** [scrypt_kdf password salt n r p dk_len] is [dk], the derived key
|
||||
of [dk_len] octets.
|
||||
[n], the cost parameter, must be larger than 1 and a power of 2.
|
||||
[p], the parallelization parameter, must be a possitive integer
|
||||
and less than or equal to 2^32 - 1 / (4 * r)
|
||||
@raise Invalid_argument when either [n], [p] or [dk_len] are not
|
||||
valid *)
|
||||
val scrypt : password:string -> salt:string -> n:int -> r:int -> p:int -> dk_len:int32 -> string
|
||||
4
unikernel/duniverse/kdf/scrypt/tests/dune
Normal file
4
unikernel/duniverse/kdf/scrypt/tests/dune
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
(test
|
||||
(name scrypt_kdf_tests)
|
||||
(modules scrypt_kdf_tests)
|
||||
(libraries kdf.scrypt ohex alcotest))
|
||||
64
unikernel/duniverse/kdf/scrypt/tests/scrypt_kdf_tests.ml
Normal file
64
unikernel/duniverse/kdf/scrypt/tests/scrypt_kdf_tests.ml
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
let test_scrypt_kdf ~password ~salt ~n ~r ~p ~dk_len ~dk =
|
||||
let dk = Ohex.decode dk in
|
||||
(fun () ->
|
||||
let edk = Scrypt.scrypt ~password ~salt ~n ~r ~p ~dk_len in
|
||||
Alcotest.check Alcotest.string "Scrypt test" edk dk)
|
||||
|
||||
let scrypt_kdf_test1 =
|
||||
test_scrypt_kdf
|
||||
~password:""
|
||||
~salt:""
|
||||
~n:16
|
||||
~r:1
|
||||
~p:1
|
||||
~dk_len:64l
|
||||
~dk:"77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906"
|
||||
|
||||
let scrypt_kdf_test2 =
|
||||
test_scrypt_kdf
|
||||
~password:"password"
|
||||
~salt:"NaCl"
|
||||
~n:1024
|
||||
~r:8
|
||||
~p:16
|
||||
~dk_len:64l
|
||||
~dk:"fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640"
|
||||
|
||||
let scrypt_kdf_test3 =
|
||||
test_scrypt_kdf
|
||||
~password:"pleaseletmein"
|
||||
~salt:"SodiumChloride"
|
||||
~n:16384
|
||||
~r:8
|
||||
~p:1
|
||||
~dk_len:64l
|
||||
~dk:"7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887"
|
||||
|
||||
let scrypt_kdf_test4 =
|
||||
test_scrypt_kdf
|
||||
~password:"pleaseletmein"
|
||||
~salt:"SodiumChloride"
|
||||
~n:1048576
|
||||
~r:8
|
||||
~p:1
|
||||
~dk_len:64l
|
||||
~dk:"2101cb9b6a511aaeaddbbe09cf70f881ec568d574a2ffd4dabe5ee9820adaa478e56fd8f4ba5d09ffa1c6d927c40f4c337304049e8a952fbcbf45c6fa77a41a4"
|
||||
|
||||
let scrypt_kdf_tests () =
|
||||
let tests = [
|
||||
"Test Case 1", `Quick, scrypt_kdf_test1;
|
||||
"Test Case 2", `Quick, scrypt_kdf_test2;
|
||||
] in
|
||||
(* 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 *)
|
||||
if Sys.int_size <= 31 then
|
||||
tests
|
||||
else
|
||||
tests @ [
|
||||
"Test Case 3", `Quick, scrypt_kdf_test3;
|
||||
"Test Case 4", `Slow, scrypt_kdf_test4;
|
||||
]
|
||||
|
||||
let () =
|
||||
Alcotest.run "Scrypt kdf Tests" [
|
||||
"Scrypt kdf tests", scrypt_kdf_tests ();
|
||||
]
|
||||
Loading…
Add table
Add a link
Reference in a new issue