From 23d15b60a26005ae95479ebadcc19f996524250f Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 12 Oct 2024 12:44:44 +0200 Subject: [PATCH 1/5] core: implement `DeterministicRandomSource` ACP: rust-lang/libs-team#394 Tracking issue: #131606 The version implemented here uses ChaCha8 as RNG. Whether this is the right choice is still open for debate, so I've included the RNG name in the feature gate to make sure people need to recheck their code if we change the RNG. Also, I've made one minor change to the API proposed in the ACP: in accordance with [C-GETTER](https://rust-lang.github.io/api-guidelines/naming.html#getter-names-follow-rust-convention-c-getter), `get_seed` is now named `seed`. --- library/core/src/random/deterministic.rs | 264 ++++++++++++++++++ library/core/src/{random.rs => random/mod.rs} | 13 +- library/core/tests/lib.rs | 4 + library/core/tests/random.rs | 37 +++ library/std/src/random.rs | 4 +- 5 files changed, 320 insertions(+), 2 deletions(-) create mode 100644 library/core/src/random/deterministic.rs rename library/core/src/{random.rs => random/mod.rs} (81%) create mode 100644 library/core/tests/random.rs diff --git a/library/core/src/random/deterministic.rs b/library/core/src/random/deterministic.rs new file mode 100644 index 0000000000000..74e66a174bd0f --- /dev/null +++ b/library/core/src/random/deterministic.rs @@ -0,0 +1,264 @@ +use super::{Random, RandomSource}; +use crate::fmt::{self, Debug}; + +/// A seeded, insecure random number generator. +/// +/// **DO NOT USE THIS FOR CRYPTOGRAPHY PURPOSES! EVER! NO, YOUR USECASE IS NOT +/// SPECIAL! IF YOU USE THIS IN SECURITY-SENSITIVE CONTEXTS, FERRIS WILL BE +/// ZOMBIFIED AND EAT YOU ALIVE!** +/// +/// If you require secure randomness, use `DefaultRandomSource` instead. In +/// particular, this source: +/// * Does *not* provide forward secrecy, so key compromise will result in *all* +/// output being predictable. +/// * Is *vulnerable* to side-channel attacks such as timing based attacks. +/// * Does *not* reseed on `fork`, VM fork or in similar scenarios, meaning the +/// generated bytes will be the same. +/// +/// That said, if you do *not* need security, this ChaCha8-based [`RandomSource`] +/// can be used to quickly generate good-quality, deterministic random data +/// usable for purposes such as Monte-Carlo integration, video game RNG, etc. +/// +/// # Stability +/// +/// This random source is guaranteed to always produce the same bytes from a +/// given seed, irrespective of the platform or Rust version. +/// +/// # Examples +/// +/// Test if a coin flip is fair by simulating it 100 times: +/// ```rust +/// #![feature(random, deterministic_random_chacha8)] +/// +/// use std::random::{DeterministicRandomSource, Random}; +/// +/// // Seed chosen by fair dice roll. Guaranteed to be random. +/// let mut rng = DeterministicRandomSource::from_seed([4; 32]); +/// +/// let mut heads = 0usize; +/// for _ in 0..100 { +/// if bool::random(&mut rng) { +/// heads += 1; +/// } +/// } +/// +/// // With a confidence of one standard deviation, the number of heads of +/// // will be within this range: +/// assert!(heads.abs_diff(50) < 20); +/// ``` +/// +/// A Monty-Hall-problem-inspired game: +/// ```rust,no_run +/// #![feature(random, deterministic_random_chacha8)] +/// +/// use std::io::stdin; +/// use std::random::{DefaultRandomSource, DeterministicRandomSource, Random}; +/// +/// // Use a random seed so that the generated numbers will be different every +/// // time the program is run. +/// let mut rng = DeterministicRandomSource::random(&mut DefaultRandomSource); +/// +/// // Pick a random door, avoiding bias. +/// let door = loop { +/// let num = u8::random(&mut rng); +/// if num < 255 { +/// break num % 3; +/// } +/// }; +/// +/// let mut input = stdin().lines().map(Result::unwrap); +/// let guess = loop { +/// println!("Pick a door from 1, 2 or 3:"); +/// match input.next().as_deref() { +/// Some("1") => break 0, +/// Some("2") => break 1, +/// Some("3") => break 2, +/// _ => println!("That's not a valid door"), +/// } +/// }; +/// +/// let reveal = match (guess, door) { +/// // Choose which door the moderator must open. +/// // Since both unpicked doors contain a goat, we decide by fair coin flip. +/// (0, 0) | (1, 1) | (2, 2) => { +/// let diceroll = bool::random(&mut rng) as u8; +/// (door + diceroll) % 3 +/// } +/// (0, 1) | (1, 0) => 2, +/// (0, 2) | (2, 0) => 1, +/// (1, 2) | (2, 1) => 0, +/// _ => unreachable!(), +/// }; +/// println!("Door {} contains a goat. Do you want to change your guess (y/n)?", reveal + 1); +/// +/// let guess = loop { +/// match input.next().as_deref() { +/// Some("y") => break match (guess, reveal) { +/// (0, 1) | (1, 0) => 2, +/// (0, 2) | (2, 0) => 1, +/// (1, 2) | (2, 1) => 0, +/// _ => unreachable!(), +/// }, +/// Some("n") => break guess, +/// _ => println!("Well, what? Answer with either yes (y) or no (n)."), +/// } +/// }; +/// +/// if guess == door { +/// println!("Congratulations, you won a bike for Ferris (also known as a Ferris-wheel)!"); +/// } else { +/// println!("Congratulations, you won a goat! You did not want a goat? Well, better luck next time ;-)."); +/// } +/// ``` +#[unstable(feature = "deterministic_random_chacha8", issue = "131606")] +pub struct DeterministicRandomSource { + seed: [u8; 32], + // We use both the 32-bit counter and the 96-bit nonce from the RFC as + // block counter, resulting in a 128-bit counter that will realistically + // never roll over. + counter_nonce: u128, +} + +/// Implement the ChaCha round function as defined by +/// [RFC 8439](https://datatracker.ietf.org/doc/html/rfc8439), but with reduced rounds. +#[doc(hidden)] +#[unstable(feature = "deterministic_random_internals", issue = "none")] // Used for testing only. +pub mod chacha { + pub const fn quarter_round( + mut a: u32, + mut b: u32, + mut c: u32, + mut d: u32, + ) -> (u32, u32, u32, u32) { + a = a.wrapping_add(b); + d ^= a; + d = d.rotate_left(16); + + c = c.wrapping_add(d); + b ^= c; + b = b.rotate_left(12); + + a = a.wrapping_add(b); + d ^= a; + d = d.rotate_left(8); + + c = c.wrapping_add(d); + b ^= c; + b = b.rotate_left(7); + + (a, b, c, d) + } + + pub fn block(key: &[u8; 32], counter_nonce: u128, rounds: u32) -> [u8; 64] { + assert!(rounds % 2 == 0); + + let mut state = [0; 16]; + state[0] = 0x61707865; + state[1] = 0x3320646e; + state[2] = 0x79622d32; + state[3] = 0x6b206574; + + for (i, word) in key.array_chunks().enumerate() { + state[4 + i] = u32::from_le_bytes(*word); + } + + state[12] = (counter_nonce >> 0) as u32; + state[13] = (counter_nonce >> 32) as u32; + state[14] = (counter_nonce >> 64) as u32; + state[15] = (counter_nonce >> 96) as u32; + + let mut block = state; + let mut qr = |a, b, c, d| { + let res = quarter_round(block[a], block[b], block[c], block[d]); + block[a] = res.0; + block[b] = res.1; + block[c] = res.2; + block[d] = res.3; + }; + + for _ in 0..rounds / 2 { + qr(0, 4, 8, 12); + qr(1, 5, 9, 13); + qr(2, 6, 10, 14); + qr(3, 7, 11, 15); + + qr(0, 5, 10, 15); + qr(1, 6, 11, 12); + qr(2, 7, 8, 13); + qr(3, 4, 9, 14); + } + + let mut out = [0; 64]; + for i in 0..16 { + out[4 * i..][..4].copy_from_slice(&block[i].wrapping_add(state[i]).to_le_bytes()); + } + + out + } +} + +impl DeterministicRandomSource { + const ROUNDS: u32 = 8; + + /// Creates a new random source with the given seed. + /// + /// # Example + /// + /// ```rust + /// #![feature(deterministic_random_chacha8, random)] + /// + /// use std::random::{DeterministicRandomSource, Random}; + /// + /// let mut rng = DeterministicRandomSource::from_seed([42; 32]); + /// let num = i32::random(&mut rng); + /// assert_eq!(num, 1325358262); + /// ``` + #[unstable(feature = "deterministic_random_chacha8", issue = "131606")] + pub const fn from_seed(seed: [u8; 32]) -> DeterministicRandomSource { + DeterministicRandomSource { seed, counter_nonce: 0 } + } + + /// Returns the seed this random source was initialized with. + /// + /// # Example + /// + /// ```rust + /// #![feature(deterministic_random_chacha8)] + /// + /// use std::random::DeterministicRandomSource; + /// + /// let rng = DeterministicRandomSource::from_seed([4; 32]); + /// assert_eq!(rng.seed(), &[4; 32]); + /// ``` + #[unstable(feature = "deterministic_random_chacha8", issue = "131606")] + pub const fn seed(&self) -> &[u8; 32] { + &self.seed + } +} + +#[unstable(feature = "deterministic_random_chacha8", issue = "131606")] +impl RandomSource for DeterministicRandomSource { + fn fill_bytes(&mut self, bytes: &mut [u8]) { + for block in bytes.chunks_mut(64) { + let data = chacha::block(&self.seed, self.counter_nonce, Self::ROUNDS); + block.copy_from_slice(&data[..block.len()]); + self.counter_nonce = self.counter_nonce.wrapping_add(1); + } + } +} + +#[unstable(feature = "deterministic_random_chacha8", issue = "131606")] +impl Random for DeterministicRandomSource { + fn random(source: &mut (impl RandomSource + ?Sized)) -> DeterministicRandomSource { + let mut seed = [0; 32]; + source.fill_bytes(&mut seed); + DeterministicRandomSource::from_seed(seed) + } +} + +#[unstable(feature = "deterministic_random_chacha8", issue = "131606")] +impl Debug for DeterministicRandomSource { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("DeterministcRandomSource").finish_non_exhaustive() + } +} diff --git a/library/core/src/random.rs b/library/core/src/random/mod.rs similarity index 81% rename from library/core/src/random.rs rename to library/core/src/random/mod.rs index 051fe26086389..f8c85b6eababc 100644 --- a/library/core/src/random.rs +++ b/library/core/src/random/mod.rs @@ -3,6 +3,15 @@ //! The [`Random`] trait allows generating a random value for a type using a //! given [`RandomSource`]. +mod deterministic; + +#[unstable(feature = "deterministic_random_chacha8", issue = "131606")] +pub use deterministic::DeterministicRandomSource; +#[doc(hidden)] +#[unstable(feature = "deterministic_random_internals", issue = "none")] +// Used for testing only. +pub use deterministic::chacha; + /// A source of randomness. #[unstable(feature = "random", issue = "130703")] pub trait RandomSource { @@ -42,7 +51,9 @@ macro_rules! impl_primitive { fn random(source: &mut (impl RandomSource + ?Sized)) -> Self { let mut bytes = (0 as Self).to_ne_bytes(); source.fill_bytes(&mut bytes); - Self::from_ne_bytes(bytes) + // Use LE-ordering to guarantee that the number is the same, + // irrespective of the platform. + Self::from_le_bytes(bytes) } } }; diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 0d4ec96f8426f..0a40f52ff7049 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -38,6 +38,8 @@ #![feature(core_private_diy_float)] #![feature(debug_more_non_exhaustive)] #![feature(dec2flt)] +#![feature(deterministic_random_chacha8)] +#![feature(deterministic_random_internals)] #![feature(duration_constants)] #![feature(duration_constructors)] #![feature(duration_consts_float)] @@ -83,6 +85,7 @@ #![feature(pointer_is_aligned_to)] #![feature(portable_simd)] #![feature(ptr_metadata)] +#![feature(random)] #![feature(slice_from_ptr_range)] #![feature(slice_internals)] #![feature(slice_partition_dedup)] @@ -147,6 +150,7 @@ mod pattern; mod pin; mod pin_macro; mod ptr; +mod random; mod result; mod simd; mod slice; diff --git a/library/core/tests/random.rs b/library/core/tests/random.rs new file mode 100644 index 0000000000000..9d9247a01c9e9 --- /dev/null +++ b/library/core/tests/random.rs @@ -0,0 +1,37 @@ +use core::random::chacha::*; + +// Test the quarter-round function. +#[test] +fn test_round() { + let a = 0x11111111; + let b = 0x01020304; + let c = 0x9b8d6f43; + let d = 0x01234567; + let (a, b, c, d) = quarter_round(a, b, c, d); + assert_eq!(a, 0xea2a92f4); + assert_eq!(b, 0xcb1cf8ce); + assert_eq!(c, 0x4581472e); + assert_eq!(d, 0x5881c4bb); +} + +// Test the block function. +// RFC 8439 only gives a test vector for 20 rounds, so we use that here. +#[test] +fn test_block() { + let key = [ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f, + ]; + let counter_nonce = 0x00_00_00_00_4a_00_00_00_09_00_00_00_00_00_00_01; + + let block = block(&key, counter_nonce, 20); + + #[rustfmt::skip] // Preserve the formatting from the RFC. + assert_eq!(block, [ + 0x10, 0xf1, 0xe7, 0xe4, 0xd1, 0x3b, 0x59, 0x15, 0x50, 0x0f, 0xdd, 0x1f, 0xa3, 0x20, 0x71, 0xc4, + 0xc7, 0xd1, 0xf4, 0xc7, 0x33, 0xc0, 0x68, 0x03, 0x04, 0x22, 0xaa, 0x9a, 0xc3, 0xd4, 0x6c, 0x4e, + 0xd2, 0x82, 0x64, 0x46, 0x07, 0x9f, 0xaa, 0x09, 0x14, 0xc2, 0xd7, 0x05, 0xd9, 0x8b, 0x02, 0xa2, + 0xb5, 0x12, 0x9c, 0xd1, 0xde, 0x16, 0x4e, 0xb9, 0xcb, 0xd0, 0x83, 0xe8, 0xa2, 0x50, 0x3c, 0x4e, + ]); +} diff --git a/library/std/src/random.rs b/library/std/src/random.rs index 604fa4df11066..fc27cbc83c55a 100644 --- a/library/std/src/random.rs +++ b/library/std/src/random.rs @@ -3,8 +3,10 @@ //! The [`Random`] trait allows generating a random value for a type using a //! given [`RandomSource`]. +#[unstable(feature = "deterministic_random_chacha8", issue = "131606")] +pub use core::random::DeterministicRandomSource; #[unstable(feature = "random", issue = "130703")] -pub use core::random::*; +pub use core::random::{Random, RandomSource}; use crate::sys::random as sys; From f4a20b223adf109fa8b5ce571092e4004b0deab6 Mon Sep 17 00:00:00 2001 From: joboet Date: Mon, 28 Oct 2024 15:18:04 +0100 Subject: [PATCH 2/5] core: cache the RNG output --- library/core/src/random/deterministic.rs | 34 +++++++++++++++++++----- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/library/core/src/random/deterministic.rs b/library/core/src/random/deterministic.rs index 74e66a174bd0f..99fcfc8f01af8 100644 --- a/library/core/src/random/deterministic.rs +++ b/library/core/src/random/deterministic.rs @@ -117,6 +117,9 @@ pub struct DeterministicRandomSource { // block counter, resulting in a 128-bit counter that will realistically // never roll over. counter_nonce: u128, + block: [u8; 64], + // The amount of bytes in block that were already used. + used: u8, } /// Implement the ChaCha round function as defined by @@ -215,7 +218,7 @@ impl DeterministicRandomSource { /// ``` #[unstable(feature = "deterministic_random_chacha8", issue = "131606")] pub const fn from_seed(seed: [u8; 32]) -> DeterministicRandomSource { - DeterministicRandomSource { seed, counter_nonce: 0 } + DeterministicRandomSource { seed, counter_nonce: 0, block: [0; 64], used: 64 } } /// Returns the seed this random source was initialized with. @@ -234,15 +237,34 @@ impl DeterministicRandomSource { pub const fn seed(&self) -> &[u8; 32] { &self.seed } + + fn next_block(&mut self) -> [u8; 64] { + let block = chacha::block(&self.seed, self.counter_nonce, Self::ROUNDS); + self.counter_nonce = self.counter_nonce.wrapping_add(1); + block + } } #[unstable(feature = "deterministic_random_chacha8", issue = "131606")] impl RandomSource for DeterministicRandomSource { - fn fill_bytes(&mut self, bytes: &mut [u8]) { - for block in bytes.chunks_mut(64) { - let data = chacha::block(&self.seed, self.counter_nonce, Self::ROUNDS); - block.copy_from_slice(&data[..block.len()]); - self.counter_nonce = self.counter_nonce.wrapping_add(1); + fn fill_bytes(&mut self, mut bytes: &mut [u8]) { + if self.used as usize != self.block.len() { + let len = usize::min(self.block.len() - self.used as usize, bytes.len()); + bytes[..len].copy_from_slice(&self.block[self.used as usize..][..len]); + bytes = &mut bytes[len..]; + self.used += len as u8; + } + + let mut blocks = bytes.array_chunks_mut::<64>(); + for block in &mut blocks { + block.copy_from_slice(&self.next_block()); + } + + let bytes = blocks.into_remainder(); + if !bytes.is_empty() { + self.block = self.next_block(); + bytes.copy_from_slice(&self.block[..bytes.len()]); + self.used = bytes.len() as u8; } } } From 4f633074b0f8b75f62ef65ce45758594f26b4d19 Mon Sep 17 00:00:00 2001 From: joboet Date: Mon, 28 Oct 2024 15:18:22 +0100 Subject: [PATCH 3/5] core: add RNG tests --- library/core/tests/random.rs | 45 ++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/library/core/tests/random.rs b/library/core/tests/random.rs index 9d9247a01c9e9..254da51626d2b 100644 --- a/library/core/tests/random.rs +++ b/library/core/tests/random.rs @@ -1,4 +1,5 @@ use core::random::chacha::*; +use core::random::{DeterministicRandomSource, RandomSource}; // Test the quarter-round function. #[test] @@ -35,3 +36,47 @@ fn test_block() { 0xb5, 0x12, 0x9c, 0xd1, 0xde, 0x16, 0x4e, 0xb9, 0xcb, 0xd0, 0x83, 0xe8, 0xa2, 0x50, 0x3c, 0x4e, ]); } + +#[test] +fn test_source() { + // Test that two sources produce the same output after a zero-sized request. + let mut rng1 = DeterministicRandomSource::from_seed([42; 32]); + let mut rng2 = DeterministicRandomSource::from_seed([42; 32]); + rng1.fill_bytes(&mut []); + let mut b1 = [0; 64]; + let mut b2 = [0; 64]; + rng1.fill_bytes(&mut b1); + rng2.fill_bytes(&mut b2); + assert_eq!(b1, b2); + + // Test that two sources with different seeds produce different data. + let mut rng1 = DeterministicRandomSource::from_seed([b'A'; 32]); + let mut rng2 = DeterministicRandomSource::from_seed([b'B'; 32]); + let mut b1 = [0; 64]; // This size should be large enough to guarantee uniqueness. + let mut b2 = [0; 64]; + rng1.fill_bytes(&mut b1); + rng2.fill_bytes(&mut b2); + assert_ne!(b1, b2); + + // Test that the source always generates the same bytes, irrespective of the + // size of the individual requests. + let mut rng1 = DeterministicRandomSource::from_seed([4; 32]); + let mut rng2 = DeterministicRandomSource::from_seed([4; 32]); + let mut b1 = [0; 128]; + let mut b2 = [0; 128]; + rng1.fill_bytes(&mut b1[0..63]); + rng1.fill_bytes(&mut b1[63..128]); + rng2.fill_bytes(&mut b2); + assert_eq!(b1, b2); + + // Test the output of the RNG. + let mut rng = DeterministicRandomSource::from_seed([42; 32]); + let mut bytes = [0; 64]; + rng.fill_bytes(&mut bytes); + assert_eq!(bytes, [ + 182, 92, 255, 78, 146, 170, 167, 19, 201, 210, 224, 219, 84, 107, 104, 196, 224, 111, 107, + 198, 98, 121, 52, 115, 177, 219, 124, 69, 52, 111, 194, 63, 248, 202, 181, 174, 85, 116, + 46, 203, 37, 238, 86, 5, 123, 158, 53, 234, 229, 76, 64, 169, 181, 145, 115, 128, 64, 187, + 25, 75, 60, 217, 10, 169 + ]); +} From 2e401ba3a356dc531e0fc14a7a0253dea7ee8efa Mon Sep 17 00:00:00 2001 From: joboet Date: Mon, 4 Nov 2024 19:20:58 +0100 Subject: [PATCH 4/5] core: use a `const` for the block size, use `usize` instead of `u8` --- library/core/src/random/deterministic.rs | 35 ++++++++++++++---------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/library/core/src/random/deterministic.rs b/library/core/src/random/deterministic.rs index 99fcfc8f01af8..dca677b18b894 100644 --- a/library/core/src/random/deterministic.rs +++ b/library/core/src/random/deterministic.rs @@ -117,16 +117,18 @@ pub struct DeterministicRandomSource { // block counter, resulting in a 128-bit counter that will realistically // never roll over. counter_nonce: u128, - block: [u8; 64], + block: [u8; chacha::BLOCK_SIZE], // The amount of bytes in block that were already used. - used: u8, + used: usize, } -/// Implement the ChaCha round function as defined by -/// [RFC 8439](https://datatracker.ietf.org/doc/html/rfc8439), but with reduced rounds. +/// Implements the ChaCha block function as defined by +/// [RFC 8439](https://datatracker.ietf.org/doc/html/rfc8439). #[doc(hidden)] #[unstable(feature = "deterministic_random_internals", issue = "none")] // Used for testing only. pub mod chacha { + pub const BLOCK_SIZE: usize = 64; + pub const fn quarter_round( mut a: u32, mut b: u32, @@ -152,7 +154,7 @@ pub mod chacha { (a, b, c, d) } - pub fn block(key: &[u8; 32], counter_nonce: u128, rounds: u32) -> [u8; 64] { + pub fn block(key: &[u8; 32], counter_nonce: u128, rounds: u32) -> [u8; BLOCK_SIZE] { assert!(rounds % 2 == 0); let mut state = [0; 16]; @@ -191,7 +193,7 @@ pub mod chacha { qr(3, 4, 9, 14); } - let mut out = [0; 64]; + let mut out = [0; BLOCK_SIZE]; for i in 0..16 { out[4 * i..][..4].copy_from_slice(&block[i].wrapping_add(state[i]).to_le_bytes()); } @@ -218,7 +220,12 @@ impl DeterministicRandomSource { /// ``` #[unstable(feature = "deterministic_random_chacha8", issue = "131606")] pub const fn from_seed(seed: [u8; 32]) -> DeterministicRandomSource { - DeterministicRandomSource { seed, counter_nonce: 0, block: [0; 64], used: 64 } + DeterministicRandomSource { + seed, + counter_nonce: 0, + block: [0; chacha::BLOCK_SIZE], + used: chacha::BLOCK_SIZE, + } } /// Returns the seed this random source was initialized with. @@ -238,7 +245,7 @@ impl DeterministicRandomSource { &self.seed } - fn next_block(&mut self) -> [u8; 64] { + fn next_block(&mut self) -> [u8; chacha::BLOCK_SIZE] { let block = chacha::block(&self.seed, self.counter_nonce, Self::ROUNDS); self.counter_nonce = self.counter_nonce.wrapping_add(1); block @@ -248,14 +255,14 @@ impl DeterministicRandomSource { #[unstable(feature = "deterministic_random_chacha8", issue = "131606")] impl RandomSource for DeterministicRandomSource { fn fill_bytes(&mut self, mut bytes: &mut [u8]) { - if self.used as usize != self.block.len() { - let len = usize::min(self.block.len() - self.used as usize, bytes.len()); - bytes[..len].copy_from_slice(&self.block[self.used as usize..][..len]); + if self.used != self.block.len() { + let len = usize::min(self.block.len() - self.used, bytes.len()); + bytes[..len].copy_from_slice(&self.block[self.used..][..len]); bytes = &mut bytes[len..]; - self.used += len as u8; + self.used += len; } - let mut blocks = bytes.array_chunks_mut::<64>(); + let mut blocks = bytes.array_chunks_mut::<{ chacha::BLOCK_SIZE }>(); for block in &mut blocks { block.copy_from_slice(&self.next_block()); } @@ -264,7 +271,7 @@ impl RandomSource for DeterministicRandomSource { if !bytes.is_empty() { self.block = self.next_block(); bytes.copy_from_slice(&self.block[..bytes.len()]); - self.used = bytes.len() as u8; + self.used = bytes.len(); } } } From fa7ae2e1b5884705d2f1dc88ad92a9d6ea78078a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20B=C3=B6ttiger?= Date: Mon, 11 Nov 2024 15:04:28 +0100 Subject: [PATCH 5/5] core: improve comments Co-authored-by: Josh Triplett --- library/core/src/random/deterministic.rs | 2 +- library/core/tests/random.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/library/core/src/random/deterministic.rs b/library/core/src/random/deterministic.rs index dca677b18b894..9b7731e5dd370 100644 --- a/library/core/src/random/deterministic.rs +++ b/library/core/src/random/deterministic.rs @@ -12,7 +12,7 @@ use crate::fmt::{self, Debug}; /// * Does *not* provide forward secrecy, so key compromise will result in *all* /// output being predictable. /// * Is *vulnerable* to side-channel attacks such as timing based attacks. -/// * Does *not* reseed on `fork`, VM fork or in similar scenarios, meaning the +/// * Does *not* reseed on `fork`, VM fork, or in similar scenarios, meaning the /// generated bytes will be the same. /// /// That said, if you do *not* need security, this ChaCha8-based [`RandomSource`] diff --git a/library/core/tests/random.rs b/library/core/tests/random.rs index 254da51626d2b..8cf716684252f 100644 --- a/library/core/tests/random.rs +++ b/library/core/tests/random.rs @@ -70,6 +70,8 @@ fn test_source() { assert_eq!(b1, b2); // Test the output of the RNG. + // If this test fails, this is a bug in the RNG, not the test. + // Do not change the generated bytes below. let mut rng = DeterministicRandomSource::from_seed([42; 32]); let mut bytes = [0; 64]; rng.fill_bytes(&mut bytes);