blob: 308b0fea951ace93c56d49d8350a2951e4175060 [file] [log] [blame]
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
3 * 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-config.hpp"
23#include "security/transform/base64-encode.hpp"
24#include "security/transform/buffer-source.hpp"
25#include "security/transform/stream-sink.hpp"
26#include "util/logger.hpp"
27#include "util/io.hpp"
28
29#include "boost-test.hpp"
30#include "validator-config/common.hpp"
31#include "validator-fixture.hpp"
32
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 {
235 return time::hours(1);
236 }
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 {
251 return time::minutes(1);
252 }
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 {
267 return time::seconds(1);
268 }
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
380using Packets = boost::mpl::vector<Interest, Data>;
381
382BOOST_FIXTURE_TEST_CASE_TEMPLATE(TrustAnchorWildcard, Packet, Packets, ValidationPolicyConfigFixture<Packet>)
383{
384 this->policy.load(R"CONF(
385 trust-anchor
386 {
387 type any
388 }
389 )CONF", "test-config");
390
391 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
392 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, true);
393 BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 0);
394 BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 0);
395
396 Packet unsignedPacket("/Security/V2/ValidatorFixture/Sub1/Sub2/Packet");
397
398 Packet packet = unsignedPacket;
399 VALIDATE_SUCCESS(packet, "Policy should accept everything");
400
401 packet = unsignedPacket;
402 this->m_keyChain.sign(packet, signingWithSha256());
403 VALIDATE_SUCCESS(packet, "Policy should accept everything");
404
405 packet = unsignedPacket;
406 this->m_keyChain.sign(packet, signingByIdentity(this->identity));
407 VALIDATE_SUCCESS(packet, "Policy should accept everything");
408
409 packet = unsignedPacket;
410 this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
411 VALIDATE_SUCCESS(packet, "Policy should accept everything");
412
413 packet = unsignedPacket;
414 this->m_keyChain.sign(packet, signingByIdentity(this->otherIdentity));
415 VALIDATE_SUCCESS(packet, "Policy should accept everything");
416
417 packet = unsignedPacket;
418 this->m_keyChain.sign(packet, signingByIdentity(this->subSelfSignedIdentity));
419 VALIDATE_SUCCESS(packet, "Policy should accept everything");
420}
421
422using ReloadedPolicies = boost::mpl::vector<Refresh1h, Refresh1m, Refresh1s>;
423
424// Somehow, didn't work without this wrapper
425template<typename ReloadPolicy>
426class ReloadPolicyFixture : public LoadStringWithDirAnchor<Data, ReloadPolicy>
427{
428public:
429};
430
431BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateReload, Reload, ReloadedPolicies, ReloadPolicyFixture<Reload>)
432{
433 using Packet = Data;
434 Packet unsignedPacket("/Security/V2/ValidatorFixture/Sub1/Sub2/Packet");
435
436 boost::filesystem::remove(this->path / "keys" / "identity.ndncert");
437 this->advanceClocks(Reload::getRefreshTime(), 3);
438
439 Packet packet = unsignedPacket;
440 this->m_keyChain.sign(packet, signingByIdentity(this->identity));
441 VALIDATE_FAILURE(packet, "Should fail, as the trust anchor should no longer exist");
442
443 packet = unsignedPacket;
444 this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
445 VALIDATE_FAILURE(packet, "Should fail, as the trust anchor should no longer exist");
446}
447
448BOOST_AUTO_TEST_SUITE_END() // TestValidationPolicyConfig
449BOOST_AUTO_TEST_SUITE_END() // V2
450BOOST_AUTO_TEST_SUITE_END() // Security
451
452} // namespace tests
453} // namespace validator_config
454} // namespace v2
455} // namespace security
456} // namespace ndn