mte/unikernel/duniverse/mirage-crypto/rng/mirage_crypto_rng.mli
2025-11-11 02:07:51 +01:00

303 lines
11 KiB
OCaml

(** {1 Randomness} *)
(** Secure random number generation.
There are several parts of this module:
{ul
{- The {{!Generator}signature} of generator modules, together with a
facility to convert such modules into actual {{!g}generators}, and
functions that operate on this representation.}
{- A global generator instance, which needs to be initialized by calling
{!set_default_generator}.}}
*)
(** {1 Usage notes} *)
(** {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 getrandom/getentropy/BCryptGenRandom).
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).
Suitable entropy feeding of generators are provided by other libraries
{{!Mirage_crypto_rng_mirage}mirage-crypto-rng-mirage} (for MirageOS),
and {{!Mirage_crypto_rng_miou_unix}mirage-crypto-miou-unix} (for Miou_unix).
The intention is that "initialize" in the respective sub-library is called
once, which sets the default generator and registers entropy
harvesting asynchronous tasks. The semantics is that the entropy is always
fed to the {{!default_generator}default generator}, which is not necessarily
the one set by "initialize". The reasoning behind this is that the default
generator should be used in most setting, and that should be fed a constant
stream of entropy.
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.
Although this module exposes a more fine-grained interface, e.g. allowing
manual seeding of generators, this is intended either for implementing
entropy-harvesting modules, or very specialized purposes. Users of this
library should almost certainly use one of the above entropy libraries, and
avoid manually managing the generator seeding.
Similarly, although it is possible to swap the default generator and gain
control over the random stream, this is also intended for specialized
applications such as testing or similar scenarios where the RNG needs to be
fully deterministic (RFC 6979, deterministic usage of DSA), or as a
component of deterministic algorithms which internally rely on pseudorandom
streams.
In the general case, users should not maintain their local instances of
{{!g}g}. All of the generators in a process have to compete for entropy, and
it is likely that the overall result will have lower effective
unpredictability.
The recommended way to use these functions is either to accept an optional
generator and pass it down, or to ignore the generator altogether, as
illustrated in the {{!rng_examples}examples}.
*)
(** {1 Interface} *)
type g
(** A generator (PRNG) with its state. *)
exception Unseeded_generator
(** Thrown when using an uninitialized {{!g}generator}. *)
exception No_default_generator
(** Thrown when {!set_default_generator} has not been called. *)
(** Entropy sources and collection *)
module Entropy : sig
(** Entropy sources. *)
type source
val sources : unit -> source list
(** [sources ()] returns the list of available sources. *)
val pp_source : Format.formatter -> source -> unit
(** [pp_source ppf source] pretty-prints the entropy [source] on [ppf]. *)
val register_source : string -> source
(** [register_source name] registers [name] as entropy source. *)
(** {1 Bootstrap} *)
val whirlwind_bootstrap : int -> string
(** [whirlwind_bootstrap id] exploits CPU-level data races which lead to
execution-time variability. It returns 200 bytes random data prefixed
by [id].
See {{:http://www.ieee-security.org/TC/SP2014/papers/Not-So-RandomNumbersinVirtualizedLinuxandtheWhirlwindRNG.pdf}}
for further details. *)
val cpu_rng_bootstrap : (int -> string, [`Not_supported]) Result.t
(** [cpu_rng_bootstrap id] returns 8 bytes of random data using the CPU
RNG (rdseed). On 32bit platforms, only 4 bytes are filled.
The [id] is used as prefix. If only rdrand is available, the return
value is the concatenation of 512 calls to rdrand.
@raise Failure if rdrand fails 512 times, or if rdseed fails and rdrand
is not available.
*)
val bootstrap : int -> string
(** [bootstrap id] is either [cpu_rng_bootstrap], if the CPU supports it, or
[whirlwind_bootstrap] if not. *)
(** {1 Timer source} *)
val interrupt_hook : unit -> string
(** [interrupt_hook] collects lower bytes from the cycle counter, to be
used for entropy collection in the event loop. *)
val timer_accumulator : g option -> unit -> unit
(** [timer_accumulator g] is the accumulator for the timer source,
applying {!interrupt_hook} on each call. *)
(** {1 Periodic pulled sources} *)
val feed_pools : g option -> source -> (unit -> (string, [ `No_random_available ]) result) -> unit
(** [feed_pools g source f] feeds all pools of [g] using [source] by executing
[f] for each pool. *)
val cpu_rng : (g option -> unit -> unit, [`Not_supported]) Result.t
(** [cpu_rng g] uses the CPU RNG (rdrand or rdseed) to feed all pools
of [g]. It uses {!feed_pools} internally. If neither rdrand nor rdseed
are available, [`Not_supported] is returned. *)
val rdrand_calls : unit -> int
(** [rdrand_calls ()] returns the number of rdrand calls. *)
val rdrand_failures : unit -> int
(** [rdrand_failures ()] returns the number of rdrand failures. *)
val rdseed_calls : unit -> int
(** [rdseed_calls ()] returns the number of rdseed calls. *)
val rdseed_failures : unit -> int
(** [rdseed_failures ()] returns the number of rdseed failures. *)
(**/**)
val id : source -> int
(** [id source] is the identifier used for [source]. *)
val header : int -> string -> string
(** [header id data] constructs a unique header with [id], length of [data],
and [data]. *)
(**/**)
end
(** A single PRNG algorithm. *)
module type Generator = sig
type g
(** State type for this generator. *)
val block : int
(** Internally, this generator's {{!generate}generate} always produces
[k * block] bytes. *)
val create : ?time:(unit -> int64) -> unit -> g
(** Create a new, unseeded {{!g}g}. *)
val generate_into : g:g -> bytes -> off:int -> int -> unit
[@@alert unsafe "Does not do bounds checks. Use Mirage_crypto_rng.generate_into instead."]
(** [generate_into ~g buf ~off n] produces [n] uniformly distributed random
bytes into [buf] at offset [off], updating the state of [g].
Assumes that [buf] is at least [off + n] bytes long. Also assumes that
[off] and [n] are positive integers. Caution: do not use in your
application, use [Mirage_crypto_rng.generate_into] instead.
*)
val reseed : g:g -> string -> unit
(** [reseed ~g bytes] directly updates [g]. Its new state depends both on
[bytes] and the previous state.
A generator is seded after a single application of [reseed]. *)
val accumulate : g:g -> Entropy.source -> [`Acc of string -> unit]
(** [accumulate ~g] is a closure suitable for incrementally feeding
small amounts of environmentally sourced entropy into [g].
Its operation should be fast enough for repeated calling from e.g.
event loops. Systems with several distinct, stable entropy sources
should use stable [source] to distinguish their sources. *)
val seeded : g:g -> bool
(** [seeded ~g] is [true] iff operations won't throw
{{!Unseeded_generator}Unseeded_generator}. *)
val pools : int
(** [pools] is the amount of pools if any. *)
end
type 'a generator = (module Generator with type g = 'a)
(** Ready-to-use RNG algorithms. *)
(** {b Fortuna}, a CSPRNG {{: https://www.schneier.com/fortuna.html} proposed}
by Schneier. *)
module Fortuna : Generator
(** {b HMAC_DRBG}: A NIST-specified RNG based on HMAC construction over the
provided hash. *)
module Hmac_drbg (H : Digestif.S) : Generator
val create : ?g:'a -> ?seed:string -> ?strict:bool ->
?time:(unit -> int64) -> 'a generator -> g
(** [create ~g ~seed ~strict ~time module] uses a module conforming to the
{{!Generator}Generator} signature to instantiate the generic generator
{{!g}g}.
[g] is the state to use, otherwise a fresh one is created.
[seed] can be provided to immediately reseed the generator with.
[strict] puts the generator into a more standards-conformant, but slighty
slower mode. Useful if the outputs need to match published test-vectors.
[time] is used to limit the amount of reseedings. Fortuna uses at most once
every second. *)
val default_generator : unit -> g
(** [default_generator ()] is the default generator. Functions in this module
use this generator when not explicitly supplied one.
@raise No_default_generator if {!set_default_generator} has not been called. *)
val set_default_generator : g -> unit
(** [set_default_generator g] sets the default generator to [g]. This function
must be called once. *)
(**/**)
(* This function is only used by eio to set the default generator to None when
the entropy harvesting tasks are finished. *)
val unset_default_generator : unit -> unit
(** [unset_default_generator ()] sets the default generator to [None]. *)
(**/**)
val generate_into : ?g:g -> bytes -> ?off:int -> int -> unit
(** [generate_into ~g buf ~off len] invokes
{{!Generator.generate_into}generate_into} on [g] or
{{!generator}default generator}. The random data is put into [buf] starting
at [off] (defaults to 0) with [len] bytes.
@raise Invalid_argument if buffer is too small (it must be: [Bytes.length
buf - off >= n]) or [off] or [n] are negative.
*)
val generate : ?g:g -> int -> string
(** Invoke {!generate_into} on [g] or {{!generator}default generator} and a
freshly allocated string. *)
val block : g option -> int
(** {{!Generator.block}Block} size of [g] or
{{!generator}default generator}. *)
(**/**)
(* The following functions expose the seeding interface. They are meant to
* connect the RNG with entropy-providing libraries and subject to change.
* Client applications should not use them directly. *)
val reseed : ?g:g -> string -> unit
val accumulate : g option -> Entropy.source -> [`Acc of string -> unit]
val seeded : g option -> bool
val pools : g option -> int
val strict : g option -> bool
(**/**)
(** {1:rng_examples Examples}
Generating a random 13-byte string:
{[let cs = Rng.generate 13]}
Generating a list of string, passing down an optional {{!g}generator}:
{[let rec f1 ?g ~n i =
if i < 1 then [] else Rng.generate ?g n :: f1 ?g ~n (i - 1)]}
Generating a [Z.t] smaller than [10]:
{[let f2 ?g () = Mirage_crypto_pk.Z_extra.gen ?g Z.(~$10)]}
Creating a local Fortuna instance and using it as a key-derivation function:
{[let f3 secret =
let g = Rng.(create ~seed:secret (module Generators.Fortuna)) in
Rng.generate ~g 32]}
*)