1 // Copyright 2016 The Fuchsia Authors
2 //
3 // Use of this source code is governed by a MIT-style
4 // license that can be found in the LICENSE file or at
5 // https://opensource.org/licenses/MIT
6
7 #include <lib/crypto/prng.h>
8
9 #include <lib/unittest/unittest.h>
10 #include <stdint.h>
11
12 namespace crypto {
13
14 namespace {
instantiate()15 bool instantiate() {
16 BEGIN_TEST;
17
18 { PRNG prng("", 0); }
19
20 END_TEST;
21 }
22
non_thread_safe_prng_same_behavior()23 bool non_thread_safe_prng_same_behavior() {
24 BEGIN_TEST;
25
26 static const char kSeed1[32] = {'1', '2', '3'};
27 static const int kSeed1Size = sizeof(kSeed1);
28 static const char kSeed2[32] = {'a', 'b', 'c'};
29 static const int kSeed2Size = sizeof(kSeed2);
30 static const int kDrawSize = 13;
31
32 PRNG prng1(kSeed1, kSeed1Size, PRNG::NonThreadSafeTag());
33 PRNG prng2(kSeed1, kSeed1Size);
34
35 EXPECT_FALSE(prng1.is_thread_safe(), "unexpected PRNG state");
36 EXPECT_TRUE(prng2.is_thread_safe(), "unexpected PRNG state");
37
38 uint8_t out1[kDrawSize] = {0};
39 uint8_t out2[kDrawSize] = {0};
40 prng1.Draw(out1, sizeof(out1));
41 prng2.Draw(out2, sizeof(out2));
42 EXPECT_EQ(0, memcmp(out1, out2, sizeof(out1)), "inconsistent prng");
43
44 // Verify they stay in sync after adding entropy
45 prng1.AddEntropy(kSeed2, kSeed2Size);
46 prng2.AddEntropy(kSeed2, kSeed2Size);
47
48 prng1.Draw(out1, sizeof(out1));
49 prng2.Draw(out2, sizeof(out2));
50 EXPECT_EQ(0, memcmp(out1, out2, sizeof(out1)), "inconsistent prng");
51
52 // Verify they stay in sync after the non-thread-safe one transitions
53 // to being thread-safe.
54 prng1.BecomeThreadSafe();
55 EXPECT_TRUE(prng1.is_thread_safe(), "unexpected PRNG state");
56
57 prng1.Draw(out1, sizeof(out1));
58 prng2.Draw(out2, sizeof(out2));
59 EXPECT_EQ(0, memcmp(out1, out2, sizeof(out1)), "inconsistent prng");
60
61 END_TEST;
62 }
63
prng_output()64 bool prng_output() {
65 BEGIN_TEST;
66
67 static const char kSeed1[32] = {'a', 'b', 'c'};
68 static const int kSeed1Size = sizeof(kSeed1);
69 static const int kDrawSize = 13;
70
71 PRNG prng1(kSeed1, kSeed1Size);
72 uint8_t out1[kDrawSize] = {0};
73 prng1.Draw(out1, sizeof(out1));
74
75 PRNG prng2(kSeed1, kSeed1Size);
76 uint8_t out2[kDrawSize] = {0};
77 prng2.Draw(out2, sizeof(out2));
78
79 EXPECT_EQ(0, memcmp(out1, out2, sizeof(out1)), "inconsistent prng");
80
81 // Draw from prng1 again. Check that the output is different this time.
82 // There is no theoritical guarantee that the output is different, but
83 // kDrawSize is large enough that the probability of this happening is
84 // negligible. Also this test is fully deterministic for one given PRNG
85 // implementation.
86 prng1.Draw(out1, sizeof(out1));
87
88 EXPECT_NE(0, memcmp(out1, out2, sizeof(out1)), "prng output is constant");
89
90 // We can expect the same output from prng2.
91 prng2.Draw(out2, sizeof(out2));
92
93 EXPECT_EQ(0, memcmp(out1, out2, sizeof(out1)), "inconsistent prng");
94
95 // Now verify that different seeds produce different outputs.
96 static const char kSeed2[33] = {'b', 'l', 'a', 'h'};
97 PRNG prng3(kSeed2, sizeof(kSeed2));
98 uint8_t out3[kDrawSize] = {0};
99 prng3.Draw(out3, sizeof(out3));
100
101 static const char kSeed3[33] = {'b', 'l', 'e', 'h'};
102 PRNG prng4(kSeed3, sizeof(kSeed3));
103 uint8_t out4[kDrawSize] = {0};
104 prng3.Draw(out4, sizeof(out4));
105
106 EXPECT_NE(0, memcmp(out3, out4, sizeof(out3)), "prng output is constant");
107
108 END_TEST;
109 }
110
prng_randint()111 bool prng_randint() {
112 BEGIN_TEST;
113
114 static const char kSeed[32] = {'a', 'b', 'c'};
115 static const int kSeedSize = sizeof(kSeed);
116
117 PRNG prng(kSeed, kSeedSize);
118
119 // Technically could fall out of the log2 loop below, but let's be explicit
120 // about this case.
121 for (int i = 0; i < 100; ++i) {
122 EXPECT_EQ(prng.RandInt(1), 0u, "RandInt(1) must equal 0");
123 }
124
125 for (int log2 = 1; log2 < 64; ++log2) {
126 for (int i = 0; i < 100; ++i) {
127 uint64_t bound = 1ull << log2;
128 EXPECT_LT(prng.RandInt(bound), bound, "RandInt(2^i) must be less than 2^i");
129 }
130 }
131
132 bool high_bit = false;
133 for (int i = 0; i < 100; ++i) {
134 high_bit |= !!(prng.RandInt(UINT64_MAX) & (1ull << 63));
135 }
136 EXPECT_TRUE(high_bit, "RandInt(UINT64_MAX) should have high bit set sometimes");
137
138 END_TEST;
139 }
140
141 } // namespace
142
143 UNITTEST_START_TESTCASE(prng_tests)
144 UNITTEST("Instantiate", instantiate)
145 UNITTEST("NonThreadSafeMode", non_thread_safe_prng_same_behavior)
146 UNITTEST("Test Output", prng_output)
147 UNITTEST("Test RandInt", prng_randint)
148 UNITTEST_END_TESTCASE(prng_tests, "prng",
149 "Test pseudo-random number generator implementation.");
150
151 } // namespace crypto
152