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

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

Powered by Google App Engine
This is Rietveld 408576698