| 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_val; |
| 14 | |
| 15 | use rand_core::block::{BlockRng, BlockRngCore, CryptoBlockRng}; |
| 16 | use rand_core::{CryptoRng, RngCore, SeedableRng, TryCryptoRng, TryRngCore}; |
| 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 | /// - After the PRNG has generated a configurable number of random bytes. |
| 26 | /// |
| 27 | /// # When should reseeding after a fixed number of generated bytes be used? |
| 28 | /// |
| 29 | /// Reseeding after a fixed number of generated bytes is never strictly |
| 30 | /// *necessary*. Cryptographic PRNGs don't have a limited number of bytes they |
| 31 | /// can output, or at least not a limit reachable in any practical way. There is |
| 32 | /// no such thing as 'running out of entropy'. |
| 33 | /// |
| 34 | /// Occasionally reseeding can be seen as some form of 'security in depth'. Even |
| 35 | /// if in the future a cryptographic weakness is found in the CSPRNG being used, |
| 36 | /// or a flaw in the implementation, occasionally reseeding should make |
| 37 | /// exploiting it much more difficult or even impossible. |
| 38 | /// |
| 39 | /// Use [`ReseedingRng::new`] with a `threshold` of `0` to disable reseeding |
| 40 | /// after a fixed number of generated bytes. |
| 41 | /// |
| 42 | /// # Error handling |
| 43 | /// |
| 44 | /// Although unlikely, reseeding the wrapped PRNG can fail. `ReseedingRng` will |
| 45 | /// never panic but try to handle the error intelligently through some |
| 46 | /// combination of retrying and delaying reseeding until later. |
| 47 | /// If handling the source error fails `ReseedingRng` will continue generating |
| 48 | /// data from the wrapped PRNG without reseeding. |
| 49 | /// |
| 50 | /// Manually calling [`reseed()`] will not have this retry or delay logic, but |
| 51 | /// reports the error. |
| 52 | /// |
| 53 | /// # Example |
| 54 | /// |
| 55 | /// ``` |
| 56 | /// use rand::prelude::*; |
| 57 | /// use rand_chacha::ChaCha20Core; // Internal part of ChaChaRng that |
| 58 | /// // implements BlockRngCore |
| 59 | /// use rand::rngs::OsRng; |
| 60 | /// use rand::rngs::ReseedingRng; |
| 61 | /// |
| 62 | /// let mut reseeding_rng = ReseedingRng::<ChaCha20Core, _>::new(0, OsRng).unwrap(); |
| 63 | /// |
| 64 | /// println!("{}" , reseeding_rng.random::<u64>()); |
| 65 | /// |
| 66 | /// let mut cloned_rng = reseeding_rng.clone(); |
| 67 | /// assert!(reseeding_rng.random::<u64>() != cloned_rng.random::<u64>()); |
| 68 | /// ``` |
| 69 | /// |
| 70 | /// [`BlockRngCore`]: rand_core::block::BlockRngCore |
| 71 | /// [`ReseedingRng::new`]: ReseedingRng::new |
| 72 | /// [`reseed()`]: ReseedingRng::reseed |
| 73 | #[derive (Debug)] |
| 74 | pub struct ReseedingRng<R, Rsdr>(BlockRng<ReseedingCore<R, Rsdr>>) |
| 75 | where |
| 76 | R: BlockRngCore + SeedableRng, |
| 77 | Rsdr: TryRngCore; |
| 78 | |
| 79 | impl<R, Rsdr> ReseedingRng<R, Rsdr> |
| 80 | where |
| 81 | R: BlockRngCore + SeedableRng, |
| 82 | Rsdr: TryRngCore, |
| 83 | { |
| 84 | /// Create a new `ReseedingRng` from an existing PRNG, combined with a RNG |
| 85 | /// to use as reseeder. |
| 86 | /// |
| 87 | /// `threshold` sets the number of generated bytes after which to reseed the |
| 88 | /// PRNG. Set it to zero to never reseed based on the number of generated |
| 89 | /// values. |
| 90 | pub fn new(threshold: u64, reseeder: Rsdr) -> Result<Self, Rsdr::Error> { |
| 91 | Ok(ReseedingRng(BlockRng::new(core:ReseedingCore::new( |
| 92 | threshold, reseeder, |
| 93 | )?))) |
| 94 | } |
| 95 | |
| 96 | /// Immediately reseed the generator |
| 97 | /// |
| 98 | /// This discards any remaining random data in the cache. |
| 99 | pub fn reseed(&mut self) -> Result<(), Rsdr::Error> { |
| 100 | self.0.reset(); |
| 101 | self.0.core.reseed() |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | // TODO: this should be implemented for any type where the inner type |
| 106 | // implements RngCore, but we can't specify that because ReseedingCore is private |
| 107 | impl<R, Rsdr> RngCore for ReseedingRng<R, Rsdr> |
| 108 | where |
| 109 | R: BlockRngCore<Item = u32> + SeedableRng, |
| 110 | Rsdr: TryRngCore, |
| 111 | { |
| 112 | #[inline (always)] |
| 113 | fn next_u32(&mut self) -> u32 { |
| 114 | self.0.next_u32() |
| 115 | } |
| 116 | |
| 117 | #[inline (always)] |
| 118 | fn next_u64(&mut self) -> u64 { |
| 119 | self.0.next_u64() |
| 120 | } |
| 121 | |
| 122 | fn fill_bytes(&mut self, dest: &mut [u8]) { |
| 123 | self.0.fill_bytes(dst:dest) |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | impl<R, Rsdr> Clone for ReseedingRng<R, Rsdr> |
| 128 | where |
| 129 | R: BlockRngCore + SeedableRng + Clone, |
| 130 | Rsdr: TryRngCore + Clone, |
| 131 | { |
| 132 | fn clone(&self) -> ReseedingRng<R, Rsdr> { |
| 133 | // Recreating `BlockRng` seems easier than cloning it and resetting |
| 134 | // the index. |
| 135 | ReseedingRng(BlockRng::new(self.0.core.clone())) |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | impl<R, Rsdr> CryptoRng for ReseedingRng<R, Rsdr> |
| 140 | where |
| 141 | R: BlockRngCore<Item = u32> + SeedableRng + CryptoBlockRng, |
| 142 | Rsdr: TryCryptoRng, |
| 143 | { |
| 144 | } |
| 145 | |
| 146 | #[derive (Debug)] |
| 147 | struct ReseedingCore<R, Rsdr> { |
| 148 | inner: R, |
| 149 | reseeder: Rsdr, |
| 150 | threshold: i64, |
| 151 | bytes_until_reseed: i64, |
| 152 | } |
| 153 | |
| 154 | impl<R, Rsdr> BlockRngCore for ReseedingCore<R, Rsdr> |
| 155 | where |
| 156 | R: BlockRngCore + SeedableRng, |
| 157 | Rsdr: TryRngCore, |
| 158 | { |
| 159 | type Item = <R as BlockRngCore>::Item; |
| 160 | type Results = <R as BlockRngCore>::Results; |
| 161 | |
| 162 | fn generate(&mut self, results: &mut Self::Results) { |
| 163 | if self.bytes_until_reseed <= 0 { |
| 164 | // We get better performance by not calling only `reseed` here |
| 165 | // and continuing with the rest of the function, but by directly |
| 166 | // returning from a non-inlined function. |
| 167 | return self.reseed_and_generate(results); |
| 168 | } |
| 169 | let num_bytes: usize = size_of_val(results.as_ref()); |
| 170 | self.bytes_until_reseed -= num_bytes as i64; |
| 171 | self.inner.generate(results); |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | impl<R, Rsdr> ReseedingCore<R, Rsdr> |
| 176 | where |
| 177 | R: BlockRngCore + SeedableRng, |
| 178 | Rsdr: TryRngCore, |
| 179 | { |
| 180 | /// Create a new `ReseedingCore`. |
| 181 | /// |
| 182 | /// `threshold` is the maximum number of bytes produced by |
| 183 | /// [`BlockRngCore::generate`] before attempting reseeding. |
| 184 | fn new(threshold: u64, mut reseeder: Rsdr) -> Result<Self, Rsdr::Error> { |
| 185 | // Because generating more values than `i64::MAX` takes centuries on |
| 186 | // current hardware, we just clamp to that value. |
| 187 | // Also we set a threshold of 0, which indicates no limit, to that |
| 188 | // value. |
| 189 | let threshold = if threshold == 0 { |
| 190 | i64::MAX |
| 191 | } else if threshold <= i64::MAX as u64 { |
| 192 | threshold as i64 |
| 193 | } else { |
| 194 | i64::MAX |
| 195 | }; |
| 196 | |
| 197 | let inner = R::try_from_rng(&mut reseeder)?; |
| 198 | |
| 199 | Ok(ReseedingCore { |
| 200 | inner, |
| 201 | reseeder, |
| 202 | threshold, |
| 203 | bytes_until_reseed: threshold, |
| 204 | }) |
| 205 | } |
| 206 | |
| 207 | /// Reseed the internal PRNG. |
| 208 | fn reseed(&mut self) -> Result<(), Rsdr::Error> { |
| 209 | R::try_from_rng(&mut self.reseeder).map(|result| { |
| 210 | self.bytes_until_reseed = self.threshold; |
| 211 | self.inner = result |
| 212 | }) |
| 213 | } |
| 214 | |
| 215 | #[inline (never)] |
| 216 | fn reseed_and_generate(&mut self, results: &mut <Self as BlockRngCore>::Results) { |
| 217 | trace!("Reseeding RNG (periodic reseed)" ); |
| 218 | |
| 219 | let num_bytes = size_of_val(results.as_ref()); |
| 220 | |
| 221 | if let Err(e) = self.reseed() { |
| 222 | warn!("Reseeding RNG failed: {}" , e); |
| 223 | let _ = e; |
| 224 | } |
| 225 | |
| 226 | self.bytes_until_reseed = self.threshold - num_bytes as i64; |
| 227 | self.inner.generate(results); |
| 228 | } |
| 229 | } |
| 230 | |
| 231 | impl<R, Rsdr> Clone for ReseedingCore<R, Rsdr> |
| 232 | where |
| 233 | R: BlockRngCore + SeedableRng + Clone, |
| 234 | Rsdr: TryRngCore + Clone, |
| 235 | { |
| 236 | fn clone(&self) -> ReseedingCore<R, Rsdr> { |
| 237 | ReseedingCore { |
| 238 | inner: self.inner.clone(), |
| 239 | reseeder: self.reseeder.clone(), |
| 240 | threshold: self.threshold, |
| 241 | bytes_until_reseed: 0, // reseed clone on first use |
| 242 | } |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | impl<R, Rsdr> CryptoBlockRng for ReseedingCore<R, Rsdr> |
| 247 | where |
| 248 | R: BlockRngCore<Item = u32> + SeedableRng + CryptoBlockRng, |
| 249 | Rsdr: TryCryptoRng, |
| 250 | { |
| 251 | } |
| 252 | |
| 253 | #[cfg (feature = "std_rng" )] |
| 254 | #[cfg (test)] |
| 255 | mod test { |
| 256 | use crate::rngs::mock::StepRng; |
| 257 | use crate::rngs::std::Core; |
| 258 | use crate::Rng; |
| 259 | |
| 260 | use super::ReseedingRng; |
| 261 | |
| 262 | #[test ] |
| 263 | fn test_reseeding() { |
| 264 | let zero = StepRng::new(0, 0); |
| 265 | let thresh = 1; // reseed every time the buffer is exhausted |
| 266 | let mut reseeding = ReseedingRng::<Core, _>::new(thresh, zero).unwrap(); |
| 267 | |
| 268 | // RNG buffer size is [u32; 64] |
| 269 | // Debug is only implemented up to length 32 so use two arrays |
| 270 | let mut buf = ([0u32; 32], [0u32; 32]); |
| 271 | reseeding.fill(&mut buf.0); |
| 272 | reseeding.fill(&mut buf.1); |
| 273 | let seq = buf; |
| 274 | for _ in 0..10 { |
| 275 | reseeding.fill(&mut buf.0); |
| 276 | reseeding.fill(&mut buf.1); |
| 277 | assert_eq!(buf, seq); |
| 278 | } |
| 279 | } |
| 280 | |
| 281 | #[test ] |
| 282 | #[allow (clippy::redundant_clone)] |
| 283 | fn test_clone_reseeding() { |
| 284 | let zero = StepRng::new(0, 0); |
| 285 | let mut rng1 = ReseedingRng::<Core, _>::new(32 * 4, zero).unwrap(); |
| 286 | |
| 287 | let first: u32 = rng1.random(); |
| 288 | for _ in 0..10 { |
| 289 | let _ = rng1.random::<u32>(); |
| 290 | } |
| 291 | |
| 292 | let mut rng2 = rng1.clone(); |
| 293 | assert_eq!(first, rng2.random::<u32>()); |
| 294 | } |
| 295 | } |
| 296 | |