Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(177)

Side by Side Diff: net/quic/crypto/strike_register_test.cc

Issue 13520010: Merge QUIC StrikeRegister code to chromium. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Changed DCHECK_EQ to CHECK_EQ Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « net/quic/crypto/strike_register.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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.
4
5 #include "net/quic/crypto/strike_register.h"
6
7 #include <set>
8 #include <string>
9
10 #include "base/basictypes.h"
11 #include "testing/gtest/include/gtest/gtest.h"
12
13 namespace {
14
15 using net::StrikeRegister;
16 using std::set;
17 using std::string;
18
19 const uint8 kOrbit[8] = {1, 2, 3, 4, 5, 6, 7, 8};
20
21 void
22 NonceSetTimeAndOrbit(uint8 nonce[32], unsigned time, const uint8 orbit[8]) {
23 nonce[0] = time >> 24;
24 nonce[1] = time >> 16;
25 nonce[2] = time >> 8;
26 nonce[3] = time;
27 memcpy(nonce + 4, orbit, 8);
28 }
29
30 TEST(StrikeRegisterTest, SimpleHorizon) {
31 // The set must reject values created on or before its own creation time.
32 StrikeRegister set(10 /* max size */, 1000 /* current time */,
33 100 /* window secs */, kOrbit);
34 uint8 nonce[32];
35 NonceSetTimeAndOrbit(nonce, 999, kOrbit);
36 ASSERT_FALSE(set.Insert(nonce, 1000));
37 NonceSetTimeAndOrbit(nonce, 1000, kOrbit);
38 ASSERT_FALSE(set.Insert(nonce, 1000));
39 }
40
41 TEST(StrikeRegisterTest, WindowFuture) {
42 // The set must reject values outside the window.
43 StrikeRegister set(10 /* max size */, 1000 /* current time */,
44 100 /* window secs */, kOrbit);
45 uint8 nonce[32];
46 NonceSetTimeAndOrbit(nonce, 1101, kOrbit);
47 ASSERT_FALSE(set.Insert(nonce, 1000));
48 NonceSetTimeAndOrbit(nonce, 999, kOrbit);
49 ASSERT_FALSE(set.Insert(nonce, 1100));
50 }
51
52 TEST(StrikeRegisterTest, BadOrbit) {
53 // The set must reject values with the wrong orbit
54 StrikeRegister set(10 /* max size */, 1000 /* current time */,
55 100 /* window secs */, kOrbit);
56 uint8 nonce[32];
57 static const uint8 kBadOrbit[8] = {0, 0, 0, 0, 1, 1, 1, 1};
58 NonceSetTimeAndOrbit(nonce, 1101, kBadOrbit);
59 ASSERT_FALSE(set.Insert(nonce, 1100));
60 }
61
62 TEST(StrikeRegisterTest, OneValue) {
63 StrikeRegister set(10 /* max size */, 1000 /* current time */,
64 100 /* window secs */, kOrbit);
65 uint8 nonce[32];
66 NonceSetTimeAndOrbit(nonce, 1101, kOrbit);
67 ASSERT_TRUE(set.Insert(nonce, 1100));
68 }
69
70 TEST(StrikeRegisterTest, RejectDuplicate) {
71 // The set must reject values with the wrong orbit
72 StrikeRegister set(10 /* max size */, 1000 /* current time */,
73 100 /* window secs */, kOrbit);
74 uint8 nonce[32];
75 memset(nonce, 0, sizeof(nonce));
76 NonceSetTimeAndOrbit(nonce, 1101, kOrbit);
77 ASSERT_TRUE(set.Insert(nonce, 1100));
78 ASSERT_FALSE(set.Insert(nonce, 1100));
79 }
80
81 TEST(StrikeRegisterTest, HorizonUpdating) {
82 StrikeRegister set(5 /* max size */, 1000 /* current time */,
83 100 /* window secs */, kOrbit);
84 uint8 nonce[6][32];
85 for (unsigned i = 0; i < 5; i++) {
86 NonceSetTimeAndOrbit(nonce[i], 1101, kOrbit);
87 memset(nonce[i] + 12, 0, 20);
88 nonce[i][31] = i;
89 ASSERT_TRUE(set.Insert(nonce[i], 1100));
90 }
91
92 // This should push the oldest value out and force the horizon to be updated.
93 NonceSetTimeAndOrbit(nonce[5], 1102, kOrbit);
94 memset(nonce[5] + 12, 0, 20);
95 ASSERT_TRUE(set.Insert(nonce[5], 1100));
96
97 // This should be behind the horizon now:
98 NonceSetTimeAndOrbit(nonce[5], 1101, kOrbit);
99 memset(nonce[5] + 12, 0, 20);
100 nonce[5][31] = 10;
101 ASSERT_FALSE(set.Insert(nonce[5], 1100));
102 }
103
104 TEST(StrikeRegisterTest, InsertMany) {
105 StrikeRegister set(5000 /* max size */, 1000 /* current time */,
106 500 /* window secs */, kOrbit);
107
108 uint8 nonce[32];
109 NonceSetTimeAndOrbit(nonce, 1101, kOrbit);
110 for (unsigned i = 0; i < 100000; i++) {
111 NonceSetTimeAndOrbit(nonce, 1101 + i/500, kOrbit);
112 memcpy(nonce + 12, &i, sizeof(i));
113 set.Insert(nonce, 1100);
114 }
115 }
116
117
118 // For the following test we create a slow, but simple, version of a
119 // StrikeRegister. The behaviour of this object is much easier to understand
120 // than the fully fledged version. We then create a test to show, empirically,
121 // that the two objects have identical behaviour.
122
123 // A SlowStrikeRegister has the same public interface as a StrikeRegister, but
124 // is much slower. Hopefully it is also more obviously correct and we can
125 // empirically test that their behaviours are identical.
126 class SlowStrikeRegister {
127 public:
128 SlowStrikeRegister(unsigned max_entries, uint32 current_time,
129 uint32 window_secs, const uint8 orbit[8])
130 : max_entries_(max_entries),
131 window_secs_(window_secs),
132 horizon_(current_time + window_secs) {
133 memcpy(orbit_, orbit, sizeof(orbit_));
134 }
135
136 bool Insert(const uint8 nonce_bytes[32], const uint32 current_time) {
137 // Same overflow and underflow check as the real StrikeRegister.
138 if (current_time < window_secs_ ||
139 current_time + window_secs_ < current_time) {
140 nonces_.clear();
141 horizon_ = current_time;
142 return false;
143 }
144
145 // Check to see if the orbit is correct.
146 if (memcmp(nonce_bytes + 4, orbit_, sizeof(orbit_))) {
147 return false;
148 }
149 const uint32 nonce_time = TimeFromBytes(nonce_bytes);
150 // We have dropped one or more nonces with a time value of |horizon_|, so
151 // we have to reject anything with a timestamp less than or equal to that.
152 if (nonce_time <= horizon_) {
153 return false;
154 }
155
156 // Check that the timestamp is in the current window.
157 if (nonce_time < (current_time - window_secs_) ||
158 nonce_time > (current_time + window_secs_)) {
159 return false;
160 }
161
162 const string nonce(reinterpret_cast<const char*>(nonce_bytes), 32);
163
164 set<string>::const_iterator it = nonces_.find(nonce);
165 if (it != nonces_.end()) {
166 return false;
167 }
168
169 if (nonces_.size() == max_entries_) {
170 DropOldestEntry();
171 }
172
173 nonces_.insert(nonce);
174 return true;
175 }
176
177 private:
178 // TimeFromBytes returns a big-endian uint32 from |d|.
179 static uint32 TimeFromBytes(const uint8 d[4]) {
180 return static_cast<uint32>(d[0]) << 24 |
181 static_cast<uint32>(d[1]) << 16 |
182 static_cast<uint32>(d[2]) << 8 |
183 static_cast<uint32>(d[3]);
184 }
185
186 void DropOldestEntry() {
187 set<string>::iterator oldest = nonces_.begin(), it;
188 uint32 oldest_time =
189 TimeFromBytes(reinterpret_cast<const uint8*>(oldest->data()));
190
191 for (it = oldest; it != nonces_.end(); it++) {
192 uint32 t = TimeFromBytes(reinterpret_cast<const uint8*>(it->data()));
193 if (t < oldest_time ||
194 (t == oldest_time && memcmp(it->data(), oldest->data(), 32) < 0)) {
195 oldest_time = t;
196 oldest = it;
197 }
198 }
199
200 nonces_.erase(oldest);
201 horizon_ = oldest_time;
202 }
203
204 const unsigned max_entries_;
205 const unsigned window_secs_;
206 uint8 orbit_[8];
207 uint32 horizon_;
208
209 set<string> nonces_;
210 };
211
212 TEST(StrikeRegisterStressTest, Stress) {
213 // Fixed seed gives reproducibility for this test.
214 srand(42);
215 unsigned max_entries = 64;
216 uint32 current_time = 10000, window = 200;
217 scoped_ptr<StrikeRegister> s1(new StrikeRegister(
218 max_entries, current_time, window, kOrbit));
219 scoped_ptr<SlowStrikeRegister> s2(new SlowStrikeRegister(
220 max_entries, current_time, window, kOrbit));
221 uint64 i;
222
223 // When making changes it's worth removing the limit on this test and running
224 // it for a while. For the initial development an opt binary was left running
225 // for 10 minutes.
226 const uint64 kMaxIterations = 10000;
227 for (i = 0; i < kMaxIterations; i++) {
228 if (rand() % 1000 == 0) {
229 // 0.1% chance of resetting the sets.
230 max_entries = rand() % 300 + 2;
231 current_time = rand() % 10000;
232 window = rand() % 500;
233 s1.reset(new StrikeRegister(max_entries, current_time, window, kOrbit));
234 s2.reset(new SlowStrikeRegister(max_entries, current_time, window,
235 kOrbit));
236 }
237
238 int32 time_delta = rand() % (window * 4);
239 time_delta -= window * 2;
240 const uint32 time = current_time + time_delta;
241 if (time_delta < 0 && time > current_time) {
242 continue; // overflow
243 }
244
245 uint8 nonce[32];
246 NonceSetTimeAndOrbit(nonce, time, kOrbit);
247 memset(nonce + 12, 0, 20);
248
249 // There are 2048 possible nonce values:
250 const uint32 v = rand() % 2048;
251 nonce[30] = v >> 8;
252 nonce[31] = v;
253
254 const bool r2 = s2->Insert(nonce, time);
255 const bool r1 = s1->Insert(nonce, time);
256
257 if (r1 != r2) {
258 break;
259 }
260
261 if (i % 10 == 0) {
262 s1->Validate();
263 }
264 }
265
266 if (i != kMaxIterations) {
267 FAIL() << "Failed after " << i << " iterations";
268 }
269 }
270
271 } // anonymous namespace
OLDNEW
« no previous file with comments | « net/quic/crypto/strike_register.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698