blob: 9d33a1fc3fe0e7f19888edb11ecd1ee65d3b3b3b [file] [log] [blame]
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
Davide Pesavento0f830802018-01-16 23:58:58 -05003 * Copyright (c) 2013-2018 Regents of the University of California.
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -08004 *
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
Davide Pesavento7e780642018-11-24 15:51:34 -050022#include "ndn-cxx/security/v2/validation-policy-config.hpp"
23#include "ndn-cxx/security/transform/base64-encode.hpp"
24#include "ndn-cxx/security/transform/buffer-source.hpp"
25#include "ndn-cxx/security/transform/stream-sink.hpp"
26#include "ndn-cxx/util/logger.hpp"
27#include "ndn-cxx/util/io.hpp"
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -080028
Davide Pesavento7e780642018-11-24 15:51:34 -050029#include "tests/boost-test.hpp"
30#include "tests/unit/security/v2/validator-config/common.hpp"
31#include "tests/unit/security/v2/validator-fixture.hpp"
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -080032
33namespace ndn {
34namespace security {
35namespace v2 {
36namespace validator_config {
37namespace tests {
38
39using namespace ndn::tests;
40using namespace ndn::security::v2::tests;
41
42
43BOOST_AUTO_TEST_SUITE(Security)
44BOOST_AUTO_TEST_SUITE(V2)
45BOOST_AUTO_TEST_SUITE(TestValidationPolicyConfig)
46
47template<typename Packet>
48class PacketName;
49
50template<>
51class PacketName<Interest>
52{
53public:
54 static std::string
55 getName()
56 {
57 return "interest";
58 }
59};
60
61template<>
62class PacketName<Data>
63{
64public:
65 static std::string
66 getName()
67 {
68 return "data";
69 }
70};
71
72template<typename PacketType>
73class ValidationPolicyConfigFixture : public HierarchicalValidatorFixture<ValidationPolicyConfig>
74{
75public:
76 ValidationPolicyConfigFixture()
77 : path(boost::filesystem::path(UNIT_TEST_CONFIG_PATH) / "security" / "v2" / "validation-policy-config")
78 {
79 boost::filesystem::create_directories(path);
80 baseConfig = R"CONF(
81 rule
82 {
83 id test-rule-id
84 for )CONF" + PacketName<Packet>::getName() + R"CONF(
85 filter
86 {
87 type name
88 name )CONF" + identity.getName().toUri() + R"CONF(
89 relation is-prefix-of
90 }
91 checker
92 {
93 type hierarchical
94 sig-type rsa-sha256
95 }
96 }
97 )CONF";
98 }
99
100 ~ValidationPolicyConfigFixture()
101 {
102 boost::filesystem::remove_all(path);
103 }
104
105public:
106 using Packet = PacketType;
107
108 const boost::filesystem::path path;
109 std::string baseConfig;
110};
111
112template<typename PacketType>
113class LoadStringWithFileAnchor : public ValidationPolicyConfigFixture<PacketType>
114{
115public:
116 LoadStringWithFileAnchor()
117 {
118 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
119
120 this->saveCertificate(this->identity, (this->path / "identity.ndncert").string());
121 this->policy.load(this->baseConfig + R"CONF(
122 trust-anchor
123 {
124 type file
125 file-name "trust-anchor.ndncert"
126 }
127 )CONF", (this->path / "test-config").string());
128
129 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
130 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
131 }
132};
133
134template<typename PacketType>
135class LoadFileWithFileAnchor : public ValidationPolicyConfigFixture<PacketType>
136{
137public:
138 LoadFileWithFileAnchor()
139 {
140 std::string configFile = (this->path / "config.conf").string();
141 {
142 std::ofstream config(configFile.c_str());
143 config << this->baseConfig << R"CONF(
144 trust-anchor
145 {
146 type file
147 file-name "trust-anchor.ndncert"
148 }
149 )CONF";
150 }
151 this->saveCertificate(this->identity, (this->path / "identity.ndncert").string());
152
153 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
154
155 this->policy.load(configFile);
156
157 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
158 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
159 }
160};
161
162template<typename PacketType>
163class LoadSectionWithFileAnchor : public ValidationPolicyConfigFixture<PacketType>
164{
165public:
166 LoadSectionWithFileAnchor()
167 {
168 auto section = makeSection(this->baseConfig + R"CONF(
169 trust-anchor
170 {
171 type file
172 file-name "trust-anchor.ndncert"
173 }
174 )CONF");
175
176 this->saveCertificate(this->identity, (this->path / "identity.ndncert").string());
177
178 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
179
180 this->policy.load(section, (this->path / "test-config").string());
181
182 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
183 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
184 }
185};
186
187template<typename PacketType>
188class LoadStringWithBase64Anchor : public ValidationPolicyConfigFixture<PacketType>
189{
190public:
191 LoadStringWithBase64Anchor()
192 {
193 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
194
195 std::ostringstream os;
196 using namespace ndn::security::transform;
197 const auto& cert = this->identity.getDefaultKey().getDefaultCertificate();
198 bufferSource(cert.wireEncode().wire(), cert.wireEncode().size()) >> base64Encode(false) >> streamSink(os);
199
200 this->policy.load(this->baseConfig + R"CONF(
201 trust-anchor
202 {
203 type base64
204 base64-string ")CONF" + os.str() + R"CONF("
205 }
206 )CONF", (this->path / "test-config").string());
207
208 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
209 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
210 }
211};
212
213class NoRefresh
214{
215public:
216 static std::string
217 getRefreshString()
218 {
219 return "";
220 }
221};
222
223class Refresh1h
224{
225public:
226 static std::string
227 getRefreshString()
228 {
229 return "refresh 1h";
230 }
231
232 static time::milliseconds
233 getRefreshTime()
234 {
Davide Pesavento0f830802018-01-16 23:58:58 -0500235 return 1_h;
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800236 }
237};
238
239class Refresh1m
240{
241public:
242 static std::string
243 getRefreshString()
244 {
245 return "refresh 1m";
246 }
247
248 static time::milliseconds
249 getRefreshTime()
250 {
Davide Pesavento0f830802018-01-16 23:58:58 -0500251 return 1_min;
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800252 }
253};
254
255class Refresh1s
256{
257public:
258 static std::string
259 getRefreshString()
260 {
261 return "refresh 1s";
262 }
263
264 static time::milliseconds
265 getRefreshTime()
266 {
Davide Pesavento0f830802018-01-16 23:58:58 -0500267 return 1_s;
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800268 }
269};
270
271template<typename PacketType, typename Refresh = NoRefresh>
272class LoadStringWithDirAnchor : public ValidationPolicyConfigFixture<PacketType>
273{
274public:
275 LoadStringWithDirAnchor()
276 {
277 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
278
279 boost::filesystem::create_directories(this->path / "keys");
280 this->saveCertificate(this->identity, (this->path / "keys" / "identity.ndncert").string());
281
282 this->policy.load(this->baseConfig + R"CONF(
283 trust-anchor
284 {
285 type dir
286 dir keys
287 )CONF" + Refresh::getRefreshString() + R"CONF(
288 }
289 )CONF", (this->path / "test-config").string());
290
291 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
292 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
293 }
294};
295
296using DataPolicies = boost::mpl::vector<LoadStringWithFileAnchor<Data>,
297 LoadFileWithFileAnchor<Data>,
298 LoadSectionWithFileAnchor<Data>,
299 LoadStringWithBase64Anchor<Data>,
300 LoadStringWithDirAnchor<Data>,
301 LoadStringWithDirAnchor<Data, Refresh1h>,
302 LoadStringWithDirAnchor<Data, Refresh1m>,
303 LoadStringWithDirAnchor<Data, Refresh1s>
304 >;
305
306using InterestPolicies = boost::mpl::vector<LoadStringWithFileAnchor<Interest>,
307 LoadFileWithFileAnchor<Interest>,
308 LoadSectionWithFileAnchor<Interest>,
309 LoadStringWithBase64Anchor<Interest>,
310 LoadStringWithDirAnchor<Interest>,
311 LoadStringWithDirAnchor<Interest, Refresh1h>,
312 LoadStringWithDirAnchor<Interest, Refresh1m>,
313 LoadStringWithDirAnchor<Interest, Refresh1s>
314 >;
315
316BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateData, Policy, DataPolicies, Policy)
317{
318 BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 1);
319 BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 0);
320
321 using Packet = typename Policy::Packet;
322 Packet unsignedPacket("/Security/V2/ValidatorFixture/Sub1/Sub2/Packet");
323
324 Packet packet = unsignedPacket;
325 VALIDATE_FAILURE(packet, "Unsigned");
326
327 packet = unsignedPacket;
328 this->m_keyChain.sign(packet, signingWithSha256());
329 VALIDATE_FAILURE(packet, "Policy doesn't accept Sha256Digest signature");
330
331 packet = unsignedPacket;
332 this->m_keyChain.sign(packet, signingByIdentity(this->identity));
333 VALIDATE_SUCCESS(packet, "Should get accepted, as signed by the anchor");
334
335 packet = unsignedPacket;
336 this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
337 VALIDATE_SUCCESS(packet, "Should get accepted, as signed by the policy-compliant cert");
338
339 packet = unsignedPacket;
340 this->m_keyChain.sign(packet, signingByIdentity(this->otherIdentity));
341 VALIDATE_FAILURE(packet, "Should fail, as signed by the policy-violating cert");
342
343 packet = unsignedPacket;
344 this->m_keyChain.sign(packet, signingByIdentity(this->subSelfSignedIdentity));
345 VALIDATE_FAILURE(packet, "Should fail, because subSelfSignedIdentity is not a trust anchor");
346}
347
348BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateInterest, Policy, InterestPolicies, Policy)
349{
350 BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 0);
351 BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 1);
352
353 using Packet = typename Policy::Packet;
354 Packet unsignedPacket("/Security/V2/ValidatorFixture/Sub1/Sub2/Packet");
355
356 Packet packet = unsignedPacket;
357 VALIDATE_FAILURE(packet, "Unsigned");
358
359 packet = unsignedPacket;
360 this->m_keyChain.sign(packet, signingWithSha256());
361 VALIDATE_FAILURE(packet, "Policy doesn't accept Sha256Digest signature");
362
363 packet = unsignedPacket;
364 this->m_keyChain.sign(packet, signingByIdentity(this->identity));
365 VALIDATE_SUCCESS(packet, "Should get accepted, as signed by the anchor");
366
367 packet = unsignedPacket;
368 this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
369 VALIDATE_FAILURE(packet, "Should fail, as there is no matching rule for data");
370
371 packet = unsignedPacket;
372 this->m_keyChain.sign(packet, signingByIdentity(this->otherIdentity));
373 VALIDATE_FAILURE(packet, "Should fail, as signed by the policy-violating cert");
374
375 packet = unsignedPacket;
376 this->m_keyChain.sign(packet, signingByIdentity(this->subSelfSignedIdentity));
377 VALIDATE_FAILURE(packet, "Should fail, because subSelfSignedIdentity is not a trust anchor");
378}
379
Alexander Afanasyev6aff0242017-08-29 17:14:44 -0400380BOOST_FIXTURE_TEST_CASE(Reload, HierarchicalValidatorFixture<ValidationPolicyConfig>)
381{
382 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
383 this->policy.load(R"CONF(
384 rule
385 {
386 id test-rule-data-id
387 for data
388 filter
389 {
390 type name
391 name /foo/bar
392 relation is-prefix-of
393 }
394 checker
395 {
396 type hierarchical
397 sig-type rsa-sha256
398 }
399 }
400 rule
401 {
402 id test-rule-interest-id
403 for interest
404 filter
405 {
406 type name
407 name /foo/bar
408 relation is-prefix-of
409 }
410 checker
411 {
412 type hierarchical
413 sig-type rsa-sha256
414 }
415 }
416 trust-anchor
417 {
418 type dir
419 dir keys
420 refresh 1h
421 }
422 )CONF", "test-config");
423 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
424 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
425 BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 1);
426 BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 1);
427
428 this->policy.load(R"CONF(
429 trust-anchor
430 {
431 type any
432 }
433 )CONF", "test-config");
434 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
435 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, true);
436 BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 0);
437 BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 0);
438}
439
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800440using Packets = boost::mpl::vector<Interest, Data>;
441
442BOOST_FIXTURE_TEST_CASE_TEMPLATE(TrustAnchorWildcard, Packet, Packets, ValidationPolicyConfigFixture<Packet>)
443{
444 this->policy.load(R"CONF(
445 trust-anchor
446 {
447 type any
448 }
449 )CONF", "test-config");
450
451 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
452 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, true);
453 BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 0);
454 BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 0);
455
456 Packet unsignedPacket("/Security/V2/ValidatorFixture/Sub1/Sub2/Packet");
457
458 Packet packet = unsignedPacket;
459 VALIDATE_SUCCESS(packet, "Policy should accept everything");
460
461 packet = unsignedPacket;
462 this->m_keyChain.sign(packet, signingWithSha256());
463 VALIDATE_SUCCESS(packet, "Policy should accept everything");
464
465 packet = unsignedPacket;
466 this->m_keyChain.sign(packet, signingByIdentity(this->identity));
467 VALIDATE_SUCCESS(packet, "Policy should accept everything");
468
469 packet = unsignedPacket;
470 this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
471 VALIDATE_SUCCESS(packet, "Policy should accept everything");
472
473 packet = unsignedPacket;
474 this->m_keyChain.sign(packet, signingByIdentity(this->otherIdentity));
475 VALIDATE_SUCCESS(packet, "Policy should accept everything");
476
477 packet = unsignedPacket;
478 this->m_keyChain.sign(packet, signingByIdentity(this->subSelfSignedIdentity));
479 VALIDATE_SUCCESS(packet, "Policy should accept everything");
480}
481
Alexander Afanasyev6aff0242017-08-29 17:14:44 -0400482using RefreshPolicies = boost::mpl::vector<Refresh1h, Refresh1m, Refresh1s>;
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800483
484// Somehow, didn't work without this wrapper
Alexander Afanasyev6aff0242017-08-29 17:14:44 -0400485template<typename RefreshPolicy>
486class RefreshPolicyFixture : public LoadStringWithDirAnchor<Data, RefreshPolicy>
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800487{
488public:
489};
490
Alexander Afanasyev6aff0242017-08-29 17:14:44 -0400491BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateRefresh, Refresh, RefreshPolicies, RefreshPolicyFixture<Refresh>)
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800492{
493 using Packet = Data;
494 Packet unsignedPacket("/Security/V2/ValidatorFixture/Sub1/Sub2/Packet");
495
496 boost::filesystem::remove(this->path / "keys" / "identity.ndncert");
Alexander Afanasyev6aff0242017-08-29 17:14:44 -0400497 this->advanceClocks(Refresh::getRefreshTime(), 3);
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800498
499 Packet packet = unsignedPacket;
500 this->m_keyChain.sign(packet, signingByIdentity(this->identity));
501 VALIDATE_FAILURE(packet, "Should fail, as the trust anchor should no longer exist");
502
503 packet = unsignedPacket;
504 this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
505 VALIDATE_FAILURE(packet, "Should fail, as the trust anchor should no longer exist");
506}
507
Alexander Afanasyev7b112462018-10-17 11:51:52 -0400508BOOST_FIXTURE_TEST_CASE(OrphanedPolicyLoad, HierarchicalValidatorFixture<ValidationPolicyConfig>) // Bug #4758
509{
510 ValidationPolicyConfig policy1;
511 BOOST_CHECK_THROW(policy1.load("trust-anchor { type any }", "test-config"), Error);
512
513 // Reloading would have triggered a segfault
514 BOOST_CHECK_THROW(policy1.load("trust-anchor { type any }", "test-config"), Error);
515
516 ValidationPolicyConfig policy2;
517
518 std::string config = R"CONF(
519 trust-anchor
520 {
521 type dir
522 dir keys
523 refresh 1h
524 }
525 )CONF";
526
527 // Inserting trust anchor would have triggered a segfault
528 BOOST_CHECK_THROW(policy2.load(config, "test-config"), Error);
529}
530
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800531BOOST_AUTO_TEST_SUITE_END() // TestValidationPolicyConfig
532BOOST_AUTO_TEST_SUITE_END() // V2
533BOOST_AUTO_TEST_SUITE_END() // Security
534
535} // namespace tests
536} // namespace validator_config
537} // namespace v2
538} // namespace security
539} // namespace ndn