/rust/registry/src/index.crates.io-1949cf8c6b5b557f/chacha20-0.10.0/src/rng.rs
Line | Count | Source |
1 | | #![allow(clippy::cast_possible_truncation, reason = "needs triage")] |
2 | | #![allow(clippy::undocumented_unsafe_blocks, reason = "TODO")] |
3 | | |
4 | | use core::fmt; |
5 | | |
6 | | use rand_core::{ |
7 | | Infallible, SeedableRng, TryCryptoRng, TryRng, |
8 | | block::{BlockRng, Generator}, |
9 | | }; |
10 | | |
11 | | #[cfg(feature = "zeroize")] |
12 | | use zeroize::{Zeroize, ZeroizeOnDrop}; |
13 | | |
14 | | use crate::{ |
15 | | ChaChaCore, R8, R12, R20, Rounds, backends, |
16 | | variants::{Legacy, Variant}, |
17 | | }; |
18 | | |
19 | | use cfg_if::cfg_if; |
20 | | |
21 | | /// Seed value used to initialize ChaCha-based RNGs. |
22 | | pub type Seed = [u8; 32]; |
23 | | |
24 | | /// Serialized RNG state. |
25 | | pub type SerializedRngState = [u8; 49]; |
26 | | |
27 | | /// Number of 32-bit words per ChaCha block (fixed by algorithm definition). |
28 | | pub(crate) const BLOCK_WORDS: u8 = 16; |
29 | | |
30 | | /// Number of blocks generated by RNG core. |
31 | | const BUF_BLOCKS: u8 = 4; |
32 | | /// Buffer size in words used by buffered RNG. |
33 | | const BUFFER_SIZE: usize = (BLOCK_WORDS * BUF_BLOCKS) as usize; |
34 | | |
35 | | impl<R: Rounds, V: Variant> SeedableRng for ChaChaCore<R, V> { |
36 | | type Seed = Seed; |
37 | | |
38 | | #[inline] |
39 | 0 | fn from_seed(seed: Self::Seed) -> Self { |
40 | 0 | ChaChaCore::new_internal(&seed, &[0u8; 8]) |
41 | 0 | } Unexecuted instantiation: <chacha20::ChaChaCore<chacha20::R12, chacha20::variants::Legacy> as rand_core::seedable_rng::SeedableRng>::from_seed Unexecuted instantiation: <chacha20::ChaChaCore<_, _> as rand_core::seedable_rng::SeedableRng>::from_seed |
42 | | } |
43 | | |
44 | | impl<R: Rounds, V: Variant> Generator for ChaChaCore<R, V> { |
45 | | type Output = [u32; BUFFER_SIZE]; |
46 | | |
47 | | /// Generates 4 blocks in parallel with avx2 & neon, but merely fills |
48 | | /// 4 blocks with sse2 & soft |
49 | 0 | fn generate(&mut self, buffer: &mut [u32; BUFFER_SIZE]) { |
50 | | cfg_if! { |
51 | | if #[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"))] { |
54 | | cfg_if! { |
55 | | // AVX-512 doesn't support RNG, so use AVX-2 instead |
56 | | if #[cfg(any(chacha20_backend = "avx2", chacha20_backend = "avx512"))] { |
57 | | unsafe { |
58 | | backends::avx2::rng_inner::<R, V>(self, buffer); |
59 | | } |
60 | | } else if #[cfg(chacha20_backend = "sse2")] { |
61 | | unsafe { |
62 | | backends::sse2::rng_inner::<R, V>(self, buffer); |
63 | | } |
64 | | } else { |
65 | | #[cfg(chacha20_avx512)] |
66 | | let (_avx512_token, avx2_token, sse2_token) = self.tokens; |
67 | | #[cfg(not(chacha20_avx512))] |
68 | 0 | let (avx2_token, sse2_token) = self.tokens; |
69 | | |
70 | 0 | if avx2_token.get() { |
71 | 0 | unsafe { |
72 | 0 | backends::avx2::rng_inner::<R, V>(self, buffer); |
73 | 0 | } |
74 | 0 | } else if sse2_token.get() { |
75 | 0 | unsafe { |
76 | 0 | backends::sse2::rng_inner::<R, V>(self, buffer); |
77 | 0 | } |
78 | 0 | } else { |
79 | 0 | backends::soft::Backend(self).gen_ks_blocks(buffer); |
80 | 0 | } |
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 |
85 | | unsafe { |
86 | | backends::neon::rng_inner::<R, V>(self, buffer); |
87 | | } |
88 | | } else { |
89 | | backends::soft::Backend(self).gen_ks_blocks(buffer); |
90 | | } |
91 | | } |
92 | 0 | } Unexecuted instantiation: <chacha20::ChaChaCore<chacha20::R12, chacha20::variants::Legacy> as rand_core::block::Generator>::generate Unexecuted instantiation: <chacha20::ChaChaCore<_, _> as rand_core::block::Generator>::generate |
93 | | |
94 | | // `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")] |
97 | | fn drop(&mut self, output: &mut Self::Output) { |
98 | | output.zeroize(); |
99 | | } |
100 | | } |
101 | | |
102 | | macro_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. |
134 | | pub struct $Rng { |
135 | | core: BlockRng<ChaChaCore<$rounds, Legacy>>, |
136 | | } |
137 | | |
138 | | impl SeedableRng for $Rng { |
139 | | type Seed = Seed; |
140 | | |
141 | | #[inline] |
142 | 0 | fn from_seed(seed: Self::Seed) -> Self { |
143 | 0 | let core = ChaChaCore::new_internal(&seed, &[0u8; 8]); |
144 | 0 | Self { |
145 | 0 | core: BlockRng::new(core), |
146 | 0 | } |
147 | 0 | } Unexecuted instantiation: <chacha20::rng::ChaCha8Rng as rand_core::seedable_rng::SeedableRng>::from_seed Unexecuted instantiation: <chacha20::rng::ChaCha12Rng as rand_core::seedable_rng::SeedableRng>::from_seed Unexecuted instantiation: <chacha20::rng::ChaCha20Rng as rand_core::seedable_rng::SeedableRng>::from_seed |
148 | | } |
149 | | |
150 | | impl TryRng for $Rng { |
151 | | type Error = Infallible; |
152 | | |
153 | | #[inline] |
154 | 0 | fn try_next_u32(&mut self) -> Result<u32, Self::Error> { |
155 | 0 | Ok(self.core.next_word()) |
156 | 0 | } Unexecuted instantiation: <chacha20::rng::ChaCha8Rng as rand_core::TryRng>::try_next_u32 Unexecuted instantiation: <chacha20::rng::ChaCha12Rng as rand_core::TryRng>::try_next_u32 Unexecuted instantiation: <chacha20::rng::ChaCha20Rng as rand_core::TryRng>::try_next_u32 |
157 | | #[inline] |
158 | 0 | fn try_next_u64(&mut self) -> Result<u64, Self::Error> { |
159 | 0 | Ok(self.core.next_u64_from_u32()) |
160 | 0 | } Unexecuted instantiation: <chacha20::rng::ChaCha8Rng as rand_core::TryRng>::try_next_u64 Unexecuted instantiation: <chacha20::rng::ChaCha12Rng as rand_core::TryRng>::try_next_u64 Unexecuted instantiation: <chacha20::rng::ChaCha20Rng as rand_core::TryRng>::try_next_u64 |
161 | | #[inline] |
162 | 0 | fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> { |
163 | 0 | self.core.fill_bytes(dest); |
164 | 0 | Ok(()) |
165 | 0 | } Unexecuted instantiation: <chacha20::rng::ChaCha8Rng as rand_core::TryRng>::try_fill_bytes Unexecuted instantiation: <chacha20::rng::ChaCha12Rng as rand_core::TryRng>::try_fill_bytes Unexecuted instantiation: <chacha20::rng::ChaCha20Rng as rand_core::TryRng>::try_fill_bytes |
166 | | } |
167 | | |
168 | | impl TryCryptoRng for $Rng {} |
169 | | |
170 | | #[cfg(feature = "zeroize")] |
171 | | impl ZeroizeOnDrop for $Rng {} |
172 | | |
173 | | // 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. |
176 | | impl PartialEq<$Rng> for $Rng { |
177 | 0 | fn eq(&self, rhs: &$Rng) -> bool { |
178 | 0 | (self.get_seed() == rhs.get_seed()) |
179 | 0 | && (self.get_stream() == rhs.get_stream()) |
180 | 0 | && (self.get_word_pos() == rhs.get_word_pos()) |
181 | 0 | } Unexecuted instantiation: <chacha20::rng::ChaCha8Rng as core::cmp::PartialEq>::eq Unexecuted instantiation: <chacha20::rng::ChaCha12Rng as core::cmp::PartialEq>::eq Unexecuted instantiation: <chacha20::rng::ChaCha20Rng as core::cmp::PartialEq>::eq |
182 | | } |
183 | | |
184 | | impl Eq for $Rng {} |
185 | | |
186 | | // Custom Debug implementation that does not expose the internal state |
187 | | impl fmt::Debug for $Rng { |
188 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
189 | 0 | write!(f, concat!(stringify!($Rng), " {{ ... }}")) |
190 | 0 | } Unexecuted instantiation: <chacha20::rng::ChaCha8Rng as core::fmt::Debug>::fmt Unexecuted instantiation: <chacha20::rng::ChaCha12Rng as core::fmt::Debug>::fmt Unexecuted instantiation: <chacha20::rng::ChaCha20Rng as core::fmt::Debug>::fmt |
191 | | } |
192 | | |
193 | | impl $Rng { |
194 | | /// Get the offset from the start of the stream, in 32-bit words. |
195 | | /// |
196 | | /// Since the generated blocks are 64 words (2<sup>6</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] |
202 | 0 | pub fn get_word_pos(&self) -> u128 { |
203 | 0 | let mut block_counter = (u64::from(self.core.core.state[13]) << 32) |
204 | 0 | | u64::from(self.core.core.state[12]); |
205 | 0 | if self.core.word_offset() != 0 { |
206 | 0 | block_counter = block_counter.wrapping_sub(u64::from(BUF_BLOCKS)); |
207 | 0 | } |
208 | 0 | let word_pos = u128::from(block_counter) * u128::from(BLOCK_WORDS) |
209 | 0 | + self.core.word_offset() as u128; |
210 | | // eliminate bits above the 68th bit |
211 | 0 | word_pos & ((1 << 68) - 1) |
212 | 0 | } Unexecuted instantiation: <chacha20::rng::ChaCha8Rng>::get_word_pos Unexecuted instantiation: <chacha20::rng::ChaCha12Rng>::get_word_pos Unexecuted instantiation: <chacha20::rng::ChaCha20Rng>::get_word_pos |
213 | | |
214 | | /// 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] |
224 | 0 | pub fn set_word_pos(&mut self, word_offset: u128) { |
225 | 0 | let index = (word_offset % u128::from(BLOCK_WORDS)) as usize; |
226 | 0 | let counter = word_offset / u128::from(BLOCK_WORDS); |
227 | | //self.set_block_pos(counter as u64); |
228 | 0 | self.core.core.state[12] = counter as u32; |
229 | 0 | self.core.core.state[13] = (counter >> 32) as u32; |
230 | 0 | self.core.reset_and_skip(index); |
231 | 0 | } Unexecuted instantiation: <chacha20::rng::ChaCha8Rng>::set_word_pos Unexecuted instantiation: <chacha20::rng::ChaCha12Rng>::set_word_pos Unexecuted instantiation: <chacha20::rng::ChaCha20Rng>::set_word_pos |
232 | | |
233 | | /// 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)] |
242 | 0 | pub fn set_block_pos(&mut self, block_pos: u64) { |
243 | 0 | self.core.reset_and_skip(0); |
244 | 0 | self.core.core.set_block_pos(block_pos); |
245 | 0 | } Unexecuted instantiation: <chacha20::rng::ChaCha8Rng>::set_block_pos Unexecuted instantiation: <chacha20::rng::ChaCha12Rng>::set_block_pos Unexecuted instantiation: <chacha20::rng::ChaCha20Rng>::set_block_pos |
246 | | |
247 | | /// Get the block pos. |
248 | | #[inline] |
249 | | #[allow(unused)] |
250 | | #[must_use] |
251 | 0 | pub fn get_block_pos(&self) -> u64 { |
252 | 0 | let counter = self.core.core.get_block_pos(); |
253 | 0 | let offset = self.core.word_offset(); |
254 | 0 | if offset != 0 { |
255 | 0 | counter - u64::from(BUF_BLOCKS) + offset as u64 / 16 |
256 | | } else { |
257 | 0 | counter |
258 | | } |
259 | 0 | } Unexecuted instantiation: <chacha20::rng::ChaCha8Rng>::get_block_pos Unexecuted instantiation: <chacha20::rng::ChaCha12Rng>::get_block_pos Unexecuted instantiation: <chacha20::rng::ChaCha20Rng>::get_block_pos |
260 | | |
261 | | /// Set the stream ID and reset the `word_pos` to 0. |
262 | | #[inline] |
263 | 0 | pub fn set_stream(&mut self, stream: u64) { |
264 | 0 | self.core.core.state[14] = stream as u32; |
265 | 0 | self.core.core.state[15] = (stream >> 32) as u32; |
266 | 0 | self.set_block_pos(0); |
267 | 0 | } Unexecuted instantiation: <chacha20::rng::ChaCha8Rng>::set_stream Unexecuted instantiation: <chacha20::rng::ChaCha12Rng>::set_stream Unexecuted instantiation: <chacha20::rng::ChaCha20Rng>::set_stream |
268 | | |
269 | | /// Get the stream number (nonce). |
270 | | #[inline] |
271 | | #[must_use] |
272 | 0 | pub fn get_stream(&self) -> u64 { |
273 | 0 | let mut result = [0u8; 8]; |
274 | 0 | result[..4].copy_from_slice(&self.core.core.state[14].to_le_bytes()); |
275 | 0 | result[4..].copy_from_slice(&self.core.core.state[15].to_le_bytes()); |
276 | 0 | u64::from_le_bytes(result) |
277 | 0 | } Unexecuted instantiation: <chacha20::rng::ChaCha8Rng>::get_stream Unexecuted instantiation: <chacha20::rng::ChaCha12Rng>::get_stream Unexecuted instantiation: <chacha20::rng::ChaCha20Rng>::get_stream |
278 | | |
279 | | /// Get the RNG seed. |
280 | | #[inline] |
281 | | #[must_use] |
282 | 0 | pub fn get_seed(&self) -> [u8; 32] { |
283 | 0 | let seed = &self.core.core.state[4..12]; |
284 | 0 | let mut result = [0u8; 32]; |
285 | 0 | for (src, dst) in seed.iter().zip(result.chunks_exact_mut(4)) { |
286 | 0 | dst.copy_from_slice(&src.to_le_bytes()) |
287 | | } |
288 | 0 | result |
289 | 0 | } Unexecuted instantiation: <chacha20::rng::ChaCha8Rng>::get_seed Unexecuted instantiation: <chacha20::rng::ChaCha12Rng>::get_seed Unexecuted instantiation: <chacha20::rng::ChaCha20Rng>::get_seed |
290 | | |
291 | | /// Serialize RNG state. |
292 | | /// |
293 | | /// # Warning |
294 | | /// Leaking serialized RNG state to an attacker defeats security properties |
295 | | /// provided by the RNG. |
296 | | #[inline] |
297 | 0 | pub fn serialize_state(&self) -> SerializedRngState { |
298 | 0 | let seed = self.get_seed(); |
299 | 0 | let stream = self.get_stream().to_le_bytes(); |
300 | 0 | let word_pos = self.get_word_pos().to_le_bytes(); |
301 | | |
302 | 0 | let mut res = [0u8; 49]; |
303 | 0 | let (seed_dst, res_rem) = res.split_at_mut(32); |
304 | 0 | let (stream_dst, word_pos_dst) = res_rem.split_at_mut(8); |
305 | | |
306 | 0 | seed_dst.copy_from_slice(&seed); |
307 | 0 | stream_dst.copy_from_slice(&stream); |
308 | 0 | word_pos_dst.copy_from_slice(&word_pos[..9]); |
309 | | |
310 | 0 | debug_assert_eq!(&word_pos[9..], &[0u8; 7]); |
311 | | |
312 | 0 | res |
313 | 0 | } Unexecuted instantiation: <chacha20::rng::ChaCha8Rng>::serialize_state Unexecuted instantiation: <chacha20::rng::ChaCha12Rng>::serialize_state Unexecuted instantiation: <chacha20::rng::ChaCha20Rng>::serialize_state |
314 | | |
315 | | /// Deserialize RNG state. |
316 | | #[inline] |
317 | 0 | pub fn deserialize_state(state: &SerializedRngState) -> Self { |
318 | 0 | let (seed, state_rem) = state.split_at(32); |
319 | 0 | let (stream, word_pos_raw) = state_rem.split_at(8); |
320 | | |
321 | 0 | let seed: &[u8; 32] = seed.try_into().expect("seed.len() is equal to 32"); |
322 | 0 | let stream: &[u8; 8] = stream.try_into().expect("stream.len() is equal to 8"); |
323 | | |
324 | | // 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. |
326 | 0 | let mut word_pos_buf = [0u8; 16]; |
327 | 0 | word_pos_buf[..9].copy_from_slice(word_pos_raw); |
328 | 0 | let word_pos = u128::from_le_bytes(word_pos_buf); |
329 | | |
330 | 0 | let core = ChaChaCore::new_internal(seed, stream); |
331 | 0 | let mut res = Self { |
332 | 0 | core: BlockRng::new(core), |
333 | 0 | }; |
334 | | |
335 | 0 | res.set_word_pos(word_pos); |
336 | 0 | res |
337 | 0 | } Unexecuted instantiation: <chacha20::rng::ChaCha8Rng>::deserialize_state Unexecuted instantiation: <chacha20::rng::ChaCha12Rng>::deserialize_state Unexecuted instantiation: <chacha20::rng::ChaCha20Rng>::deserialize_state |
338 | | } |
339 | | }; |
340 | | } |
341 | | |
342 | | impl_chacha_rng!(ChaCha8Rng, R8); |
343 | | impl_chacha_rng!(ChaCha12Rng, R12); |
344 | | impl_chacha_rng!(ChaCha20Rng, R20); |