Skip to content

Commit

Permalink
Add /dev/urandom and getentropy RNG generators
Browse files Browse the repository at this point in the history
Provide guidance to use these by default, document that Fortuna is not
thread-safe. As suggested in mirage#249
  • Loading branch information
hannesm committed Nov 27, 2024
1 parent 53da5fb commit f6bf346
Show file tree
Hide file tree
Showing 11 changed files with 98 additions and 7 deletions.
13 changes: 13 additions & 0 deletions rng/mirage_crypto_rng.mli
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
7 changes: 6 additions & 1 deletion rng/rng.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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: \
Expand Down
4 changes: 2 additions & 2 deletions rng/unix/dune
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
20 changes: 20 additions & 0 deletions rng/unix/getentropy.ml
Original file line number Diff line number Diff line change
@@ -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
16 changes: 16 additions & 0 deletions rng/unix/mirage_crypto_rng_unix.ml
Original file line number Diff line number Diff line change
@@ -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)

Expand Down
10 changes: 10 additions & 0 deletions rng/unix/mirage_crypto_rng_unix.mli
Original file line number Diff line number Diff line change
Expand Up @@ -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
27 changes: 27 additions & 0 deletions rng/unix/urandom.ml
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion tests/test_ec.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion tests/test_entropy.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion tests/test_pk_runner.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion tests/test_random_runner.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit f6bf346

Please sign in to comment.