1#![allow(clippy::cast_possible_truncation, reason = "needs triage")]
2#![allow(clippy::undocumented_unsafe_blocks, reason = "TODO")]
34use core::fmt;
56use rand_core::{
7Infallible, SeedableRng, TryCryptoRng, TryRng,
8 block::{BlockRng, Generator},
9};
1011#[cfg(feature = "zeroize")]
12use zeroize::{Zeroize, ZeroizeOnDrop};
1314use crate::{
15ChaChaCore, R8, R12, R20, Rounds, backends,
16 variants::{Legacy, Variant},
17};
1819use cfg_if::cfg_if;
2021/// Seed value used to initialize ChaCha-based RNGs.
22pub type Seed = [u8; 32];
2324/// Serialized RNG state.
25pub type SerializedRngState = [u8; 49];
2627/// Number of 32-bit words per ChaCha block (fixed by algorithm definition).
28pub(crate) const BLOCK_WORDS: u8 = 16;
2930/// Number of blocks generated by RNG core.
31const BUF_BLOCKS: u8 = 4;
32/// Buffer size in words used by buffered RNG.
33const BUFFER_SIZE: usize = (BLOCK_WORDS * BUF_BLOCKS) as usize;
3435impl<R: Rounds, V: Variant> SeedableRngfor ChaChaCore<R, V> {
36type Seed = Seed;
3738#[inline]
39fn from_seed(seed: Self::Seed) -> Self {
40ChaChaCore::new_internal(&seed, &[0u8; 8])
41 }
42}
4344impl<R: Rounds, V: Variant> Generatorfor ChaChaCore<R, V> {
45type Output = [u32; BUFFER_SIZE];
4647/// Generates 4 blocks in parallel with avx2 & neon, but merely fills
48 /// 4 blocks with sse2 & soft
49fn generate(&mut self, buffer: &mut [u32; BUFFER_SIZE]) {
50cfg_if! {
51if #[cfg(chacha20_backend = "soft")] {
52 backends::soft::Backend(self).gen_ks_blocks(buffer);
53 } else if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
54cfg_if! {
55// AVX-512 doesn't support RNG, so use AVX-2 instead
56if #[cfg(any(chacha20_backend = "avx2", chacha20_backend = "avx512"))] {
57unsafe {
58 backends::avx2::rng_inner::<R, V>(self, buffer);
59 }
60 } else if #[cfg(chacha20_backend = "sse2")] {
61unsafe {
62 backends::sse2::rng_inner::<R, V>(self, buffer);
63 }
64 } else {
65#[cfg(chacha20_avx512)]
66let (_avx512_token, avx2_token, sse2_token) = self.tokens;
67#[cfg(not(chacha20_avx512))]
68let (avx2_token, sse2_token) = self.tokens;
6970if avx2_token.get() {
71unsafe {
72 backends::avx2::rng_inner::<R, V>(self, buffer);
73 }
74 } else if sse2_token.get() {
75unsafe {
76 backends::sse2::rng_inner::<R, V>(self, buffer);
77 }
78 } else {
79 backends::soft::Backend(self).gen_ks_blocks(buffer);
80 }
81 }
82 }
83 } else if #[cfg(all(target_arch = "aarch64", target_feature = "neon"))] {
84// SAFETY: we have used conditional compilation to ensure NEON is available
85unsafe {
86 backends::neon::rng_inner::<R, V>(self, buffer);
87 }
88 } else {
89 backends::soft::Backend(self).gen_ks_blocks(buffer);
90 }
91 }
92 }
9394// `Drop` impl of `BlockRng` calls this method and passes reference to
95 // its internal buffer in `output`. So we zeroize its contents here.
96#[cfg(feature = "zeroize")]
97fn drop(&mut self, output: &mut Self::Output) {
98 output.zeroize();
99 }
100}
101102macro_rules! impl_chacha_rng {
103 ($Rng:ident, $rounds:ident) => {
104/// A cryptographically secure random number generator that uses the ChaCha stream cipher.
105 ///
106 /// See the [crate docs][crate] for more information about the underlying stream cipher.
107 ///
108 /// This RNG implementation uses a 64-bit counter and 64-bit stream identifier (a.k.a nonce).
109 /// A 64-bit counter over 64-byte (16 word) blocks allows 1 ZiB of output before cycling,
110 /// and the stream identifier allows 2<sup>64</sup> unique streams of output per seed.
111 /// Both counter and stream are initialized to zero but may be set via the [`set_word_pos`]
112 /// and [`set_stream`] methods.
113 ///
114 /// [`set_word_pos`]: Self::set_word_pos
115 /// [`set_stream`]: Self::set_stream
116 ///
117 /// # Example
118 ///
119 /// ```rust
120#[doc = concat!("use chacha20::", stringify!($Rng), ";")]
121/// use rand_core::{SeedableRng, Rng};
122 ///
123 /// let seed = [42u8; 32];
124#[doc = concat!("let mut rng = ", stringify!($Rng), "::from_seed(seed);")]
125///
126 /// let random_u32 = rng.next_u32();
127 /// let random_u64 = rng.next_u64();
128 ///
129 /// let mut random_bytes = [0u8; 3];
130 /// rng.fill_bytes(&mut random_bytes);
131 /// ```
132 ///
133 /// See the [`rand`](https://docs.rs/rand/) crate for more advanced RNG functionality.
134pub struct $Rng {
135 core: BlockRng<ChaChaCore<$rounds, Legacy>>,
136 }
137138impl SeedableRng for $Rng {
139type Seed = Seed;
140141#[inline]
142fn from_seed(seed: Self::Seed) -> Self {
143let core = ChaChaCore::new_internal(&seed, &[0u8; 8]);
144Self {
145 core: BlockRng::new(core),
146 }
147 }
148 }
149150impl TryRng for $Rng {
151type Error = Infallible;
152153#[inline]
154fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
155Ok(self.core.next_word())
156 }
157#[inline]
158fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
159Ok(self.core.next_u64_from_u32())
160 }
161#[inline]
162fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
163self.core.fill_bytes(dest);
164Ok(())
165 }
166 }
167168impl TryCryptoRng for $Rng {}
169170#[cfg(feature = "zeroize")]
171impl ZeroizeOnDrop for $Rng {}
172173// We use custom implementation of `PartialEq` because RNG states
174 // may buffer different parts of the same keystream, while keeping
175 // buffer cursor pointing towards the same keystream point.
176impl PartialEq<$Rng> for $Rng {
177fn eq(&self, rhs: &$Rng) -> bool {
178 (self.get_seed() == rhs.get_seed())
179 && (self.get_stream() == rhs.get_stream())
180 && (self.get_word_pos() == rhs.get_word_pos())
181 }
182 }
183184impl Eq for $Rng {}
185186// Custom Debug implementation that does not expose the internal state
187impl fmt::Debug for $Rng {
188fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
189write!(f, concat!(stringify!($Rng), " {{ ... }}"))
190 }
191 }
192193impl $Rng {
194/// Get the offset from the start of the stream, in 32-bit words.
195 ///
196 /// Since the generated blocks are 16 words (2<sup>4</sup>) long and the
197 /// counter is 64-bits, the offset is a 68-bit number. Sub-word offsets are
198 /// not supported, hence the result can simply be multiplied by 4 to get a
199 /// byte-offset.
200#[inline]
201 #[must_use]
202pub fn get_word_pos(&self) -> u128 {
203let mut block_counter = (u64::from(self.core.core.state[13]) << 32)
204 | u64::from(self.core.core.state[12]);
205if self.core.word_offset() != 0 {
206 block_counter = block_counter.wrapping_sub(u64::from(BUF_BLOCKS));
207 }
208let word_pos = u128::from(block_counter) * u128::from(BLOCK_WORDS)
209 + self.core.word_offset() as u128;
210// eliminate bits above the 68th bit
211word_pos & ((1 << 68) - 1)
212 }
213214/// Set the offset from the start of the stream, in 32-bit words.
215 ///
216 /// **This value will be erased when calling `set_stream()`,
217 /// so call `set_stream()` before calling `set_word_pos()`**
218 /// if you intend on using both of them together.
219 ///
220 /// As with `get_word_pos`, we use a 68-bit number. Since the generator
221 /// simply cycles at the end of its period (1 ZiB), we ignore the upper
222 /// 60 bits.
223#[inline]
224pub fn set_word_pos(&mut self, word_offset: u128) {
225let index = (word_offset % u128::from(BLOCK_WORDS)) as usize;
226let counter = word_offset / u128::from(BLOCK_WORDS);
227//self.set_block_pos(counter as u64);
228self.core.core.state[12] = counter as u32;
229self.core.core.state[13] = (counter >> 32) as u32;
230self.core.reset_and_skip(index);
231 }
232233/// Sets the block pos and resets the RNG's index.
234 ///
235 /// **This value will be erased when calling `set_stream()`,
236 /// so call `set_stream()` before calling `set_block_pos()`**
237 /// if you intend on using both of them together.
238 ///
239 /// The word pos will be equal to `block_pos * 16 words per block`.
240#[inline]
241 #[allow(unused)]
242pub fn set_block_pos(&mut self, block_pos: u64) {
243self.core.reset_and_skip(0);
244self.core.core.set_block_pos(block_pos);
245 }
246247/// Get the block pos.
248#[inline]
249 #[allow(unused)]
250 #[must_use]
251pub fn get_block_pos(&self) -> u64 {
252let counter = self.core.core.get_block_pos();
253let offset = self.core.word_offset();
254if offset != 0 {
255 counter - u64::from(BUF_BLOCKS) + offset as u64 / u64::from(BLOCK_WORDS)
256 } else {
257 counter
258 }
259 }
260261/// Set the stream ID and reset the `word_pos` to 0.
262#[inline]
263pub fn set_stream(&mut self, stream: u64) {
264self.core.core.state[14] = stream as u32;
265self.core.core.state[15] = (stream >> 32) as u32;
266self.set_block_pos(0);
267 }
268269/// Get the stream number (nonce).
270#[inline]
271 #[must_use]
272pub fn get_stream(&self) -> u64 {
273let mut result = [0u8; 8];
274 result[..4].copy_from_slice(&self.core.core.state[14].to_le_bytes());
275 result[4..].copy_from_slice(&self.core.core.state[15].to_le_bytes());
276 u64::from_le_bytes(result)
277 }
278279/// Get the RNG seed.
280#[inline]
281 #[must_use]
282pub fn get_seed(&self) -> [u8; 32] {
283let seed = &self.core.core.state[4..12];
284let mut result = [0u8; 32];
285for (src, dst) in seed.iter().zip(result.chunks_exact_mut(4)) {
286 dst.copy_from_slice(&src.to_le_bytes())
287 }
288 result
289 }
290291/// Serialize RNG state.
292 ///
293 /// # Warning
294 /// Leaking serialized RNG state to an attacker defeats security properties
295 /// provided by the RNG.
296#[inline]
297pub fn serialize_state(&self) -> SerializedRngState {
298let seed = self.get_seed();
299let stream = self.get_stream().to_le_bytes();
300let word_pos = self.get_word_pos().to_le_bytes();
301302let mut res = [0u8; 49];
303let (seed_dst, res_rem) = res.split_at_mut(32);
304let (stream_dst, word_pos_dst) = res_rem.split_at_mut(8);
305306 seed_dst.copy_from_slice(&seed);
307 stream_dst.copy_from_slice(&stream);
308 word_pos_dst.copy_from_slice(&word_pos[..9]);
309310debug_assert_eq!(&word_pos[9..], &[0u8; 7]);
311312 res
313 }
314315/// Deserialize RNG state.
316#[inline]
317pub fn deserialize_state(state: &SerializedRngState) -> Self {
318let (seed, state_rem) = state.split_at(32);
319let (stream, word_pos_raw) = state_rem.split_at(8);
320321let seed: &[u8; 32] = seed.try_into().expect("seed.len() is equal to 32");
322let stream: &[u8; 8] = stream.try_into().expect("stream.len() is equal to 8");
323324// Note that we use only 68 bits from `word_pos_raw`, i.e. 4 remaining bits
325 // get ignored and should be equal to zero in practice.
326let mut word_pos_buf = [0u8; 16];
327 word_pos_buf[..9].copy_from_slice(word_pos_raw);
328let word_pos = u128::from_le_bytes(word_pos_buf);
329330let core = ChaChaCore::new_internal(seed, stream);
331let mut res = Self {
332 core: BlockRng::new(core),
333 };
334335 res.set_word_pos(word_pos);
336 res
337 }
338 }
339 };
340}
341342/// A cryptographically secure random number generator that uses the ChaCha stream cipher.
///
/// See the [crate docs][crate] for more information about the underlying stream cipher.
///
/// This RNG implementation uses a 64-bit counter and 64-bit stream identifier (a.k.a nonce).
/// A 64-bit counter over 64-byte (16 word) blocks allows 1 ZiB of output before cycling,
/// and the stream identifier allows 2<sup>64</sup> unique streams of output per seed.
/// Both counter and stream are initialized to zero but may be set via the [`set_word_pos`]
/// and [`set_stream`] methods.
///
/// [`set_word_pos`]: Self::set_word_pos
/// [`set_stream`]: Self::set_stream
///
/// # Example
///
/// ```rust
#[doc = "use chacha20::ChaCha8Rng;"]
/// use rand_core::{SeedableRng, Rng};
///
/// let seed = [42u8; 32];
#[doc = "let mut rng = ChaCha8Rng::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 ChaCha8Rng {
core: BlockRng<ChaChaCore<R8, Legacy>>,
}
impl SeedableRng for ChaCha8Rng {
type Seed = Seed;
#[inline]
fn from_seed(seed: Self::Seed) -> Self {
let core = ChaChaCore::new_internal(&seed, &[0u8; 8]);
Self { core: BlockRng::new(core) }
}
}
impl TryRng for ChaCha8Rng {
type Error = Infallible;
#[inline]
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
Ok(self.core.next_word())
}
#[inline]
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
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 ChaCha8Rng {}
impl PartialEq<ChaCha8Rng> for ChaCha8Rng {
fn eq(&self, rhs: &ChaCha8Rng) -> bool {
(self.get_seed() == rhs.get_seed()) &&
(self.get_stream() == rhs.get_stream()) &&
(self.get_word_pos() == rhs.get_word_pos())
}
}
impl Eq for ChaCha8Rng {}
impl fmt::Debug for ChaCha8Rng {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_fmt(format_args!("ChaCha8Rng {{ ... }}"))
}
}
impl ChaCha8Rng {
/// Get the offset from the start of the stream, in 32-bit words.
///
/// Since the generated blocks are 16 words (2<sup>4</sup>) long and the
/// counter is 64-bits, the offset is a 68-bit number. Sub-word offsets are
/// not supported, hence the result can simply be multiplied by 4 to get a
/// byte-offset.
#[inline]
#[must_use]
pub fn get_word_pos(&self) -> u128 {
let mut block_counter =
(u64::from(self.core.core.state[13]) << 32) |
u64::from(self.core.core.state[12]);
if self.core.word_offset() != 0 {
block_counter = block_counter.wrapping_sub(u64::from(BUF_BLOCKS));
}
let word_pos =
u128::from(block_counter) * u128::from(BLOCK_WORDS) +
self.core.word_offset() as u128;
word_pos & ((1 << 68) - 1)
}
/// Set the offset from the start of the stream, in 32-bit words.
///
/// **This value will be erased when calling `set_stream()`,
/// so call `set_stream()` before calling `set_word_pos()`**
/// if you intend on using both of them together.
///
/// As with `get_word_pos`, we use a 68-bit number. Since the generator
/// simply cycles at the end of its period (1 ZiB), we ignore the upper
/// 60 bits.
#[inline]
pub fn set_word_pos(&mut self, word_offset: u128) {
let index = (word_offset % u128::from(BLOCK_WORDS)) as usize;
let counter = word_offset / u128::from(BLOCK_WORDS);
self.core.core.state[12] = counter as u32;
self.core.core.state[13] = (counter >> 32) as u32;
self.core.reset_and_skip(index);
}
/// Sets the block pos and resets the RNG's index.
///
/// **This value will be erased when calling `set_stream()`,
/// so call `set_stream()` before calling `set_block_pos()`**
/// if you intend on using both of them together.
///
/// The word pos will be equal to `block_pos * 16 words per block`.
#[inline]
#[allow(unused)]
pub fn set_block_pos(&mut self, block_pos: u64) {
self.core.reset_and_skip(0);
self.core.core.set_block_pos(block_pos);
}
/// Get the block pos.
#[inline]
#[allow(unused)]
#[must_use]
pub fn get_block_pos(&self) -> u64 {
let counter = self.core.core.get_block_pos();
let offset = self.core.word_offset();
if offset != 0 {
counter - u64::from(BUF_BLOCKS) +
offset as u64 / u64::from(BLOCK_WORDS)
} else { counter }
}
/// Set the stream ID and reset the `word_pos` to 0.
#[inline]
pub fn set_stream(&mut self, stream: u64) {
self.core.core.state[14] = stream as u32;
self.core.core.state[15] = (stream >> 32) as u32;
self.set_block_pos(0);
}
/// Get the stream number (nonce).
#[inline]
#[must_use]
pub fn get_stream(&self) -> u64 {
let mut result = [0u8; 8];
result[..4].copy_from_slice(&self.core.core.state[14].to_le_bytes());
result[4..].copy_from_slice(&self.core.core.state[15].to_le_bytes());
u64::from_le_bytes(result)
}
/// Get the RNG seed.
#[inline]
#[must_use]
pub fn get_seed(&self) -> [u8; 32] {
let seed = &self.core.core.state[4..12];
let mut result = [0u8; 32];
for (src, dst) in seed.iter().zip(result.chunks_exact_mut(4)) {
dst.copy_from_slice(&src.to_le_bytes())
}
result
}
/// Serialize RNG state.
///
/// # Warning
/// Leaking serialized RNG state to an attacker defeats security properties
/// provided by the RNG.
#[inline]
pub fn serialize_state(&self) -> SerializedRngState {
let seed = self.get_seed();
let stream = self.get_stream().to_le_bytes();
let word_pos = self.get_word_pos().to_le_bytes();
let mut res = [0u8; 49];
let (seed_dst, res_rem) = res.split_at_mut(32);
let (stream_dst, word_pos_dst) = res_rem.split_at_mut(8);
seed_dst.copy_from_slice(&seed);
stream_dst.copy_from_slice(&stream);
word_pos_dst.copy_from_slice(&word_pos[..9]);
if true {
{
match (&&word_pos[9..], &&[0u8; 7]) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};
};
res
}
/// Deserialize RNG state.
#[inline]
pub fn deserialize_state(state: &SerializedRngState) -> Self {
let (seed, state_rem) = state.split_at(32);
let (stream, word_pos_raw) = state_rem.split_at(8);
let seed: &[u8; 32] =
seed.try_into().expect("seed.len() is equal to 32");
let stream: &[u8; 8] =
stream.try_into().expect("stream.len() is equal to 8");
let mut word_pos_buf = [0u8; 16];
word_pos_buf[..9].copy_from_slice(word_pos_raw);
let word_pos = u128::from_le_bytes(word_pos_buf);
let core = ChaChaCore::new_internal(seed, stream);
let mut res = Self { core: BlockRng::new(core) };
res.set_word_pos(word_pos);
res
}
}impl_chacha_rng!(ChaCha8Rng, R8);
343/// A cryptographically secure random number generator that uses the ChaCha stream cipher.
///
/// See the [crate docs][crate] for more information about the underlying stream cipher.
///
/// This RNG implementation uses a 64-bit counter and 64-bit stream identifier (a.k.a nonce).
/// A 64-bit counter over 64-byte (16 word) blocks allows 1 ZiB of output before cycling,
/// and the stream identifier allows 2<sup>64</sup> unique streams of output per seed.
/// Both counter and stream are initialized to zero but may be set via the [`set_word_pos`]
/// and [`set_stream`] methods.
///
/// [`set_word_pos`]: Self::set_word_pos
/// [`set_stream`]: Self::set_stream
///
/// # Example
///
/// ```rust
#[doc = "use chacha20::ChaCha12Rng;"]
/// use rand_core::{SeedableRng, Rng};
///
/// let seed = [42u8; 32];
#[doc = "let mut rng = ChaCha12Rng::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 ChaCha12Rng {
core: BlockRng<ChaChaCore<R12, Legacy>>,
}
impl SeedableRng for ChaCha12Rng {
type Seed = Seed;
#[inline]
fn from_seed(seed: Self::Seed) -> Self {
let core = ChaChaCore::new_internal(&seed, &[0u8; 8]);
Self { core: BlockRng::new(core) }
}
}
impl TryRng for ChaCha12Rng {
type Error = Infallible;
#[inline]
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
Ok(self.core.next_word())
}
#[inline]
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
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 ChaCha12Rng {}
impl PartialEq<ChaCha12Rng> for ChaCha12Rng {
fn eq(&self, rhs: &ChaCha12Rng) -> bool {
(self.get_seed() == rhs.get_seed()) &&
(self.get_stream() == rhs.get_stream()) &&
(self.get_word_pos() == rhs.get_word_pos())
}
}
impl Eq for ChaCha12Rng {}
impl fmt::Debug for ChaCha12Rng {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_fmt(format_args!("ChaCha12Rng {{ ... }}"))
}
}
impl ChaCha12Rng {
/// Get the offset from the start of the stream, in 32-bit words.
///
/// Since the generated blocks are 16 words (2<sup>4</sup>) long and the
/// counter is 64-bits, the offset is a 68-bit number. Sub-word offsets are
/// not supported, hence the result can simply be multiplied by 4 to get a
/// byte-offset.
#[inline]
#[must_use]
pub fn get_word_pos(&self) -> u128 {
let mut block_counter =
(u64::from(self.core.core.state[13]) << 32) |
u64::from(self.core.core.state[12]);
if self.core.word_offset() != 0 {
block_counter = block_counter.wrapping_sub(u64::from(BUF_BLOCKS));
}
let word_pos =
u128::from(block_counter) * u128::from(BLOCK_WORDS) +
self.core.word_offset() as u128;
word_pos & ((1 << 68) - 1)
}
/// Set the offset from the start of the stream, in 32-bit words.
///
/// **This value will be erased when calling `set_stream()`,
/// so call `set_stream()` before calling `set_word_pos()`**
/// if you intend on using both of them together.
///
/// As with `get_word_pos`, we use a 68-bit number. Since the generator
/// simply cycles at the end of its period (1 ZiB), we ignore the upper
/// 60 bits.
#[inline]
pub fn set_word_pos(&mut self, word_offset: u128) {
let index = (word_offset % u128::from(BLOCK_WORDS)) as usize;
let counter = word_offset / u128::from(BLOCK_WORDS);
self.core.core.state[12] = counter as u32;
self.core.core.state[13] = (counter >> 32) as u32;
self.core.reset_and_skip(index);
}
/// Sets the block pos and resets the RNG's index.
///
/// **This value will be erased when calling `set_stream()`,
/// so call `set_stream()` before calling `set_block_pos()`**
/// if you intend on using both of them together.
///
/// The word pos will be equal to `block_pos * 16 words per block`.
#[inline]
#[allow(unused)]
pub fn set_block_pos(&mut self, block_pos: u64) {
self.core.reset_and_skip(0);
self.core.core.set_block_pos(block_pos);
}
/// Get the block pos.
#[inline]
#[allow(unused)]
#[must_use]
pub fn get_block_pos(&self) -> u64 {
let counter = self.core.core.get_block_pos();
let offset = self.core.word_offset();
if offset != 0 {
counter - u64::from(BUF_BLOCKS) +
offset as u64 / u64::from(BLOCK_WORDS)
} else { counter }
}
/// Set the stream ID and reset the `word_pos` to 0.
#[inline]
pub fn set_stream(&mut self, stream: u64) {
self.core.core.state[14] = stream as u32;
self.core.core.state[15] = (stream >> 32) as u32;
self.set_block_pos(0);
}
/// Get the stream number (nonce).
#[inline]
#[must_use]
pub fn get_stream(&self) -> u64 {
let mut result = [0u8; 8];
result[..4].copy_from_slice(&self.core.core.state[14].to_le_bytes());
result[4..].copy_from_slice(&self.core.core.state[15].to_le_bytes());
u64::from_le_bytes(result)
}
/// Get the RNG seed.
#[inline]
#[must_use]
pub fn get_seed(&self) -> [u8; 32] {
let seed = &self.core.core.state[4..12];
let mut result = [0u8; 32];
for (src, dst) in seed.iter().zip(result.chunks_exact_mut(4)) {
dst.copy_from_slice(&src.to_le_bytes())
}
result
}
/// Serialize RNG state.
///
/// # Warning
/// Leaking serialized RNG state to an attacker defeats security properties
/// provided by the RNG.
#[inline]
pub fn serialize_state(&self) -> SerializedRngState {
let seed = self.get_seed();
let stream = self.get_stream().to_le_bytes();
let word_pos = self.get_word_pos().to_le_bytes();
let mut res = [0u8; 49];
let (seed_dst, res_rem) = res.split_at_mut(32);
let (stream_dst, word_pos_dst) = res_rem.split_at_mut(8);
seed_dst.copy_from_slice(&seed);
stream_dst.copy_from_slice(&stream);
word_pos_dst.copy_from_slice(&word_pos[..9]);
if true {
{
match (&&word_pos[9..], &&[0u8; 7]) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};
};
res
}
/// Deserialize RNG state.
#[inline]
pub fn deserialize_state(state: &SerializedRngState) -> Self {
let (seed, state_rem) = state.split_at(32);
let (stream, word_pos_raw) = state_rem.split_at(8);
let seed: &[u8; 32] =
seed.try_into().expect("seed.len() is equal to 32");
let stream: &[u8; 8] =
stream.try_into().expect("stream.len() is equal to 8");
let mut word_pos_buf = [0u8; 16];
word_pos_buf[..9].copy_from_slice(word_pos_raw);
let word_pos = u128::from_le_bytes(word_pos_buf);
let core = ChaChaCore::new_internal(seed, stream);
let mut res = Self { core: BlockRng::new(core) };
res.set_word_pos(word_pos);
res
}
}impl_chacha_rng!(ChaCha12Rng, R12);
344/// A cryptographically secure random number generator that uses the ChaCha stream cipher.
///
/// See the [crate docs][crate] for more information about the underlying stream cipher.
///
/// This RNG implementation uses a 64-bit counter and 64-bit stream identifier (a.k.a nonce).
/// A 64-bit counter over 64-byte (16 word) blocks allows 1 ZiB of output before cycling,
/// and the stream identifier allows 2<sup>64</sup> unique streams of output per seed.
/// Both counter and stream are initialized to zero but may be set via the [`set_word_pos`]
/// and [`set_stream`] methods.
///
/// [`set_word_pos`]: Self::set_word_pos
/// [`set_stream`]: Self::set_stream
///
/// # Example
///
/// ```rust
#[doc = "use chacha20::ChaCha20Rng;"]
/// use rand_core::{SeedableRng, Rng};
///
/// let seed = [42u8; 32];
#[doc = "let mut rng = ChaCha20Rng::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 ChaCha20Rng {
core: BlockRng<ChaChaCore<R20, Legacy>>,
}
impl SeedableRng for ChaCha20Rng {
type Seed = Seed;
#[inline]
fn from_seed(seed: Self::Seed) -> Self {
let core = ChaChaCore::new_internal(&seed, &[0u8; 8]);
Self { core: BlockRng::new(core) }
}
}
impl TryRng for ChaCha20Rng {
type Error = Infallible;
#[inline]
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
Ok(self.core.next_word())
}
#[inline]
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
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 ChaCha20Rng {}
impl PartialEq<ChaCha20Rng> for ChaCha20Rng {
fn eq(&self, rhs: &ChaCha20Rng) -> bool {
(self.get_seed() == rhs.get_seed()) &&
(self.get_stream() == rhs.get_stream()) &&
(self.get_word_pos() == rhs.get_word_pos())
}
}
impl Eq for ChaCha20Rng {}
impl fmt::Debug for ChaCha20Rng {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_fmt(format_args!("ChaCha20Rng {{ ... }}"))
}
}
impl ChaCha20Rng {
/// Get the offset from the start of the stream, in 32-bit words.
///
/// Since the generated blocks are 16 words (2<sup>4</sup>) long and the
/// counter is 64-bits, the offset is a 68-bit number. Sub-word offsets are
/// not supported, hence the result can simply be multiplied by 4 to get a
/// byte-offset.
#[inline]
#[must_use]
pub fn get_word_pos(&self) -> u128 {
let mut block_counter =
(u64::from(self.core.core.state[13]) << 32) |
u64::from(self.core.core.state[12]);
if self.core.word_offset() != 0 {
block_counter = block_counter.wrapping_sub(u64::from(BUF_BLOCKS));
}
let word_pos =
u128::from(block_counter) * u128::from(BLOCK_WORDS) +
self.core.word_offset() as u128;
word_pos & ((1 << 68) - 1)
}
/// Set the offset from the start of the stream, in 32-bit words.
///
/// **This value will be erased when calling `set_stream()`,
/// so call `set_stream()` before calling `set_word_pos()`**
/// if you intend on using both of them together.
///
/// As with `get_word_pos`, we use a 68-bit number. Since the generator
/// simply cycles at the end of its period (1 ZiB), we ignore the upper
/// 60 bits.
#[inline]
pub fn set_word_pos(&mut self, word_offset: u128) {
let index = (word_offset % u128::from(BLOCK_WORDS)) as usize;
let counter = word_offset / u128::from(BLOCK_WORDS);
self.core.core.state[12] = counter as u32;
self.core.core.state[13] = (counter >> 32) as u32;
self.core.reset_and_skip(index);
}
/// Sets the block pos and resets the RNG's index.
///
/// **This value will be erased when calling `set_stream()`,
/// so call `set_stream()` before calling `set_block_pos()`**
/// if you intend on using both of them together.
///
/// The word pos will be equal to `block_pos * 16 words per block`.
#[inline]
#[allow(unused)]
pub fn set_block_pos(&mut self, block_pos: u64) {
self.core.reset_and_skip(0);
self.core.core.set_block_pos(block_pos);
}
/// Get the block pos.
#[inline]
#[allow(unused)]
#[must_use]
pub fn get_block_pos(&self) -> u64 {
let counter = self.core.core.get_block_pos();
let offset = self.core.word_offset();
if offset != 0 {
counter - u64::from(BUF_BLOCKS) +
offset as u64 / u64::from(BLOCK_WORDS)
} else { counter }
}
/// Set the stream ID and reset the `word_pos` to 0.
#[inline]
pub fn set_stream(&mut self, stream: u64) {
self.core.core.state[14] = stream as u32;
self.core.core.state[15] = (stream >> 32) as u32;
self.set_block_pos(0);
}
/// Get the stream number (nonce).
#[inline]
#[must_use]
pub fn get_stream(&self) -> u64 {
let mut result = [0u8; 8];
result[..4].copy_from_slice(&self.core.core.state[14].to_le_bytes());
result[4..].copy_from_slice(&self.core.core.state[15].to_le_bytes());
u64::from_le_bytes(result)
}
/// Get the RNG seed.
#[inline]
#[must_use]
pub fn get_seed(&self) -> [u8; 32] {
let seed = &self.core.core.state[4..12];
let mut result = [0u8; 32];
for (src, dst) in seed.iter().zip(result.chunks_exact_mut(4)) {
dst.copy_from_slice(&src.to_le_bytes())
}
result
}
/// Serialize RNG state.
///
/// # Warning
/// Leaking serialized RNG state to an attacker defeats security properties
/// provided by the RNG.
#[inline]
pub fn serialize_state(&self) -> SerializedRngState {
let seed = self.get_seed();
let stream = self.get_stream().to_le_bytes();
let word_pos = self.get_word_pos().to_le_bytes();
let mut res = [0u8; 49];
let (seed_dst, res_rem) = res.split_at_mut(32);
let (stream_dst, word_pos_dst) = res_rem.split_at_mut(8);
seed_dst.copy_from_slice(&seed);
stream_dst.copy_from_slice(&stream);
word_pos_dst.copy_from_slice(&word_pos[..9]);
if true {
{
match (&&word_pos[9..], &&[0u8; 7]) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};
};
res
}
/// Deserialize RNG state.
#[inline]
pub fn deserialize_state(state: &SerializedRngState) -> Self {
let (seed, state_rem) = state.split_at(32);
let (stream, word_pos_raw) = state_rem.split_at(8);
let seed: &[u8; 32] =
seed.try_into().expect("seed.len() is equal to 32");
let stream: &[u8; 8] =
stream.try_into().expect("stream.len() is equal to 8");
let mut word_pos_buf = [0u8; 16];
word_pos_buf[..9].copy_from_slice(word_pos_raw);
let word_pos = u128::from_le_bytes(word_pos_buf);
let core = ChaChaCore::new_internal(seed, stream);
let mut res = Self { core: BlockRng::new(core) };
res.set_word_pos(word_pos);
res
}
}impl_chacha_rng!(ChaCha20Rng, R20);