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