This commit is contained in:
parent
aa2ff7b2f0
commit
2f3113f55d
11742 changed files with 1223940 additions and 0 deletions
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