forked from mirage/mirage-crypto
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmirage_crypto_rng.mli
289 lines (213 loc) · 11 KB
/
mirage_crypto_rng.mli
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
(** {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 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.
Suitable entropy feeding of generators are provided by other libraries
{{!Mirage_crypto_rng_lwt}mirage-crypto-rng-lwt} (for Lwt),
{{!Mirage_crypto_rng_async}mirage-crypto-rng-async} (for Async),
{{!Mirage_crypto_rng_mirage}mirage-crypto-rng-mirage} (for MirageOS),
{{!Mirage_crypto_rng_unix}mirage-crypto-rng.unix},
{{!Mirage_crypto_rng_eio}mirage-crypto-rng-eio} (for Eio),
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.
[mirage-crypto-rng-eio] package differs slightly from other rng packages.
Instead of the [initialize] function a [run] function is provided with
similar behaviour, i.e. RNG setup, entropy collection and periodic reseeding.
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 or rdrand). On 32bit platforms, only 4 bytes are filled.
The [id] is used as prefix.
@raise Failure if no CPU RNG is available, or if it doesn't return a
random value. *)
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) -> 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, [fun () -> ()] is returned. *)
(**/**)
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
(** [generate_into ~g buf ~off n] produces [n] uniformly distributed random
bytes into [buf] at offset [off], updating the state of [g].
@raise Invalid_argument if buffer is too small (it must be: Bytes.length buf - off >= n)
*)
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. *)
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]}
*)