diff --git a/Cargo.lock b/Cargo.lock index c49e7b75..f007c4d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,8 +117,7 @@ dependencies = [ [[package]] name = "rand_core" version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" +source = "git+https://github.com/rust-random/rand_core.git?rev=45fb1f9609a874e146a118bb9c697e96293d6cad#45fb1f9609a874e146a118bb9c697e96293d6cad" [[package]] name = "rc4" diff --git a/Cargo.toml b/Cargo.toml index 26910c8c..6796ac5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,7 @@ members = [ [profile.dev] opt-level = 2 + +[patch.crates-io.rand_core] +git = "https://github.com/rust-random/rand_core.git" +rev = "45fb1f9609a874e146a118bb9c697e96293d6cad" diff --git a/chacha20/src/lib.rs b/chacha20/src/lib.rs index dbf726cb..cc5bf5be 100644 --- a/chacha20/src/lib.rs +++ b/chacha20/src/lib.rs @@ -29,7 +29,10 @@ pub use legacy::{ChaCha20Legacy, ChaCha20LegacyCore, LegacyNonce}; #[cfg(feature = "rng")] pub use rand_core; #[cfg(feature = "rng")] -pub use rng::{ChaCha8Rng, ChaCha12Rng, ChaCha20Rng, Seed, SerializedRngState}; +pub use rng::{ + ChaCha8Rng, ChaCha12Rng, ChaCha20Rng, FastErasureChaCha8Rng, FastErasureChaCha12Rng, + FastErasureChaCha20Rng, FastErasureCore, Seed, SerializedRngState, +}; #[cfg(feature = "xchacha")] pub use xchacha::{XChaCha8, XChaCha12, XChaCha20, XNonce, hchacha}; diff --git a/chacha20/src/rng.rs b/chacha20/src/rng.rs index d8c28240..33f0bca3 100644 --- a/chacha20/src/rng.rs +++ b/chacha20/src/rng.rs @@ -42,11 +42,12 @@ impl SeedableRng for ChaChaCore { } impl Generator for ChaChaCore { + type Word = u32; type Output = [u32; BUFFER_SIZE]; /// Generates 4 blocks in parallel with avx2 & neon, but merely fills /// 4 blocks with sse2 & soft - fn generate(&mut self, buffer: &mut [u32; BUFFER_SIZE]) { + fn generate(&mut self, buffer: &mut [u32; BUFFER_SIZE]) -> usize { cfg_if! { if #[cfg(chacha20_backend = "soft")] { backends::soft::Backend(self).gen_ks_blocks(buffer); @@ -89,6 +90,8 @@ impl Generator for ChaChaCore { backends::soft::Backend(self).gen_ks_blocks(buffer); } } + + 0 } // `Drop` impl of `BlockRng` calls this method and passes reference to @@ -342,3 +345,128 @@ macro_rules! impl_chacha_rng { impl_chacha_rng!(ChaCha8Rng, R8); impl_chacha_rng!(ChaCha12Rng, R12); impl_chacha_rng!(ChaCha20Rng, R20); + +/// ChaCha core with fast erasure +#[derive(Debug)] +pub struct FastErasureCore(ChaChaCore); + +impl SeedableRng for FastErasureCore { + type Seed = Seed; + + #[inline] + fn from_seed(seed: Self::Seed) -> Self { + FastErasureCore(ChaChaCore::from_seed(seed)) + } +} + +impl FastErasureCore { + /// Get the current block position. + #[inline(always)] + #[must_use] + pub fn get_block_pos(&self) -> V::Counter { + self.0.get_block_pos() + } +} + +impl Generator for FastErasureCore { + type Word = u32; + type Output = [u32; BUFFER_SIZE]; + + // Generate a block, overwriting the seed + // + // The counter is incremented like usual (i.e. it is not reset). + fn generate(&mut self, buffer: &mut [u32; BUFFER_SIZE]) -> usize { + let _ = self.0.generate(buffer); + self.0.state[4..12].copy_from_slice(&buffer[0..8]); + 8 + } + + fn erase(slice: &mut [Self::Word]) { + // Zero consumed values. We don't need zeroize to observe the result + // since the buffer is not deallocated here. + for word in slice { + *word = 0; + } + } + + // drop() method of inner type is called +} + +macro_rules! impl_chacha_rng { + ($Rng:ident, $rounds:ident) => { + /// A cryptographically secure random number generator with fast key erasure using the ChaCha stream cipher. + /// + /// See the [crate docs][crate] for more information about the underlying stream cipher. + /// + /// This RNG implementation uses fast key erasure to provide backtracking resistance. Each + /// time a new buffer of results are generated, the first 32 bytes are used to overwrite the + /// key. Each time any value is consumed from the buffer, it is overwritten with zero. + /// + /// # Example + /// + /// ```rust + #[doc = concat!("use chacha20::", stringify!($Rng), ";")] + /// use rand_core::{SeedableRng, Rng}; + /// + /// let seed = [42u8; 32]; + #[doc = concat!("let mut rng = ", stringify!($Rng), "::from_seed(seed);")] + /// + /// let random_u32 = rng.next_u32(); + /// let random_u64 = rng.next_u64(); + /// + /// let mut random_bytes = [0u8; 3]; + /// rng.fill_bytes(&mut random_bytes); + /// ``` + /// + /// See the [`rand`](https://docs.rs/rand/) crate for more advanced RNG functionality. + pub struct $Rng { + core: BlockRng>, + } + + impl SeedableRng for $Rng { + type Seed = Seed; + + #[inline] + fn from_seed(seed: Self::Seed) -> Self { + let core = FastErasureCore(ChaChaCore::new_internal(&seed, &[0u8; 8])); + Self { + core: BlockRng::new(core), + } + } + } + + impl TryRng for $Rng { + type Error = Infallible; + + #[inline] + fn try_next_u32(&mut self) -> Result { + Ok(self.core.next_word()) + } + #[inline] + fn try_next_u64(&mut self) -> Result { + Ok(self.core.next_u64_from_u32()) + } + #[inline] + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> { + self.core.fill_bytes(dest); + Ok(()) + } + } + + impl TryCryptoRng for $Rng {} + + #[cfg(feature = "zeroize")] + impl ZeroizeOnDrop for $Rng {} + + // Custom Debug implementation that does not expose the internal state + impl fmt::Debug for $Rng { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, concat!(stringify!($Rng), " {{ ... }}")) + } + } + }; +} + +impl_chacha_rng!(FastErasureChaCha8Rng, R8); +impl_chacha_rng!(FastErasureChaCha12Rng, R12); +impl_chacha_rng!(FastErasureChaCha20Rng, R20);