1 // Copyright 2018 Developers of the Rand project.
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
9 //! Entropy generator, or wrapper around external generators
11 use rand_core
::{RngCore, CryptoRng, Error, ErrorKind, impls}
;
15 /// An interface returning random data from external source(s), provided
16 /// specifically for securely seeding algorithmic generators (PRNGs).
18 /// Where possible, `EntropyRng` retrieves random data from the operating
19 /// system's interface for random numbers ([`OsRng`]); if that fails it will
20 /// fall back to the [`JitterRng`] entropy collector. In the latter case it will
21 /// still try to use [`OsRng`] on the next usage.
23 /// If no secure source of entropy is available `EntropyRng` will panic on use;
24 /// i.e. it should never output predictable data.
26 /// This is either a little slow ([`OsRng`] requires a system call) or extremely
27 /// slow ([`JitterRng`] must use significant CPU time to generate sufficient
28 /// jitter); for better performance it is common to seed a local PRNG from
29 /// external entropy then primarily use the local PRNG ([`thread_rng`] is
30 /// provided as a convenient, local, automatically-seeded CSPRNG).
34 /// On most systems, like Windows, Linux, macOS and *BSD on common hardware, it
35 /// is highly unlikely for both [`OsRng`] and [`JitterRng`] to fail. But on
36 /// combinations like webassembly without Emscripten or stdweb both sources are
37 /// unavailable. If both sources fail, only [`try_fill_bytes`] is able to
38 /// report the error, and only the one from `OsRng`. The other [`RngCore`]
39 /// methods will panic in case of an error.
41 /// [`OsRng`]: struct.OsRng.html
42 /// [`JitterRng`]: jitter/struct.JitterRng.html
43 /// [`thread_rng`]: ../fn.thread_rng.html
44 /// [`RngCore`]: ../trait.RngCore.html
45 /// [`try_fill_bytes`]: ../trait.RngCore.html#method.tymethod.try_fill_bytes
47 pub struct EntropyRng
{
60 /// Create a new `EntropyRng`.
62 /// This method will do no system calls or other initialization routines,
63 /// those are done on first use. This is done to make `new` infallible,
64 /// and `try_fill_bytes` the only place to report errors.
65 pub fn new() -> Self {
66 EntropyRng { source: Source::None }
70 impl Default
for EntropyRng
{
71 fn default() -> Self {
76 impl RngCore
for EntropyRng
{
77 fn next_u32(&mut self) -> u32 {
78 impls
::next_u32_via_fill(self)
81 fn next_u64(&mut self) -> u64 {
82 impls
::next_u64_via_fill(self)
85 fn fill_bytes(&mut self, dest
: &mut [u8]) {
86 self.try_fill_bytes(dest
).unwrap_or_else(|err
|
87 panic
!("all entropy sources failed; first error: {}", err
))
90 fn try_fill_bytes(&mut self, dest
: &mut [u8]) -> Result
<(), Error
> {
91 let mut reported_error
= None
;
93 if let Source
::Os(ref mut os_rng
) = self.source
{
94 match os_rng
.fill(dest
) {
95 Ok(()) => return Ok(()),
97 warn
!("EntropyRng: OsRng failed \
98 [trying other entropy sources]: {}", err
);
99 reported_error
= Some(err
);
102 } else if Os
::is_supported() {
103 match Os
::new_and_fill(dest
) {
105 debug
!("EntropyRng: using OsRng");
106 self.source
= Source
::Os(os_rng
);
109 Err(err
) => { reported_error = reported_error.or(Some(err)) }
,
113 if let Source
::Custom(ref mut rng
) = self.source
{
114 match rng
.fill(dest
) {
115 Ok(()) => return Ok(()),
117 warn
!("EntropyRng: custom entropy source failed \
118 [trying other entropy sources]: {}", err
);
119 reported_error
= Some(err
);
122 } else if Custom
::is_supported() {
123 match Custom
::new_and_fill(dest
) {
125 debug
!("EntropyRng: using custom entropy source");
126 self.source
= Source
::Custom(custom
);
129 Err(err
) => { reported_error = reported_error.or(Some(err)) }
,
133 if let Source
::Jitter(ref mut jitter_rng
) = self.source
{
134 match jitter_rng
.fill(dest
) {
135 Ok(()) => return Ok(()),
137 warn
!("EntropyRng: JitterRng failed: {}", err
);
138 reported_error
= Some(err
);
141 } else if Jitter
::is_supported() {
142 match Jitter
::new_and_fill(dest
) {
144 debug
!("EntropyRng: using JitterRng");
145 self.source
= Source
::Jitter(jitter_rng
);
148 Err(err
) => { reported_error = reported_error.or(Some(err)) }
,
152 if let Some(err
) = reported_error
{
153 Err(Error
::with_cause(ErrorKind
::Unavailable
,
154 "All entropy sources failed",
157 Err(Error
::new(ErrorKind
::Unavailable
,
158 "No entropy sources available"))
163 impl CryptoRng
for EntropyRng {}
167 trait EntropySource
{
168 fn new_and_fill(dest
: &mut [u8]) -> Result
<Self, Error
>
171 fn fill(&mut self, dest
: &mut [u8]) -> Result
<(), Error
>;
173 fn is_supported() -> bool { true }
177 #[derive(Clone, Debug)]
181 impl EntropySource
for NoSource
{
182 fn new_and_fill(dest
: &mut [u8]) -> Result
<Self, Error
> {
183 Err(Error
::new(ErrorKind
::Unavailable
, "Source not supported"))
186 fn fill(&mut self, dest
: &mut [u8]) -> Result
<(), Error
> {
190 fn is_supported() -> bool { false }
194 #[cfg(all(feature="std",
195 any(target_os
= "linux", target_os
= "android",
196 target_os
= "netbsd",
197 target_os
= "dragonfly",
199 target_os
= "emscripten",
200 target_os
= "solaris",
201 target_os
= "cloudabi",
202 target_os
= "macos", target_os
= "ios",
203 target_os
= "freebsd",
204 target_os
= "openbsd", target_os
= "bitrig",
206 target_os
= "fuchsia",
208 all(target_arch
= "wasm32", feature
= "stdweb"),
209 all(target_arch
= "wasm32", feature
= "wasm-bindgen"),
211 #[derive(Clone, Debug)]
212 pub struct Os(rngs
::OsRng
);
214 #[cfg(all(feature="std",
215 any(target_os
= "linux", target_os
= "android",
216 target_os
= "netbsd",
217 target_os
= "dragonfly",
219 target_os
= "emscripten",
220 target_os
= "solaris",
221 target_os
= "cloudabi",
222 target_os
= "macos", target_os
= "ios",
223 target_os
= "freebsd",
224 target_os
= "openbsd", target_os
= "bitrig",
226 target_os
= "fuchsia",
228 all(target_arch
= "wasm32", feature
= "stdweb"),
229 all(target_arch
= "wasm32", feature
= "wasm-bindgen"),
231 impl EntropySource
for Os
{
232 fn new_and_fill(dest
: &mut [u8]) -> Result
<Self, Error
> {
233 let mut rng
= rngs
::OsRng
::new()?
;
234 rng
.try_fill_bytes(dest
)?
;
238 fn fill(&mut self, dest
: &mut [u8]) -> Result
<(), Error
> {
239 self.0.try_fill_bytes(dest
)
243 #[cfg(not(all(feature="std",
244 any(target_os
= "linux", target_os
= "android",
245 target_os
= "netbsd",
246 target_os
= "dragonfly",
248 target_os
= "emscripten",
249 target_os
= "solaris",
250 target_os
= "cloudabi",
251 target_os
= "macos", target_os
= "ios",
252 target_os
= "freebsd",
253 target_os
= "openbsd", target_os
= "bitrig",
255 target_os
= "fuchsia",
257 all(target_arch
= "wasm32", feature
= "stdweb"),
258 all(target_arch
= "wasm32", feature
= "wasm-bindgen"),
263 type Custom
= NoSource
;
266 #[cfg(not(target_arch = "wasm32"))]
267 #[derive(Clone, Debug)]
268 pub struct Jitter(rngs
::JitterRng
);
270 #[cfg(not(target_arch = "wasm32"))]
271 impl EntropySource
for Jitter
{
272 fn new_and_fill(dest
: &mut [u8]) -> Result
<Self, Error
> {
273 let mut rng
= rngs
::JitterRng
::new()?
;
274 rng
.try_fill_bytes(dest
)?
;
278 fn fill(&mut self, dest
: &mut [u8]) -> Result
<(), Error
> {
279 self.0.try_fill_bytes(dest
)
283 #[cfg(target_arch = "wasm32")]
284 type Jitter
= NoSource
;
293 let mut rng
= EntropyRng
::new();
294 let n
= (rng
.next_u32() ^ rng
.next_u32()).count_ones();
295 assert
!(n
>= 2); // p(failure) approx 1e-7