blob: 887214ae213d009db3dea636f4284aafb80e9670 [file] [log] [blame]
Alexander Afanasyev93338872017-01-30 22:37:00 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Junxiao Shi2bea5c42017-08-14 20:10:32 +00002/*
Alexander Afanasyev93338872017-01-30 22:37:00 -08003 * Copyright (c) 2013-2017 Regents of the University of California.
4 *
5 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
6 *
7 * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8 * terms of the GNU Lesser General Public License as published by the Free Software
9 * Foundation, either version 3 of the License, or (at your option) any later version.
10 *
11 * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14 *
15 * You should have received copies of the GNU General Public License and GNU Lesser
16 * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17 * <http://www.gnu.org/licenses/>.
18 *
19 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
20 */
21
22#include "security/v2/validation-policy-command-interest.hpp"
23#include "security/v2/validation-policy-simple-hierarchy.hpp"
24#include "security/v2/validation-policy-accept-all.hpp"
25#include "security/command-interest-signer.hpp"
26#include "security/signing-helpers.hpp"
27
28#include "boost-test.hpp"
29#include "validator-fixture.hpp"
Junxiao Shi2bea5c42017-08-14 20:10:32 +000030#include "make-interest-data.hpp"
Alexander Afanasyev93338872017-01-30 22:37:00 -080031
32#include <boost/lexical_cast.hpp>
33#include <boost/mpl/vector.hpp>
34
35namespace ndn {
36namespace security {
37namespace v2 {
38namespace tests {
39
40using namespace ndn::tests;
41
42BOOST_AUTO_TEST_SUITE(Security)
43BOOST_AUTO_TEST_SUITE(V2)
44
45class DefaultOptions
46{
47public:
48 static ValidationPolicyCommandInterest::Options
49 getOptions()
50 {
51 return {};
52 }
53};
54
55template<class T, class InnerPolicy>
56class CommandInterestPolicyWrapper : public ValidationPolicyCommandInterest
57{
58public:
59 CommandInterestPolicyWrapper()
60 : ValidationPolicyCommandInterest(make_unique<InnerPolicy>(), T::getOptions())
61 {
62 }
63};
64
65template<class T, class InnerPolicy = ValidationPolicySimpleHierarchy>
66class ValidationPolicyCommandInterestFixture : public HierarchicalValidatorFixture<CommandInterestPolicyWrapper<T, InnerPolicy>>
67{
68public:
69 ValidationPolicyCommandInterestFixture()
70 : m_signer(this->m_keyChain)
71 {
72 }
73
74 Interest
75 makeCommandInterest(const Identity& identity)
76 {
77 return m_signer.makeCommandInterest(Name(identity.getName()).append("CMD"),
78 signingByIdentity(identity));
79 }
80
81public:
82 CommandInterestSigner m_signer;
83};
84
85BOOST_FIXTURE_TEST_SUITE(TestValidationPolicyCommandInterest, ValidationPolicyCommandInterestFixture<DefaultOptions>)
86
87BOOST_AUTO_TEST_SUITE(Accepts)
88
89BOOST_AUTO_TEST_CASE(Basic)
90{
91 auto i1 = makeCommandInterest(identity);
92 VALIDATE_SUCCESS(i1, "Should succeed (within grace period)");
93
94 advanceClocks(time::milliseconds(5));
95 auto i2 = makeCommandInterest(identity);
96 VALIDATE_SUCCESS(i2, "Should succeed (timestamp larger than previous)");
97}
98
99BOOST_AUTO_TEST_CASE(DataPassthru)
100{
101 Data d1("/Security/V2/ValidatorFixture/Sub1");
102 m_keyChain.sign(d1);
103 VALIDATE_SUCCESS(d1, "Should succeed (fallback on inner validation policy for data)");
104}
105
106BOOST_AUTO_TEST_SUITE_END() // Accepts
107
108BOOST_AUTO_TEST_SUITE(Rejects)
109
110BOOST_AUTO_TEST_CASE(NameTooShort)
111{
112 auto i1 = makeInterest("/name/too/short");
113 VALIDATE_FAILURE(*i1, "Should fail (name is too short)");
114}
115
116BOOST_AUTO_TEST_CASE(BadTimestamp)
117{
118 auto i1 = makeCommandInterest(identity);
119 setNameComponent(i1, command_interest::POS_TIMESTAMP, "not-timestamp");
120 VALIDATE_FAILURE(i1, "Should fail (timestamp is missing)");
121}
122
123BOOST_AUTO_TEST_CASE(BadSigInfo)
124{
125 auto i1 = makeCommandInterest(identity);
126 setNameComponent(i1, command_interest::POS_SIG_INFO, "not-SignatureInfo");
127 VALIDATE_FAILURE(i1, "Should fail (signature info is missing)");
128}
129
130BOOST_AUTO_TEST_CASE(MissingKeyLocator)
131{
132 auto i1 = makeCommandInterest(identity);
133 SignatureInfo sigInfo;
134 setNameComponent(i1, command_interest::POS_SIG_INFO,
135 sigInfo.wireEncode().begin(), sigInfo.wireEncode().end());
136 VALIDATE_FAILURE(i1, "Should fail (missing KeyLocator)");
137}
138
139BOOST_AUTO_TEST_CASE(BadKeyLocatorType)
140{
141 auto i1 = makeCommandInterest(identity);
142 KeyLocator kl;
143 kl.setKeyDigest(makeBinaryBlock(tlv::KeyDigest, "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD", 8));
144 SignatureInfo sigInfo;
145 sigInfo.setKeyLocator(kl);
146 setNameComponent(i1, command_interest::POS_SIG_INFO,
147 sigInfo.wireEncode().begin(), sigInfo.wireEncode().end());
148 VALIDATE_FAILURE(i1, "Should fail (bad KeyLocator type)");
149}
150
151BOOST_AUTO_TEST_CASE(BadCertName)
152{
153 auto i1 = makeCommandInterest(identity);
154 KeyLocator kl;
155 kl.setName("/bad/cert/name");
156 SignatureInfo sigInfo;
157 sigInfo.setKeyLocator(kl);
158 setNameComponent(i1, command_interest::POS_SIG_INFO,
159 sigInfo.wireEncode().begin(), sigInfo.wireEncode().end());
160 VALIDATE_FAILURE(i1, "Should fail (bad certificate name)");
161}
162
163BOOST_AUTO_TEST_CASE(InnerPolicyReject)
164{
165 auto i1 = makeCommandInterest(otherIdentity);
166 VALIDATE_FAILURE(i1, "Should fail (inner policy should reject)");
167}
168
169class GracePeriod15Sec
170{
171public:
172 static ValidationPolicyCommandInterest::Options
173 getOptions()
174 {
175 ValidationPolicyCommandInterest::Options options;
176 options.gracePeriod = time::seconds(15);
177 return options;
178 }
179};
180
181BOOST_FIXTURE_TEST_CASE(TimestampOutOfGracePositive, ValidationPolicyCommandInterestFixture<GracePeriod15Sec>)
182{
183 auto i1 = makeCommandInterest(identity); // signed at 0s
184 advanceClocks(time::seconds(16)); // verifying at +16s
185 VALIDATE_FAILURE(i1, "Should fail (timestamp outside the grace period)");
186 rewindClockAfterValidation();
187
188 auto i2 = makeCommandInterest(identity); // signed at +16s
189 VALIDATE_SUCCESS(i2, "Should succeed");
190}
191
192BOOST_FIXTURE_TEST_CASE(TimestampOutOfGraceNegative, ValidationPolicyCommandInterestFixture<GracePeriod15Sec>)
193{
194 auto i1 = makeCommandInterest(identity); // signed at 0s
195 advanceClocks(time::seconds(1));
196 auto i2 = makeCommandInterest(identity); // signed at +1s
197 advanceClocks(time::seconds(1));
198 auto i3 = makeCommandInterest(identity); // signed at +2s
199
200 systemClock->advance(time::seconds(-18)); // verifying at -16s
201 VALIDATE_FAILURE(i1, "Should fail (timestamp outside the grace period)");
202 rewindClockAfterValidation();
203
204 // CommandInterestValidator should not remember i1's timestamp
205 VALIDATE_FAILURE(i2, "Should fail (timestamp outside the grace period)");
206 rewindClockAfterValidation();
207
208 // CommandInterestValidator should not remember i2's timestamp, and should treat i3 as initial
209 advanceClocks(time::seconds(18)); // verifying at +2s
210 VALIDATE_SUCCESS(i3, "Should succeed");
211}
212
213BOOST_AUTO_TEST_CASE(TimestampReorderEqual)
214{
215 auto i1 = makeCommandInterest(identity); // signed at 0s
216 VALIDATE_SUCCESS(i1, "Should succeed");
217
218 auto i2 = makeCommandInterest(identity); // signed at 0s
219 setNameComponent(i2, command_interest::POS_TIMESTAMP,
220 i1.getName()[command_interest::POS_TIMESTAMP]);
221 VALIDATE_FAILURE(i2, "Should fail (timestamp reordered)");
222
223 advanceClocks(time::seconds(2));
224 auto i3 = makeCommandInterest(identity); // signed at +2s
225 VALIDATE_SUCCESS(i3, "Should succeed");
226}
227
228BOOST_AUTO_TEST_CASE(TimestampReorderNegative)
229{
230 auto i2 = makeCommandInterest(identity); // signed at 0ms
231 advanceClocks(time::milliseconds(200));
232 auto i3 = makeCommandInterest(identity); // signed at +200ms
233 advanceClocks(time::milliseconds(900));
234 auto i1 = makeCommandInterest(identity); // signed at +1100ms
235 advanceClocks(time::milliseconds(300));
236 auto i4 = makeCommandInterest(identity); // signed at +1400ms
237
238 systemClock->advance(time::milliseconds(-300)); // verifying at +1100ms
239 VALIDATE_SUCCESS(i1, "Should succeed");
240 rewindClockAfterValidation();
241
242 systemClock->advance(time::milliseconds(-1100)); // verifying at 0ms
243 VALIDATE_FAILURE(i2, "Should fail (timestamp reordered)");
244 rewindClockAfterValidation();
245
246 // CommandInterestValidator should not remember i2's timestamp
247 advanceClocks(time::milliseconds(200)); // verifying at +200ms
248 VALIDATE_FAILURE(i3, "Should fail (timestamp reordered)");
249 rewindClockAfterValidation();
250
251 advanceClocks(time::milliseconds(1200)); // verifying at 1400ms
252 VALIDATE_SUCCESS(i4, "Should succeed");
253}
254
255BOOST_AUTO_TEST_SUITE_END() // Rejects
256
257BOOST_AUTO_TEST_SUITE(Options)
258
259template<class T>
260class GracePeriod
261{
262public:
263 static ValidationPolicyCommandInterest::Options
264 getOptions()
265 {
266 ValidationPolicyCommandInterest::Options options;
267 options.gracePeriod = time::seconds(T::value);
268 return options;
269 }
270};
271
272typedef boost::mpl::vector<
273 GracePeriod<boost::mpl::int_<0>>,
274 GracePeriod<boost::mpl::int_<-1>>
275> GraceNonPositiveValues;
276
277BOOST_FIXTURE_TEST_CASE_TEMPLATE(GraceNonPositive, GracePeriod, GraceNonPositiveValues,
278 ValidationPolicyCommandInterestFixture<GracePeriod>)
279{
280 auto i1 = this->makeCommandInterest(this->identity); // signed at 0ms
281 auto i2 = this->makeCommandInterest(this->subIdentity); // signed at 0ms
282 for (auto interest : {&i1, &i2}) {
283 setNameComponent(*interest, command_interest::POS_TIMESTAMP,
284 name::Component::fromNumber(time::toUnixTimestamp(time::system_clock::now()).count()));
285 } // ensure timestamps are exactly 0ms
286
287 VALIDATE_SUCCESS(i1, "Should succeed when validating at 0ms");
288 this->rewindClockAfterValidation();
289
290 this->advanceClocks(time::milliseconds(1));
291 VALIDATE_FAILURE(i2, "Should fail when validating at 1ms");
292}
293
294class LimitedRecordsOptions
295{
296public:
297 static ValidationPolicyCommandInterest::Options
298 getOptions()
299 {
300 ValidationPolicyCommandInterest::Options options;
301 options.gracePeriod = time::seconds(15);
302 options.maxRecords = 3;
303 return options;
304 }
305};
306
307BOOST_FIXTURE_TEST_CASE(LimitedRecords, ValidationPolicyCommandInterestFixture<LimitedRecordsOptions>)
308{
309 Identity id1 = this->addSubCertificate("/Security/V2/ValidatorFixture/Sub1", identity);
310 this->cache.insert(id1.getDefaultKey().getDefaultCertificate());
311 Identity id2 = this->addSubCertificate("/Security/V2/ValidatorFixture/Sub2", identity);
312 this->cache.insert(id2.getDefaultKey().getDefaultCertificate());
313 Identity id3 = this->addSubCertificate("/Security/V2/ValidatorFixture/Sub3", identity);
314 this->cache.insert(id3.getDefaultKey().getDefaultCertificate());
315 Identity id4 = this->addSubCertificate("/Security/V2/ValidatorFixture/Sub4", identity);
316 this->cache.insert(id4.getDefaultKey().getDefaultCertificate());
317
318 auto i1 = makeCommandInterest(id2);
319 auto i2 = makeCommandInterest(id3);
320 auto i3 = makeCommandInterest(id4);
321 auto i00 = makeCommandInterest(id1); // signed at 0s
322 advanceClocks(time::seconds(1));
323 auto i01 = makeCommandInterest(id1); // signed at 1s
324 advanceClocks(time::seconds(1));
325 auto i02 = makeCommandInterest(id1); // signed at 2s
326
327 VALIDATE_SUCCESS(i00, "Should succeed");
328 rewindClockAfterValidation();
329
330 VALIDATE_SUCCESS(i02, "Should succeed");
331 rewindClockAfterValidation();
332
333 VALIDATE_SUCCESS(i1, "Should succeed");
334 rewindClockAfterValidation();
335
336 VALIDATE_SUCCESS(i2, "Should succeed");
337 rewindClockAfterValidation();
338
339 VALIDATE_SUCCESS(i3, "Should succeed, forgets identity id1");
340 rewindClockAfterValidation();
341
342 VALIDATE_SUCCESS(i01, "Should succeed despite timestamp is reordered, because record has been evicted");
343}
344
345class UnlimitedRecordsOptions
346{
347public:
348 static ValidationPolicyCommandInterest::Options
349 getOptions()
350 {
351 ValidationPolicyCommandInterest::Options options;
352 options.gracePeriod = time::seconds(15);
353 options.maxRecords = -1;
354 return options;
355 }
356};
357
358BOOST_FIXTURE_TEST_CASE(UnlimitedRecords, ValidationPolicyCommandInterestFixture<UnlimitedRecordsOptions>)
359{
360 std::vector<Identity> identities;
361 for (int i = 0; i < 20; ++i) {
362 Identity id = this->addSubCertificate("/Security/V2/ValidatorFixture/Sub" + to_string(i), identity);
363 this->cache.insert(id.getDefaultKey().getDefaultCertificate());
364 identities.push_back(id);
365 }
366
367 auto i1 = makeCommandInterest(identities.at(0)); // signed at 0s
368 advanceClocks(time::seconds(1));
369 for (int i = 0; i < 20; ++i) {
370 auto i2 = makeCommandInterest(identities.at(i)); // signed at +1s
371
372 VALIDATE_SUCCESS(i2, "Should succeed");
373 rewindClockAfterValidation();
374 }
375 VALIDATE_FAILURE(i1, "Should fail (timestamp reorder)");
376}
377
378class ZeroRecordsOptions
379{
380public:
381 static ValidationPolicyCommandInterest::Options
382 getOptions()
383 {
384 ValidationPolicyCommandInterest::Options options;
385 options.gracePeriod = time::seconds(15);
386 options.maxRecords = 0;
387 return options;
388 }
389};
390
391BOOST_FIXTURE_TEST_CASE(ZeroRecords, ValidationPolicyCommandInterestFixture<ZeroRecordsOptions>)
392{
393 auto i1 = makeCommandInterest(identity); // signed at 0s
394 advanceClocks(time::seconds(1));
395 auto i2 = makeCommandInterest(identity); // signed at +1s
396 VALIDATE_SUCCESS(i2, "Should succeed");
397 rewindClockAfterValidation();
398
399 VALIDATE_SUCCESS(i1, "Should succeed despite timestamp is reordered, because record isn't kept");
400}
401
402class LimitedRecordLifetimeOptions
403{
404public:
405 static ValidationPolicyCommandInterest::Options
406 getOptions()
407 {
408 ValidationPolicyCommandInterest::Options options;
409 options.gracePeriod = time::seconds(400);
410 options.recordLifetime = time::seconds(300);
411 return options;
412 }
413};
414
415BOOST_FIXTURE_TEST_CASE(LimitedRecordLifetime, ValidationPolicyCommandInterestFixture<LimitedRecordLifetimeOptions>)
416{
417 auto i1 = makeCommandInterest(identity); // signed at 0s
418 advanceClocks(time::seconds(240));
419 auto i2 = makeCommandInterest(identity); // signed at +240s
420 advanceClocks(time::seconds(120));
421 auto i3 = makeCommandInterest(identity); // signed at +360s
422
423 systemClock->advance(time::seconds(-360)); // rewind system clock to 0s
424 VALIDATE_SUCCESS(i1, "Should succeed");
425 rewindClockAfterValidation();
426
427 VALIDATE_SUCCESS(i3, "Should succeed");
428 rewindClockAfterValidation();
429
430 advanceClocks(time::seconds(30), time::seconds(301)); // advance steady clock by 301s, and system clock to +301s
431 VALIDATE_SUCCESS(i2, "Should succeed despite timestamp is reordered, because record has been expired");
432}
433
434class ZeroRecordLifetimeOptions
435{
436public:
437 static ValidationPolicyCommandInterest::Options
438 getOptions()
439 {
440 ValidationPolicyCommandInterest::Options options;
441 options.gracePeriod = time::seconds(15);
442 options.recordLifetime = time::seconds::zero();
443 return options;
444 }
445};
446
447BOOST_FIXTURE_TEST_CASE(ZeroRecordLifetime, ValidationPolicyCommandInterestFixture<ZeroRecordLifetimeOptions>)
448{
449 auto i1 = makeCommandInterest(identity); // signed at 0s
450 advanceClocks(time::seconds(1));
451 auto i2 = makeCommandInterest(identity); // signed at +1s
452 VALIDATE_SUCCESS(i2, "Should succeed");
453 rewindClockAfterValidation();
454
455 VALIDATE_SUCCESS(i1, "Should succeed despite timestamp is reordered, because record has been expired");
456}
457
458BOOST_AUTO_TEST_SUITE_END() // Options
459
460BOOST_AUTO_TEST_SUITE_END() // TestValidationPolicyCommandInterest
461BOOST_AUTO_TEST_SUITE_END() // V2
462BOOST_AUTO_TEST_SUITE_END() // Security
463
464} // namespace tests
465} // namespace v2
466} // namespace security
467} // namespace ndn