hashx/rand.rs
1//! Pseudorandom number utilities for HashX's program generator
2//!
3//! HashX uses pseudorandom numbers to make individual decisions in the program
4//! generation process. The program generator consumes u8 and u32 values that
5//! use a shared u64 generator, implemented using SipHash1,3.
6//!
7//! We use the [`Rng`] trait for this underlying u64 generator,
8//! allowing substitute random number generators for testing or for special
9//! purposes that don't require compatibility with HashX proper.
10//!
11//! The stateful u8 and u32 layer comes from this module's [`RngBuffer`].
12//! It's important for the u8 and u32 queues to share a common generator.
13//! The order of dequeueing u8 items vs u32 items intentionally modifies the
14//! assignment of particular u64 [`Rng`] values to the two queues.
15
16use std::convert::Infallible;
17
18use crate::siphash::{SipState, siphash13_ctr};
19use arrayvec::ArrayVec;
20use rand_core::{Rng, TryRng};
21
22/// Wrap a [`Rng`] implementation for fast `u8` and `u32` output.
23///
24/// This maintains small queues for each data type: up to one `u32` and up to
25/// 7 bytes. The queueing behavior matches conventions required by HashX:
26/// The underlying `u64` values are always generated lazily, and component
27/// values are extracted in big endian order.
28#[derive(Debug)]
29pub(crate) struct RngBuffer<'a, T: Rng> {
30 /// Inner [`Rng`] implementation
31 inner: &'a mut T,
32 /// Buffer of remaining u8 values from breaking up a u64
33 u8_vec: ArrayVec<u8, 7>,
34 /// Up to one buffered u32 value
35 u32_opt: Option<u32>,
36}
37
38impl<'a, T: Rng> RngBuffer<'a, T> {
39 /// Construct a new empty buffer around a [`Rng`] implementation.
40 ///
41 /// No actual random numbers will be generated until the first call to
42 /// [`Self::next_u8`] or [`Self::next_u32`].
43 #[inline(always)]
44 pub(crate) fn new(rng: &'a mut T) -> Self {
45 Self {
46 inner: rng,
47 u8_vec: Default::default(),
48 u32_opt: None,
49 }
50 }
51
52 /// Request 32 bits from the buffered random number generator.
53 ///
54 /// If we have buffered data stored, returns that. If not,
55 /// requests 64 bits from the [`Rng`] and saves half for later.
56 #[inline(always)]
57 pub(crate) fn next_u32(&mut self) -> u32 {
58 let previous = self.u32_opt;
59 match previous {
60 Some(value) => {
61 self.u32_opt = None;
62 value
63 }
64 None => {
65 let value = self.inner.next_u64();
66 self.u32_opt = Some(value as u32);
67 (value >> 32) as u32
68 }
69 }
70 }
71
72 /// Request 8 bits from the buffered random number generator.
73 ///
74 /// If we have buffered data stored, returns that. If not,
75 /// requests 64 bits from the [`Rng`] and saves 7 bytes for later.
76 #[inline(always)]
77 pub(crate) fn next_u8(&mut self) -> u8 {
78 let value = self.u8_vec.pop();
79 match value {
80 Some(value) => value,
81 None => {
82 // Little endian (reversed) order here,
83 // because we dequeue items from the end of the Vec.
84 let bytes = self.inner.next_u64().to_le_bytes();
85 let (last, saved) = bytes.split_last().expect("u64 has nonzero length");
86 self.u8_vec
87 .try_extend_from_slice(saved)
88 .expect("slice length correct");
89 *last
90 }
91 }
92 }
93}
94
95/// HashX-style random number generator built on SipHash1,3
96///
97/// This is an implementation of [`Rng`] using SipHash1,3 as
98/// the 64-bit PRNG layer needed by HashX's program generator.
99#[derive(Debug, Clone)]
100pub struct SipRand {
101 /// SipHash state vector used as input to SipHash1,3 in counter mode
102 key: SipState,
103 /// Next unused counter value
104 counter: u64,
105}
106
107impl SipRand {
108 /// Build a new SipHash random number generator.
109 ///
110 /// The internal SipHash1,3 generator is initialized to a supplied
111 /// internal state, and the counter is reset to zero.
112 #[inline(always)]
113 pub fn new(key: SipState) -> Self {
114 Self::new_with_counter(key, 0)
115 }
116
117 /// Build a new [`SipRand`] with a specific initial counter value.
118 #[inline(always)]
119 pub fn new_with_counter(key: SipState, counter: u64) -> Self {
120 Self { key, counter }
121 }
122}
123
124impl TryRng for SipRand {
125 type Error = Infallible;
126
127 /// Generate a full 64-bit random result using SipHash1,3.
128 fn try_next_u64(&mut self) -> Result<u64, Infallible> {
129 let value = siphash13_ctr(self.key, self.counter);
130 self.counter += 1;
131 Ok(value)
132 }
133
134 /// Return a 32-bit value by discarding the upper half of a 64-bit result.
135 fn try_next_u32(&mut self) -> Result<u32, Infallible> {
136 Ok(self.next_u64() as u32)
137 }
138
139 /// Fill `dest` with random data.
140 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Infallible> {
141 rand_core::utils::fill_bytes_via_next_word(dest, || Ok::<u64, Infallible>(self.next_u64()))
142 }
143}
144
145#[cfg(test)]
146mod test {
147 use super::{RngBuffer, SipRand, SipState};
148
149 #[test]
150 fn rng_vectors() {
151 // Check against pseudorandom number streams seen during tor unit tests
152
153 let (key0, _key1) = SipState::pair_from_seed(b"abc");
154 let mut rng_inner = SipRand::new(key0);
155 let mut rng = RngBuffer::new(&mut rng_inner);
156
157 #[derive(Debug, PartialEq)]
158 enum Value {
159 U32(u32),
160 U8(u8),
161 }
162
163 let expected = vec![
164 Value::U32(0xf695edd0),
165 Value::U32(0x2205449d),
166 Value::U32(0x51c1ac51),
167 Value::U32(0xcd19a7d1),
168 Value::U8(0xad),
169 Value::U32(0x79793a52),
170 Value::U32(0xd965083d),
171 Value::U8(0xf4),
172 Value::U32(0x915e9969),
173 Value::U32(0x7563b6e2),
174 Value::U32(0x4e5a9d8b),
175 Value::U32(0xef2bb9ce),
176 Value::U8(0xcb),
177 Value::U32(0xa4beee16),
178 Value::U32(0x78fa6e6f),
179 Value::U8(0x30),
180 Value::U32(0xc321cb9f),
181 Value::U32(0xbbf29635),
182 Value::U32(0x919450f4),
183 Value::U32(0xf3d8f358),
184 Value::U8(0x3b),
185 Value::U32(0x818a72e9),
186 Value::U32(0x58225fcf),
187 Value::U8(0x98),
188 Value::U32(0x3fcb5059),
189 Value::U32(0xaf5bcb70),
190 Value::U8(0x14),
191 Value::U32(0xd41e0326),
192 Value::U32(0xe79aebc6),
193 Value::U32(0xa348672c),
194 Value::U8(0xcf),
195 Value::U32(0x5d51b520),
196 Value::U32(0x73afc36f),
197 Value::U32(0x31348711),
198 Value::U32(0xca25b040),
199 Value::U32(0x3700c37b),
200 Value::U8(0x62),
201 Value::U32(0xf0d1d6a6),
202 Value::U32(0xc1edebf3),
203 Value::U8(0x9d),
204 Value::U32(0x9bb1f33f),
205 Value::U32(0xf1309c95),
206 Value::U32(0x0797718a),
207 Value::U32(0xa3bbcf7e),
208 Value::U8(0x80),
209 Value::U8(0x28),
210 Value::U8(0xe9),
211 Value::U8(0x2e),
212 Value::U32(0xf5506289),
213 Value::U32(0x97b46d7c),
214 Value::U8(0x64),
215 Value::U32(0xc99fe4ad),
216 Value::U32(0x6e756189),
217 Value::U8(0x54),
218 Value::U8(0xf7),
219 Value::U8(0x0f),
220 Value::U8(0x7d),
221 Value::U32(0x38c983eb),
222 ];
223
224 let mut actual = Vec::new();
225 for item in &expected {
226 match item {
227 Value::U8(_) => actual.push(Value::U8(rng.next_u8())),
228 Value::U32(_) => actual.push(Value::U32(rng.next_u32())),
229 }
230 }
231
232 assert_eq!(expected, actual);
233 }
234}