blob: e8c4eeb5b19766336b016687479e48b9c2151c95 [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"
Alexander Afanasyev80782e02017-01-04 13:16:54 -080023#include "security/command-interest-signer.hpp"
Junxiao Shidc2d6d22016-08-04 14:30:23 +000024#include "security/signing-helpers.hpp"
Junxiao Shidc2d6d22016-08-04 14:30:23 +000025
26#include "boost-test.hpp"
Davide Pesaventoeee3e822016-11-26 19:19:34 +010027#include "dummy-validator.hpp"
Junxiao Shidc2d6d22016-08-04 14:30:23 +000028#include "../identity-management-time-fixture.hpp"
29#include "../make-interest-data.hpp"
30
Davide Pesaventoeee3e822016-11-26 19:19:34 +010031#include <boost/lexical_cast.hpp>
32
Junxiao Shidc2d6d22016-08-04 14:30:23 +000033namespace ndn {
34namespace security {
35namespace tests {
36
37using namespace ndn::tests;
38
Alexander Afanasyev80782e02017-01-04 13:16:54 -080039class CommandInterestValidatorFixture : public IdentityManagementTimeFixture
Junxiao Shidc2d6d22016-08-04 14:30:23 +000040{
41protected:
42 CommandInterestValidatorFixture()
Alexander Afanasyev80782e02017-01-04 13:16:54 -080043 : signer(m_keyChain)
Junxiao Shidc2d6d22016-08-04 14:30:23 +000044 {
45 this->initialize(CommandInterestValidator::Options{});
46 }
47
48 void
49 initialize(const CommandInterestValidator::Options& options)
50 {
51 auto inner = make_unique<DummyValidator>();
52 this->inner = inner.get();
53 this->validator = make_unique<CommandInterestValidator>(std::move(inner), options);
54 }
55
56 Name
Davide Pesaventoeee3e822016-11-26 19:19:34 +010057 makeIdentity(uint64_t identity)
Junxiao Shidc2d6d22016-08-04 14:30:23 +000058 {
59 Name name("/localhost/CommandInterestValidatorIdentity");
60 name.appendSequenceNumber(identity);
Alexander Afanasyevfc99b512017-01-04 11:10:36 -080061 this->addIdentity(name);
Junxiao Shidc2d6d22016-08-04 14:30:23 +000062 return name;
63 }
64
65 shared_ptr<Interest>
Davide Pesaventoeee3e822016-11-26 19:19:34 +010066 makeCommandInterest(uint64_t identity = 0)
Junxiao Shidc2d6d22016-08-04 14:30:23 +000067 {
Alexander Afanasyev80782e02017-01-04 13:16:54 -080068 auto interest = signer.makeCommandInterest("/CommandInterestPrefix", signingByIdentity(makeIdentity(identity)));
69 return make_shared<Interest>(std::move(interest));
Junxiao Shidc2d6d22016-08-04 14:30:23 +000070 }
71
72 /** \brief check that validator accepts interest
73 * \param interest to be validated
74 */
75 void
76 assertAccept(const Interest& interest)
77 {
Junxiao Shidc2d6d22016-08-04 14:30:23 +000078 int nAccepts = 0;
79 validator->validate(interest,
80 [&nAccepts] (const shared_ptr<const Interest>&) { ++nAccepts; },
81 [] (const shared_ptr<const Interest>&, const std::string& msg) {
82 BOOST_ERROR("validation request should succeed but fails with: " << msg);
83 });
84 BOOST_CHECK_EQUAL(nAccepts, 1);
85 }
86
87 /** \brief check that validator rejects interest
88 * \param interest to be validated
89 * \param error if not NONE, further check the error code matches \p error
90 * if NONE, error code is not checked
91 */
92 void
93 assertReject(const Interest& interest, CommandInterestValidator::ErrorCode error)
94 {
Junxiao Shidc2d6d22016-08-04 14:30:23 +000095 int nRejects = 0;
96 validator->validate(interest,
97 [] (const shared_ptr<const Interest>&) {
98 BOOST_ERROR("validation request should fail but succeeds");
99 },
100 [&nRejects, error] (const shared_ptr<const Interest>&, const std::string& msg) {
101 ++nRejects;
102 if (error != CommandInterestValidator::ErrorCode::NONE) {
103 BOOST_CHECK_EQUAL(msg, boost::lexical_cast<std::string>(error));
104 }
105 });
106 BOOST_CHECK_EQUAL(nRejects, 1);
107 }
108
109protected:
Alexander Afanasyev80782e02017-01-04 13:16:54 -0800110 CommandInterestSigner signer;
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000111 DummyValidator* inner;
112 unique_ptr<CommandInterestValidator> validator;
113};
114
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000115BOOST_AUTO_TEST_SUITE(Security)
116BOOST_FIXTURE_TEST_SUITE(TestCommandInterestValidator, CommandInterestValidatorFixture)
117
Davide Pesaventoeee3e822016-11-26 19:19:34 +0100118BOOST_AUTO_TEST_SUITE(Accepts)
119
120BOOST_AUTO_TEST_CASE(Basic)
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000121{
122 auto i1 = makeCommandInterest();
123 assertAccept(*i1);
124
125 advanceClocks(time::milliseconds(5));
126 auto i2 = makeCommandInterest();
127 assertAccept(*i2);
128
129 advanceClocks(time::seconds(2));
130 auto i3 = makeCommandInterest();
131 assertAccept(*i3);
132}
133
134BOOST_AUTO_TEST_CASE(DataPassthru)
135{
136 auto d1 = makeData("/data");
137 int nAccepts = 0;
138 validator->validate(*d1,
139 [&nAccepts] (const shared_ptr<const Data>&) { ++nAccepts; },
140 [] (const shared_ptr<const Data>&, const std::string& msg) {
Davide Pesaventoeee3e822016-11-26 19:19:34 +0100141 BOOST_ERROR("validation request should succeed but fails with: " << msg);
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000142 });
143 BOOST_CHECK_EQUAL(nAccepts, 1);
144}
145
Davide Pesaventoeee3e822016-11-26 19:19:34 +0100146BOOST_AUTO_TEST_SUITE_END() // Accepts
147
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000148BOOST_AUTO_TEST_SUITE(Rejects)
149
150BOOST_AUTO_TEST_CASE(NameTooShort)
151{
152 auto i1 = makeInterest("/name/too/short");
153 assertReject(*i1, CommandInterestValidator::ErrorCode::NAME_TOO_SHORT);
154}
155
156BOOST_AUTO_TEST_CASE(BadTimestamp)
157{
158 auto i1 = makeCommandInterest();
Alexander Afanasyev70244f42017-01-04 12:47:12 -0800159 setNameComponent(*i1, command_interest::POS_TIMESTAMP, "not-timestamp");
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000160 assertReject(*i1, CommandInterestValidator::ErrorCode::BAD_TIMESTAMP);
161}
162
163BOOST_AUTO_TEST_CASE(BadSigInfo)
164{
165 auto i1 = makeCommandInterest();
Junxiao Shi198c3812016-08-12 19:24:18 +0000166 setNameComponent(*i1, signed_interest::POS_SIG_INFO, "not-SignatureInfo");
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000167 assertReject(*i1, CommandInterestValidator::ErrorCode::BAD_SIG_INFO);
168}
169
170BOOST_AUTO_TEST_CASE(MissingKeyLocator)
171{
172 auto i1 = makeCommandInterest();
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000173 SignatureInfo sigInfo;
Junxiao Shi198c3812016-08-12 19:24:18 +0000174 setNameComponent(*i1, signed_interest::POS_SIG_INFO,
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000175 sigInfo.wireEncode().begin(), sigInfo.wireEncode().end());
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000176 assertReject(*i1, CommandInterestValidator::ErrorCode::MISSING_KEY_LOCATOR);
177}
178
179BOOST_AUTO_TEST_CASE(BadKeyLocatorType)
180{
181 auto i1 = makeCommandInterest();
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000182 KeyLocator kl;
183 kl.setKeyDigest(makeBinaryBlock(tlv::KeyDigest, "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD", 8));
184 SignatureInfo sigInfo;
185 sigInfo.setKeyLocator(kl);
Junxiao Shi198c3812016-08-12 19:24:18 +0000186 setNameComponent(*i1, signed_interest::POS_SIG_INFO,
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000187 sigInfo.wireEncode().begin(), sigInfo.wireEncode().end());
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000188 assertReject(*i1, CommandInterestValidator::ErrorCode::BAD_KEY_LOCATOR_TYPE);
189}
190
191BOOST_AUTO_TEST_CASE(BadCertName)
192{
193 auto i1 = makeCommandInterest();
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000194 KeyLocator kl;
195 kl.setName("/bad/cert/name");
196 SignatureInfo sigInfo;
197 sigInfo.setKeyLocator(kl);
Junxiao Shi198c3812016-08-12 19:24:18 +0000198 setNameComponent(*i1, signed_interest::POS_SIG_INFO,
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000199 sigInfo.wireEncode().begin(), sigInfo.wireEncode().end());
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000200 assertReject(*i1, CommandInterestValidator::ErrorCode::BAD_CERT_NAME);
201}
202
203BOOST_AUTO_TEST_CASE(InnerReject)
204{
205 inner->setResult(false);
206 auto i1 = makeCommandInterest();
207 assertReject(*i1, CommandInterestValidator::ErrorCode::NONE);
208}
209
210BOOST_AUTO_TEST_CASE(TimestampOutOfGracePositive)
211{
212 CommandInterestValidator::Options options;
213 options.gracePeriod = time::seconds(15);
214 initialize(options);
215
216 auto i1 = makeCommandInterest(); // signed at 0s
217 advanceClocks(time::seconds(16)); // verifying at +16s
218 assertReject(*i1, CommandInterestValidator::ErrorCode::TIMESTAMP_OUT_OF_GRACE);
219
220 auto i2 = makeCommandInterest(); // signed at +16s
221 assertAccept(*i2); // verifying at +16s
222}
223
224BOOST_AUTO_TEST_CASE(TimestampOutOfGraceNegative)
225{
226 CommandInterestValidator::Options options;
227 options.gracePeriod = time::seconds(15);
228 initialize(options);
229
230 auto i1 = makeCommandInterest(); // signed at 0s
231 advanceClocks(time::seconds(1));
232 auto i2 = makeCommandInterest(); // signed at +1s
233 advanceClocks(time::seconds(1));
234 auto i3 = makeCommandInterest(); // signed at +2s
235
236 systemClock->advance(time::seconds(-18)); // verifying at -16s
237 assertReject(*i1, CommandInterestValidator::ErrorCode::TIMESTAMP_OUT_OF_GRACE);
238
239 // CommandInterestValidator should not remember i1's timestamp
240 assertReject(*i2, CommandInterestValidator::ErrorCode::TIMESTAMP_OUT_OF_GRACE);
241
242 // CommandInterestValidator should not remember i2's timestamp, and should treat i3 as initial
243 advanceClocks(time::seconds(18)); // verifying at +2s
244 assertAccept(*i3);
245}
246
247BOOST_AUTO_TEST_CASE(TimestampReorderEqual)
248{
249 auto i1 = makeCommandInterest(); // signed at 0s
250 assertAccept(*i1);
251
Junxiao Shi198c3812016-08-12 19:24:18 +0000252 auto i2 = makeCommandInterest(); // signed at 0s
Alexander Afanasyev70244f42017-01-04 12:47:12 -0800253 setNameComponent(*i2, command_interest::POS_TIMESTAMP,
254 i1->getName()[command_interest::POS_TIMESTAMP]);
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000255 assertReject(*i2, CommandInterestValidator::ErrorCode::TIMESTAMP_REORDER);
256
257 advanceClocks(time::seconds(2));
258 auto i3 = makeCommandInterest(); // signed at +2s
259 assertAccept(*i3);
260}
261
262BOOST_AUTO_TEST_CASE(TimestampReorderNegative)
263{
264 auto i2 = makeCommandInterest(); // signed at 0ms
265 advanceClocks(time::milliseconds(200));
266 auto i3 = makeCommandInterest(); // signed at +200ms
267 advanceClocks(time::milliseconds(900));
268 auto i1 = makeCommandInterest(); // signed at +1100ms
269 advanceClocks(time::milliseconds(300));
270 auto i4 = makeCommandInterest(); // signed at +1400ms
271
272 systemClock->advance(time::milliseconds(-300)); // verifying at +1100ms
273 assertAccept(*i1);
274
275 systemClock->advance(time::milliseconds(-1100)); // verifying at 0ms
276 assertReject(*i2, CommandInterestValidator::ErrorCode::TIMESTAMP_REORDER);
277
278 // CommandInterestValidator should not remember i2's timestamp
279 advanceClocks(time::milliseconds(200)); // verifying at +200ms
280 assertReject(*i3, CommandInterestValidator::ErrorCode::TIMESTAMP_REORDER);
281
282 advanceClocks(time::milliseconds(1200)); // verifying at 1400ms
283 assertAccept(*i4);
284}
285
286BOOST_AUTO_TEST_SUITE_END() // Rejects
287
288BOOST_AUTO_TEST_SUITE(Options)
289
290typedef boost::mpl::vector<
291 boost::mpl::int_<0>,
292 boost::mpl::int_<-1>
293> GraceNonPositiveValues;
294
295BOOST_AUTO_TEST_CASE_TEMPLATE(GraceNonPositive, VALUE, GraceNonPositiveValues)
296{
297 CommandInterestValidator::Options options;
298 options.gracePeriod = time::seconds(VALUE::value);
299 initialize(options);
300
301 auto i1 = makeCommandInterest(1); // signed at 0ms
302 auto i2 = makeCommandInterest(2); // signed at 0ms
303 for (auto interest : {i1, i2}) {
Alexander Afanasyev70244f42017-01-04 12:47:12 -0800304 setNameComponent(*interest, command_interest::POS_TIMESTAMP,
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000305 name::Component::fromNumber(time::toUnixTimestamp(time::system_clock::now()).count()));
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000306 } // ensure timestamps are exactly 0ms
307
308 assertAccept(*i1); // verifying at 0ms
309
310 advanceClocks(time::milliseconds(1));
311 assertReject(*i2, CommandInterestValidator::ErrorCode::TIMESTAMP_OUT_OF_GRACE); // verifying at 1ms
312}
313
314BOOST_AUTO_TEST_CASE(TimestampsLimited)
315{
316 CommandInterestValidator::Options options;
317 options.gracePeriod = time::seconds(15);
318 options.maxTimestamps = 3;
319 initialize(options);
320
321 auto i1 = makeCommandInterest(1);
322 auto i2 = makeCommandInterest(2);
323 auto i3 = makeCommandInterest(3);
324 auto i00 = makeCommandInterest(0); // signed at 0s
325 advanceClocks(time::seconds(1));
326 auto i01 = makeCommandInterest(0); // signed at 1s
327 advanceClocks(time::seconds(1));
328 auto i02 = makeCommandInterest(0); // signed at 2s
329
330 assertAccept(*i00);
331 assertAccept(*i02);
332 assertAccept(*i1);
333 assertAccept(*i2);
334 assertAccept(*i3); // forgets identity 0
335 assertAccept(*i01); // accepted despite timestamp is reordered, because record has been evicted
336}
337
338BOOST_AUTO_TEST_CASE(TimestampsUnlimited)
339{
340 CommandInterestValidator::Options options;
341 options.gracePeriod = time::seconds(15);
342 options.maxTimestamps = -1;
343 initialize(options);
344
345 auto i1 = makeCommandInterest(0); // signed at 0s
346 advanceClocks(time::seconds(1));
347 for (int identity = 0; identity < 20; ++identity) {
348 auto i2 = makeCommandInterest(identity); // signed at +1s
349 assertAccept(*i2);
350 }
351 assertReject(*i1, CommandInterestValidator::ErrorCode::TIMESTAMP_REORDER);
352}
353
354BOOST_AUTO_TEST_CASE(TimestampsDisabled)
355{
356 CommandInterestValidator::Options options;
357 options.gracePeriod = time::seconds(15);
358 options.maxTimestamps = 0;
359 initialize(options);
360
361 auto i1 = makeCommandInterest(); // signed at 0s
362 advanceClocks(time::seconds(1));
363 auto i2 = makeCommandInterest(); // signed at +1s
364 assertAccept(*i2);
365
366 assertAccept(*i1); // accepted despite timestamp is reordered, because record isn't kept
367}
368
369BOOST_AUTO_TEST_CASE(TtlLimited)
370{
371 CommandInterestValidator::Options options;
372 options.gracePeriod = time::seconds(120);
373 options.timestampTtl = time::seconds(300);
374 initialize(options);
375
376 auto i1 = makeCommandInterest(); // signed at 0s
377 advanceClocks(time::seconds(240));
378 auto i2 = makeCommandInterest(); // signed at +240s
379 advanceClocks(time::seconds(120));
380 auto i3 = makeCommandInterest(); // signed at +360s
381
382 systemClock->advance(time::seconds(-360)); // rewind system clock to 0s
383 assertAccept(*i1);
384 assertAccept(*i3);
385
386 advanceClocks(time::seconds(30), time::seconds(301)); // advance steady clock by 301s, and system clock to +301s
387 assertAccept(*i2); // accepted despite timestamp is reordered, because record has been expired
388}
389
390BOOST_AUTO_TEST_CASE(TtlZero)
391{
392 CommandInterestValidator::Options options;
393 options.gracePeriod = time::seconds(15);
394 options.timestampTtl = time::seconds::zero();
395 initialize(options);
396
397 auto i1 = makeCommandInterest(); // signed at 0s
398 advanceClocks(time::seconds(1));
399 auto i2 = makeCommandInterest(); // signed at +1s
400 assertAccept(*i2);
401
402 assertAccept(*i1); // accepted despite timestamp is reordered, because record has been expired
403}
404
405BOOST_AUTO_TEST_SUITE_END() // Options
406
407BOOST_AUTO_TEST_SUITE_END() // TestCommandInterestValidator
408BOOST_AUTO_TEST_SUITE_END() // Security
409
410} // namespace tests
411} // namespace security
412} // namespace ndn