blob: ef4734cef63d8fe63a9a3232392c9c7d1df8aea7 [file] [log] [blame]
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! The HC-128 random number generator.
use core::fmt;
use rand_core::block::{BlockRng, BlockRngCore};
use rand_core::{le, CryptoRng, Error, RngCore, SeedableRng};
const SEED_WORDS: usize = 8; // 128 bit key followed by 128 bit iv
/// A cryptographically secure random number generator that uses the HC-128
/// algorithm.
///
/// HC-128 is a stream cipher designed by Hongjun Wu[^1], that we use as an
/// RNG. It is selected as one of the "stream ciphers suitable for widespread
/// adoption" by eSTREAM[^2].
///
/// HC-128 is an array based RNG. In this it is similar to RC-4 and ISAAC before
/// it, but those have never been proven cryptographically secure (or have even
/// been significantly compromised, as in the case of RC-4[^5]).
///
/// Because HC-128 works with simple indexing into a large array and with a few
/// operations that parallelize well, it has very good performance. The size of
/// the array it needs, 4kb, can however be a disadvantage.
///
/// This implementation is not based on the version of HC-128 submitted to the
/// eSTREAM contest, but on a later version by the author with a few small
/// improvements from December 15, 2009[^3].
///
/// HC-128 has no known weaknesses that are easier to exploit than doing a
/// brute-force search of 2<sup>128</sup>. A very comprehensive analysis of the
/// current state of known attacks / weaknesses of HC-128 is given in *Some
/// Results On Analysis And Implementation Of HC-128 Stream Cipher*[^4].
///
/// The average cycle length is expected to be
/// 2<sup>1024*32+10-1</sup> = 2<sup>32777</sup>.
/// We support seeding with a 256-bit array, which matches the 128-bit key
/// concatenated with a 128-bit IV from the stream cipher.
///
/// This implementation uses an output buffer of sixteen `u32` words, and uses
/// [`BlockRng`] to implement the [`RngCore`] methods.
///
/// ## References
/// [^1]: Hongjun Wu (2008). ["The Stream Cipher HC-128"](
/// http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc128_p3.pdf).
/// *The eSTREAM Finalists*, LNCS 4986, pp. 39–47, Springer-Verlag.
///
/// [^2]: [eSTREAM: the ECRYPT Stream Cipher Project](
/// http://www.ecrypt.eu.org/stream/)
///
/// [^3]: Hongjun Wu, [Stream Ciphers HC-128 and HC-256](
/// https://www.ntu.edu.sg/home/wuhj/research/hc/index.html)
///
/// [^4]: Shashwat Raizada (January 2015),["Some Results On Analysis And
/// Implementation Of HC-128 Stream Cipher"](
/// http://library.isical.ac.in:8080/jspui/bitstream/123456789/6636/1/TH431.pdf).
///
/// [^5]: Internet Engineering Task Force (February 2015),
/// ["Prohibiting RC4 Cipher Suites"](https://tools.ietf.org/html/rfc7465).
#[derive(Clone, Debug)]
pub struct Hc128Rng(BlockRng<Hc128Core>);
impl RngCore for Hc128Rng {
#[inline]
fn next_bool(&mut self) -> bool {
self.0.next_bool()
}
#[inline]
fn next_u32(&mut self) -> u32 {
self.0.next_u32()
}
#[inline]
fn next_u64(&mut self) -> u64 {
self.0.next_u64()
}
#[inline]
fn fill_bytes(&mut self, dest: &mut [u8]) {
self.0.fill_bytes(dest)
}
#[inline]
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
self.0.try_fill_bytes(dest)
}
}
impl SeedableRng for Hc128Rng {
type Seed = <Hc128Core as SeedableRng>::Seed;
#[inline]
fn from_seed(seed: Self::Seed) -> Self {
Hc128Rng(BlockRng::<Hc128Core>::from_seed(seed))
}
#[inline]
fn from_rng<R: RngCore>(rng: R) -> Result<Self, Error> {
BlockRng::<Hc128Core>::from_rng(rng).map(Hc128Rng)
}
}
impl CryptoRng for Hc128Rng {}
impl PartialEq for Hc128Rng {
fn eq(&self, rhs: &Self) -> bool {
self.0.eq(&rhs.0)
}
}
impl Eq for Hc128Rng {}
/// The core of `Hc128Rng`, used with `BlockRng`.
#[derive(Clone)]
pub struct Hc128Core {
t: [u32; 1024],
counter1024: usize,
}
// Custom Debug implementation that does not expose the internal state
impl fmt::Debug for Hc128Core {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Hc128Core {{}}")
}
}
/// Type representing result of the [`Hc128Core`] iteration
#[derive(Eq, PartialEq, Clone, Debug)]
#[repr(align(4))]
pub struct Results([u8; 64]);
impl Default for Results {
fn default() -> Self {
Self([0u8; 64])
}
}
impl AsRef<[u8]> for Results {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsMut<[u8]> for Results {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
impl BlockRngCore for Hc128Core {
type Results = Results;
fn generate(&mut self, results: &mut Self::Results) {
assert!(self.counter1024 % 16 == 0);
let cc = self.counter1024 % 512;
let dd = (cc + 16) % 512;
let ee = cc.wrapping_sub(16) % 512;
// These asserts let the compiler optimize out the bounds checks.
// Some of them may be superflous, and that's fine:
// they'll be optimized out if that's the case.
assert!(ee + 15 < 512);
assert!(cc + 15 < 512);
assert!(dd < 512);
macro_rules! step {
(
$f:ident, $n:expr,
$a:expr, $b:expr, $c:expr, $d:expr, $e:expr
) => {
let v = self.$f($a, $b, $c, $d, $e);
results.0[4*$n..4*($n+1)].copy_from_slice(&v.to_le_bytes());
};
}
if self.counter1024 & 512 == 0 {
// P block
step!(step_p, 0, cc+0, cc+1, ee+13, ee+6, ee+4);
step!(step_p, 1, cc+1, cc+2, ee+14, ee+7, ee+5);
step!(step_p, 2, cc+2, cc+3, ee+15, ee+8, ee+6);
step!(step_p, 3, cc+3, cc+4, cc+0, ee+9, ee+7);
step!(step_p, 4, cc+4, cc+5, cc+1, ee+10, ee+8);
step!(step_p, 5, cc+5, cc+6, cc+2, ee+11, ee+9);
step!(step_p, 6, cc+6, cc+7, cc+3, ee+12, ee+10);
step!(step_p, 7, cc+7, cc+8, cc+4, ee+13, ee+11);
step!(step_p, 8, cc+8, cc+9, cc+5, ee+14, ee+12);
step!(step_p, 9, cc+9, cc+10, cc+6, ee+15, ee+13);
step!(step_p, 10, cc+10, cc+11, cc+7, cc+0, ee+14);
step!(step_p, 11, cc+11, cc+12, cc+8, cc+1, ee+15);
step!(step_p, 12, cc+12, cc+13, cc+9, cc+2, cc+0);
step!(step_p, 13, cc+13, cc+14, cc+10, cc+3, cc+1);
step!(step_p, 14, cc+14, cc+15, cc+11, cc+4, cc+2);
step!(step_p, 15, cc+15, dd+0, cc+12, cc+5, cc+3);
} else {
// Q block
step!(step_q, 0, cc+0, cc+1, ee+13, ee+6, ee+4);
step!(step_q, 1, cc+1, cc+2, ee+14, ee+7, ee+5);
step!(step_q, 2, cc+2, cc+3, ee+15, ee+8, ee+6);
step!(step_q, 3, cc+3, cc+4, cc+0, ee+9, ee+7);
step!(step_q, 4, cc+4, cc+5, cc+1, ee+10, ee+8);
step!(step_q, 5, cc+5, cc+6, cc+2, ee+11, ee+9);
step!(step_q, 6, cc+6, cc+7, cc+3, ee+12, ee+10);
step!(step_q, 7, cc+7, cc+8, cc+4, ee+13, ee+11);
step!(step_q, 8, cc+8, cc+9, cc+5, ee+14, ee+12);
step!(step_q, 9, cc+9, cc+10, cc+6, ee+15, ee+13);
step!(step_q, 10, cc+10, cc+11, cc+7, cc+0, ee+14);
step!(step_q, 11, cc+11, cc+12, cc+8, cc+1, ee+15);
step!(step_q, 12, cc+12, cc+13, cc+9, cc+2, cc+0);
step!(step_q, 13, cc+13, cc+14, cc+10, cc+3, cc+1);
step!(step_q, 14, cc+14, cc+15, cc+11, cc+4, cc+2);
step!(step_q, 15, cc+15, dd+0, cc+12, cc+5, cc+3);
}
self.counter1024 = self.counter1024.wrapping_add(16);
}
}
impl Hc128Core {
// One step of HC-128, update P and generate 32 bits keystream
#[inline(always)]
fn step_p(&mut self, i: usize, i511: usize, i3: usize, i10: usize, i12: usize) -> u32 {
let (p, q) = self.t.split_at_mut(512);
let temp0 = p[i511].rotate_right(23);
let temp1 = p[i3].rotate_right(10);
let temp2 = p[i10].rotate_right(8);
p[i] = p[i]
.wrapping_add(temp2)
.wrapping_add(temp0 ^ temp1);
let temp3 = {
// The h1 function in HC-128
let a = p[i12] as u8;
let c = (p[i12] >> 16) as u8;
q[a as usize].wrapping_add(q[256 + c as usize])
};
temp3 ^ p[i]
}
// One step of HC-128, update Q and generate 32 bits keystream
// Similar to `step_p`, but `p` and `q` are swapped, and the rotates are to
// the left instead of to the right.
#[inline(always)]
fn step_q(&mut self, i: usize, i511: usize, i3: usize, i10: usize, i12: usize) -> u32 {
let (p, q) = self.t.split_at_mut(512);
let temp0 = q[i511].rotate_left(23);
let temp1 = q[i3].rotate_left(10);
let temp2 = q[i10].rotate_left(8);
q[i] = q
[i]
.wrapping_add(temp2)
.wrapping_add(temp0 ^ temp1);
let temp3 = {
// The h2 function in HC-128
let a = q[i12] as u8;
let c = (q[i12] >> 16) as u8;
p[a as usize].wrapping_add(p[256 + c as usize])
};
temp3 ^ q[i]
}
fn sixteen_steps(&mut self) {
assert!(self.counter1024 % 16 == 0);
let cc = self.counter1024 % 512;
let dd = (cc + 16) % 512;
let ee = cc.wrapping_sub(16) % 512;
// These asserts let the compiler optimize out the bounds checks.
// Some of them may be superflous, and that's fine:
// they'll be optimized out if that's the case.
assert!(ee + 15 < 512);
assert!(cc + 15 < 512);
assert!(dd < 512);
if self.counter1024 < 512 {
// P block
self.t[cc+0] = self.step_p(cc+0, cc+1, ee+13, ee+6, ee+4);
self.t[cc+1] = self.step_p(cc+1, cc+2, ee+14, ee+7, ee+5);
self.t[cc+2] = self.step_p(cc+2, cc+3, ee+15, ee+8, ee+6);
self.t[cc+3] = self.step_p(cc+3, cc+4, cc+0, ee+9, ee+7);
self.t[cc+4] = self.step_p(cc+4, cc+5, cc+1, ee+10, ee+8);
self.t[cc+5] = self.step_p(cc+5, cc+6, cc+2, ee+11, ee+9);
self.t[cc+6] = self.step_p(cc+6, cc+7, cc+3, ee+12, ee+10);
self.t[cc+7] = self.step_p(cc+7, cc+8, cc+4, ee+13, ee+11);
self.t[cc+8] = self.step_p(cc+8, cc+9, cc+5, ee+14, ee+12);
self.t[cc+9] = self.step_p(cc+9, cc+10, cc+6, ee+15, ee+13);
self.t[cc+10] = self.step_p(cc+10, cc+11, cc+7, cc+0, ee+14);
self.t[cc+11] = self.step_p(cc+11, cc+12, cc+8, cc+1, ee+15);
self.t[cc+12] = self.step_p(cc+12, cc+13, cc+9, cc+2, cc+0);
self.t[cc+13] = self.step_p(cc+13, cc+14, cc+10, cc+3, cc+1);
self.t[cc+14] = self.step_p(cc+14, cc+15, cc+11, cc+4, cc+2);
self.t[cc+15] = self.step_p(cc+15, dd+0, cc+12, cc+5, cc+3);
} else {
// Q block
self.t[cc+512+0] = self.step_q(cc+0, cc+1, ee+13, ee+6, ee+4);
self.t[cc+512+1] = self.step_q(cc+1, cc+2, ee+14, ee+7, ee+5);
self.t[cc+512+2] = self.step_q(cc+2, cc+3, ee+15, ee+8, ee+6);
self.t[cc+512+3] = self.step_q(cc+3, cc+4, cc+0, ee+9, ee+7);
self.t[cc+512+4] = self.step_q(cc+4, cc+5, cc+1, ee+10, ee+8);
self.t[cc+512+5] = self.step_q(cc+5, cc+6, cc+2, ee+11, ee+9);
self.t[cc+512+6] = self.step_q(cc+6, cc+7, cc+3, ee+12, ee+10);
self.t[cc+512+7] = self.step_q(cc+7, cc+8, cc+4, ee+13, ee+11);
self.t[cc+512+8] = self.step_q(cc+8, cc+9, cc+5, ee+14, ee+12);
self.t[cc+512+9] = self.step_q(cc+9, cc+10, cc+6, ee+15, ee+13);
self.t[cc+512+10] = self.step_q(cc+10, cc+11, cc+7, cc+0, ee+14);
self.t[cc+512+11] = self.step_q(cc+11, cc+12, cc+8, cc+1, ee+15);
self.t[cc+512+12] = self.step_q(cc+12, cc+13, cc+9, cc+2, cc+0);
self.t[cc+512+13] = self.step_q(cc+13, cc+14, cc+10, cc+3, cc+1);
self.t[cc+512+14] = self.step_q(cc+14, cc+15, cc+11, cc+4, cc+2);
self.t[cc+512+15] = self.step_q(cc+15, dd+0, cc+12, cc+5, cc+3);
}
self.counter1024 += 16;
}
// Initialize an HC-128 random number generator. The seed has to be
// 256 bits in length (`[u32; 8]`), matching the 128 bit `key` followed by
// 128 bit `iv` when HC-128 where to be used as a stream cipher.
#[inline(always)] // single use: SeedableRng::from_seed
fn init(seed: [u32; SEED_WORDS]) -> Self {
#[inline]
fn f1(x: u32) -> u32 {
x.rotate_right(7) ^ x.rotate_right(18) ^ (x >> 3)
}
#[inline]
fn f2(x: u32) -> u32 {
x.rotate_right(17) ^ x.rotate_right(19) ^ (x >> 10)
}
let mut t = [0u32; 1024];
// Expand the key and iv into P and Q
let (key, iv) = seed.split_at(4);
t[..4].copy_from_slice(key);
t[4..8].copy_from_slice(key);
t[8..12].copy_from_slice(iv);
t[12..16].copy_from_slice(iv);
// Generate the 256 intermediate values W[16] ... W[256+16-1], and
// copy the last 16 generated values to the start op P.
for i in 16..256 + 16 {
t[i] = f2(t[i - 2])
.wrapping_add(t[i - 7])
.wrapping_add(f1(t[i - 15]))
.wrapping_add(t[i - 16])
.wrapping_add(i as u32);
}
{
let (p1, p2) = t.split_at_mut(256);
p1[0..16].copy_from_slice(&p2[0..16]);
}
// Generate both the P and Q tables
for i in 16..1024 {
t[i] = f2(t[i - 2])
.wrapping_add(t[i - 7])
.wrapping_add(f1(t[i - 15]))
.wrapping_add(t[i - 16])
.wrapping_add(256 + i as u32);
}
let mut core = Self { t, counter1024: 0 };
// run the cipher 1024 steps
for _ in 0..64 {
core.sixteen_steps()
}
core.counter1024 = 0;
core
}
}
impl SeedableRng for Hc128Core {
type Seed = [u8; SEED_WORDS * 4];
/// Create an HC-128 random number generator with a seed. The seed has to be
/// 256 bits in length, matching the 128 bit `key` followed by 128 bit `iv`
/// when HC-128 where to be used as a stream cipher.
fn from_seed(seed: Self::Seed) -> Self {
let mut seed_u32 = [0u32; SEED_WORDS];
le::read_u32_into(&seed, &mut seed_u32);
Self::init(seed_u32)
}
}
impl CryptoRng for Hc128Core {}
// Custom PartialEq implementation as it can't currently be derived from an array of size 1024
impl PartialEq for Hc128Core {
fn eq(&self, rhs: &Self) -> bool {
&self.t[..] == &rhs.t[..] && self.counter1024 == rhs.counter1024
}
}
impl Eq for Hc128Core {}
#[cfg(test)]
mod test {
use super::Hc128Rng;
use ::rand_core::{RngCore, SeedableRng};
#[test]
// Test vector 1 from the paper "The Stream Cipher HC-128"
fn test_hc128_true_values_a() {
#[rustfmt::skip]
let seed = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv
let mut rng = Hc128Rng::from_seed(seed);
let mut results = [0u32; 16];
for i in results.iter_mut() {
*i = rng.next_u32();
}
#[rustfmt::skip]
let expected = [0x73150082, 0x3bfd03a0, 0xfb2fd77f, 0xaa63af0e,
0xde122fc6, 0xa7dc29b6, 0x62a68527, 0x8b75ec68,
0x9036db1e, 0x81896005, 0x00ade078, 0x491fbf9a,
0x1cdc3013, 0x6c3d6e24, 0x90f664b2, 0x9cd57102];
assert_eq!(results, expected);
}
#[test]
// Test vector 2 from the paper "The Stream Cipher HC-128"
fn test_hc128_true_values_b() {
#[rustfmt::skip]
let seed = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key
1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv
let mut rng = Hc128Rng::from_seed(seed);
let mut results = [0u32; 16];
for i in results.iter_mut() {
*i = rng.next_u32();
}
#[rustfmt::skip]
let expected = [0xc01893d5, 0xb7dbe958, 0x8f65ec98, 0x64176604,
0x36fc6724, 0xc82c6eec, 0x1b1c38a7, 0xc9b42a95,
0x323ef123, 0x0a6a908b, 0xce757b68, 0x9f14f7bb,
0xe4cde011, 0xaeb5173f, 0x89608c94, 0xb5cf46ca];
assert_eq!(results, expected);
}
#[test]
// Test vector 3 from the paper "The Stream Cipher HC-128"
fn test_hc128_true_values_c() {
#[rustfmt::skip]
let seed = [0x55,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv
let mut rng = Hc128Rng::from_seed(seed);
let mut results = [0u32; 16];
for i in results.iter_mut() {
*i = rng.next_u32();
}
#[rustfmt::skip]
let expected = [0x518251a4, 0x04b4930a, 0xb02af931, 0x0639f032,
0xbcb4a47a, 0x5722480b, 0x2bf99f72, 0xcdc0e566,
0x310f0c56, 0xd3cc83e8, 0x663db8ef, 0x62dfe07f,
0x593e1790, 0xc5ceaa9c, 0xab03806f, 0xc9a6e5a0];
assert_eq!(results, expected);
}
#[test]
fn test_hc128_true_values_u64() {
#[rustfmt::skip]
let seed = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv
let mut rng = Hc128Rng::from_seed(seed);
let mut results = [0u64; 8];
for i in results.iter_mut() {
*i = rng.next_u64();
}
#[rustfmt::skip]
let expected = [0x3bfd03a073150082, 0xaa63af0efb2fd77f,
0xa7dc29b6de122fc6, 0x8b75ec6862a68527,
0x818960059036db1e, 0x491fbf9a00ade078,
0x6c3d6e241cdc3013, 0x9cd5710290f664b2];
assert_eq!(results, expected);
// The RNG operates in a P block of 512 results and next a Q block.
// After skipping 2*800 u32 results we end up somewhere in the Q block
// of the second round
for _ in 0..800 {
rng.next_u64();
}
for i in results.iter_mut() {
*i = rng.next_u64();
}
#[rustfmt::skip]
let expected = [0xd8c4d6ca84d0fc10, 0xf16a5d91dc66e8e7,
0xd800de5bc37a8653, 0x7bae1f88c0dfbb4c,
0x3bfe1f374e6d4d14, 0x424b55676be3fa06,
0xe3a1e8758cbff579, 0x417f7198c5652bcd];
assert_eq!(results, expected);
}
#[test]
fn test_hc128_true_values_bytes() {
#[rustfmt::skip]
let seed = [0x55,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv
let mut rng = Hc128Rng::from_seed(seed);
#[rustfmt::skip]
let expected = [0x31, 0xf9, 0x2a, 0xb0, 0x32, 0xf0, 0x39, 0x06,
0x7a, 0xa4, 0xb4, 0xbc, 0x0b, 0x48, 0x22, 0x57,
0x72, 0x9f, 0xf9, 0x2b, 0x66, 0xe5, 0xc0, 0xcd,
0x56, 0x0c, 0x0f, 0x31, 0xe8, 0x83, 0xcc, 0xd3,
0xef, 0xb8, 0x3d, 0x66, 0x7f, 0xe0, 0xdf, 0x62,
0x90, 0x17, 0x3e, 0x59, 0x9c, 0xaa, 0xce, 0xc5,
0x6f, 0x80, 0x03, 0xab, 0xa0, 0xe5, 0xa6, 0xc9,
0x60, 0x95, 0x84, 0x7a, 0xa5, 0x68, 0x5a, 0x84,
0xea, 0xd5, 0xf3, 0xea, 0x73, 0xa9, 0xad, 0x01,
0x79, 0x7d, 0xbe, 0x9f, 0xea, 0xe3, 0xf9, 0x74,
0x0e, 0xda, 0x2f, 0xa0, 0xe4, 0x7b, 0x4b, 0x1b,
0xdd, 0x17, 0x69, 0x4a, 0xfe, 0x9f, 0x56, 0x95,
0xad, 0x83, 0x6b, 0x9d, 0x60, 0xa1, 0x99, 0x96,
0x90, 0x00, 0x66, 0x7f, 0xfa, 0x7e, 0x65, 0xe9,
0xac, 0x8b, 0x92, 0x34, 0x77, 0xb4, 0x23, 0xd0,
0xb9, 0xab, 0xb1, 0x47, 0x7d, 0x4a, 0x13, 0x0a];
// Pick a somewhat large buffer so we can test filling with the
// remainder from `state.results`, directly filling the buffer, and
// filling the remainder of the buffer.
let mut buffer = [0u8; 16 * 4 * 2];
// Consume a value so that we have a remainder.
assert!(rng.next_u64() == 0x04b4930a518251a4);
rng.fill_bytes(&mut buffer);
// [u8; 128] doesn't implement PartialEq
assert_eq!(buffer.len(), expected.len());
for (b, e) in buffer.iter().zip(expected.iter()) {
assert_eq!(b, e);
}
}
#[test]
fn test_hc128_clone() {
#[rustfmt::skip]
let seed = [0x55,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv
let mut rng1 = Hc128Rng::from_seed(seed);
let mut rng2 = rng1.clone();
for _ in 0..16 {
assert_eq!(rng1.next_u32(), rng2.next_u32());
}
}
}