Skip to content

Commit

Permalink
Use Mirage crypto RNG instead of reading from /dev/urandom
Browse files Browse the repository at this point in the history
/dev/urandom may not be available.
mirage-crypto can use getrandom syscall if needed.

This is now fast enough that we don't need the Random based generator:
```
╭───────────────────────────────────────┬───────────────────────────┬───────────────────────────┬───────────────────────────╮
│name                                   │  major-allocated          │  minor-allocated          │  monotonic-clock          │
├───────────────────────────────────────┼───────────────────────────┼───────────────────────────┼───────────────────────────┤
│  uuidx creation/Uuidx.make            │             0.0080 mjw/run│            58.0004 mnw/run│            375.5079 ns/run│
│  uuidx creation/Uuidx.make_uuid_urnd  │             0.0000 mjw/run│            19.0059 mnw/run│           5510.3318 ns/run│
╰───────────────────────────────────────┴───────────────────────────┴───────────────────────────┴───────────────────────────╯
```

Using Random would be ~4x faster, but Fortuna is already >10x faster than /dev/urandom and avoids using system calls,
except for seeding:
```
│  uuidx creation/Uuidx.make            │             0.0000 mjw/run│            19.0001 mnw/run│             95.5587 ns/run│
```

There is also a newer version of mirage-crypto-rng that can avoid the Cstruct and give us the string more directly, which we'll soon upgrade to.

Signed-off-by: Edwin Török <[email protected]>
  • Loading branch information
edwintorok committed Oct 23, 2024
1 parent bd172bb commit ae26a6c
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 20 deletions.
3 changes: 3 additions & 0 deletions ocaml/libs/uuid/dune
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
(public_name uuid)
(modules uuidx)
(libraries
cstruct
mirage-crypto-rng
mirage-crypto-rng.unix
mtime
mtime.clock.os
ptime
Expand Down
28 changes: 11 additions & 17 deletions ocaml/libs/uuid/uuidx.ml
Original file line number Diff line number Diff line change
Expand Up @@ -135,26 +135,19 @@ let read_bytes dev n =
)
(fun () -> Unix.close fd)

let make_uuid_urnd () = of_bytes (read_bytes dev_urandom 16) |> Option.get
let () = Mirage_crypto_rng_unix.initialize (module Mirage_crypto_rng.Fortuna)

(* State for random number generation. Random.State.t isn't thread safe, so
only use this via with_non_csprng_state, which takes care of this.
*)
let rstate = Random.State.make_self_init ()
let csprng_urandom = read_bytes dev_urandom

let rstate_m = Mutex.create ()
let csprng_fortuna n = Mirage_crypto_rng.generate n |> Cstruct.to_string

let with_non_csprng_state =
(* On OCaml 5 we could use Random.State.split instead,
and on OCaml 4 the mutex may not be strictly needed
*)
let finally () = Mutex.unlock rstate_m in
fun f ->
Mutex.lock rstate_m ;
Fun.protect ~finally (f rstate)
let make_uuid csprng = of_bytes (csprng 16) |> Option.get

(** Use non-CSPRNG by default, for CSPRNG see {!val:make_uuid_urnd} *)
let make_uuid_fast () = with_non_csprng_state Uuidm.v4_gen
let make_uuid_urnd () = make_uuid csprng_urandom

let make_uuid_fortuna () = make_uuid csprng_fortuna

let make_uuid_fast = make_uuid_fortuna

let make_default = ref make_uuid_urnd

Expand All @@ -163,7 +156,8 @@ let make () = !make_default ()
let make_v7_uuid_from_parts time_ns rand_b = Uuidm.v7_ns ~time_ns ~rand_b

let rand64 () =
with_non_csprng_state (fun rstate () -> Random.State.bits64 rstate)
let b = csprng_fortuna 8 in
String.get_int64_ne b 0

let now_ns =
let start = Mtime_clock.counter () in
Expand Down
6 changes: 3 additions & 3 deletions ocaml/xapi/xapi_globs.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1612,11 +1612,11 @@ let other_options =
, (fun () -> string_of_bool !disable_webserver)
, "Disable the host webserver"
)
; ( "use-prng-uuid-gen"
(* eventually this'll be the default, except for Sessions *)
; ( "use-fortuna-uuid-gen"
(* eventually this'll be the default *)
, Arg.Unit (fun () -> Uuidx.make_default := Uuidx.make_uuid_fast)
, (fun () -> !Uuidx.make_default == Uuidx.make_uuid_fast |> string_of_bool)
, "Use PRNG based UUID generator instead of CSPRNG"
, "Use Fortuna based UUID generator instead of /dev/urandom"
)
]

Expand Down

0 comments on commit ae26a6c

Please sign in to comment.