Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / net / quic / crypto / strike_register_test.cc
blobdf723579a1b25949ba702f1827aed2c6c5f4d2bc
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/quic/crypto/strike_register.h"
7 #include <set>
8 #include <string>
10 #include "base/basictypes.h"
11 #include "base/rand_util.h"
12 #include "testing/gtest/include/gtest/gtest.h"
14 namespace {
16 using net::InsertStatus;
17 using net::StrikeRegister;
18 using std::make_pair;
19 using std::min;
20 using std::pair;
21 using std::set;
22 using std::string;
24 const uint8 kOrbit[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
26 // StrikeRegisterTests don't look at the random bytes so this function can
27 // simply set the random bytes to 0.
28 void SetNonce(uint8 nonce[32], unsigned time, const uint8 orbit[8]) {
29 nonce[0] = time >> 24;
30 nonce[1] = time >> 16;
31 nonce[2] = time >> 8;
32 nonce[3] = time;
33 memcpy(nonce + 4, orbit, 8);
34 memset(nonce + 12, 0, 20);
37 TEST(StrikeRegisterTest, SimpleHorizon) {
38 // The set must reject values created on or before its own creation time.
39 StrikeRegister set(10 /* max size */, 1000 /* current time */,
40 100 /* window secs */, kOrbit,
41 StrikeRegister::DENY_REQUESTS_AT_STARTUP);
42 uint8 nonce[32];
43 SetNonce(nonce, 999, kOrbit);
44 EXPECT_EQ(net::NONCE_INVALID_TIME_FAILURE, set.Insert(nonce, 1000));
45 SetNonce(nonce, 1000, kOrbit);
46 EXPECT_EQ(net::NONCE_INVALID_TIME_FAILURE, set.Insert(nonce, 1000));
48 EXPECT_EQ(0u, set.GetCurrentValidWindowSecs(1000 /* current time */));
49 EXPECT_EQ(0u, set.GetCurrentValidWindowSecs(1100 /* current time */));
50 EXPECT_EQ(1u, set.GetCurrentValidWindowSecs(1101 /* current time */));
51 EXPECT_EQ(50u, set.GetCurrentValidWindowSecs(1150 /* current time */));
52 EXPECT_EQ(100u, set.GetCurrentValidWindowSecs(1200 /* current time */));
53 EXPECT_EQ(101u, set.GetCurrentValidWindowSecs(1300 /* current time */));
56 TEST(StrikeRegisterTest, NoStartupMode) {
57 // Check that a strike register works immediately if NO_STARTUP_PERIOD_NEEDED
58 // is specified.
59 StrikeRegister set(10 /* max size */, 1000 /* current time */,
60 100 /* window secs */, kOrbit,
61 StrikeRegister::NO_STARTUP_PERIOD_NEEDED);
62 uint8 nonce[32];
63 SetNonce(nonce, 1000, kOrbit);
64 EXPECT_EQ(net::NONCE_OK, set.Insert(nonce, 1000));
65 EXPECT_EQ(net::NONCE_NOT_UNIQUE_FAILURE, set.Insert(nonce, 1000));
67 EXPECT_EQ(101u, set.GetCurrentValidWindowSecs(1000 /* current time */));
68 EXPECT_EQ(101u, set.GetCurrentValidWindowSecs(1050 /* current time */));
69 EXPECT_EQ(101u, set.GetCurrentValidWindowSecs(1100 /* current time */));
70 EXPECT_EQ(101u, set.GetCurrentValidWindowSecs(1200 /* current time */));
71 EXPECT_EQ(101u, set.GetCurrentValidWindowSecs(1300 /* current time */));
74 TEST(StrikeRegisterTest, WindowFuture) {
75 // The set must reject values outside the window.
76 StrikeRegister set(10 /* max size */, 1000 /* current time */,
77 100 /* window secs */, kOrbit,
78 StrikeRegister::DENY_REQUESTS_AT_STARTUP);
79 uint8 nonce[32];
80 SetNonce(nonce, 1101, kOrbit);
81 EXPECT_EQ(net::NONCE_INVALID_TIME_FAILURE, set.Insert(nonce, 1000));
82 SetNonce(nonce, 999, kOrbit);
83 EXPECT_EQ(net::NONCE_INVALID_TIME_FAILURE, set.Insert(nonce, 1100));
86 TEST(StrikeRegisterTest, BadOrbit) {
87 // The set must reject values with the wrong orbit
88 StrikeRegister set(10 /* max size */, 1000 /* current time */,
89 100 /* window secs */, kOrbit,
90 StrikeRegister::DENY_REQUESTS_AT_STARTUP);
91 uint8 nonce[32];
92 static const uint8 kBadOrbit[8] = { 0, 0, 0, 0, 1, 1, 1, 1 };
93 SetNonce(nonce, 1101, kBadOrbit);
94 EXPECT_EQ(net::NONCE_INVALID_ORBIT_FAILURE, set.Insert(nonce, 1100));
97 TEST(StrikeRegisterTest, OneValue) {
98 StrikeRegister set(10 /* max size */, 1000 /* current time */,
99 100 /* window secs */, kOrbit,
100 StrikeRegister::DENY_REQUESTS_AT_STARTUP);
101 uint8 nonce[32];
102 SetNonce(nonce, 1101, kOrbit);
103 EXPECT_EQ(net::NONCE_OK, set.Insert(nonce, 1101));
106 TEST(StrikeRegisterTest, RejectDuplicate) {
107 // The set must reject values with the wrong orbit
108 StrikeRegister set(10 /* max size */, 1000 /* current time */,
109 100 /* window secs */, kOrbit,
110 StrikeRegister::DENY_REQUESTS_AT_STARTUP);
111 uint8 nonce[32];
112 SetNonce(nonce, 1101, kOrbit);
113 EXPECT_EQ(net::NONCE_OK, set.Insert(nonce, 1101));
114 EXPECT_EQ(net::NONCE_NOT_UNIQUE_FAILURE, set.Insert(nonce, 1101));
117 TEST(StrikeRegisterTest, HorizonUpdating) {
118 StrikeRegister::StartupType startup_types[] = {
119 StrikeRegister::DENY_REQUESTS_AT_STARTUP,
120 StrikeRegister::NO_STARTUP_PERIOD_NEEDED
123 for (size_t type_idx = 0; type_idx < arraysize(startup_types); ++type_idx) {
124 StrikeRegister set(5 /* max size */, 500 /* current time */,
125 100 /* window secs */, kOrbit,
126 startup_types[type_idx]);
127 uint8 nonce[6][32];
128 for (unsigned i = 0; i < 5; i++) {
129 SetNonce(nonce[i], 1101 + i, kOrbit);
130 nonce[i][31] = i;
131 EXPECT_EQ(net::NONCE_OK, set.Insert(nonce[i], 1100));
134 // Valid window is still equal to |window_secs + 1|.
135 EXPECT_EQ(101u, set.GetCurrentValidWindowSecs(1100));
137 // This should push the oldest value out and force the horizon to
138 // be updated.
139 SetNonce(nonce[5], 1110, kOrbit);
140 EXPECT_EQ(net::NONCE_OK, set.Insert(nonce[5], 1110));
141 // Effective horizon is computed based on the timestamp of the
142 // value that was pushed out.
143 EXPECT_EQ(9u, set.GetCurrentValidWindowSecs(1110));
145 SetNonce(nonce[5], 1111, kOrbit);
146 EXPECT_EQ(net::NONCE_OK, set.Insert(nonce[5], 1110));
147 EXPECT_EQ(8u, set.GetCurrentValidWindowSecs(1110));
149 // This should be behind the horizon now:
150 SetNonce(nonce[5], 1101, kOrbit);
151 nonce[5][31] = 10;
152 EXPECT_EQ(net::NONCE_INVALID_TIME_FAILURE, set.Insert(nonce[5], 1110));
154 // Insert beyond the valid range.
155 SetNonce(nonce[5], 1117, kOrbit);
156 nonce[5][31] = 2;
157 EXPECT_EQ(net::NONCE_INVALID_TIME_FAILURE, set.Insert(nonce[5], 1110));
159 // Insert at the upper valid range.
160 SetNonce(nonce[5], 1116, kOrbit);
161 nonce[5][31] = 1;
162 EXPECT_EQ(net::NONCE_OK, set.Insert(nonce[5], 1110));
164 // This should be beyond the upper valid range now:
165 SetNonce(nonce[5], 1116, kOrbit);
166 nonce[5][31] = 2;
167 EXPECT_EQ(net::NONCE_INVALID_TIME_FAILURE, set.Insert(nonce[5], 1110));
171 TEST(StrikeRegisterTest, InsertMany) {
172 StrikeRegister set(5000 /* max size */, 1000 /* current time */,
173 500 /* window secs */, kOrbit,
174 StrikeRegister::DENY_REQUESTS_AT_STARTUP);
176 uint8 nonce[32];
177 SetNonce(nonce, 1101, kOrbit);
178 for (unsigned i = 0; i < 100000; i++) {
179 SetNonce(nonce, 1101 + i/500, kOrbit);
180 memcpy(nonce + 12, &i, sizeof(i));
181 EXPECT_EQ(net::NONCE_INVALID_TIME_FAILURE, set.Insert(nonce, 1100));
186 // For the following test we create a slow, but simple, version of a
187 // StrikeRegister. The behaviour of this object is much easier to understand
188 // than the fully fledged version. We then create a test to show, empirically,
189 // that the two objects have identical behaviour.
191 // A SlowStrikeRegister has the same public interface as a StrikeRegister, but
192 // is much slower. Hopefully it is also more obviously correct and we can
193 // empirically test that their behaviours are identical.
194 class SlowStrikeRegister {
195 public:
196 SlowStrikeRegister(unsigned max_entries, uint32 current_time,
197 uint32 window_secs, const uint8 orbit[8])
198 : max_entries_(max_entries),
199 window_secs_(window_secs),
200 creation_time_(current_time),
201 horizon_(ExternalTimeToInternal(current_time + window_secs) + 1) {
202 memcpy(orbit_, orbit, sizeof(orbit_));
205 InsertStatus Insert(const uint8 nonce_bytes[32],
206 const uint32 nonce_time_external,
207 const uint32 current_time_external) {
208 if (nonces_.size() == max_entries_) {
209 DropOldestEntry();
212 const uint32 current_time = ExternalTimeToInternal(current_time_external);
214 // Check to see if the orbit is correct.
215 if (memcmp(nonce_bytes + 4, orbit_, sizeof(orbit_))) {
216 return net::NONCE_INVALID_ORBIT_FAILURE;
218 const uint32 nonce_time =
219 ExternalTimeToInternal(TimeFromBytes(nonce_bytes));
220 EXPECT_EQ(ExternalTimeToInternal(nonce_time_external), nonce_time);
221 // We have dropped one or more nonces with a time value of |horizon_ - 1|,
222 // so we have to reject anything with a timestamp less than or
223 // equal to that.
224 if (nonce_time < horizon_) {
225 return net::NONCE_INVALID_TIME_FAILURE;
228 // Check that the timestamp is in the current window.
229 if ((current_time > window_secs_ &&
230 nonce_time < (current_time - window_secs_)) ||
231 nonce_time > (current_time + window_secs_)) {
232 return net::NONCE_INVALID_TIME_FAILURE;
235 pair<uint32, string> nonce = make_pair(
236 nonce_time,
237 string(reinterpret_cast<const char*>(nonce_bytes), 32));
239 set<pair<uint32, string> >::const_iterator it = nonces_.find(nonce);
240 if (it != nonces_.end()) {
241 return net::NONCE_NOT_UNIQUE_FAILURE;
244 nonces_.insert(nonce);
245 return net::NONCE_OK;
248 uint32 GetCurrentValidWindowSecs(const uint32 current_time_external) const {
249 const uint32 current_time = ExternalTimeToInternal(current_time_external);
250 if (horizon_ > current_time) {
251 return 0;
253 return 1 + min(current_time - horizon_, window_secs_);
256 private:
257 // TimeFromBytes returns a big-endian uint32 from |d|.
258 static uint32 TimeFromBytes(const uint8 d[4]) {
259 return static_cast<uint32>(d[0]) << 24 |
260 static_cast<uint32>(d[1]) << 16 |
261 static_cast<uint32>(d[2]) << 8 |
262 static_cast<uint32>(d[3]);
265 uint32 ExternalTimeToInternal(uint32 external_time) const {
266 static const uint32 kCreationTimeFromInternalEpoch = 63115200.0;
267 uint32 internal_epoch = 0;
268 if (creation_time_ > kCreationTimeFromInternalEpoch) {
269 internal_epoch = creation_time_ - kCreationTimeFromInternalEpoch;
272 return external_time - internal_epoch;
275 void DropOldestEntry() {
276 set<pair<uint32, string> >::iterator oldest = nonces_.begin();
277 horizon_ = oldest->first + 1;
278 nonces_.erase(oldest);
281 const unsigned max_entries_;
282 const unsigned window_secs_;
283 const uint32 creation_time_;
284 uint8 orbit_[8];
285 uint32 horizon_;
287 set<pair<uint32, string> > nonces_;
290 class StrikeRegisterStressTest : public ::testing::Test {
293 TEST_F(StrikeRegisterStressTest, InOrderInsertion) {
294 // Fixed seed gives reproducibility for this test.
295 srand(42);
297 unsigned max_entries = 64;
298 uint32 current_time = 10000, window = 200;
299 scoped_ptr<StrikeRegister> s1(
300 new StrikeRegister(max_entries, current_time, window, kOrbit,
301 StrikeRegister::DENY_REQUESTS_AT_STARTUP));
302 scoped_ptr<SlowStrikeRegister> s2(
303 new SlowStrikeRegister(max_entries, current_time, window, kOrbit));
305 uint64 i;
306 const uint64 kMaxIterations = 10000;
307 for (i = 0; i < kMaxIterations; i++) {
308 const uint32 time = current_time + i;
310 uint8 nonce[32];
311 SetNonce(nonce, time, kOrbit);
313 // There are 2048 possible nonce values:
314 const uint32 v = rand() % 2048;
315 nonce[30] = v >> 8;
316 nonce[31] = v;
318 const InsertStatus nonce_error2 = s2->Insert(nonce, time, time);
319 const InsertStatus nonce_error1 = s1->Insert(nonce, time);
320 EXPECT_EQ(nonce_error1, nonce_error2);
322 // Inserts succeed after the startup period.
323 if (time > current_time + window) {
324 EXPECT_EQ(net::NONCE_OK, nonce_error1);
325 } else {
326 EXPECT_EQ(net::NONCE_INVALID_TIME_FAILURE, nonce_error1);
328 EXPECT_EQ(s1->GetCurrentValidWindowSecs(time),
329 s2->GetCurrentValidWindowSecs(time));
331 if (i % 10 == 0) {
332 s1->Validate();
335 if (HasFailure()) {
336 break;
340 if (i != kMaxIterations) {
341 FAIL() << "Failed after " << i << " iterations";
345 TEST_F(StrikeRegisterStressTest, Stress) {
346 // Fixed seed gives reproducibility for this test.
347 srand(42);
348 unsigned max_entries = 64;
349 uint32 current_time = 10000, window = 200;
350 scoped_ptr<StrikeRegister> s1(
351 new StrikeRegister(max_entries, current_time, window, kOrbit,
352 StrikeRegister::DENY_REQUESTS_AT_STARTUP));
353 scoped_ptr<SlowStrikeRegister> s2(
354 new SlowStrikeRegister(max_entries, current_time, window, kOrbit));
355 uint64 i;
357 // When making changes it's worth removing the limit on this test and running
358 // it for a while. For the initial development an opt binary was left running
359 // for 10 minutes.
360 const uint64 kMaxIterations = 10000;
361 for (i = 0; i < kMaxIterations; i++) {
362 if (rand() % 1000 == 0) {
363 // 0.1% chance of resetting the sets.
364 max_entries = rand() % 300 + 2;
365 current_time = rand() % 10000;
366 window = rand() % 500;
367 s1.reset(new StrikeRegister(max_entries, current_time, window, kOrbit,
368 StrikeRegister::DENY_REQUESTS_AT_STARTUP));
369 s2.reset(
370 new SlowStrikeRegister(max_entries, current_time, window, kOrbit));
373 int32 time_delta = rand() % (window * 4);
374 time_delta -= window * 2;
375 const uint32 time = current_time + time_delta;
376 if (time_delta < 0 && time > current_time) {
377 continue; // overflow
380 uint8 nonce[32];
381 SetNonce(nonce, time, kOrbit);
383 // There are 2048 possible nonce values:
384 const uint32 v = rand() % 2048;
385 nonce[30] = v >> 8;
386 nonce[31] = v;
388 const InsertStatus nonce_error2 = s2->Insert(nonce, time, time);
389 const InsertStatus nonce_error1 = s1->Insert(nonce, time);
390 EXPECT_EQ(nonce_error1, nonce_error2);
391 EXPECT_EQ(s1->GetCurrentValidWindowSecs(time),
392 s2->GetCurrentValidWindowSecs(time));
394 if (i % 10 == 0) {
395 s1->Validate();
398 if (HasFailure()) {
399 break;
403 if (i != kMaxIterations) {
404 FAIL() << "Failed after " << i << " iterations";
408 } // anonymous namespace