blob: cb01b36c2f8632539f105352ec56ed34d44be232 [file] [log] [blame]
Junxiao Shidc2d6d22016-08-04 14:30:23 +00001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Alexander Afanasyevfc99b512017-01-04 11:10:36 -08003 * Copyright (c) 2013-2017 Regents of the University of California.
Junxiao Shidc2d6d22016-08-04 14:30:23 +00004 *
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/command-interest-validator.hpp"
23#include "security/signing-helpers.hpp"
Junxiao Shidc2d6d22016-08-04 14:30:23 +000024
25#include "boost-test.hpp"
Davide Pesaventoeee3e822016-11-26 19:19:34 +010026#include "dummy-validator.hpp"
Junxiao Shidc2d6d22016-08-04 14:30:23 +000027#include "../identity-management-time-fixture.hpp"
28#include "../make-interest-data.hpp"
29
Davide Pesaventoeee3e822016-11-26 19:19:34 +010030#include <boost/lexical_cast.hpp>
31
Junxiao Shidc2d6d22016-08-04 14:30:23 +000032namespace ndn {
33namespace security {
34namespace tests {
35
36using namespace ndn::tests;
37
Alexander Afanasyev70244f42017-01-04 12:47:12 -080038class CommandInterestValidatorFixture : public IdentityManagementV1TimeFixture
Junxiao Shidc2d6d22016-08-04 14:30:23 +000039{
40protected:
41 CommandInterestValidatorFixture()
42 {
43 this->initialize(CommandInterestValidator::Options{});
44 }
45
46 void
47 initialize(const CommandInterestValidator::Options& options)
48 {
49 auto inner = make_unique<DummyValidator>();
50 this->inner = inner.get();
51 this->validator = make_unique<CommandInterestValidator>(std::move(inner), options);
52 }
53
54 Name
Davide Pesaventoeee3e822016-11-26 19:19:34 +010055 makeIdentity(uint64_t identity)
Junxiao Shidc2d6d22016-08-04 14:30:23 +000056 {
57 Name name("/localhost/CommandInterestValidatorIdentity");
58 name.appendSequenceNumber(identity);
Alexander Afanasyevfc99b512017-01-04 11:10:36 -080059 this->addIdentity(name);
Junxiao Shidc2d6d22016-08-04 14:30:23 +000060 return name;
61 }
62
63 shared_ptr<Interest>
Davide Pesaventoeee3e822016-11-26 19:19:34 +010064 makeCommandInterest(uint64_t identity = 0)
Junxiao Shidc2d6d22016-08-04 14:30:23 +000065 {
66 auto interest = makeInterest("/CommandInterestPrefix");
67 m_keyChain.sign(*interest, signingByIdentity(makeIdentity(identity)));
68 BOOST_TEST_MESSAGE("makeCommandInterest " << interest->getName());
69 return interest;
70 }
71
72 /** \brief check that validator accepts interest
73 * \param interest to be validated
74 */
75 void
76 assertAccept(const Interest& interest)
77 {
78 BOOST_TEST_MESSAGE("assertAccept " << interest.getName());
79 int nAccepts = 0;
80 validator->validate(interest,
81 [&nAccepts] (const shared_ptr<const Interest>&) { ++nAccepts; },
82 [] (const shared_ptr<const Interest>&, const std::string& msg) {
83 BOOST_ERROR("validation request should succeed but fails with: " << msg);
84 });
85 BOOST_CHECK_EQUAL(nAccepts, 1);
86 }
87
88 /** \brief check that validator rejects interest
89 * \param interest to be validated
90 * \param error if not NONE, further check the error code matches \p error
91 * if NONE, error code is not checked
92 */
93 void
94 assertReject(const Interest& interest, CommandInterestValidator::ErrorCode error)
95 {
96 BOOST_TEST_MESSAGE("assertReject " << interest.getName());
97 int nRejects = 0;
98 validator->validate(interest,
99 [] (const shared_ptr<const Interest>&) {
100 BOOST_ERROR("validation request should fail but succeeds");
101 },
102 [&nRejects, error] (const shared_ptr<const Interest>&, const std::string& msg) {
103 ++nRejects;
104 if (error != CommandInterestValidator::ErrorCode::NONE) {
105 BOOST_CHECK_EQUAL(msg, boost::lexical_cast<std::string>(error));
106 }
107 });
108 BOOST_CHECK_EQUAL(nRejects, 1);
109 }
110
111protected:
112 DummyValidator* inner;
113 unique_ptr<CommandInterestValidator> validator;
114};
115
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000116BOOST_AUTO_TEST_SUITE(Security)
117BOOST_FIXTURE_TEST_SUITE(TestCommandInterestValidator, CommandInterestValidatorFixture)
118
Davide Pesaventoeee3e822016-11-26 19:19:34 +0100119BOOST_AUTO_TEST_SUITE(Accepts)
120
121BOOST_AUTO_TEST_CASE(Basic)
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000122{
123 auto i1 = makeCommandInterest();
124 assertAccept(*i1);
125
126 advanceClocks(time::milliseconds(5));
127 auto i2 = makeCommandInterest();
128 assertAccept(*i2);
129
130 advanceClocks(time::seconds(2));
131 auto i3 = makeCommandInterest();
132 assertAccept(*i3);
133}
134
135BOOST_AUTO_TEST_CASE(DataPassthru)
136{
137 auto d1 = makeData("/data");
138 int nAccepts = 0;
139 validator->validate(*d1,
140 [&nAccepts] (const shared_ptr<const Data>&) { ++nAccepts; },
141 [] (const shared_ptr<const Data>&, const std::string& msg) {
Davide Pesaventoeee3e822016-11-26 19:19:34 +0100142 BOOST_ERROR("validation request should succeed but fails with: " << msg);
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000143 });
144 BOOST_CHECK_EQUAL(nAccepts, 1);
145}
146
Davide Pesaventoeee3e822016-11-26 19:19:34 +0100147BOOST_AUTO_TEST_SUITE_END() // Accepts
148
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000149BOOST_AUTO_TEST_SUITE(Rejects)
150
151BOOST_AUTO_TEST_CASE(NameTooShort)
152{
153 auto i1 = makeInterest("/name/too/short");
154 assertReject(*i1, CommandInterestValidator::ErrorCode::NAME_TOO_SHORT);
155}
156
157BOOST_AUTO_TEST_CASE(BadTimestamp)
158{
159 auto i1 = makeCommandInterest();
Alexander Afanasyev70244f42017-01-04 12:47:12 -0800160 setNameComponent(*i1, command_interest::POS_TIMESTAMP, "not-timestamp");
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000161 assertReject(*i1, CommandInterestValidator::ErrorCode::BAD_TIMESTAMP);
162}
163
164BOOST_AUTO_TEST_CASE(BadSigInfo)
165{
166 auto i1 = makeCommandInterest();
Junxiao Shi198c3812016-08-12 19:24:18 +0000167 setNameComponent(*i1, signed_interest::POS_SIG_INFO, "not-SignatureInfo");
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000168 assertReject(*i1, CommandInterestValidator::ErrorCode::BAD_SIG_INFO);
169}
170
171BOOST_AUTO_TEST_CASE(MissingKeyLocator)
172{
173 auto i1 = makeCommandInterest();
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000174 SignatureInfo sigInfo;
Junxiao Shi198c3812016-08-12 19:24:18 +0000175 setNameComponent(*i1, signed_interest::POS_SIG_INFO,
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000176 sigInfo.wireEncode().begin(), sigInfo.wireEncode().end());
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000177 assertReject(*i1, CommandInterestValidator::ErrorCode::MISSING_KEY_LOCATOR);
178}
179
180BOOST_AUTO_TEST_CASE(BadKeyLocatorType)
181{
182 auto i1 = makeCommandInterest();
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000183 KeyLocator kl;
184 kl.setKeyDigest(makeBinaryBlock(tlv::KeyDigest, "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD", 8));
185 SignatureInfo sigInfo;
186 sigInfo.setKeyLocator(kl);
Junxiao Shi198c3812016-08-12 19:24:18 +0000187 setNameComponent(*i1, signed_interest::POS_SIG_INFO,
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000188 sigInfo.wireEncode().begin(), sigInfo.wireEncode().end());
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000189 assertReject(*i1, CommandInterestValidator::ErrorCode::BAD_KEY_LOCATOR_TYPE);
190}
191
192BOOST_AUTO_TEST_CASE(BadCertName)
193{
194 auto i1 = makeCommandInterest();
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000195 KeyLocator kl;
196 kl.setName("/bad/cert/name");
197 SignatureInfo sigInfo;
198 sigInfo.setKeyLocator(kl);
Junxiao Shi198c3812016-08-12 19:24:18 +0000199 setNameComponent(*i1, signed_interest::POS_SIG_INFO,
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000200 sigInfo.wireEncode().begin(), sigInfo.wireEncode().end());
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000201 assertReject(*i1, CommandInterestValidator::ErrorCode::BAD_CERT_NAME);
202}
203
204BOOST_AUTO_TEST_CASE(InnerReject)
205{
206 inner->setResult(false);
207 auto i1 = makeCommandInterest();
208 assertReject(*i1, CommandInterestValidator::ErrorCode::NONE);
209}
210
211BOOST_AUTO_TEST_CASE(TimestampOutOfGracePositive)
212{
213 CommandInterestValidator::Options options;
214 options.gracePeriod = time::seconds(15);
215 initialize(options);
216
217 auto i1 = makeCommandInterest(); // signed at 0s
218 advanceClocks(time::seconds(16)); // verifying at +16s
219 assertReject(*i1, CommandInterestValidator::ErrorCode::TIMESTAMP_OUT_OF_GRACE);
220
221 auto i2 = makeCommandInterest(); // signed at +16s
222 assertAccept(*i2); // verifying at +16s
223}
224
225BOOST_AUTO_TEST_CASE(TimestampOutOfGraceNegative)
226{
227 CommandInterestValidator::Options options;
228 options.gracePeriod = time::seconds(15);
229 initialize(options);
230
231 auto i1 = makeCommandInterest(); // signed at 0s
232 advanceClocks(time::seconds(1));
233 auto i2 = makeCommandInterest(); // signed at +1s
234 advanceClocks(time::seconds(1));
235 auto i3 = makeCommandInterest(); // signed at +2s
236
237 systemClock->advance(time::seconds(-18)); // verifying at -16s
238 assertReject(*i1, CommandInterestValidator::ErrorCode::TIMESTAMP_OUT_OF_GRACE);
239
240 // CommandInterestValidator should not remember i1's timestamp
241 assertReject(*i2, CommandInterestValidator::ErrorCode::TIMESTAMP_OUT_OF_GRACE);
242
243 // CommandInterestValidator should not remember i2's timestamp, and should treat i3 as initial
244 advanceClocks(time::seconds(18)); // verifying at +2s
245 assertAccept(*i3);
246}
247
248BOOST_AUTO_TEST_CASE(TimestampReorderEqual)
249{
250 auto i1 = makeCommandInterest(); // signed at 0s
251 assertAccept(*i1);
252
Junxiao Shi198c3812016-08-12 19:24:18 +0000253 auto i2 = makeCommandInterest(); // signed at 0s
Alexander Afanasyev70244f42017-01-04 12:47:12 -0800254 setNameComponent(*i2, command_interest::POS_TIMESTAMP,
255 i1->getName()[command_interest::POS_TIMESTAMP]);
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000256 assertReject(*i2, CommandInterestValidator::ErrorCode::TIMESTAMP_REORDER);
257
258 advanceClocks(time::seconds(2));
259 auto i3 = makeCommandInterest(); // signed at +2s
260 assertAccept(*i3);
261}
262
263BOOST_AUTO_TEST_CASE(TimestampReorderNegative)
264{
265 auto i2 = makeCommandInterest(); // signed at 0ms
266 advanceClocks(time::milliseconds(200));
267 auto i3 = makeCommandInterest(); // signed at +200ms
268 advanceClocks(time::milliseconds(900));
269 auto i1 = makeCommandInterest(); // signed at +1100ms
270 advanceClocks(time::milliseconds(300));
271 auto i4 = makeCommandInterest(); // signed at +1400ms
272
273 systemClock->advance(time::milliseconds(-300)); // verifying at +1100ms
274 assertAccept(*i1);
275
276 systemClock->advance(time::milliseconds(-1100)); // verifying at 0ms
277 assertReject(*i2, CommandInterestValidator::ErrorCode::TIMESTAMP_REORDER);
278
279 // CommandInterestValidator should not remember i2's timestamp
280 advanceClocks(time::milliseconds(200)); // verifying at +200ms
281 assertReject(*i3, CommandInterestValidator::ErrorCode::TIMESTAMP_REORDER);
282
283 advanceClocks(time::milliseconds(1200)); // verifying at 1400ms
284 assertAccept(*i4);
285}
286
287BOOST_AUTO_TEST_SUITE_END() // Rejects
288
289BOOST_AUTO_TEST_SUITE(Options)
290
291typedef boost::mpl::vector<
292 boost::mpl::int_<0>,
293 boost::mpl::int_<-1>
294> GraceNonPositiveValues;
295
296BOOST_AUTO_TEST_CASE_TEMPLATE(GraceNonPositive, VALUE, GraceNonPositiveValues)
297{
298 CommandInterestValidator::Options options;
299 options.gracePeriod = time::seconds(VALUE::value);
300 initialize(options);
301
302 auto i1 = makeCommandInterest(1); // signed at 0ms
303 auto i2 = makeCommandInterest(2); // signed at 0ms
304 for (auto interest : {i1, i2}) {
Alexander Afanasyev70244f42017-01-04 12:47:12 -0800305 setNameComponent(*interest, command_interest::POS_TIMESTAMP,
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000306 name::Component::fromNumber(time::toUnixTimestamp(time::system_clock::now()).count()));
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000307 } // ensure timestamps are exactly 0ms
308
309 assertAccept(*i1); // verifying at 0ms
310
311 advanceClocks(time::milliseconds(1));
312 assertReject(*i2, CommandInterestValidator::ErrorCode::TIMESTAMP_OUT_OF_GRACE); // verifying at 1ms
313}
314
315BOOST_AUTO_TEST_CASE(TimestampsLimited)
316{
317 CommandInterestValidator::Options options;
318 options.gracePeriod = time::seconds(15);
319 options.maxTimestamps = 3;
320 initialize(options);
321
322 auto i1 = makeCommandInterest(1);
323 auto i2 = makeCommandInterest(2);
324 auto i3 = makeCommandInterest(3);
325 auto i00 = makeCommandInterest(0); // signed at 0s
326 advanceClocks(time::seconds(1));
327 auto i01 = makeCommandInterest(0); // signed at 1s
328 advanceClocks(time::seconds(1));
329 auto i02 = makeCommandInterest(0); // signed at 2s
330
331 assertAccept(*i00);
332 assertAccept(*i02);
333 assertAccept(*i1);
334 assertAccept(*i2);
335 assertAccept(*i3); // forgets identity 0
336 assertAccept(*i01); // accepted despite timestamp is reordered, because record has been evicted
337}
338
339BOOST_AUTO_TEST_CASE(TimestampsUnlimited)
340{
341 CommandInterestValidator::Options options;
342 options.gracePeriod = time::seconds(15);
343 options.maxTimestamps = -1;
344 initialize(options);
345
346 auto i1 = makeCommandInterest(0); // signed at 0s
347 advanceClocks(time::seconds(1));
348 for (int identity = 0; identity < 20; ++identity) {
349 auto i2 = makeCommandInterest(identity); // signed at +1s
350 assertAccept(*i2);
351 }
352 assertReject(*i1, CommandInterestValidator::ErrorCode::TIMESTAMP_REORDER);
353}
354
355BOOST_AUTO_TEST_CASE(TimestampsDisabled)
356{
357 CommandInterestValidator::Options options;
358 options.gracePeriod = time::seconds(15);
359 options.maxTimestamps = 0;
360 initialize(options);
361
362 auto i1 = makeCommandInterest(); // signed at 0s
363 advanceClocks(time::seconds(1));
364 auto i2 = makeCommandInterest(); // signed at +1s
365 assertAccept(*i2);
366
367 assertAccept(*i1); // accepted despite timestamp is reordered, because record isn't kept
368}
369
370BOOST_AUTO_TEST_CASE(TtlLimited)
371{
372 CommandInterestValidator::Options options;
373 options.gracePeriod = time::seconds(120);
374 options.timestampTtl = time::seconds(300);
375 initialize(options);
376
377 auto i1 = makeCommandInterest(); // signed at 0s
378 advanceClocks(time::seconds(240));
379 auto i2 = makeCommandInterest(); // signed at +240s
380 advanceClocks(time::seconds(120));
381 auto i3 = makeCommandInterest(); // signed at +360s
382
383 systemClock->advance(time::seconds(-360)); // rewind system clock to 0s
384 assertAccept(*i1);
385 assertAccept(*i3);
386
387 advanceClocks(time::seconds(30), time::seconds(301)); // advance steady clock by 301s, and system clock to +301s
388 assertAccept(*i2); // accepted despite timestamp is reordered, because record has been expired
389}
390
391BOOST_AUTO_TEST_CASE(TtlZero)
392{
393 CommandInterestValidator::Options options;
394 options.gracePeriod = time::seconds(15);
395 options.timestampTtl = time::seconds::zero();
396 initialize(options);
397
398 auto i1 = makeCommandInterest(); // signed at 0s
399 advanceClocks(time::seconds(1));
400 auto i2 = makeCommandInterest(); // signed at +1s
401 assertAccept(*i2);
402
403 assertAccept(*i1); // accepted despite timestamp is reordered, because record has been expired
404}
405
406BOOST_AUTO_TEST_SUITE_END() // Options
407
408BOOST_AUTO_TEST_SUITE_END() // TestCommandInterestValidator
409BOOST_AUTO_TEST_SUITE_END() // Security
410
411} // namespace tests
412} // namespace security
413} // namespace ndn