From 0a67fe4cf2643740c6c196bd0060bb3e23bad67b Mon Sep 17 00:00:00 2001 From: Hannes Mehnert Date: Mon, 28 Oct 2024 21:20:17 +0100 Subject: [PATCH 01/14] provide getrandom-into from mirage-crypto-rng-unix --- bench/speed.ml | 5 +++++ rng/unix/dune | 1 + rng/unix/mc_getrandom_stubs.c | 6 ++++-- rng/unix/mirage_crypto_rng_unix.ml | 7 +++++-- rng/unix/mirage_crypto_rng_unix.mli | 4 ++++ 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/bench/speed.ml b/bench/speed.ml index 3b1c90e5..c9db7643 100644 --- a/bench/speed.ml +++ b/bench/speed.ml @@ -486,6 +486,11 @@ let benchmarks = [ throughput name (fun buf -> let buf = Bytes.unsafe_of_string buf in generate_into ~g buf ~off:0 (Bytes.length buf))) ; + + bm "getrandom" (fun name -> + throughput name (fun buf -> + let buf = Bytes.unsafe_of_string buf in + Mirage_crypto_rng_unix.getrandom_into buf ~off:0 ~len:(Bytes.length buf))) ; ] let help () = diff --git a/rng/unix/dune b/rng/unix/dune index 8b74e449..28f6aca8 100644 --- a/rng/unix/dune +++ b/rng/unix/dune @@ -20,6 +20,7 @@ (libraries mirage-crypto-rng unix logs) (foreign_stubs (language c) + (include_dirs ../../src/native) (names mc_getrandom_stubs)) (c_library_flags (:include rng_c_flags.sexp))) diff --git a/rng/unix/mc_getrandom_stubs.c b/rng/unix/mc_getrandom_stubs.c index 79f6200e..356bba12 100644 --- a/rng/unix/mc_getrandom_stubs.c +++ b/rng/unix/mc_getrandom_stubs.c @@ -2,6 +2,8 @@ # include #endif +#include "mirage_crypto.h" + #include #include #include @@ -72,7 +74,7 @@ void raw_getrandom(uint8_t *data, uint32_t len) { #error "Retrieving random data not supported on this platform" #endif -CAMLprim value mc_getrandom (value buf, value len) { - raw_getrandom(Bytes_val(buf), Int_val(len)); +CAMLprim value mc_getrandom (value buf, value off, value len) { + raw_getrandom(_bp_uint8_off(buf, off), Int_val(len)); return Val_unit; } diff --git a/rng/unix/mirage_crypto_rng_unix.ml b/rng/unix/mirage_crypto_rng_unix.ml index 1240a1d3..5fc77c1e 100644 --- a/rng/unix/mirage_crypto_rng_unix.ml +++ b/rng/unix/mirage_crypto_rng_unix.ml @@ -3,11 +3,14 @@ open Mirage_crypto_rng let src = Logs.Src.create "mirage-crypto-rng.unix" ~doc:"Mirage crypto RNG Unix" module Log = (val Logs.src_log src : Logs.LOG) -external getrandom_buf : bytes -> int -> unit = "mc_getrandom" [@@noalloc] +external getrandom_buf : bytes -> int -> int -> unit = "mc_getrandom" [@@noalloc] + +let getrandom_into buf ~off ~len = + getrandom_buf buf off len let getrandom size = let buf = Bytes.create size in - getrandom_buf buf size; + getrandom_into buf ~off:0 ~len:size; Bytes.unsafe_to_string buf let getrandom_init i = diff --git a/rng/unix/mirage_crypto_rng_unix.mli b/rng/unix/mirage_crypto_rng_unix.mli index 4808e433..2d830040 100644 --- a/rng/unix/mirage_crypto_rng_unix.mli +++ b/rng/unix/mirage_crypto_rng_unix.mli @@ -11,3 +11,7 @@ val initialize : ?g:'a -> 'a Mirage_crypto_rng.generator -> unit (** [getrandom size] returns a buffer of [size] filled with random bytes. *) val getrandom : int -> string + +(** [getrandom_into buf ~off ~len] fills [buf] with random data ([len] octets), + starting at [off]. *) +val getrandom_into : bytes -> off:int -> len:int -> unit From 9eff38677e24da155551e0ef5055c443c688252f Mon Sep 17 00:00:00 2001 From: Hannes Mehnert Date: Tue, 29 Oct 2024 09:25:50 +0100 Subject: [PATCH 02/14] add pfortuna to the benchmarks --- bench/dune | 2 +- bench/speed.ml | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/bench/dune b/bench/dune index dec1e4f9..25312d7c 100644 --- a/bench/dune +++ b/bench/dune @@ -2,7 +2,7 @@ (names speed) (modules speed) (libraries mirage-crypto mirage-crypto-rng mirage-crypto-rng.unix - mirage-crypto-pk mirage-crypto-ec)) + mirage-crypto-pk mirage-crypto-ec mirage-crypto-rng-miou-unix)) ; marking as "(optional)" leads to OCaml-CI failures ; marking with "(package mirage-crypto-rng-miou-unix)" only has an effect with a "public_name" diff --git a/bench/speed.ml b/bench/speed.ml index c9db7643..cd35d8a0 100644 --- a/bench/speed.ml +++ b/bench/speed.ml @@ -487,6 +487,17 @@ let benchmarks = [ let buf = Bytes.unsafe_of_string buf in generate_into ~g buf ~off:0 (Bytes.length buf))) ; + bm "pfortuna" (fun name -> + let open Mirage_crypto_rng_miou_unix.Pfortuna in + Miou_unix.run ~domains:2 @@ fun () -> + let rng = Mirage_crypto_rng_miou_unix.(initialize (module Pfortuna)) in + let g = create () in + reseed ~g "abcd" ; + throughput name (fun buf -> + let buf = Bytes.unsafe_of_string buf in + generate_into ~g buf ~off:0 (Bytes.length buf)); + Mirage_crypto_rng_miou_unix.kill rng) ; + bm "getrandom" (fun name -> throughput name (fun buf -> let buf = Bytes.unsafe_of_string buf in From 53da5fb405f3dd8db56f4a72893dea70d8f87698 Mon Sep 17 00:00:00 2001 From: Hannes Mehnert Date: Tue, 29 Oct 2024 09:26:42 +0100 Subject: [PATCH 03/14] add urandom-channel to the benchmarks --- bench/dune | 2 +- bench/speed.ml | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/bench/dune b/bench/dune index 25312d7c..9b2b4828 100644 --- a/bench/dune +++ b/bench/dune @@ -2,7 +2,7 @@ (names speed) (modules speed) (libraries mirage-crypto mirage-crypto-rng mirage-crypto-rng.unix - mirage-crypto-pk mirage-crypto-ec mirage-crypto-rng-miou-unix)) + mirage-crypto-pk mirage-crypto-ec mirage-crypto-rng-miou-unix threads.posix)) ; marking as "(optional)" leads to OCaml-CI failures ; marking with "(package mirage-crypto-rng-miou-unix)" only has an effect with a "public_name" diff --git a/bench/speed.ml b/bench/speed.ml index cd35d8a0..5efef307 100644 --- a/bench/speed.ml +++ b/bench/speed.ml @@ -502,6 +502,15 @@ let benchmarks = [ throughput name (fun buf -> let buf = Bytes.unsafe_of_string buf in Mirage_crypto_rng_unix.getrandom_into buf ~off:0 ~len:(Bytes.length buf))) ; + + bm "urandom-channel" (fun name -> + In_channel.with_open_bin "/dev/urandom" @@ fun ic -> + let m = Mutex.create () in + let finally () = Mutex.unlock m in + throughput name (fun buf -> + let buf = Bytes.unsafe_of_string buf in + Mutex.lock m; + Fun.protect ~finally (fun () -> really_input ic buf 0 (Bytes.length buf)))); ] let help () = From f6bf346c529b5b7995067082e34ab522d7d62099 Mon Sep 17 00:00:00 2001 From: Hannes Mehnert Date: Wed, 27 Nov 2024 20:24:20 +0100 Subject: [PATCH 04/14] Add /dev/urandom and getentropy RNG generators Provide guidance to use these by default, document that Fortuna is not thread-safe. As suggested in #249 --- rng/mirage_crypto_rng.mli | 13 +++++++++++++ rng/rng.ml | 7 ++++++- rng/unix/dune | 4 ++-- rng/unix/getentropy.ml | 20 ++++++++++++++++++++ rng/unix/mirage_crypto_rng_unix.ml | 16 ++++++++++++++++ rng/unix/mirage_crypto_rng_unix.mli | 10 ++++++++++ rng/unix/urandom.ml | 27 +++++++++++++++++++++++++++ tests/test_ec.ml | 2 +- tests/test_entropy.ml | 2 +- tests/test_pk_runner.ml | 2 +- tests/test_random_runner.ml | 2 +- 11 files changed, 98 insertions(+), 7 deletions(-) create mode 100644 rng/unix/getentropy.ml create mode 100644 rng/unix/urandom.ml diff --git a/rng/mirage_crypto_rng.mli b/rng/mirage_crypto_rng.mli index ffb69e34..ac5a26a2 100644 --- a/rng/mirage_crypto_rng.mli +++ b/rng/mirage_crypto_rng.mli @@ -16,6 +16,19 @@ (** {b TL;DR} Don't forget to seed; don't maintain your own [g]. + For common operations on Unix (independent of your asynchronous task + library, you can use /dev/urandom or getentropy(3) (actually getrandom(3) on + Linux, getentropy() on macOS and BSD systems, BCryptGenRandom on Windows). + + Please ensure to call [Mirage_crypto_rng_unix.use_default], or + [Mirage_crypto_rng_unix.use_dev_urandom] (if you only want to use + /dev/urandom), or [Mirage_crypto_rng_unix.use_getentropy] (if you only want + to use getentropy). + + For fine-grained control (doing entropy harvesting, etc.), please continue + reading the documentation below. Please be aware that the feeding of Fortuna + and producing random numbers is not thread-safe (it is on Miou_unix). + The RNGs here are merely the deterministic part of a full random number generation suite. For proper operation, they need to be seeded with a high-quality entropy source. diff --git a/rng/rng.ml b/rng/rng.ml index 4dbef40e..475a8f69 100644 --- a/rng/rng.ml +++ b/rng/rng.ml @@ -5,7 +5,12 @@ exception Unseeded_generator exception No_default_generator let setup_rng = - "\nTo initialize the RNG with a default generator, and set up entropy \ + "\nPlease setup your default random number generator. On Unix, the best \ + path is to call [Mirage_crypto_rng_unix.use_default ()].\ + \nBut you can use Fortuna (or any other RNG) and setup the seeding \ + (done by default in MirageOS): \ + \n\ + \nTo initialize the RNG with a default generator, and set up entropy \ collection and periodic reseeding as a background task, do the \ following:\ \n If you are using MirageOS, use the random device in config.ml: \ diff --git a/rng/unix/dune b/rng/unix/dune index 28f6aca8..caaa33ed 100644 --- a/rng/unix/dune +++ b/rng/unix/dune @@ -16,8 +16,8 @@ (library (name mirage_crypto_rng_unix) (public_name mirage-crypto-rng.unix) - (modules mirage_crypto_rng_unix) - (libraries mirage-crypto-rng unix logs) + (modules mirage_crypto_rng_unix urandom getentropy) + (libraries mirage-crypto-rng unix logs threads.posix) (foreign_stubs (language c) (include_dirs ../../src/native) diff --git a/rng/unix/getentropy.ml b/rng/unix/getentropy.ml new file mode 100644 index 00000000..841b6cc7 --- /dev/null +++ b/rng/unix/getentropy.ml @@ -0,0 +1,20 @@ + +external getrandom_buf : bytes -> int -> int -> unit = "mc_getrandom" [@@noalloc] + +type g = unit + +let block = 256 + +let create ?time:_ () = () + +let generate_into ~g:_ buf ~off len = + getrandom_buf buf off len + +let reseed ~g:_ _data = () + +let accumulate ~g:_ _source = + `Acc (fun _data -> ()) + +let seeded ~g:_ = true + +let pools = 0 diff --git a/rng/unix/mirage_crypto_rng_unix.ml b/rng/unix/mirage_crypto_rng_unix.ml index 5fc77c1e..3180e129 100644 --- a/rng/unix/mirage_crypto_rng_unix.ml +++ b/rng/unix/mirage_crypto_rng_unix.ml @@ -1,5 +1,21 @@ open Mirage_crypto_rng +module Urandom = Urandom + +module Getentropy = Getentropy + +let use_dev_urandom () = + let g = create (module Urandom) in + set_default_generator g + +let use_getentropy () = + let g = create (module Getentropy) in + set_default_generator g + +let use_default () = + try use_dev_urandom () with + | _ -> use_getentropy () + let src = Logs.Src.create "mirage-crypto-rng.unix" ~doc:"Mirage crypto RNG Unix" module Log = (val Logs.src_log src : Logs.LOG) diff --git a/rng/unix/mirage_crypto_rng_unix.mli b/rng/unix/mirage_crypto_rng_unix.mli index 2d830040..a9317223 100644 --- a/rng/unix/mirage_crypto_rng_unix.mli +++ b/rng/unix/mirage_crypto_rng_unix.mli @@ -15,3 +15,13 @@ val getrandom : int -> string (** [getrandom_into buf ~off ~len] fills [buf] with random data ([len] octets), starting at [off]. *) val getrandom_into : bytes -> off:int -> len:int -> unit + +module Urandom : Mirage_crypto_rng.Generator + +module Getentropy : Mirage_crypto_rng.Generator + +val use_default : unit -> unit + +val use_dev_urandom : unit -> unit + +val use_getentropy : unit -> unit diff --git a/rng/unix/urandom.ml b/rng/unix/urandom.ml new file mode 100644 index 00000000..4e32b7cf --- /dev/null +++ b/rng/unix/urandom.ml @@ -0,0 +1,27 @@ + +type g = In_channel.t * Mutex.t + +let block = 2048 + +let create ?time:_ () = + let ic = In_channel.open_bin "/dev/urandom" + and mutex = Mutex.create () + in + (ic, mutex) + +let generate_into ~g:(ic, m) buf ~off len = + let finally () = Mutex.unlock m in + Mutex.lock m; + Fun.protect ~finally (fun () -> + match In_channel.really_input ic buf off len with + | None -> failwith "couldn't read enough bytes from /dev/urandom" + | Some () -> ()) + +let reseed ~g:_ _data = () + +let accumulate ~g:_ _source = + `Acc (fun _data -> ()) + +let seeded ~g:_ = true + +let pools = 0 diff --git a/tests/test_ec.ml b/tests/test_ec.ml index c8aa771c..eb359906 100644 --- a/tests/test_ec.ml +++ b/tests/test_ec.ml @@ -861,7 +861,7 @@ df f8 a0 4f d3 dd 1d f0 07 78 3a 2f 29 d6 61 61 | Error _ -> Alcotest.fail "regression failed" let () = - Mirage_crypto_rng_unix.initialize (module Mirage_crypto_rng.Fortuna); + Mirage_crypto_rng_unix.use_default (); Alcotest.run "EC" [ ("P256 Key exchange", key_exchange); diff --git a/tests/test_entropy.ml b/tests/test_entropy.ml index 13cb91fe..77b69e15 100644 --- a/tests/test_entropy.ml +++ b/tests/test_entropy.ml @@ -35,7 +35,7 @@ let timer_check () = let data' = Mirage_crypto_rng.Entropy.interrupt_hook () in if String.equal !data data' then begin Ohex.pp Format.std_formatter data'; - failwith ("same data from timer at " ^ string_of_int i); + print_endline ("same data from timer at " ^ string_of_int i); end; data := data' done diff --git a/tests/test_pk_runner.ml b/tests/test_pk_runner.ml index ae47e676..11adb04b 100644 --- a/tests/test_pk_runner.ml +++ b/tests/test_pk_runner.ml @@ -9,5 +9,5 @@ let suite = ] let () = - Mirage_crypto_rng_unix.initialize (module Mirage_crypto_rng.Fortuna); + Mirage_crypto_rng_unix.use_default (); run_test_tt_main suite diff --git a/tests/test_random_runner.ml b/tests/test_random_runner.ml index 8a74eec4..21f9f701 100644 --- a/tests/test_random_runner.ml +++ b/tests/test_random_runner.ml @@ -105,5 +105,5 @@ let suite = ] let () = - Mirage_crypto_rng_unix.initialize (module Mirage_crypto_rng.Fortuna); + Mirage_crypto_rng_unix.use_default (); run_test_tt_main suite From 7a60519b2e1225adec35875588755e181a672b1f Mon Sep 17 00:00:00 2001 From: Hannes Mehnert Date: Thu, 28 Nov 2024 10:23:45 +0100 Subject: [PATCH 05/14] using in_channel requires ocaml 4.14 --- mirage-crypto-rng.opam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mirage-crypto-rng.opam b/mirage-crypto-rng.opam index edbf152f..56eb086a 100644 --- a/mirage-crypto-rng.opam +++ b/mirage-crypto-rng.opam @@ -13,7 +13,7 @@ build: [ ["dune" "subst"] {dev} ["dune" "runtest" "-p" name "-j" jobs] {with-test} ] depends: [ - "ocaml" {>= "4.13.0"} + "ocaml" {>= "4.14.0"} "dune" {>= "2.7"} "dune-configurator" {>= "2.0.0"} "duration" From 49693c99d7df0e96b1e92fa36dbd27a7033287f6 Mon Sep 17 00:00:00 2001 From: Hannes Mehnert Date: Fri, 29 Nov 2024 21:09:01 +0100 Subject: [PATCH 06/14] Update rng/mirage_crypto_rng.mli MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Reynir Björnsson --- rng/mirage_crypto_rng.mli | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rng/mirage_crypto_rng.mli b/rng/mirage_crypto_rng.mli index ac5a26a2..1ddc69f3 100644 --- a/rng/mirage_crypto_rng.mli +++ b/rng/mirage_crypto_rng.mli @@ -26,8 +26,8 @@ to use getentropy). For fine-grained control (doing entropy harvesting, etc.), please continue - reading the documentation below. Please be aware that the feeding of Fortuna - and producing random numbers is not thread-safe (it is on Miou_unix). + reading the documentation below. {b Please be aware that the feeding of Fortuna + and producing random numbers is not thread-safe} (it is on Miou_unix via Pfortuna). The RNGs here are merely the deterministic part of a full random number generation suite. For proper operation, they need to be seeded with a From 03d3e92678afeb0e1ff530c04cbc3e3cdc154edf Mon Sep 17 00:00:00 2001 From: Hannes Mehnert Date: Fri, 29 Nov 2024 21:10:13 +0100 Subject: [PATCH 07/14] Update rng/unix/mirage_crypto_rng_unix.mli MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Reynir Björnsson --- rng/unix/mirage_crypto_rng_unix.mli | 1 + 1 file changed, 1 insertion(+) diff --git a/rng/unix/mirage_crypto_rng_unix.mli b/rng/unix/mirage_crypto_rng_unix.mli index a9317223..70cf72de 100644 --- a/rng/unix/mirage_crypto_rng_unix.mli +++ b/rng/unix/mirage_crypto_rng_unix.mli @@ -21,6 +21,7 @@ module Urandom : Mirage_crypto_rng.Generator module Getentropy : Mirage_crypto_rng.Generator val use_default : unit -> unit +(** [use_default ()] initializes with [Urandom] or resorts to [Getentropy] otherwise. *) val use_dev_urandom : unit -> unit From 3abb84a51bf7ead515debcdf889544b2ada688fd Mon Sep 17 00:00:00 2001 From: Hannes Mehnert Date: Fri, 6 Dec 2024 11:24:26 +0100 Subject: [PATCH 08/14] bench/speed: use Urandom and Getentropy generators since the pfortuna benchmark has been moved to bench/speed, remove bench/miou Mirage_crypto_rng_unix: remove getrandom_into --- bench/dune | 9 +-- bench/miou.ml | 91 ----------------------------- bench/speed.ml | 20 +++---- rng/unix/mirage_crypto_rng_unix.mli | 4 -- 4 files changed, 9 insertions(+), 115 deletions(-) delete mode 100644 bench/miou.ml diff --git a/bench/dune b/bench/dune index 9b2b4828..71ad5387 100644 --- a/bench/dune +++ b/bench/dune @@ -2,11 +2,4 @@ (names speed) (modules speed) (libraries mirage-crypto mirage-crypto-rng mirage-crypto-rng.unix - mirage-crypto-pk mirage-crypto-ec mirage-crypto-rng-miou-unix threads.posix)) - -; marking as "(optional)" leads to OCaml-CI failures -; marking with "(package mirage-crypto-rng-miou-unix)" only has an effect with a "public_name" -;(executables -; (names miou) -; (modules miou) -; (libraries mirage-crypto-rng-miou-unix)) + mirage-crypto-pk mirage-crypto-ec mirage-crypto-rng-miou-unix)) diff --git a/bench/miou.ml b/bench/miou.ml deleted file mode 100644 index 27120c32..00000000 --- a/bench/miou.ml +++ /dev/null @@ -1,91 +0,0 @@ -open Mirage_crypto - -module Time = struct - - let time ~n f a = - let t1 = Sys.time () in - for _ = 1 to n do ignore (f a) done ; - let t2 = Sys.time () in - (t2 -. t1) - - let warmup () = - let x = ref 0 in - let rec go start = - if Sys.time () -. start < 1. then begin - for i = 0 to 10000 do x := !x + i done ; - go start - end in - go (Sys.time ()) - -end - -let burn_period = 2.0 - -let sizes = [16; 64; 256; 1024; 8192] -(* let sizes = [16] *) - -let burn f n = - let buf = Mirage_crypto_rng.generate n in - let (t1, i1) = - let rec loop it = - let t = Time.time ~n:it f buf in - if t > 0.2 then (t, it) else loop (it * 10) in - loop 10 in - let iters = int_of_float (float i1 *. burn_period /. t1) in - let time = Time.time ~n:iters f buf in - (iters, time, float (n * iters) /. time) - -let mb = 1024. *. 1024. - -let throughput title f = - Printf.printf "\n* [%s]\n%!" title ; - sizes |> List.iter @@ fun size -> - Gc.full_major () ; - let (iters, time, bw) = burn f size in - Printf.printf " % 5d: %04f MB/s (%d iters in %.03f s)\n%!" - size (bw /. mb) iters time - -let bm name f = (name, fun () -> f name) - -let benchmarks = [ - bm "pfortuna" (fun name -> - let open Mirage_crypto_rng_miou_unix.Pfortuna in - Miou_unix.run ~domains:2 @@ fun () -> - let rng = Mirage_crypto_rng_miou_unix.(initialize (module Pfortuna)) in - let g = create () in - reseed ~g "abcd" ; - throughput name (fun buf -> - let buf = Bytes.unsafe_of_string buf in - generate_into ~g buf ~off:0 (Bytes.length buf)); - Mirage_crypto_rng_miou_unix.kill rng) ; -] - -let help () = - Printf.printf "available benchmarks:\n "; - List.iter (fun (n, _) -> Printf.printf "%s " n) benchmarks ; - Printf.printf "\n%!" - -let runv fs = - Format.printf "accel: %a\n%!" - (fun ppf -> List.iter @@ fun x -> - Format.fprintf ppf "%s " @@ - match x with `XOR -> "XOR" | `AES -> "AES" | `GHASH -> "GHASH") - accelerated; - Time.warmup () ; - List.iter (fun f -> f ()) fs - - -let () = - let seed = "abcd" in - let g = Mirage_crypto_rng.(create ~seed (module Fortuna)) in - Mirage_crypto_rng.set_default_generator g; - match Array.to_list Sys.argv with - | _::(_::_ as args) -> begin - try - let fs = - args |> List.map @@ fun n -> - snd (benchmarks |> List.find @@ fun (n1, _) -> n = n1) in - runv fs - with Not_found -> help () - end - | _ -> help () diff --git a/bench/speed.ml b/bench/speed.ml index 5efef307..1726a0c4 100644 --- a/bench/speed.ml +++ b/bench/speed.ml @@ -480,12 +480,10 @@ let benchmarks = [ throughput_into name (fun dst cs -> DES.ECB.unsafe_encrypt_into ~key cs ~src_off:0 dst ~dst_off:0 (String.length cs))) ; bm "fortuna" (fun name -> - let open Mirage_crypto_rng.Fortuna in - let g = create () in - reseed ~g "abcd" ; + Mirage_crypto_rng_unix.initialize (module Mirage_crypto_rng.Fortuna); throughput name (fun buf -> let buf = Bytes.unsafe_of_string buf in - generate_into ~g buf ~off:0 (Bytes.length buf))) ; + Mirage_crypto_rng.generate_into buf ~off:0 (Bytes.length buf))) ; bm "pfortuna" (fun name -> let open Mirage_crypto_rng_miou_unix.Pfortuna in @@ -498,19 +496,17 @@ let benchmarks = [ generate_into ~g buf ~off:0 (Bytes.length buf)); Mirage_crypto_rng_miou_unix.kill rng) ; - bm "getrandom" (fun name -> + bm "getentropy" (fun name -> + Mirage_crypto_rng_unix.use_getentropy (); throughput name (fun buf -> let buf = Bytes.unsafe_of_string buf in - Mirage_crypto_rng_unix.getrandom_into buf ~off:0 ~len:(Bytes.length buf))) ; + Mirage_crypto_rng.generate_into buf ~off:0 (Bytes.length buf))) ; - bm "urandom-channel" (fun name -> - In_channel.with_open_bin "/dev/urandom" @@ fun ic -> - let m = Mutex.create () in - let finally () = Mutex.unlock m in + bm "urandom" (fun name -> + Mirage_crypto_rng_unix.use_dev_urandom (); throughput name (fun buf -> let buf = Bytes.unsafe_of_string buf in - Mutex.lock m; - Fun.protect ~finally (fun () -> really_input ic buf 0 (Bytes.length buf)))); + Mirage_crypto_rng.generate_into buf ~off:0 (Bytes.length buf))) ; ] let help () = diff --git a/rng/unix/mirage_crypto_rng_unix.mli b/rng/unix/mirage_crypto_rng_unix.mli index 70cf72de..8a358030 100644 --- a/rng/unix/mirage_crypto_rng_unix.mli +++ b/rng/unix/mirage_crypto_rng_unix.mli @@ -12,10 +12,6 @@ val initialize : ?g:'a -> 'a Mirage_crypto_rng.generator -> unit (** [getrandom size] returns a buffer of [size] filled with random bytes. *) val getrandom : int -> string -(** [getrandom_into buf ~off ~len] fills [buf] with random data ([len] octets), - starting at [off]. *) -val getrandom_into : bytes -> off:int -> len:int -> unit - module Urandom : Mirage_crypto_rng.Generator module Getentropy : Mirage_crypto_rng.Generator From 0917c36f35f6cba100a91fcdc256bfafde649412 Mon Sep 17 00:00:00 2001 From: Hannes Mehnert Date: Fri, 6 Dec 2024 11:38:29 +0100 Subject: [PATCH 09/14] close the /dev/urandom file descriptor at exit --- rng/unix/urandom.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/rng/unix/urandom.ml b/rng/unix/urandom.ml index 4e32b7cf..36f750bd 100644 --- a/rng/unix/urandom.ml +++ b/rng/unix/urandom.ml @@ -7,6 +7,7 @@ let create ?time:_ () = let ic = In_channel.open_bin "/dev/urandom" and mutex = Mutex.create () in + at_exit (fun () -> In_channel.close ic); (ic, mutex) let generate_into ~g:(ic, m) buf ~off len = From 36562df4173afc6781308385baae09aaede7740e Mon Sep 17 00:00:00 2001 From: Hannes Mehnert Date: Fri, 6 Dec 2024 11:38:49 +0100 Subject: [PATCH 10/14] mirage-crypto-rng-unix: more documentation --- rng/unix/mirage_crypto_rng_unix.mli | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/rng/unix/mirage_crypto_rng_unix.mli b/rng/unix/mirage_crypto_rng_unix.mli index 8a358030..69d1744c 100644 --- a/rng/unix/mirage_crypto_rng_unix.mli +++ b/rng/unix/mirage_crypto_rng_unix.mli @@ -12,13 +12,24 @@ val initialize : ?g:'a -> 'a Mirage_crypto_rng.generator -> unit (** [getrandom size] returns a buffer of [size] filled with random bytes. *) val getrandom : int -> string +(** A generator that opens /dev/urandom and reads from that file descriptor + data whenever random data is needed. The file descriptor is closed in + [at_exit]. *) module Urandom : Mirage_crypto_rng.Generator +(** A generator using [getrandom(3)] on Linux, [getentropy(3)] on BSD and macOS, + and [BCryptGenRandom()] on Windows. *) module Getentropy : Mirage_crypto_rng.Generator +(** [use_default ()] initializes the RNG [Mirage_crypto_rng.default_generator] + with [Urandom] or resorts to [Getentropy] if the urandom failed to open the + /dev/urandom device. *) val use_default : unit -> unit -(** [use_default ()] initializes with [Urandom] or resorts to [Getentropy] otherwise. *) +(** [use_dev_random ()] initializes the RNG [Mirage_crypto_rng.default_generator] + with the [Urandom] generator. *) val use_dev_urandom : unit -> unit +(** [use_getentropy ()] initializes the RNG [Mirage_crypto_rng.default_generator] + with the [Getentropy] generator. *) val use_getentropy : unit -> unit From 5c885b0c091fb6a2a5d55490a498ea7206e98f67 Mon Sep 17 00:00:00 2001 From: Hannes Mehnert Date: Fri, 6 Dec 2024 11:43:26 +0100 Subject: [PATCH 11/14] adjust CI systems: no more 4.13 --- .cirrus.yml | 1 - .github/workflows/test.yml | 4 ++-- .github/workflows/windows.yml | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index c7b6b8bb..a435b11f 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -4,7 +4,6 @@ freebsd_instance: freebsd_task: env: matrix: - - OCAML_VERSION: 4.13.1 - OCAML_VERSION: 4.14.2 pkg_install_script: pkg install -y ocaml-opam gmp gmake pkgconf bash diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 61253abb..9d6f4e2f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,7 +9,7 @@ jobs: strategy: fail-fast: false matrix: - ocaml-version: ["4.14.2", "4.13.1"] + ocaml-version: ["4.14.2"] operating-system: [macos-latest, ubuntu-latest] runs-on: ${{ matrix.operating-system }} @@ -42,7 +42,7 @@ jobs: strategy: fail-fast: false matrix: - ocaml-version: ["5.0.0"] + ocaml-version: ["5.2.1"] operating-system: [macos-latest, ubuntu-latest] runs-on: ${{ matrix.operating-system }} diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index d9eb7d60..7f0248cb 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -9,7 +9,7 @@ jobs: strategy: fail-fast: false matrix: - ocaml-version: ["4.14.2", "4.13.1"] + ocaml-version: ["4.14.2"] operating-system: [windows-latest] runs-on: ${{ matrix.operating-system }} From 53b1898e064d27d54f3df7175f159aef34a72d09 Mon Sep 17 00:00:00 2001 From: Hannes Mehnert Date: Fri, 6 Dec 2024 11:49:18 +0100 Subject: [PATCH 12/14] test_entropy: fail again, but disable on arm64 --- tests/dune | 4 +++- tests/test_entropy.ml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/dune b/tests/dune index 32afebe7..f0363cae 100644 --- a/tests/dune +++ b/tests/dune @@ -41,7 +41,9 @@ (name test_entropy) (modules test_entropy) (package mirage-crypto-rng) - (libraries mirage-crypto-rng ohex)) + (libraries mirage-crypto-rng ohex) + (enabled_if (<> %{architecture} "arm64"))) + ; see https://github.com/mirage/mirage-crypto/issues/216 (test (name test_ec) diff --git a/tests/test_entropy.ml b/tests/test_entropy.ml index 77b69e15..13cb91fe 100644 --- a/tests/test_entropy.ml +++ b/tests/test_entropy.ml @@ -35,7 +35,7 @@ let timer_check () = let data' = Mirage_crypto_rng.Entropy.interrupt_hook () in if String.equal !data data' then begin Ohex.pp Format.std_formatter data'; - print_endline ("same data from timer at " ^ string_of_int i); + failwith ("same data from timer at " ^ string_of_int i); end; data := data' done From b5262d193f0e0e6e69d31a504c0a8e36d31b9b9a Mon Sep 17 00:00:00 2001 From: Hannes Mehnert Date: Fri, 6 Dec 2024 13:21:55 +0100 Subject: [PATCH 13/14] revert putting miou-unix directly into bench/speed to allow ocaml 4 to succeed --- bench/dune | 9 ++++- bench/miou.ml | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++ bench/speed.ml | 11 ------ 3 files changed, 99 insertions(+), 12 deletions(-) create mode 100644 bench/miou.ml diff --git a/bench/dune b/bench/dune index 71ad5387..dec1e4f9 100644 --- a/bench/dune +++ b/bench/dune @@ -2,4 +2,11 @@ (names speed) (modules speed) (libraries mirage-crypto mirage-crypto-rng mirage-crypto-rng.unix - mirage-crypto-pk mirage-crypto-ec mirage-crypto-rng-miou-unix)) + mirage-crypto-pk mirage-crypto-ec)) + +; marking as "(optional)" leads to OCaml-CI failures +; marking with "(package mirage-crypto-rng-miou-unix)" only has an effect with a "public_name" +;(executables +; (names miou) +; (modules miou) +; (libraries mirage-crypto-rng-miou-unix)) diff --git a/bench/miou.ml b/bench/miou.ml new file mode 100644 index 00000000..27120c32 --- /dev/null +++ b/bench/miou.ml @@ -0,0 +1,91 @@ +open Mirage_crypto + +module Time = struct + + let time ~n f a = + let t1 = Sys.time () in + for _ = 1 to n do ignore (f a) done ; + let t2 = Sys.time () in + (t2 -. t1) + + let warmup () = + let x = ref 0 in + let rec go start = + if Sys.time () -. start < 1. then begin + for i = 0 to 10000 do x := !x + i done ; + go start + end in + go (Sys.time ()) + +end + +let burn_period = 2.0 + +let sizes = [16; 64; 256; 1024; 8192] +(* let sizes = [16] *) + +let burn f n = + let buf = Mirage_crypto_rng.generate n in + let (t1, i1) = + let rec loop it = + let t = Time.time ~n:it f buf in + if t > 0.2 then (t, it) else loop (it * 10) in + loop 10 in + let iters = int_of_float (float i1 *. burn_period /. t1) in + let time = Time.time ~n:iters f buf in + (iters, time, float (n * iters) /. time) + +let mb = 1024. *. 1024. + +let throughput title f = + Printf.printf "\n* [%s]\n%!" title ; + sizes |> List.iter @@ fun size -> + Gc.full_major () ; + let (iters, time, bw) = burn f size in + Printf.printf " % 5d: %04f MB/s (%d iters in %.03f s)\n%!" + size (bw /. mb) iters time + +let bm name f = (name, fun () -> f name) + +let benchmarks = [ + bm "pfortuna" (fun name -> + let open Mirage_crypto_rng_miou_unix.Pfortuna in + Miou_unix.run ~domains:2 @@ fun () -> + let rng = Mirage_crypto_rng_miou_unix.(initialize (module Pfortuna)) in + let g = create () in + reseed ~g "abcd" ; + throughput name (fun buf -> + let buf = Bytes.unsafe_of_string buf in + generate_into ~g buf ~off:0 (Bytes.length buf)); + Mirage_crypto_rng_miou_unix.kill rng) ; +] + +let help () = + Printf.printf "available benchmarks:\n "; + List.iter (fun (n, _) -> Printf.printf "%s " n) benchmarks ; + Printf.printf "\n%!" + +let runv fs = + Format.printf "accel: %a\n%!" + (fun ppf -> List.iter @@ fun x -> + Format.fprintf ppf "%s " @@ + match x with `XOR -> "XOR" | `AES -> "AES" | `GHASH -> "GHASH") + accelerated; + Time.warmup () ; + List.iter (fun f -> f ()) fs + + +let () = + let seed = "abcd" in + let g = Mirage_crypto_rng.(create ~seed (module Fortuna)) in + Mirage_crypto_rng.set_default_generator g; + match Array.to_list Sys.argv with + | _::(_::_ as args) -> begin + try + let fs = + args |> List.map @@ fun n -> + snd (benchmarks |> List.find @@ fun (n1, _) -> n = n1) in + runv fs + with Not_found -> help () + end + | _ -> help () diff --git a/bench/speed.ml b/bench/speed.ml index 1726a0c4..dcb25267 100644 --- a/bench/speed.ml +++ b/bench/speed.ml @@ -485,17 +485,6 @@ let benchmarks = [ let buf = Bytes.unsafe_of_string buf in Mirage_crypto_rng.generate_into buf ~off:0 (Bytes.length buf))) ; - bm "pfortuna" (fun name -> - let open Mirage_crypto_rng_miou_unix.Pfortuna in - Miou_unix.run ~domains:2 @@ fun () -> - let rng = Mirage_crypto_rng_miou_unix.(initialize (module Pfortuna)) in - let g = create () in - reseed ~g "abcd" ; - throughput name (fun buf -> - let buf = Bytes.unsafe_of_string buf in - generate_into ~g buf ~off:0 (Bytes.length buf)); - Mirage_crypto_rng_miou_unix.kill rng) ; - bm "getentropy" (fun name -> Mirage_crypto_rng_unix.use_getentropy (); throughput name (fun buf -> From 9d30a605683f92ad63f1134597e66fb4b1092f25 Mon Sep 17 00:00:00 2001 From: Hannes Mehnert Date: Fri, 6 Dec 2024 13:27:14 +0100 Subject: [PATCH 14/14] github CI: increase versions --- .github/workflows/test.yml | 8 ++++---- .github/workflows/windows.yml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9d6f4e2f..c9d5415b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,10 +16,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Use OCaml ${{ matrix.ocaml-version }} - uses: ocaml/setup-ocaml@v2 + uses: ocaml/setup-ocaml@v3 with: opam-local-packages: | *.opam @@ -49,10 +49,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Use OCaml ${{ matrix.ocaml-version }} - uses: ocaml/setup-ocaml@v2 + uses: ocaml/setup-ocaml@v3 with: opam-local-packages: | mirage-crypto.opam diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 7f0248cb..453831ca 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -16,10 +16,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Use OCaml ${{ matrix.ocaml-compiler }} - uses: ocaml/setup-ocaml@v2 + uses: ocaml/setup-ocaml@v3 with: opam-repositories: | opam-repository-mingw: https://github.com/ocaml-opam/opam-repository-mingw.git#sunset