1 // Copyright 2018 Developers of the Rand project. 2 // 3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or 4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license 5 // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your 6 // option. This file may not be copied, modified, or distributed 7 // except according to those terms. 8 9 //! The ChaCha random number generator. 10 11 #[cfg(not(feature = "std"))] use core; 12 #[cfg(feature = "std")] use std as core; 13 14 use self::core::fmt; 15 use crate::guts::ChaCha; 16 use rand_core::block::{BlockRng, BlockRngCore}; 17 use rand_core::{CryptoRng, Error, RngCore, SeedableRng}; 18 19 #[cfg(feature = "serde1")] use serde::{Serialize, Deserialize, Serializer, Deserializer}; 20 21 // NB. this must remain consistent with some currently hard-coded numbers in this module 22 const BUF_BLOCKS: u8 = 4; 23 // number of 32-bit words per ChaCha block (fixed by algorithm definition) 24 const BLOCK_WORDS: u8 = 16; 25 26 #[repr(transparent)] 27 pub struct Array64<T>([T; 64]); 28 impl<T> Default for Array64<T> 29 where T: Default 30 { 31 #[rustfmt::skip] default() -> Self32 fn default() -> Self { 33 Self([ 34 T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), 35 T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), 36 T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), 37 T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), 38 T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), 39 T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), 40 T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), 41 T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), 42 ]) 43 } 44 } 45 impl<T> AsRef<[T]> for Array64<T> { as_ref(&self) -> &[T]46 fn as_ref(&self) -> &[T] { 47 &self.0 48 } 49 } 50 impl<T> AsMut<[T]> for Array64<T> { as_mut(&mut self) -> &mut [T]51 fn as_mut(&mut self) -> &mut [T] { 52 &mut self.0 53 } 54 } 55 impl<T> Clone for Array64<T> 56 where T: Copy + Default 57 { clone(&self) -> Self58 fn clone(&self) -> Self { 59 let mut new = Self::default(); 60 new.0.copy_from_slice(&self.0); 61 new 62 } 63 } 64 impl<T> fmt::Debug for Array64<T> { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result65 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 66 write!(f, "Array64 {{}}") 67 } 68 } 69 70 macro_rules! chacha_impl { 71 ($ChaChaXCore:ident, $ChaChaXRng:ident, $rounds:expr, $doc:expr, $abst:ident) => { 72 #[doc=$doc] 73 #[derive(Clone, PartialEq, Eq)] 74 pub struct $ChaChaXCore { 75 state: ChaCha, 76 } 77 78 // Custom Debug implementation that does not expose the internal state 79 impl fmt::Debug for $ChaChaXCore { 80 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 81 write!(f, "ChaChaXCore {{}}") 82 } 83 } 84 85 impl BlockRngCore for $ChaChaXCore { 86 type Item = u32; 87 type Results = Array64<u32>; 88 #[inline] 89 fn generate(&mut self, r: &mut Self::Results) { 90 // Fill slice of words by writing to equivalent slice of bytes, then fixing endianness. 91 self.state.refill4($rounds, unsafe { 92 &mut *(&mut *r as *mut Array64<u32> as *mut [u8; 256]) 93 }); 94 for x in r.as_mut() { 95 *x = x.to_le(); 96 } 97 } 98 } 99 100 impl SeedableRng for $ChaChaXCore { 101 type Seed = [u8; 32]; 102 #[inline] 103 fn from_seed(seed: Self::Seed) -> Self { 104 $ChaChaXCore { state: ChaCha::new(&seed, &[0u8; 8]) } 105 } 106 } 107 108 impl CryptoRng for $ChaChaXCore {} 109 110 /// A cryptographically secure random number generator that uses the ChaCha algorithm. 111 /// 112 /// ChaCha is a stream cipher designed by Daniel J. Bernstein[^1], that we use as an RNG. It is 113 /// an improved variant of the Salsa20 cipher family, which was selected as one of the "stream 114 /// ciphers suitable for widespread adoption" by eSTREAM[^2]. 115 /// 116 /// ChaCha uses add-rotate-xor (ARX) operations as its basis. These are safe against timing 117 /// attacks, although that is mostly a concern for ciphers and not for RNGs. We provide a SIMD 118 /// implementation to support high throughput on a variety of common hardware platforms. 119 /// 120 /// With the ChaCha algorithm it is possible to choose the number of rounds the core algorithm 121 /// should run. The number of rounds is a tradeoff between performance and security, where 8 122 /// rounds is the minimum potentially secure configuration, and 20 rounds is widely used as a 123 /// conservative choice. 124 /// 125 /// We use a 64-bit counter and 64-bit stream identifier as in Bernstein's implementation[^1] 126 /// except that we use a stream identifier in place of a nonce. A 64-bit counter over 64-byte 127 /// (16 word) blocks allows 1 ZiB of output before cycling, and the stream identifier allows 128 /// 2<sup>64</sup> unique streams of output per seed. Both counter and stream are initialized 129 /// to zero but may be set via the `set_word_pos` and `set_stream` methods. 130 /// 131 /// The word layout is: 132 /// 133 /// ```text 134 /// constant constant constant constant 135 /// seed seed seed seed 136 /// seed seed seed seed 137 /// counter counter stream_id stream_id 138 /// ``` 139 /// 140 /// This implementation uses an output buffer of sixteen `u32` words, and uses 141 /// [`BlockRng`] to implement the [`RngCore`] methods. 142 /// 143 /// [^1]: D. J. Bernstein, [*ChaCha, a variant of Salsa20*]( 144 /// https://cr.yp.to/chacha.html) 145 /// 146 /// [^2]: [eSTREAM: the ECRYPT Stream Cipher Project]( 147 /// http://www.ecrypt.eu.org/stream/) 148 #[derive(Clone, Debug)] 149 pub struct $ChaChaXRng { 150 rng: BlockRng<$ChaChaXCore>, 151 } 152 153 impl SeedableRng for $ChaChaXRng { 154 type Seed = [u8; 32]; 155 #[inline] 156 fn from_seed(seed: Self::Seed) -> Self { 157 let core = $ChaChaXCore::from_seed(seed); 158 Self { 159 rng: BlockRng::new(core), 160 } 161 } 162 } 163 164 impl RngCore for $ChaChaXRng { 165 #[inline] 166 fn next_u32(&mut self) -> u32 { 167 self.rng.next_u32() 168 } 169 #[inline] 170 fn next_u64(&mut self) -> u64 { 171 self.rng.next_u64() 172 } 173 #[inline] 174 fn fill_bytes(&mut self, bytes: &mut [u8]) { 175 self.rng.fill_bytes(bytes) 176 } 177 #[inline] 178 fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> Result<(), Error> { 179 self.rng.try_fill_bytes(bytes) 180 } 181 } 182 183 impl $ChaChaXRng { 184 // The buffer is a 4-block window, i.e. it is always at a block-aligned position in the 185 // stream but if the stream has been seeked it may not be self-aligned. 186 187 /// Get the offset from the start of the stream, in 32-bit words. 188 /// 189 /// Since the generated blocks are 16 words (2<sup>4</sup>) long and the 190 /// counter is 64-bits, the offset is a 68-bit number. Sub-word offsets are 191 /// not supported, hence the result can simply be multiplied by 4 to get a 192 /// byte-offset. 193 #[inline] 194 pub fn get_word_pos(&self) -> u128 { 195 let buf_start_block = { 196 let buf_end_block = self.rng.core.state.get_block_pos(); 197 u64::wrapping_sub(buf_end_block, BUF_BLOCKS.into()) 198 }; 199 let (buf_offset_blocks, block_offset_words) = { 200 let buf_offset_words = self.rng.index() as u64; 201 let blocks_part = buf_offset_words / u64::from(BLOCK_WORDS); 202 let words_part = buf_offset_words % u64::from(BLOCK_WORDS); 203 (blocks_part, words_part) 204 }; 205 let pos_block = u64::wrapping_add(buf_start_block, buf_offset_blocks); 206 let pos_block_words = u128::from(pos_block) * u128::from(BLOCK_WORDS); 207 pos_block_words + u128::from(block_offset_words) 208 } 209 210 /// Set the offset from the start of the stream, in 32-bit words. 211 /// 212 /// As with `get_word_pos`, we use a 68-bit number. Since the generator 213 /// simply cycles at the end of its period (1 ZiB), we ignore the upper 214 /// 60 bits. 215 #[inline] 216 pub fn set_word_pos(&mut self, word_offset: u128) { 217 let block = (word_offset / u128::from(BLOCK_WORDS)) as u64; 218 self.rng 219 .core 220 .state 221 .set_block_pos(block); 222 self.rng.generate_and_set((word_offset % u128::from(BLOCK_WORDS)) as usize); 223 } 224 225 /// Set the stream number. 226 /// 227 /// This is initialized to zero; 2<sup>64</sup> unique streams of output 228 /// are available per seed/key. 229 /// 230 /// Note that in order to reproduce ChaCha output with a specific 64-bit 231 /// nonce, one can convert that nonce to a `u64` in little-endian fashion 232 /// and pass to this function. In theory a 96-bit nonce can be used by 233 /// passing the last 64-bits to this function and using the first 32-bits as 234 /// the most significant half of the 64-bit counter (which may be set 235 /// indirectly via `set_word_pos`), but this is not directly supported. 236 #[inline] 237 pub fn set_stream(&mut self, stream: u64) { 238 self.rng 239 .core 240 .state 241 .set_nonce(stream); 242 if self.rng.index() != 64 { 243 let wp = self.get_word_pos(); 244 self.set_word_pos(wp); 245 } 246 } 247 248 /// Get the stream number. 249 #[inline] 250 pub fn get_stream(&self) -> u64 { 251 self.rng 252 .core 253 .state 254 .get_nonce() 255 } 256 257 /// Get the seed. 258 #[inline] 259 pub fn get_seed(&self) -> [u8; 32] { 260 self.rng 261 .core 262 .state 263 .get_seed() 264 } 265 } 266 267 impl CryptoRng for $ChaChaXRng {} 268 269 impl From<$ChaChaXCore> for $ChaChaXRng { 270 fn from(core: $ChaChaXCore) -> Self { 271 $ChaChaXRng { 272 rng: BlockRng::new(core), 273 } 274 } 275 } 276 277 impl PartialEq<$ChaChaXRng> for $ChaChaXRng { 278 fn eq(&self, rhs: &$ChaChaXRng) -> bool { 279 let a: $abst::$ChaChaXRng = self.into(); 280 let b: $abst::$ChaChaXRng = rhs.into(); 281 a == b 282 } 283 } 284 impl Eq for $ChaChaXRng {} 285 286 #[cfg(feature = "serde1")] 287 impl Serialize for $ChaChaXRng { 288 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error> 289 where S: Serializer { 290 $abst::$ChaChaXRng::from(self).serialize(s) 291 } 292 } 293 #[cfg(feature = "serde1")] 294 impl<'de> Deserialize<'de> for $ChaChaXRng { 295 fn deserialize<D>(d: D) -> Result<Self, D::Error> where D: Deserializer<'de> { 296 $abst::$ChaChaXRng::deserialize(d).map(|x| Self::from(&x)) 297 } 298 } 299 300 mod $abst { 301 #[cfg(feature = "serde1")] use serde::{Serialize, Deserialize}; 302 303 // The abstract state of a ChaCha stream, independent of implementation choices. The 304 // comparison and serialization of this object is considered a semver-covered part of 305 // the API. 306 #[derive(Debug, PartialEq, Eq)] 307 #[cfg_attr( 308 feature = "serde1", 309 derive(Serialize, Deserialize), 310 )] 311 pub(crate) struct $ChaChaXRng { 312 seed: [u8; 32], 313 stream: u64, 314 word_pos: u128, 315 } 316 317 impl From<&super::$ChaChaXRng> for $ChaChaXRng { 318 // Forget all information about the input except what is necessary to determine the 319 // outputs of any sequence of pub API calls. 320 fn from(r: &super::$ChaChaXRng) -> Self { 321 Self { 322 seed: r.get_seed(), 323 stream: r.get_stream(), 324 word_pos: r.get_word_pos(), 325 } 326 } 327 } 328 329 impl From<&$ChaChaXRng> for super::$ChaChaXRng { 330 // Construct one of the possible concrete RNGs realizing an abstract state. 331 fn from(a: &$ChaChaXRng) -> Self { 332 use rand_core::SeedableRng; 333 let mut r = Self::from_seed(a.seed); 334 r.set_stream(a.stream); 335 r.set_word_pos(a.word_pos); 336 r 337 } 338 } 339 } 340 } 341 } 342 343 chacha_impl!(ChaCha20Core, ChaCha20Rng, 10, "ChaCha with 20 rounds", abstract20); 344 chacha_impl!(ChaCha12Core, ChaCha12Rng, 6, "ChaCha with 12 rounds", abstract12); 345 chacha_impl!(ChaCha8Core, ChaCha8Rng, 4, "ChaCha with 8 rounds", abstract8); 346 347 #[cfg(test)] 348 mod test { 349 use rand_core::{RngCore, SeedableRng}; 350 351 #[cfg(feature = "serde1")] use super::{ChaCha20Rng, ChaCha12Rng, ChaCha8Rng}; 352 353 type ChaChaRng = super::ChaCha20Rng; 354 355 #[cfg(feature = "serde1")] 356 #[test] test_chacha_serde_roundtrip()357 fn test_chacha_serde_roundtrip() { 358 let seed = [ 359 1, 0, 52, 0, 0, 0, 0, 0, 1, 0, 10, 0, 22, 32, 0, 0, 2, 0, 55, 49, 0, 11, 0, 0, 3, 0, 0, 0, 0, 360 0, 2, 92, 361 ]; 362 let mut rng1 = ChaCha20Rng::from_seed(seed); 363 let mut rng2 = ChaCha12Rng::from_seed(seed); 364 let mut rng3 = ChaCha8Rng::from_seed(seed); 365 366 let encoded1 = serde_json::to_string(&rng1).unwrap(); 367 let encoded2 = serde_json::to_string(&rng2).unwrap(); 368 let encoded3 = serde_json::to_string(&rng3).unwrap(); 369 370 let mut decoded1: ChaCha20Rng = serde_json::from_str(&encoded1).unwrap(); 371 let mut decoded2: ChaCha12Rng = serde_json::from_str(&encoded2).unwrap(); 372 let mut decoded3: ChaCha8Rng = serde_json::from_str(&encoded3).unwrap(); 373 374 assert_eq!(rng1, decoded1); 375 assert_eq!(rng2, decoded2); 376 assert_eq!(rng3, decoded3); 377 378 assert_eq!(rng1.next_u32(), decoded1.next_u32()); 379 assert_eq!(rng2.next_u32(), decoded2.next_u32()); 380 assert_eq!(rng3.next_u32(), decoded3.next_u32()); 381 } 382 383 // This test validates that: 384 // 1. a hard-coded serialization demonstrating the format at time of initial release can still 385 // be deserialized to a ChaChaRng 386 // 2. re-serializing the resultant object produces exactly the original string 387 // 388 // Condition 2 is stronger than necessary: an equivalent serialization (e.g. with field order 389 // permuted, or whitespace differences) would also be admissible, but would fail this test. 390 // However testing for equivalence of serialized data is difficult, and there shouldn't be any 391 // reason we need to violate the stronger-than-needed condition, e.g. by changing the field 392 // definition order. 393 #[cfg(feature = "serde1")] 394 #[test] test_chacha_serde_format_stability()395 fn test_chacha_serde_format_stability() { 396 let j = r#"{"seed":[4,8,15,16,23,42,4,8,15,16,23,42,4,8,15,16,23,42,4,8,15,16,23,42,4,8,15,16,23,42,4,8],"stream":27182818284,"word_pos":314159265359}"#; 397 let r: ChaChaRng = serde_json::from_str(&j).unwrap(); 398 let j1 = serde_json::to_string(&r).unwrap(); 399 assert_eq!(j, j1); 400 } 401 402 #[test] test_chacha_construction()403 fn test_chacha_construction() { 404 let seed = [ 405 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 406 0, 0, 0, 407 ]; 408 let mut rng1 = ChaChaRng::from_seed(seed); 409 assert_eq!(rng1.next_u32(), 137206642); 410 411 let mut rng2 = ChaChaRng::from_rng(rng1).unwrap(); 412 assert_eq!(rng2.next_u32(), 1325750369); 413 } 414 415 #[test] test_chacha_true_values_a()416 fn test_chacha_true_values_a() { 417 // Test vectors 1 and 2 from 418 // https://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04 419 let seed = [0u8; 32]; 420 let mut rng = ChaChaRng::from_seed(seed); 421 422 let mut results = [0u32; 16]; 423 for i in results.iter_mut() { 424 *i = rng.next_u32(); 425 } 426 let expected = [ 427 0xade0b876, 0x903df1a0, 0xe56a5d40, 0x28bd8653, 0xb819d2bd, 0x1aed8da0, 0xccef36a8, 428 0xc70d778b, 0x7c5941da, 0x8d485751, 0x3fe02477, 0x374ad8b8, 0xf4b8436a, 0x1ca11815, 429 0x69b687c3, 0x8665eeb2, 430 ]; 431 assert_eq!(results, expected); 432 433 for i in results.iter_mut() { 434 *i = rng.next_u32(); 435 } 436 let expected = [ 437 0xbee7079f, 0x7a385155, 0x7c97ba98, 0x0d082d73, 0xa0290fcb, 0x6965e348, 0x3e53c612, 438 0xed7aee32, 0x7621b729, 0x434ee69c, 0xb03371d5, 0xd539d874, 0x281fed31, 0x45fb0a51, 439 0x1f0ae1ac, 0x6f4d794b, 440 ]; 441 assert_eq!(results, expected); 442 } 443 444 #[test] test_chacha_true_values_b()445 fn test_chacha_true_values_b() { 446 // Test vector 3 from 447 // https://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04 448 let seed = [ 449 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 450 0, 0, 1, 451 ]; 452 let mut rng = ChaChaRng::from_seed(seed); 453 454 // Skip block 0 455 for _ in 0..16 { 456 rng.next_u32(); 457 } 458 459 let mut results = [0u32; 16]; 460 for i in results.iter_mut() { 461 *i = rng.next_u32(); 462 } 463 let expected = [ 464 0x2452eb3a, 0x9249f8ec, 0x8d829d9b, 0xddd4ceb1, 0xe8252083, 0x60818b01, 0xf38422b8, 465 0x5aaa49c9, 0xbb00ca8e, 0xda3ba7b4, 0xc4b592d1, 0xfdf2732f, 0x4436274e, 0x2561b3c8, 466 0xebdd4aa6, 0xa0136c00, 467 ]; 468 assert_eq!(results, expected); 469 } 470 471 #[test] test_chacha_true_values_c()472 fn test_chacha_true_values_c() { 473 // Test vector 4 from 474 // https://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04 475 let seed = [ 476 0, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 477 0, 0, 0, 0, 478 ]; 479 let expected = [ 480 0xfb4dd572, 0x4bc42ef1, 0xdf922636, 0x327f1394, 0xa78dea8f, 0x5e269039, 0xa1bebbc1, 481 0xcaf09aae, 0xa25ab213, 0x48a6b46c, 0x1b9d9bcb, 0x092c5be6, 0x546ca624, 0x1bec45d5, 482 0x87f47473, 0x96f0992e, 483 ]; 484 let expected_end = 3 * 16; 485 let mut results = [0u32; 16]; 486 487 // Test block 2 by skipping block 0 and 1 488 let mut rng1 = ChaChaRng::from_seed(seed); 489 for _ in 0..32 { 490 rng1.next_u32(); 491 } 492 for i in results.iter_mut() { 493 *i = rng1.next_u32(); 494 } 495 assert_eq!(results, expected); 496 assert_eq!(rng1.get_word_pos(), expected_end); 497 498 // Test block 2 by using `set_word_pos` 499 let mut rng2 = ChaChaRng::from_seed(seed); 500 rng2.set_word_pos(2 * 16); 501 for i in results.iter_mut() { 502 *i = rng2.next_u32(); 503 } 504 assert_eq!(results, expected); 505 assert_eq!(rng2.get_word_pos(), expected_end); 506 507 // Test skipping behaviour with other types 508 let mut buf = [0u8; 32]; 509 rng2.fill_bytes(&mut buf[..]); 510 assert_eq!(rng2.get_word_pos(), expected_end + 8); 511 rng2.fill_bytes(&mut buf[0..25]); 512 assert_eq!(rng2.get_word_pos(), expected_end + 15); 513 rng2.next_u64(); 514 assert_eq!(rng2.get_word_pos(), expected_end + 17); 515 rng2.next_u32(); 516 rng2.next_u64(); 517 assert_eq!(rng2.get_word_pos(), expected_end + 20); 518 rng2.fill_bytes(&mut buf[0..1]); 519 assert_eq!(rng2.get_word_pos(), expected_end + 21); 520 } 521 522 #[test] test_chacha_multiple_blocks()523 fn test_chacha_multiple_blocks() { 524 let seed = [ 525 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 7, 526 0, 0, 0, 527 ]; 528 let mut rng = ChaChaRng::from_seed(seed); 529 530 // Store the 17*i-th 32-bit word, 531 // i.e., the i-th word of the i-th 16-word block 532 let mut results = [0u32; 16]; 533 for i in results.iter_mut() { 534 *i = rng.next_u32(); 535 for _ in 0..16 { 536 rng.next_u32(); 537 } 538 } 539 let expected = [ 540 0xf225c81a, 0x6ab1be57, 0x04d42951, 0x70858036, 0x49884684, 0x64efec72, 0x4be2d186, 541 0x3615b384, 0x11cfa18e, 0xd3c50049, 0x75c775f6, 0x434c6530, 0x2c5bad8f, 0x898881dc, 542 0x5f1c86d9, 0xc1f8e7f4, 543 ]; 544 assert_eq!(results, expected); 545 } 546 547 #[test] test_chacha_true_bytes()548 fn test_chacha_true_bytes() { 549 let seed = [0u8; 32]; 550 let mut rng = ChaChaRng::from_seed(seed); 551 let mut results = [0u8; 32]; 552 rng.fill_bytes(&mut results); 553 let expected = [ 554 118, 184, 224, 173, 160, 241, 61, 144, 64, 93, 106, 229, 83, 134, 189, 40, 189, 210, 555 25, 184, 160, 141, 237, 26, 168, 54, 239, 204, 139, 119, 13, 199, 556 ]; 557 assert_eq!(results, expected); 558 } 559 560 #[test] test_chacha_nonce()561 fn test_chacha_nonce() { 562 // Test vector 5 from 563 // https://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04 564 // Although we do not support setting a nonce, we try it here anyway so 565 // we can use this test vector. 566 let seed = [0u8; 32]; 567 let mut rng = ChaChaRng::from_seed(seed); 568 // 96-bit nonce in LE order is: 0,0,0,0, 0,0,0,0, 0,0,0,2 569 rng.set_stream(2u64 << (24 + 32)); 570 571 let mut results = [0u32; 16]; 572 for i in results.iter_mut() { 573 *i = rng.next_u32(); 574 } 575 let expected = [ 576 0x374dc6c2, 0x3736d58c, 0xb904e24a, 0xcd3f93ef, 0x88228b1a, 0x96a4dfb3, 0x5b76ab72, 577 0xc727ee54, 0x0e0e978a, 0xf3145c95, 0x1b748ea8, 0xf786c297, 0x99c28f5f, 0x628314e8, 578 0x398a19fa, 0x6ded1b53, 579 ]; 580 assert_eq!(results, expected); 581 } 582 583 #[test] test_chacha_clone_streams()584 fn test_chacha_clone_streams() { 585 let seed = [ 586 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 7, 587 0, 0, 0, 588 ]; 589 let mut rng = ChaChaRng::from_seed(seed); 590 let mut clone = rng.clone(); 591 for _ in 0..16 { 592 assert_eq!(rng.next_u64(), clone.next_u64()); 593 } 594 595 rng.set_stream(51); 596 for _ in 0..7 { 597 assert!(rng.next_u32() != clone.next_u32()); 598 } 599 clone.set_stream(51); // switch part way through block 600 for _ in 7..16 { 601 assert_eq!(rng.next_u32(), clone.next_u32()); 602 } 603 } 604 605 #[test] test_chacha_word_pos_wrap_exact()606 fn test_chacha_word_pos_wrap_exact() { 607 use super::{BUF_BLOCKS, BLOCK_WORDS}; 608 let mut rng = ChaChaRng::from_seed(Default::default()); 609 // refilling the buffer in set_word_pos will wrap the block counter to 0 610 let last_block = (1 << 68) - u128::from(BUF_BLOCKS * BLOCK_WORDS); 611 rng.set_word_pos(last_block); 612 assert_eq!(rng.get_word_pos(), last_block); 613 } 614 615 #[test] test_chacha_word_pos_wrap_excess()616 fn test_chacha_word_pos_wrap_excess() { 617 use super::BLOCK_WORDS; 618 let mut rng = ChaChaRng::from_seed(Default::default()); 619 // refilling the buffer in set_word_pos will wrap the block counter past 0 620 let last_block = (1 << 68) - u128::from(BLOCK_WORDS); 621 rng.set_word_pos(last_block); 622 assert_eq!(rng.get_word_pos(), last_block); 623 } 624 625 #[test] test_chacha_word_pos_zero()626 fn test_chacha_word_pos_zero() { 627 let mut rng = ChaChaRng::from_seed(Default::default()); 628 assert_eq!(rng.get_word_pos(), 0); 629 rng.set_word_pos(0); 630 assert_eq!(rng.get_word_pos(), 0); 631 } 632 } 633