]> git.proxmox.com Git - rustc.git/blob - 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
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
11 use rand_core::{RngCore, CryptoRng, Error, ErrorKind, impls};
12 #[allow(unused)]
13 use 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)]
47 pub struct EntropyRng {
48 source: Source,
49 }
50
51 #[derive(Debug)]
52 enum Source {
53 Os(Os),
54 Custom(Custom),
55 Jitter(Jitter),
56 None,
57 }
58
59 impl 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
70 impl Default for EntropyRng {
71 fn default() -> Self {
72 EntropyRng::new()
73 }
74 }
75
76 impl 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
163 impl CryptoRng for EntropyRng {}
164
165
166
167 trait 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)]
178 struct NoSource;
179
180 #[allow(unused)]
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"))
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)]
212 pub 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 )))]
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)?;
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 ))))]
260 type Os = NoSource;
261
262
263 type Custom = NoSource;
264
265
266 #[cfg(not(target_arch = "wasm32"))]
267 #[derive(Clone, Debug)]
268 pub struct Jitter(rngs::JitterRng);
269
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)?;
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")]
284 type Jitter = NoSource;
285
286
287 #[cfg(test)]
288 mod 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 }