|
239 | 239 |
|
240 | 240 | #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
|
241 | 241 | html_favicon_url = "https://www.rust-lang.org/favicon.ico",
|
242 |
| - html_root_url = "https://docs.rs/rand/0.3")] |
| 242 | + html_root_url = "https://docs.rs/rand/0.4")] |
243 | 243 |
|
244 | 244 | #![deny(missing_debug_implementations)]
|
245 | 245 |
|
|
259 | 259 | #[cfg(not(feature = "log"))] macro_rules! debug { ($($x:tt)*) => () }
|
260 | 260 | #[cfg(all(feature="std", not(feature = "log")))] macro_rules! info { ($($x:tt)*) => () }
|
261 | 261 | #[cfg(all(feature="std", not(feature = "log")))] macro_rules! warn { ($($x:tt)*) => () }
|
| 262 | +#[cfg(all(feature="std", not(feature = "log")))] macro_rules! error { ($($x:tt)*) => () } |
262 | 263 |
|
263 | 264 |
|
264 | 265 | use core::{marker, mem};
|
@@ -287,7 +288,7 @@ use prng::Isaac64Rng as IsaacWordRng;
|
287 | 288 |
|
288 | 289 | use distributions::{Range, IndependentSample};
|
289 | 290 | use distributions::range::SampleRange;
|
290 |
| -#[cfg(feature="std")] use reseeding::{ReseedingRng, ReseedWithNew}; |
| 291 | +#[cfg(feature="std")] use reseeding::ReseedingRng; |
291 | 292 |
|
292 | 293 | // public modules
|
293 | 294 | pub mod distributions;
|
@@ -843,29 +844,7 @@ pub trait NewRng: SeedableRng {
|
843 | 844 | #[cfg(feature="std")]
|
844 | 845 | impl<R: SeedableRng> NewRng for R {
|
845 | 846 | fn new() -> Result<Self, Error> {
|
846 |
| - // Note: error handling would be easier with try/catch blocks |
847 |
| - fn new_os<T: SeedableRng>() -> Result<T, Error> { |
848 |
| - let mut r = OsRng::new()?; |
849 |
| - T::from_rng(&mut r) |
850 |
| - } |
851 |
| - |
852 |
| - fn new_jitter<T: SeedableRng>() -> Result<T, Error> { |
853 |
| - let mut r = JitterRng::new()?; |
854 |
| - T::from_rng(&mut r) |
855 |
| - } |
856 |
| - |
857 |
| - trace!("Seeding new RNG"); |
858 |
| - new_os().or_else(|e1| { |
859 |
| - warn!("OsRng failed [falling back to JitterRng]: {}", e1); |
860 |
| - new_jitter().map_err(|_e2| { |
861 |
| - warn!("JitterRng failed: {}", _e2); |
862 |
| - // TODO: can we somehow return both error sources? |
863 |
| - Error::with_cause( |
864 |
| - ErrorKind::Unavailable, |
865 |
| - "seeding a new RNG failed: both OS and Jitter entropy sources failed", |
866 |
| - e1) |
867 |
| - }) |
868 |
| - }) |
| 847 | + R::from_rng(EntropyRng::new()) |
869 | 848 | }
|
870 | 849 | }
|
871 | 850 |
|
@@ -963,20 +942,19 @@ pub fn weak_rng() -> XorShiftRng {
|
963 | 942 | #[cfg(feature="std")]
|
964 | 943 | #[derive(Clone, Debug)]
|
965 | 944 | pub struct ThreadRng {
|
966 |
| - rng: Rc<RefCell<ReseedingRng<StdRng, ReseedWithNew>>>, |
| 945 | + rng: Rc<RefCell<ReseedingRng<StdRng, EntropyRng>>>, |
967 | 946 | }
|
968 | 947 |
|
969 | 948 | #[cfg(feature="std")]
|
970 | 949 | thread_local!(
|
971 |
| - static THREAD_RNG_KEY: Rc<RefCell<ReseedingRng<StdRng, ReseedWithNew>>> = { |
| 950 | + static THREAD_RNG_KEY: Rc<RefCell<ReseedingRng<StdRng, EntropyRng>>> = { |
972 | 951 | const THREAD_RNG_RESEED_THRESHOLD: u64 = 32_768;
|
973 |
| - let r = match StdRng::new() { |
974 |
| - Ok(r) => r, |
975 |
| - Err(e) => panic!("could not initialize thread_rng: {:?}", e) |
976 |
| - }; |
| 952 | + let mut entropy_source = EntropyRng::new(); |
| 953 | + let r = StdRng::from_rng(&mut entropy_source) |
| 954 | + .expect("could not initialize thread_rng"); |
977 | 955 | let rng = ReseedingRng::new(r,
|
978 | 956 | THREAD_RNG_RESEED_THRESHOLD,
|
979 |
| - ReseedWithNew); |
| 957 | + entropy_source); |
980 | 958 | Rc::new(RefCell::new(rng))
|
981 | 959 | }
|
982 | 960 | );
|
@@ -1017,6 +995,127 @@ impl Rng for ThreadRng {
|
1017 | 995 | }
|
1018 | 996 | }
|
1019 | 997 |
|
| 998 | +/// An RNG provided specifically for seeding PRNG's. |
| 999 | +/// |
| 1000 | +/// `EntropyRng` uses the interface for random numbers provided by the operating |
| 1001 | +/// system ([`OsRng`]). If that returns an error, it will fall back to the |
| 1002 | +/// [`JitterRng`] entropy collector. Every time it will then check if `OsRng` |
| 1003 | +/// is still not available, and switch back if possible. |
| 1004 | +/// |
| 1005 | +/// [`OsRng`]: os/struct.OsRng.html |
| 1006 | +/// [`JitterRng`]: jitter/struct.JitterRng.html |
| 1007 | +#[cfg(feature="std")] |
| 1008 | +#[derive(Debug)] |
| 1009 | +pub struct EntropyRng { |
| 1010 | + rng: EntropySource, |
| 1011 | +} |
| 1012 | + |
| 1013 | +#[cfg(feature="std")] |
| 1014 | +#[derive(Debug)] |
| 1015 | +enum EntropySource { |
| 1016 | + Os(OsRng), |
| 1017 | + Jitter(JitterRng), |
| 1018 | + None, |
| 1019 | +} |
| 1020 | + |
| 1021 | +#[cfg(feature="std")] |
| 1022 | +impl EntropyRng { |
| 1023 | + /// Create a new `EntropyRng`. |
| 1024 | + /// |
| 1025 | + /// This method will do no system calls or other initialization routines, |
| 1026 | + /// those are done on first use. This is done to make `new` infallible, |
| 1027 | + /// and `try_fill_bytes` the only place to report errors. |
| 1028 | + pub fn new() -> Self { |
| 1029 | + EntropyRng { rng: EntropySource::None } |
| 1030 | + } |
| 1031 | +} |
| 1032 | + |
| 1033 | +#[cfg(feature="std")] |
| 1034 | +impl Rng for EntropyRng { |
| 1035 | + fn next_u32(&mut self) -> u32 { |
| 1036 | + impls::next_u32_via_fill(self) |
| 1037 | + } |
| 1038 | + |
| 1039 | + fn next_u64(&mut self) -> u64 { |
| 1040 | + impls::next_u64_via_fill(self) |
| 1041 | + } |
| 1042 | + |
| 1043 | + fn fill_bytes(&mut self, dest: &mut [u8]) { |
| 1044 | + self.try_fill_bytes(dest).unwrap(); |
| 1045 | + } |
| 1046 | + |
| 1047 | + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { |
| 1048 | + fn try_os_new(dest: &mut [u8]) -> Result<OsRng, Error> |
| 1049 | + { |
| 1050 | + let mut rng = OsRng::new()?; |
| 1051 | + rng.try_fill_bytes(dest)?; |
| 1052 | + Ok(rng) |
| 1053 | + } |
| 1054 | + |
| 1055 | + fn try_jitter_new(dest: &mut [u8]) -> Result<JitterRng, Error> |
| 1056 | + { |
| 1057 | + let mut rng = JitterRng::new()?; |
| 1058 | + rng.try_fill_bytes(dest)?; |
| 1059 | + Ok(rng) |
| 1060 | + } |
| 1061 | + |
| 1062 | + let mut switch_rng = None; |
| 1063 | + match self.rng { |
| 1064 | + EntropySource::None => { |
| 1065 | + let os_rng_result = try_os_new(dest); |
| 1066 | + match os_rng_result { |
| 1067 | + Ok(os_rng) => { |
| 1068 | + switch_rng = Some(EntropySource::Os(os_rng)); |
| 1069 | + } |
| 1070 | + Err(os_rng_error) => { |
| 1071 | + warn!("EntropyRng: OsRng failed [falling back to JitterRng]: {}", |
| 1072 | + os_rng_error); |
| 1073 | + match try_jitter_new(dest) { |
| 1074 | + Ok(jitter_rng) => { |
| 1075 | + switch_rng = Some(EntropySource::Jitter(jitter_rng)); |
| 1076 | + } |
| 1077 | + Err(_jitter_error) => { |
| 1078 | + warn!("EntropyRng: JitterRng failed: {}", |
| 1079 | + _jitter_error); |
| 1080 | + return Err(os_rng_error); |
| 1081 | + } |
| 1082 | + } |
| 1083 | + } |
| 1084 | + } |
| 1085 | + } |
| 1086 | + EntropySource::Os(ref mut rng) => { |
| 1087 | + let os_rng_result = rng.try_fill_bytes(dest); |
| 1088 | + if let Err(os_rng_error) = os_rng_result { |
| 1089 | + warn!("EntropyRng: OsRng failed [falling back to JitterRng]: {}", |
| 1090 | + os_rng_error); |
| 1091 | + match try_jitter_new(dest) { |
| 1092 | + Ok(jitter_rng) => { |
| 1093 | + switch_rng = Some(EntropySource::Jitter(jitter_rng)); |
| 1094 | + } |
| 1095 | + Err(_jitter_error) => { |
| 1096 | + warn!("EntropyRng: JitterRng failed: {}", |
| 1097 | + _jitter_error); |
| 1098 | + return Err(os_rng_error); |
| 1099 | + } |
| 1100 | + } |
| 1101 | + } |
| 1102 | + } |
| 1103 | + EntropySource::Jitter(ref mut rng) => { |
| 1104 | + if let Ok(os_rng) = try_os_new(dest) { |
| 1105 | + info!("EntropyRng: OsRng available [switching back from JitterRng]"); |
| 1106 | + switch_rng = Some(EntropySource::Os(os_rng)); |
| 1107 | + } else { |
| 1108 | + return rng.try_fill_bytes(dest); // use JitterRng |
| 1109 | + } |
| 1110 | + } |
| 1111 | + } |
| 1112 | + if let Some(rng) = switch_rng { |
| 1113 | + self.rng = rng; |
| 1114 | + } |
| 1115 | + Ok(()) |
| 1116 | + } |
| 1117 | +} |
| 1118 | + |
1020 | 1119 | /// Generates a random value using the thread-local random number generator.
|
1021 | 1120 | ///
|
1022 | 1121 | /// `random()` can generate various types of random things, and so may require
|
|
0 commit comments