OpenVPN 3 Core Library
Loading...
Searching...
No Matches
test_randbytestore.cpp
Go to the documentation of this file.
1#include "test_common.hpp"
2
3#include <cstdint>
4#include <limits>
5#include <vector>
6
8
9using namespace openvpn;
10
11// A deterministic fake RNG satisfying the UniformRandomBitGenerator concept.
12// Returns a fixed sequence of values supplied at construction.
13template <typename T>
15{
16 public:
17 using result_type = T;
18
19 explicit FakeRNG(std::vector<T> values)
20 : values_(std::move(values))
21 {
22 }
23
24 static constexpr result_type min()
25 {
26 return std::numeric_limits<T>::min();
27 }
28
29 static constexpr result_type max()
30 {
31 return std::numeric_limits<T>::max();
32 }
33
35 {
36 EXPECT_LT(call_count_, values_.size()) << "FakeRNG called more times than expected";
37 return values_[call_count_++];
38 }
39
40 size_t call_count() const
41 {
42 return call_count_;
43 }
44
45 private:
46 std::vector<T> values_;
47 size_t call_count_ = 0;
48};
49
50// Correct behaviour: bytes are extracted LSB-first from a 32-bit value.
51TEST(randbytestore, get_byte_order_u32)
52{
53 FakeRNG<uint32_t> rng({0xDEADBEEF});
55
56 EXPECT_EQ(rbs.get_byte(rng), 0xEF);
57 EXPECT_EQ(rbs.get_byte(rng), 0xBE);
58 EXPECT_EQ(rbs.get_byte(rng), 0xAD);
59 EXPECT_EQ(rbs.get_byte(rng), 0xDE);
60
61 // Exactly one RNG call for four bytes
62 EXPECT_EQ(rng.call_count(), 1u);
63}
64
65// Correct behaviour: bytes are extracted LSB-first from a 64-bit value.
66TEST(randbytestore, get_byte_order_u64)
67{
68 FakeRNG<uint64_t> rng({0x0102030405060708ULL});
70
71 EXPECT_EQ(rbs.get_byte(rng), 0x08);
72 EXPECT_EQ(rbs.get_byte(rng), 0x07);
73 EXPECT_EQ(rbs.get_byte(rng), 0x06);
74 EXPECT_EQ(rbs.get_byte(rng), 0x05);
75 EXPECT_EQ(rbs.get_byte(rng), 0x04);
76 EXPECT_EQ(rbs.get_byte(rng), 0x03);
77 EXPECT_EQ(rbs.get_byte(rng), 0x02);
78 EXPECT_EQ(rbs.get_byte(rng), 0x01);
79
80 // Exactly one RNG call for eight bytes
81 EXPECT_EQ(rng.call_count(), 1u);
82}
83
84// Correct behaviour: a second RNG call is made only after all bytes are consumed.
85TEST(randbytestore, refill_on_exhaustion)
86{
87 FakeRNG<uint32_t> rng({0x11223344, 0xAABBCCDD});
89
90 // Consume first word (4 bytes)
91 for (int i = 0; i < 4; ++i)
92 rbs.get_byte(rng);
93 EXPECT_EQ(rng.call_count(), 1u);
94
95 // First byte of second word triggers the refill
96 EXPECT_EQ(rbs.get_byte(rng), 0xDD);
97 EXPECT_EQ(rng.call_count(), 2u);
98}
99
100// Correct behaviour: fill() populates a struct with the expected bytes.
101TEST(randbytestore, fill_struct)
102{
103 // Two successive 32-bit words: the fill covers exactly 8 bytes
104 FakeRNG<uint32_t> rng({0x01020304, 0x05060708});
106
107 uint64_t result = 0;
108 rbs.fill(result, rng);
109
110 // fill() writes bytes in get_byte() order (LSB-first per word)
111 // Word 0: 0x01020304 → bytes 04 03 02 01 at offsets 0-3
112 // Word 1: 0x05060708 → bytes 08 07 06 05 at offsets 4-7
113 const unsigned char *b = reinterpret_cast<const unsigned char *>(&result);
114 EXPECT_EQ(b[0], 0x04);
115 EXPECT_EQ(b[1], 0x03);
116 EXPECT_EQ(b[2], 0x02);
117 EXPECT_EQ(b[3], 0x01);
118 EXPECT_EQ(b[4], 0x08);
119 EXPECT_EQ(b[5], 0x07);
120 EXPECT_EQ(b[6], 0x06);
121 EXPECT_EQ(b[7], 0x05);
122
123 EXPECT_EQ(rng.call_count(), 2u);
124}
125
126// Incorrect behaviour: wrong byte order would be caught by this test.
127TEST(randbytestore, byte_order_not_msb_first)
128{
129 FakeRNG<uint32_t> rng({0xAABBCCDD});
131
132 // If extraction were MSB-first the first byte would be 0xAA — it must not be.
133 EXPECT_NE(rbs.get_byte(rng), 0xAA);
134}
135
136// Incorrect behaviour: early refill would be caught — RNG must not be called twice
137// for the first sizeof(result_type) bytes.
138TEST(randbytestore, no_early_refill)
139{
140 FakeRNG<uint32_t> rng({0x12345678, 0xDEADBEEF});
142
143 rbs.get_byte(rng);
144 rbs.get_byte(rng);
145 rbs.get_byte(rng);
146
147 // Three bytes consumed from a 32-bit word — still only one RNG call
148 EXPECT_EQ(rng.call_count(), 1u);
149}
150
151// Incorrect behaviour: a missed refill (stale data) would produce a zero byte
152// where the new RNG value should appear.
153TEST(randbytestore, no_missed_refill)
154{
155 FakeRNG<uint32_t> rng({0x00000000, 0xFF000000});
157
158 for (int i = 0; i < 4; ++i)
159 rbs.get_byte(rng);
160
161 // Without a refill the 5th byte would be 0x00 (stale); with correct
162 // behaviour the new word 0xFF000000 contributes 0x00 at byte 0 as well,
163 // so use a value that makes the distinction unambiguous.
164 FakeRNG<uint32_t> rng2({0x00000000, 0xABCDEF01});
166
167 for (int i = 0; i < 4; ++i)
168 rbs2.get_byte(rng2);
169
170 EXPECT_EQ(rbs2.get_byte(rng2), 0x01); // LSB of 0xABCDEF01
171 EXPECT_EQ(rng2.call_count(), 2u);
172}
size_t call_count() const
std::vector< T > values_
FakeRNG(std::vector< T > values)
static constexpr result_type max()
static constexpr result_type min()
result_type operator()()
TEST(randbytestore, get_byte_order_u32)