1 #include <gtest/gtest.h>
2 #include <stdexcept>
3
4 #include "encoder.h"
5 #include "openssl_hash_impl.h"
6 #include "unix_kernel_rand_impl.h"
7
8 // We need the same "random" inputs to the IRR
9 // each time to have reproducible tests.
mock_urandom(void)10 FILE* mock_urandom(void) {
11 int i;
12 FILE *fp;
13 fp = tmpfile();
14 for (i = 0; i < 1024; i++) {
15 fputc((i * 17) % 256, fp);
16 }
17 fflush(fp);
18 fp = freopen(NULL, "r", fp);
19 return fp;
20 }
21
22 class EncoderTest : public ::testing::Test {
23 protected:
EncoderTest()24 EncoderTest() {
25 encoder_id = std::string("metric-name").c_str();
26 fp = mock_urandom();
27 irr_rand = new rappor::UnixKernelRand(fp);
28 }
29
~EncoderTest()30 virtual ~EncoderTest() {
31 fclose(fp);
32 delete irr_rand;
33 delete deps;
34 delete params;
35 delete encoder;
36 }
37
38 FILE* fp; const char* encoder_id;
39 rappor::UnixKernelRand *irr_rand;
40 rappor::Deps *deps;
41 rappor::Params *params;
42 rappor::Encoder *encoder;
43 rappor::Bits bits_out;
44 std::vector<uint8_t> bits_vector;
45 };
46
47 // Uses HmacSha256 and 32-bit outputs.
48 class EncoderUint32Test : public EncoderTest {
49 protected:
EncoderUint32Test()50 EncoderUint32Test() {
51 deps = new rappor::Deps(rappor::Md5, "client-secret", rappor::HmacSha256,
52 *irr_rand);
53 params = new rappor::Params(32, // num_bits (k)
54 2, // num_hashes (h)
55 128, // num_cohorts (m)
56 0.25, // probability f for PRR
57 0.75, // probability p for IRR
58 0.5); // probability q for IRR
59 encoder = new rappor::Encoder(encoder_id, *params, *deps);
60 }
61 };
62
63 // Uses HmacDrbg and variable-size vector outputs.
64 class EncoderUnlimTest : public EncoderTest {
65 protected:
EncoderUnlimTest()66 EncoderUnlimTest() {
67 deps = new rappor::Deps(rappor::Md5, "client-secret", rappor::HmacDrbg,
68 *irr_rand);
69 params = new rappor::Params(64, // num_bits (k)
70 2, // num_hashes (h)
71 128, // num_cohorts (m)
72 0.25, // probability f for PRR
73 0.75, // probability p for IRR
74 0.5); // probability q for IRR
75 encoder = new rappor::Encoder(encoder_id, *params, *deps);
76 }
77 };
78
79
80 ///// EncoderUint32Test
TEST_F(EncoderUint32Test,EncodeStringUint32)81 TEST_F(EncoderUint32Test, EncodeStringUint32) {
82 ASSERT_TRUE(encoder->EncodeString("foo", &bits_out));
83 ASSERT_EQ(2281639167, bits_out);
84 ASSERT_EQ(3, encoder->cohort());
85 }
86
TEST_F(EncoderUint32Test,EncodeStringUint32Cohort)87 TEST_F(EncoderUint32Test, EncodeStringUint32Cohort) {
88 encoder->set_cohort(4); // Set pre-selected cohort.
89 ASSERT_TRUE(encoder->EncodeString("foo", &bits_out));
90 ASSERT_EQ(2281637247, bits_out);
91 ASSERT_EQ(4, encoder->cohort());
92 }
93
TEST_F(EncoderUint32Test,EncodeBitsUint32)94 TEST_F(EncoderUint32Test, EncodeBitsUint32) {
95 ASSERT_TRUE(encoder->EncodeBits(0x123, &bits_out));
96 ASSERT_EQ(2784956095, bits_out);
97 ASSERT_EQ(3, encoder->cohort());
98 }
99
100 // Negative tests
101 // num_bits is negative.
TEST_F(EncoderUint32Test,NumBitsMustBePositiveDeathTest)102 TEST_F(EncoderUint32Test, NumBitsMustBePositiveDeathTest) {
103 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
104 delete params;
105 params = new rappor::Params(-1, // num_bits (k) [BAD]
106 2, // num_hashes (h)
107 128, // num_cohorts (m)
108 0.25, // probability f for PRR
109 0.75, // probability p for IRR
110 0.5); // probability q for IRR
111 EXPECT_DEATH(rappor::Encoder(encoder_id, *params, *deps),
112 "Assertion.*failed");
113 }
114
115 // num_hashes is negative.
TEST_F(EncoderUint32Test,NumHashesMustBePositiveDeathTest)116 TEST_F(EncoderUint32Test, NumHashesMustBePositiveDeathTest) {
117 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
118 delete params;
119 params = new rappor::Params(32, // num_bits (k)
120 -1, // num_hashes (h) [BAD]
121 128, // num_cohorts (m)
122 0.25, // probability f for PRR
123 0.75, // probability p for IRR
124 0.5); // probability q for IRR
125 EXPECT_DEATH(rappor::Encoder(encoder_id, *params, *deps),
126 "Assertion.*failed");
127 }
128
129 // num_cohorts is negative.
TEST_F(EncoderUint32Test,NumCohortsMustBePositiveDeathTest)130 TEST_F(EncoderUint32Test, NumCohortsMustBePositiveDeathTest) {
131 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
132 delete params;
133 params = new rappor::Params(32, // num_bits (k)
134 2, // num_hashes (h)
135 -1, // num_cohorts (m) [BAD]
136 0.25, // probability f for PRR
137 0.75, // probability p for IRR
138 0.5); // probability q for IRR
139 EXPECT_DEATH(rappor::Encoder(encoder_id, *params, *deps),
140 "Encoder.*Assertion.*failed");
141 }
142
143 // Invalid probabilities.
TEST_F(EncoderUint32Test,InvalidProbabilitiesDeathTest)144 TEST_F(EncoderUint32Test, InvalidProbabilitiesDeathTest) {
145 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
146 // prob_f negative.
147 delete params;
148 params = new rappor::Params(32, // num_bits (k)
149 2, // num_hashes (h)
150 1, // num_cohorts (m)
151 -0.1, // probability f for PRR [BAD]
152 0.75, // probability p for IRR
153 0.5); // probability q for IRR
154 EXPECT_DEATH(rappor::Encoder(encoder_id, *params, *deps),
155 "Assertion.*failed");
156 // prob_f > 1.
157 delete params;
158 params = new rappor::Params(32, // num_bits (k)
159 2, // num_hashes (h)
160 1, // num_cohorts (m)
161 1.1, // probability f for PRR [BAD]
162 0.75, // probability p for IRR
163 0.5); // probability q for IRR
164 EXPECT_DEATH(rappor::Encoder(encoder_id, *params, *deps),
165 "Assertion.*failed");
166 // prob_p < 0.
167 delete params;
168 params = new rappor::Params(32, // num_bits (k)
169 2, // num_hashes (h)
170 1, // num_cohorts (m)
171 0.25, // probability f for PRR
172 -0.1, // probability p for IRR [BAD]
173 0.5); // probability q for IRR
174 EXPECT_DEATH(rappor::Encoder(encoder_id, *params, *deps),
175 "Assertion.*failed");
176 // prob_p > 1.
177 delete params;
178 params = new rappor::Params(32, // num_bits (k)
179 2, // num_hashes (h)
180 1, // num_cohorts (m)
181 0.25, // probability f for PRR
182 1.1, // probability p for IRR [BAD]
183 0.5); // probability q for IRR
184 EXPECT_DEATH(rappor::Encoder(encoder_id, *params, *deps),
185 "Assertion.*failed");
186 // prob_q < 0.
187 delete params;
188 params = new rappor::Params(32, // num_bits (k)
189 2, // num_hashes (h)
190 1, // num_cohorts (m)
191 0.25, // probability f for PRR
192 0.75, // probability p for IRR
193 -0.1); // probability q for IRR [BAD]
194 EXPECT_DEATH(rappor::Encoder(encoder_id, *params, *deps),
195 "Assertion.*failed");
196 // prob_q > 1.
197 delete params;
198 params = new rappor::Params(32, // num_bits (k)
199 2, // num_hashes (h)
200 1, // num_cohorts (m)
201 0.25, // probability f for PRR
202 0.75, // probability p for IRR
203 1.1); // probability q for IRR [BAD]
204 EXPECT_DEATH(rappor::Encoder(encoder_id, *params, *deps),
205 "Assertion.*failed");
206 }
207
208 // num_bits 64 when only 32 bits are possible.
TEST_F(EncoderUint32Test,Sha256NoMoreThan32BitsDeathTest)209 TEST_F(EncoderUint32Test, Sha256NoMoreThan32BitsDeathTest) {
210 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
211 delete params;
212 params = new rappor::Params(64, // num_bits (k)
213 2, // num_hashes (h)
214 128, // num_cohorts (m)
215 0.25, // probability f for PRR
216 0.75, // probability p for IRR
217 0.5); // probability q for IRR
218 EXPECT_DEATH(rappor::Encoder(encoder_id, *params, *deps),
219 "Assertion.*failed");
220 }
221
222 // num_hashes too high.
TEST_F(EncoderUint32Test,NumHashesNoMoreThan16DeathTest)223 TEST_F(EncoderUint32Test, NumHashesNoMoreThan16DeathTest) {
224 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
225 delete params;
226 params = new rappor::Params(32, // num_bits (k)
227 17, // num_hashes (h)
228 128, // num_cohorts (m)
229 0.25, // probability f for PRR
230 0.75, // probability p for IRR
231 0.5); // probability q for IRR
232 EXPECT_DEATH(rappor::Encoder(encoder_id, *params, *deps),
233 "Assertion.*failed");
234 }
235
236 // EncoderString with 4-byte vector and HMACSHA256 and
237 // EncoderString with Uint32 and HMACSHA256 should match.
TEST_F(EncoderUint32Test,StringUint32AndStringVectorMatch)238 TEST_F(EncoderUint32Test, StringUint32AndStringVectorMatch) {
239 ASSERT_TRUE(encoder->EncodeString("foo", &bits_out));
240 ASSERT_EQ(2281639167, bits_out);
241 std::vector<uint8_t> expected_out(4);
242 expected_out[0] = (bits_out & 0xFF000000) >> 24;
243 expected_out[1] = (bits_out & 0x00FF0000) >> 16;
244 expected_out[2] = (bits_out & 0x0000FF00) >> 8;
245 expected_out[3] = bits_out & 0x000000FF;
246
247 // Reset the mock randomizer.
248 delete irr_rand;
249 delete deps;
250 delete encoder;
251 fclose(fp);
252 fp = mock_urandom();
253 irr_rand = new rappor::UnixKernelRand(fp);
254 deps = new rappor::Deps(rappor::Md5, "client-secret", rappor::HmacSha256,
255 *irr_rand);
256 encoder = new rappor::Encoder(encoder_id, *params, *deps);
257 ASSERT_TRUE(encoder->EncodeString("foo", &bits_vector));
258 ASSERT_EQ(expected_out, bits_vector);
259 }
260
261 ///// EncoderUnlimTest
262
TEST_F(EncoderUnlimTest,EncodeStringUint64)263 TEST_F(EncoderUnlimTest, EncodeStringUint64) {
264 static const uint8_t ex[] = { 134, 255, 11, 255, 252, 119, 240, 223 };
265 std::vector<uint8_t> expected_vector(ex, ex + sizeof(ex));
266
267 ASSERT_TRUE(encoder->EncodeString("foo", &bits_vector));
268 ASSERT_EQ(expected_vector, bits_vector);
269 ASSERT_EQ(93, encoder->cohort());
270 }
271
272 // Negative tests.
TEST_F(EncoderUnlimTest,NumBitsNotMultipleOf8DeathTest)273 TEST_F(EncoderUnlimTest, NumBitsNotMultipleOf8DeathTest) {
274 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
275 delete params;
276 params = new rappor::Params(63, // num_bits (k) [BAD]
277 17, // num_hashes (h)
278 128, // num_cohorts (m)
279 0.25, // probability f for PRR
280 0.75, // probability p for IRR
281 0.5); // probability q for IRR
282 EXPECT_DEATH(rappor::Encoder(encoder_id, *params, *deps),
283 "Assertion.*failed");
284 }
285
main(int argc,char ** argv)286 int main(int argc, char **argv) {
287 ::testing::InitGoogleTest(&argc, argv);
288 return RUN_ALL_TESTS();
289 }
290