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..c9d5415b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,17 +9,17 @@ 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 }} 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 @@ -42,17 +42,17 @@ 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 }} 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 d9eb7d60..453831ca 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -9,17 +9,17 @@ 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 }} 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 diff --git a/bench/speed.ml b/bench/speed.ml index 3b1c90e5..dcb25267 100644 --- a/bench/speed.ml +++ b/bench/speed.ml @@ -480,12 +480,22 @@ 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 "getentropy" (fun name -> + Mirage_crypto_rng_unix.use_getentropy (); + throughput name (fun buf -> + let buf = Bytes.unsafe_of_string buf in + Mirage_crypto_rng.generate_into buf ~off:0 (Bytes.length buf))) ; + + bm "urandom" (fun name -> + Mirage_crypto_rng_unix.use_dev_urandom (); + throughput name (fun buf -> + let buf = Bytes.unsafe_of_string buf in + Mirage_crypto_rng.generate_into buf ~off:0 (Bytes.length buf))) ; ] let help () = 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" diff --git a/rng/mirage_crypto_rng.mli b/rng/mirage_crypto_rng.mli index ffb69e34..1ddc69f3 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. {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 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 8b74e449..caaa33ed 100644 --- a/rng/unix/dune +++ b/rng/unix/dune @@ -16,10 +16,11 @@ (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) (names mc_getrandom_stubs)) (c_library_flags (:include rng_c_flags.sexp))) 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/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..3180e129 100644 --- a/rng/unix/mirage_crypto_rng_unix.ml +++ b/rng/unix/mirage_crypto_rng_unix.ml @@ -1,13 +1,32 @@ 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) -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..69d1744c 100644 --- a/rng/unix/mirage_crypto_rng_unix.mli +++ b/rng/unix/mirage_crypto_rng_unix.mli @@ -11,3 +11,25 @@ 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_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 diff --git a/rng/unix/urandom.ml b/rng/unix/urandom.ml new file mode 100644 index 00000000..36f750bd --- /dev/null +++ b/rng/unix/urandom.ml @@ -0,0 +1,28 @@ + +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 + at_exit (fun () -> In_channel.close ic); + (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/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_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_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