|
1 | 1 | use rand; |
2 | | -use rand::{CryptoRng, RngCore}; |
| 2 | +use rand::CryptoRng; |
3 | 3 |
|
4 | 4 | use crate::internal::take_lock; |
5 | 5 | use rand::SeedableRng; |
| 6 | +use rand::rngs::SysRng; |
| 7 | +use rand::{TryCryptoRng, TryRng}; |
| 8 | +use std::convert::Infallible; |
6 | 9 | use std::default::Default; |
7 | 10 | use std::ops::DerefMut; |
8 | 11 | use std::sync::Mutex; |
9 | 12 |
|
| 13 | +/// Reseed threshold in bytes — matches ThreadRng's 64 KiB interval. |
| 14 | +const RESEED_THRESHOLD: usize = 64 * 1024; |
| 15 | + |
10 | 16 | /// Generation of random bytes for cryptographic operations |
11 | 17 | pub trait RandomBytesGen { |
12 | 18 | fn random_bytes_32(&self) -> [u8; 32]; |
13 | 19 | fn random_bytes_60(&self) -> [u8; 60]; |
14 | 20 | } |
15 | 21 |
|
16 | | -pub struct RandomBytes<T: CryptoRng + RngCore> { |
| 22 | +/// A CSPRNG wrapper that automatically reseeds from system entropy periodically. |
| 23 | +/// Implements [`CryptoRng`] so it can be used anywhere a `CryptoRng` is expected. |
| 24 | +pub struct ReseedingRng<T: CryptoRng + SeedableRng> { |
| 25 | + inner: T, |
| 26 | + bytes_generated: usize, |
| 27 | +} |
| 28 | + |
| 29 | +impl<T: CryptoRng + SeedableRng> Default for ReseedingRng<T> { |
| 30 | + fn default() -> Self { |
| 31 | + ReseedingRng::new( |
| 32 | + T::try_from_rng(&mut SysRng).expect("Failed to seed RNG from system entropy"), |
| 33 | + ) |
| 34 | + } |
| 35 | +} |
| 36 | + |
| 37 | +impl<T: CryptoRng + SeedableRng> ReseedingRng<T> { |
| 38 | + pub fn new(rng: T) -> Self { |
| 39 | + ReseedingRng { |
| 40 | + inner: rng, |
| 41 | + bytes_generated: 0, |
| 42 | + } |
| 43 | + } |
| 44 | + |
| 45 | + fn reseed_if_needed(&mut self) { |
| 46 | + if self.bytes_generated >= RESEED_THRESHOLD { |
| 47 | + if let Ok(reseeded) = T::try_from_rng(&mut SysRng) { |
| 48 | + self.inner = reseeded; |
| 49 | + } |
| 50 | + // On reseed failure, continue with existing state rather than panicking. |
| 51 | + // The current state is still cryptographically valid and the likelihood of |
| 52 | + // SysRng erroring is very low. |
| 53 | + self.bytes_generated = 0; |
| 54 | + } |
| 55 | + } |
| 56 | +} |
| 57 | + |
| 58 | +impl<T: CryptoRng + SeedableRng> TryRng for ReseedingRng<T> { |
| 59 | + type Error = Infallible; |
| 60 | + |
| 61 | + fn try_next_u32(&mut self) -> Result<u32, Self::Error> { |
| 62 | + self.reseed_if_needed(); |
| 63 | + let val = self.inner.next_u32(); |
| 64 | + self.bytes_generated = self.bytes_generated.saturating_add(4); |
| 65 | + Ok(val) |
| 66 | + } |
| 67 | + |
| 68 | + fn try_next_u64(&mut self) -> Result<u64, Self::Error> { |
| 69 | + self.reseed_if_needed(); |
| 70 | + let val = self.inner.next_u64(); |
| 71 | + self.bytes_generated = self.bytes_generated.saturating_add(8); |
| 72 | + Ok(val) |
| 73 | + } |
| 74 | + |
| 75 | + fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> { |
| 76 | + self.reseed_if_needed(); |
| 77 | + self.inner.fill_bytes(dst); |
| 78 | + self.bytes_generated = self.bytes_generated.saturating_add(dst.len()); |
| 79 | + Ok(()) |
| 80 | + } |
| 81 | +} |
| 82 | + |
| 83 | +impl<T: CryptoRng + SeedableRng> TryCryptoRng for ReseedingRng<T> {} |
| 84 | + |
| 85 | +pub struct RandomBytes<T: CryptoRng> { |
17 | 86 | pub(crate) rng: Mutex<T>, |
18 | 87 | } |
19 | 88 |
|
20 | | -impl Default for RandomBytes<rand_chacha::ChaChaRng> { |
| 89 | +impl Default for RandomBytes<ReseedingRng<rand_chacha::ChaChaRng>> { |
21 | 90 | fn default() -> Self { |
22 | | - RandomBytes::new(rand_chacha::ChaChaRng::from_os_rng()) |
| 91 | + RandomBytes::new(ReseedingRng::default()) |
23 | 92 | } |
24 | 93 | } |
25 | 94 |
|
26 | | -impl<CR: CryptoRng + RngCore> RandomBytes<CR> { |
| 95 | +impl<CR: CryptoRng> RandomBytes<CR> { |
27 | 96 | pub fn new(rng: CR) -> Self { |
28 | 97 | RandomBytes { |
29 | 98 | rng: Mutex::new(rng), |
30 | 99 | } |
31 | 100 | } |
32 | 101 | } |
33 | 102 |
|
34 | | -impl<CR: CryptoRng + RngCore> RandomBytesGen for RandomBytes<CR> { |
| 103 | +impl<CR: CryptoRng> RandomBytesGen for RandomBytes<CR> { |
35 | 104 | fn random_bytes_32(&self) -> [u8; 32] { |
36 | 105 | let mut bytes: [u8; 32] = [0u8; 32]; |
37 | 106 | take_lock(&self.rng).deref_mut().fill_bytes(&mut bytes); |
|
0 commit comments