Skip to content

Commit bb54b73

Browse files
authored
Merge pull request #64 from Fethbita/feat-test-fn-rng
Add a new page that explains testing functions that use random generator
2 parents 69a9997 + 9212e08 commit bb54b73

File tree

2 files changed

+104
-0
lines changed

2 files changed

+104
-0
lines changed

src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
- [Random processess](guide-process.md)
2222
- [Sequences](guide-seq.md)
2323
- [Error handling](guide-err.md)
24+
- [Testing functions that use RNGs](guide-test-fn-rng.md)
2425

2526
- [Updating](update.md)
2627
- [Updating to 0.5](update-0.5.md)

src/guide-test-fn-rng.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# Testing functions that use RNGs
2+
3+
Occasionally a function that uses random number generators might need to be tested. For functions that need to be tested with test vectors, the following approach might be adapted:
4+
5+
```rust
6+
use rand::{RngCore, CryptoRng, rngs::OsRng};
7+
8+
pub struct CryptoOperations<R: RngCore + CryptoRng = OsRng> {
9+
rng: R
10+
}
11+
12+
impl<R: RngCore + CryptoRng> CryptoOperations<R> {
13+
#[must_use]
14+
pub fn new(rng: R) -> Self {
15+
Self {
16+
rng
17+
}
18+
}
19+
20+
pub fn xor_with_random_bytes(&mut self, secret: &mut [u8; 8]) -> [u8; 8] {
21+
let mut mask = [0u8; 8];
22+
self.rng.fill_bytes(&mut mask);
23+
24+
for (byte, mask_byte) in secret.iter_mut().zip(mask.iter()) {
25+
*byte ^= mask_byte;
26+
}
27+
28+
mask
29+
}
30+
}
31+
32+
fn main() {
33+
let rng = OsRng;
34+
let mut crypto_ops = <CryptoOperations>::new(rng);
35+
36+
let mut secret: [u8; 8] = *b"\x00\x01\x02\x03\x04\x05\x06\x07";
37+
let mask = crypto_ops.xor_with_random_bytes(&mut secret);
38+
39+
println!("Modified Secret (XORed): {:?}", secret);
40+
println!("Mask: {:?}", mask);
41+
}
42+
```
43+
44+
To test this, we can create a `MockCryptoRng` implementing `RngCore` and `CryptoRng` in our testing module. Note that `MockCryptoRng` is private and `#[cfg(test)] mod tests` is cfg-gated to our test environment, thus ensuring that `MockCryptoRng` cannot accidentally be used in production.
45+
46+
```rust
47+
#[cfg(test)]
48+
mod tests {
49+
use super::*;
50+
51+
#[derive(Clone, Copy, Debug)]
52+
struct MockCryptoRng {
53+
data: [u8; 8],
54+
index: usize,
55+
}
56+
57+
impl MockCryptoRng {
58+
fn new(data: [u8; 8]) -> MockCryptoRng {
59+
MockCryptoRng {
60+
data,
61+
index: 0,
62+
}
63+
}
64+
}
65+
66+
impl CryptoRng for MockCryptoRng {}
67+
68+
impl RngCore for MockCryptoRng {
69+
fn next_u32(&mut self) -> u32 {
70+
unimplemented!()
71+
}
72+
73+
fn next_u64(&mut self) -> u64 {
74+
unimplemented!()
75+
}
76+
77+
fn fill_bytes(&mut self, dest: &mut [u8]) {
78+
for byte in dest.iter_mut() {
79+
*byte = self.data[self.index];
80+
self.index = (self.index + 1) % self.data.len();
81+
}
82+
}
83+
84+
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
85+
unimplemented!()
86+
}
87+
}
88+
89+
#[test]
90+
fn test_xor_with_mock_rng() {
91+
let mock_crypto_rng = MockCryptoRng::new(*b"\x57\x88\x1e\xed\x1c\x72\x01\xd8");
92+
let mut crypto_ops = CryptoOperations::new(mock_crypto_rng);
93+
94+
let mut secret: [u8; 8] = *b"\x00\x01\x02\x03\x04\x05\x06\x07";
95+
let mask = crypto_ops.xor_with_random_bytes(&mut secret);
96+
let expected_mask = *b"\x57\x88\x1e\xed\x1c\x72\x01\xd8";
97+
let expected_xored_secret = *b"\x57\x89\x1c\xee\x18\x77\x07\xdf";
98+
99+
assert_eq!(secret, expected_xored_secret);
100+
assert_eq!(mask, expected_mask);
101+
}
102+
}
103+
```

0 commit comments

Comments
 (0)