]> git.proxmox.com Git - rustc.git/blame - vendor/rand-0.6.1/src/rngs/entropy.rs
New upstream version 1.42.0+dfsg0+pve1
[rustc.git] / vendor / rand-0.6.1 / src / rngs / entropy.rs
CommitLineData
416331ca
XL
1// Copyright 2018 Developers of the Rand project.
2//
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.
8
9//! Entropy generator, or wrapper around external generators
10
11use rand_core::{RngCore, CryptoRng, Error, ErrorKind, impls};
12#[allow(unused)]
13use rngs;
14
15/// An interface returning random data from external source(s), provided
16/// specifically for securely seeding algorithmic generators (PRNGs).
17///
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.
22///
23/// If no secure source of entropy is available `EntropyRng` will panic on use;
24/// i.e. it should never output predictable data.
25///
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).
31///
32/// # Panics
33///
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.
40///
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
46#[derive(Debug)]
47pub struct EntropyRng {
48 source: Source,
49}
50
51#[derive(Debug)]
52enum Source {
53 Os(Os),
54 Custom(Custom),
55 Jitter(Jitter),
56 None,
57}
58
59impl EntropyRng {
60 /// Create a new `EntropyRng`.
61 ///
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 }
67 }
68}
69
70impl Default for EntropyRng {
71 fn default() -> Self {
72 EntropyRng::new()
73 }
74}
75
76impl RngCore for EntropyRng {
77 fn next_u32(&mut self) -> u32 {
78 impls::next_u32_via_fill(self)
79 }
80
81 fn next_u64(&mut self) -> u64 {
82 impls::next_u64_via_fill(self)
83 }
84
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))
88 }
89
90 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
91 let mut reported_error = None;
92
93 if let Source::Os(ref mut os_rng) = self.source {
94 match os_rng.fill(dest) {
95 Ok(()) => return Ok(()),
96 Err(err) => {
97 warn!("EntropyRng: OsRng failed \
98 [trying other entropy sources]: {}", err);
99 reported_error = Some(err);
100 },
101 }
102 } else if Os::is_supported() {
103 match Os::new_and_fill(dest) {
104 Ok(os_rng) => {
105 debug!("EntropyRng: using OsRng");
106 self.source = Source::Os(os_rng);
107 return Ok(());
108 },
109 Err(err) => { reported_error = reported_error.or(Some(err)) },
110 }
111 }
112
113 if let Source::Custom(ref mut rng) = self.source {
114 match rng.fill(dest) {
115 Ok(()) => return Ok(()),
116 Err(err) => {
117 warn!("EntropyRng: custom entropy source failed \
118 [trying other entropy sources]: {}", err);
119 reported_error = Some(err);
120 },
121 }
122 } else if Custom::is_supported() {
123 match Custom::new_and_fill(dest) {
124 Ok(custom) => {
125 debug!("EntropyRng: using custom entropy source");
126 self.source = Source::Custom(custom);
127 return Ok(());
128 },
129 Err(err) => { reported_error = reported_error.or(Some(err)) },
130 }
131 }
132
133 if let Source::Jitter(ref mut jitter_rng) = self.source {
134 match jitter_rng.fill(dest) {
135 Ok(()) => return Ok(()),
136 Err(err) => {
137 warn!("EntropyRng: JitterRng failed: {}", err);
138 reported_error = Some(err);
139 },
140 }
141 } else if Jitter::is_supported() {
142 match Jitter::new_and_fill(dest) {
143 Ok(jitter_rng) => {
144 debug!("EntropyRng: using JitterRng");
145 self.source = Source::Jitter(jitter_rng);
146 return Ok(());
147 },
148 Err(err) => { reported_error = reported_error.or(Some(err)) },
149 }
150 }
151
152 if let Some(err) = reported_error {
153 Err(Error::with_cause(ErrorKind::Unavailable,
154 "All entropy sources failed",
155 err))
156 } else {
157 Err(Error::new(ErrorKind::Unavailable,
158 "No entropy sources available"))
159 }
160 }
161}
162
163impl CryptoRng for EntropyRng {}
164
165
166
167trait EntropySource {
168 fn new_and_fill(dest: &mut [u8]) -> Result<Self, Error>
169 where Self: Sized;
170
171 fn fill(&mut self, dest: &mut [u8]) -> Result<(), Error>;
172
173 fn is_supported() -> bool { true }
174}
175
176#[allow(unused)]
177#[derive(Clone, Debug)]
178struct NoSource;
179
180#[allow(unused)]
181impl EntropySource for NoSource {
182 fn new_and_fill(dest: &mut [u8]) -> Result<Self, Error> {
183 Err(Error::new(ErrorKind::Unavailable, "Source not supported"))
184 }
185
186 fn fill(&mut self, dest: &mut [u8]) -> Result<(), Error> {
187 unreachable!()
188 }
189
190 fn is_supported() -> bool { false }
191}
192
193
194#[cfg(all(feature="std",
195 any(target_os = "linux", target_os = "android",
196 target_os = "netbsd",
197 target_os = "dragonfly",
198 target_os = "haiku",
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",
205 target_os = "redox",
206 target_os = "fuchsia",
207 windows,
208 all(target_arch = "wasm32", feature = "stdweb"),
209 all(target_arch = "wasm32", feature = "wasm-bindgen"),
210)))]
211#[derive(Clone, Debug)]
212pub struct Os(rngs::OsRng);
213
214#[cfg(all(feature="std",
215 any(target_os = "linux", target_os = "android",
216 target_os = "netbsd",
217 target_os = "dragonfly",
218 target_os = "haiku",
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",
225 target_os = "redox",
226 target_os = "fuchsia",
227 windows,
228 all(target_arch = "wasm32", feature = "stdweb"),
229 all(target_arch = "wasm32", feature = "wasm-bindgen"),
230)))]
231impl 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)?;
235 Ok(Os(rng))
236 }
237
238 fn fill(&mut self, dest: &mut [u8]) -> Result<(), Error> {
239 self.0.try_fill_bytes(dest)
240 }
241}
242
243#[cfg(not(all(feature="std",
244 any(target_os = "linux", target_os = "android",
245 target_os = "netbsd",
246 target_os = "dragonfly",
247 target_os = "haiku",
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",
254 target_os = "redox",
255 target_os = "fuchsia",
256 windows,
257 all(target_arch = "wasm32", feature = "stdweb"),
258 all(target_arch = "wasm32", feature = "wasm-bindgen"),
259))))]
260type Os = NoSource;
261
262
263type Custom = NoSource;
264
265
266#[cfg(not(target_arch = "wasm32"))]
267#[derive(Clone, Debug)]
268pub struct Jitter(rngs::JitterRng);
269
270#[cfg(not(target_arch = "wasm32"))]
271impl 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)?;
275 Ok(Jitter(rng))
276 }
277
278 fn fill(&mut self, dest: &mut [u8]) -> Result<(), Error> {
279 self.0.try_fill_bytes(dest)
280 }
281}
282
283#[cfg(target_arch = "wasm32")]
284type Jitter = NoSource;
285
286
287#[cfg(test)]
288mod test {
289 use super::*;
290
291 #[test]
292 fn test_entropy() {
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
296 }
297}