1 // Copyright 2018 Developers of the Rand project. 2 // Copyright 2013 The Rust Project Developers. 3 // 4 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or 5 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license 6 // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your 7 // option. This file may not be copied, modified, or distributed 8 // except according to those terms. 9 10 //! A wrapper around another PRNG that reseeds it after it 11 //! generates a certain number of random bytes. 12 13 use core::mem::size_of; 14 15 use rand_core::block::{BlockRng, BlockRngCore}; 16 use rand_core::{CryptoRng, Error, RngCore, SeedableRng}; 17 18 /// A wrapper around any PRNG that implements [`BlockRngCore`], that adds the 19 /// ability to reseed it. 20 /// 21 /// `ReseedingRng` reseeds the underlying PRNG in the following cases: 22 /// 23 /// - On a manual call to [`reseed()`]. 24 /// - After `clone()`, the clone will be reseeded on first use. 25 /// - When a process is forked on UNIX, the RNGs in both the parent and child 26 /// processes will be reseeded just before the next call to 27 /// [`BlockRngCore::generate`], i.e. "soon". For ChaCha and Hc128 this is a 28 /// maximum of fifteen `u32` values before reseeding. 29 /// - After the PRNG has generated a configurable number of random bytes. 30 /// 31 /// # When should reseeding after a fixed number of generated bytes be used? 32 /// 33 /// Reseeding after a fixed number of generated bytes is never strictly 34 /// *necessary*. Cryptographic PRNGs don't have a limited number of bytes they 35 /// can output, or at least not a limit reachable in any practical way. There is 36 /// no such thing as 'running out of entropy'. 37 /// 38 /// Occasionally reseeding can be seen as some form of 'security in depth'. Even 39 /// if in the future a cryptographic weakness is found in the CSPRNG being used, 40 /// or a flaw in the implementation, occasionally reseeding should make 41 /// exploiting it much more difficult or even impossible. 42 /// 43 /// Use [`ReseedingRng::new`] with a `threshold` of `0` to disable reseeding 44 /// after a fixed number of generated bytes. 45 /// 46 /// # Limitations 47 /// 48 /// It is recommended that a `ReseedingRng` (including `ThreadRng`) not be used 49 /// from a fork handler. 50 /// Use `OsRng` or `getrandom`, or defer your use of the RNG until later. 51 /// 52 /// # Error handling 53 /// 54 /// Although unlikely, reseeding the wrapped PRNG can fail. `ReseedingRng` will 55 /// never panic but try to handle the error intelligently through some 56 /// combination of retrying and delaying reseeding until later. 57 /// If handling the source error fails `ReseedingRng` will continue generating 58 /// data from the wrapped PRNG without reseeding. 59 /// 60 /// Manually calling [`reseed()`] will not have this retry or delay logic, but 61 /// reports the error. 62 /// 63 /// # Example 64 /// 65 /// ``` 66 /// use rand::prelude::*; 67 /// use rand_chacha::ChaCha20Core; // Internal part of ChaChaRng that 68 /// // implements BlockRngCore 69 /// use rand::rngs::OsRng; 70 /// use rand::rngs::adapter::ReseedingRng; 71 /// 72 /// let prng = ChaCha20Core::from_entropy(); 73 /// let mut reseeding_rng = ReseedingRng::new(prng, 0, OsRng); 74 /// 75 /// println!("{}", reseeding_rng.gen::<u64>()); 76 /// 77 /// let mut cloned_rng = reseeding_rng.clone(); 78 /// assert!(reseeding_rng.gen::<u64>() != cloned_rng.gen::<u64>()); 79 /// ``` 80 /// 81 /// [`BlockRngCore`]: rand_core::block::BlockRngCore 82 /// [`ReseedingRng::new`]: ReseedingRng::new 83 /// [`reseed()`]: ReseedingRng::reseed 84 #[derive(Debug)] 85 pub struct ReseedingRng<R, Rsdr>(BlockRng<ReseedingCore<R, Rsdr>>) 86 where 87 R: BlockRngCore + SeedableRng, 88 Rsdr: RngCore; 89 90 impl<R, Rsdr> ReseedingRng<R, Rsdr> 91 where 92 R: BlockRngCore + SeedableRng, 93 Rsdr: RngCore, 94 { 95 /// Create a new `ReseedingRng` from an existing PRNG, combined with a RNG 96 /// to use as reseeder. 97 /// 98 /// `threshold` sets the number of generated bytes after which to reseed the 99 /// PRNG. Set it to zero to never reseed based on the number of generated 100 /// values. new(rng: R, threshold: u64, reseeder: Rsdr) -> Self101 pub fn new(rng: R, threshold: u64, reseeder: Rsdr) -> Self { 102 ReseedingRng(BlockRng::new(ReseedingCore::new(rng, threshold, reseeder))) 103 } 104 105 /// Reseed the internal PRNG. reseed(&mut self) -> Result<(), Error>106 pub fn reseed(&mut self) -> Result<(), Error> { 107 self.0.core.reseed() 108 } 109 } 110 111 // TODO: this should be implemented for any type where the inner type 112 // implements RngCore, but we can't specify that because ReseedingCore is private 113 impl<R, Rsdr: RngCore> RngCore for ReseedingRng<R, Rsdr> 114 where 115 R: BlockRngCore<Item = u32> + SeedableRng, 116 <R as BlockRngCore>::Results: AsRef<[u32]> + AsMut<[u32]>, 117 { 118 #[inline(always)] next_u32(&mut self) -> u32119 fn next_u32(&mut self) -> u32 { 120 self.0.next_u32() 121 } 122 123 #[inline(always)] next_u64(&mut self) -> u64124 fn next_u64(&mut self) -> u64 { 125 self.0.next_u64() 126 } 127 fill_bytes(&mut self, dest: &mut [u8])128 fn fill_bytes(&mut self, dest: &mut [u8]) { 129 self.0.fill_bytes(dest) 130 } 131 try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error>132 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { 133 self.0.try_fill_bytes(dest) 134 } 135 } 136 137 impl<R, Rsdr> Clone for ReseedingRng<R, Rsdr> 138 where 139 R: BlockRngCore + SeedableRng + Clone, 140 Rsdr: RngCore + Clone, 141 { clone(&self) -> ReseedingRng<R, Rsdr>142 fn clone(&self) -> ReseedingRng<R, Rsdr> { 143 // Recreating `BlockRng` seems easier than cloning it and resetting 144 // the index. 145 ReseedingRng(BlockRng::new(self.0.core.clone())) 146 } 147 } 148 149 impl<R, Rsdr> CryptoRng for ReseedingRng<R, Rsdr> 150 where 151 R: BlockRngCore + SeedableRng + CryptoRng, 152 Rsdr: RngCore + CryptoRng, 153 { 154 } 155 156 #[derive(Debug)] 157 struct ReseedingCore<R, Rsdr> { 158 inner: R, 159 reseeder: Rsdr, 160 threshold: i64, 161 bytes_until_reseed: i64, 162 fork_counter: usize, 163 } 164 165 impl<R, Rsdr> BlockRngCore for ReseedingCore<R, Rsdr> 166 where 167 R: BlockRngCore + SeedableRng, 168 Rsdr: RngCore, 169 { 170 type Item = <R as BlockRngCore>::Item; 171 type Results = <R as BlockRngCore>::Results; 172 generate(&mut self, results: &mut Self::Results)173 fn generate(&mut self, results: &mut Self::Results) { 174 let global_fork_counter = fork::get_fork_counter(); 175 if self.bytes_until_reseed <= 0 || self.is_forked(global_fork_counter) { 176 // We get better performance by not calling only `reseed` here 177 // and continuing with the rest of the function, but by directly 178 // returning from a non-inlined function. 179 return self.reseed_and_generate(results, global_fork_counter); 180 } 181 let num_bytes = results.as_ref().len() * size_of::<Self::Item>(); 182 self.bytes_until_reseed -= num_bytes as i64; 183 self.inner.generate(results); 184 } 185 } 186 187 impl<R, Rsdr> ReseedingCore<R, Rsdr> 188 where 189 R: BlockRngCore + SeedableRng, 190 Rsdr: RngCore, 191 { 192 /// Create a new `ReseedingCore`. new(rng: R, threshold: u64, reseeder: Rsdr) -> Self193 fn new(rng: R, threshold: u64, reseeder: Rsdr) -> Self { 194 use ::core::i64::MAX; 195 fork::register_fork_handler(); 196 197 // Because generating more values than `i64::MAX` takes centuries on 198 // current hardware, we just clamp to that value. 199 // Also we set a threshold of 0, which indicates no limit, to that 200 // value. 201 let threshold = if threshold == 0 { 202 MAX 203 } else if threshold <= MAX as u64 { 204 threshold as i64 205 } else { 206 MAX 207 }; 208 209 ReseedingCore { 210 inner: rng, 211 reseeder, 212 threshold: threshold as i64, 213 bytes_until_reseed: threshold as i64, 214 fork_counter: 0, 215 } 216 } 217 218 /// Reseed the internal PRNG. reseed(&mut self) -> Result<(), Error>219 fn reseed(&mut self) -> Result<(), Error> { 220 R::from_rng(&mut self.reseeder).map(|result| { 221 self.bytes_until_reseed = self.threshold; 222 self.inner = result 223 }) 224 } 225 is_forked(&self, global_fork_counter: usize) -> bool226 fn is_forked(&self, global_fork_counter: usize) -> bool { 227 // In theory, on 32-bit platforms, it is possible for 228 // `global_fork_counter` to wrap around after ~4e9 forks. 229 // 230 // This check will detect a fork in the normal case where 231 // `fork_counter < global_fork_counter`, and also when the difference 232 // between both is greater than `isize::MAX` (wrapped around). 233 // 234 // It will still fail to detect a fork if there have been more than 235 // `isize::MAX` forks, without any reseed in between. Seems unlikely 236 // enough. 237 (self.fork_counter.wrapping_sub(global_fork_counter) as isize) < 0 238 } 239 240 #[inline(never)] reseed_and_generate( &mut self, results: &mut <Self as BlockRngCore>::Results, global_fork_counter: usize, )241 fn reseed_and_generate( 242 &mut self, results: &mut <Self as BlockRngCore>::Results, global_fork_counter: usize, 243 ) { 244 #![allow(clippy::if_same_then_else)] // false positive 245 if self.is_forked(global_fork_counter) { 246 info!("Fork detected, reseeding RNG"); 247 } else { 248 trace!("Reseeding RNG (periodic reseed)"); 249 } 250 251 let num_bytes = results.as_ref().len() * size_of::<<R as BlockRngCore>::Item>(); 252 253 if let Err(e) = self.reseed() { 254 warn!("Reseeding RNG failed: {}", e); 255 let _ = e; 256 } 257 self.fork_counter = global_fork_counter; 258 259 self.bytes_until_reseed = self.threshold - num_bytes as i64; 260 self.inner.generate(results); 261 } 262 } 263 264 impl<R, Rsdr> Clone for ReseedingCore<R, Rsdr> 265 where 266 R: BlockRngCore + SeedableRng + Clone, 267 Rsdr: RngCore + Clone, 268 { clone(&self) -> ReseedingCore<R, Rsdr>269 fn clone(&self) -> ReseedingCore<R, Rsdr> { 270 ReseedingCore { 271 inner: self.inner.clone(), 272 reseeder: self.reseeder.clone(), 273 threshold: self.threshold, 274 bytes_until_reseed: 0, // reseed clone on first use 275 fork_counter: self.fork_counter, 276 } 277 } 278 } 279 280 impl<R, Rsdr> CryptoRng for ReseedingCore<R, Rsdr> 281 where 282 R: BlockRngCore + SeedableRng + CryptoRng, 283 Rsdr: RngCore + CryptoRng, 284 { 285 } 286 287 288 #[cfg(all(unix, not(target_os = "emscripten")))] 289 mod fork { 290 use core::sync::atomic::{AtomicUsize, Ordering}; 291 use std::sync::Once; 292 293 // Fork protection 294 // 295 // We implement fork protection on Unix using `pthread_atfork`. 296 // When the process is forked, we increment `RESEEDING_RNG_FORK_COUNTER`. 297 // Every `ReseedingRng` stores the last known value of the static in 298 // `fork_counter`. If the cached `fork_counter` is less than 299 // `RESEEDING_RNG_FORK_COUNTER`, it is time to reseed this RNG. 300 // 301 // If reseeding fails, we don't deal with this by setting a delay, but just 302 // don't update `fork_counter`, so a reseed is attempted as soon as 303 // possible. 304 305 static RESEEDING_RNG_FORK_COUNTER: AtomicUsize = AtomicUsize::new(0); 306 get_fork_counter() -> usize307 pub fn get_fork_counter() -> usize { 308 RESEEDING_RNG_FORK_COUNTER.load(Ordering::Relaxed) 309 } 310 fork_handler()311 extern "C" fn fork_handler() { 312 // Note: fetch_add is defined to wrap on overflow 313 // (which is what we want). 314 RESEEDING_RNG_FORK_COUNTER.fetch_add(1, Ordering::Relaxed); 315 } 316 register_fork_handler()317 pub fn register_fork_handler() { 318 static REGISTER: Once = Once::new(); 319 REGISTER.call_once(|| { 320 // Bump the counter before and after forking (see #1169): 321 let ret = unsafe { libc::pthread_atfork( 322 Some(fork_handler), 323 Some(fork_handler), 324 Some(fork_handler), 325 ) }; 326 if ret != 0 { 327 panic!("libc::pthread_atfork failed with code {}", ret); 328 } 329 }); 330 } 331 } 332 333 #[cfg(not(all(unix, not(target_os = "emscripten"))))] 334 mod fork { get_fork_counter() -> usize335 pub fn get_fork_counter() -> usize { 336 0 337 } register_fork_handler()338 pub fn register_fork_handler() {} 339 } 340 341 342 #[cfg(feature = "std_rng")] 343 #[cfg(test)] 344 mod test { 345 use super::ReseedingRng; 346 use crate::rngs::mock::StepRng; 347 use crate::rngs::std::Core; 348 use crate::{Rng, SeedableRng}; 349 350 #[test] test_reseeding()351 fn test_reseeding() { 352 let mut zero = StepRng::new(0, 0); 353 let rng = Core::from_rng(&mut zero).unwrap(); 354 let thresh = 1; // reseed every time the buffer is exhausted 355 let mut reseeding = ReseedingRng::new(rng, thresh, zero); 356 357 // RNG buffer size is [u32; 64] 358 // Debug is only implemented up to length 32 so use two arrays 359 let mut buf = ([0u32; 32], [0u32; 32]); 360 reseeding.fill(&mut buf.0); 361 reseeding.fill(&mut buf.1); 362 let seq = buf; 363 for _ in 0..10 { 364 reseeding.fill(&mut buf.0); 365 reseeding.fill(&mut buf.1); 366 assert_eq!(buf, seq); 367 } 368 } 369 370 #[test] test_clone_reseeding()371 fn test_clone_reseeding() { 372 #![allow(clippy::redundant_clone)] 373 374 let mut zero = StepRng::new(0, 0); 375 let rng = Core::from_rng(&mut zero).unwrap(); 376 let mut rng1 = ReseedingRng::new(rng, 32 * 4, zero); 377 378 let first: u32 = rng1.gen(); 379 for _ in 0..10 { 380 let _ = rng1.gen::<u32>(); 381 } 382 383 let mut rng2 = rng1.clone(); 384 assert_eq!(first, rng2.gen::<u32>()); 385 } 386 } 387