blob: 934492f56479115ddbad29b21ac3ed4f4bde3592 [file] [log] [blame]
Alexander Afanasyev7e721412017-01-11 13:36:08 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Alexander Afanasyev6aff0242017-08-29 17:14:44 -04002/*
Davide Pesavento0f830802018-01-16 23:58:58 -05003 * Copyright (c) 2013-2018 Regents of the University of California.
Alexander Afanasyev7e721412017-01-11 13:36:08 -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/validator.hpp"
23#include "ndn-cxx/security/v2/validation-policy-simple-hierarchy.hpp"
Alexander Afanasyev7e721412017-01-11 13:36:08 -080024
Davide Pesavento7e780642018-11-24 15:51:34 -050025#include "tests/boost-test.hpp"
26#include "tests/unit/security/v2/validator-fixture.hpp"
Alexander Afanasyev7e721412017-01-11 13:36:08 -080027
28namespace ndn {
29namespace security {
30namespace v2 {
31namespace tests {
32
33using namespace ndn::tests;
34
35BOOST_AUTO_TEST_SUITE(Security)
36BOOST_AUTO_TEST_SUITE(V2)
37BOOST_FIXTURE_TEST_SUITE(TestValidator, HierarchicalValidatorFixture<ValidationPolicySimpleHierarchy>)
38
Alexander Afanasyevb54aa572017-03-21 19:40:49 -050039BOOST_AUTO_TEST_CASE(ConstructorSetValidator)
40{
41 auto middlePolicy = make_unique<ValidationPolicySimpleHierarchy>();
42 auto innerPolicy = make_unique<ValidationPolicySimpleHierarchy>();
43
44 validator.getPolicy().setInnerPolicy(std::move(middlePolicy));
45 validator.getPolicy().setInnerPolicy(std::move(innerPolicy));
46
47 BOOST_CHECK(validator.getPolicy().m_validator != nullptr);
48 BOOST_CHECK(validator.getPolicy().getInnerPolicy().m_validator != nullptr);
49 BOOST_CHECK(validator.getPolicy().getInnerPolicy().getInnerPolicy().m_validator != nullptr);
50}
51
Alexander Afanasyev7e721412017-01-11 13:36:08 -080052BOOST_AUTO_TEST_CASE(Timeouts)
53{
54 processInterest = nullptr; // no response for all interests
55
56 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
57 m_keyChain.sign(data, signingByIdentity(subIdentity));
58
59 VALIDATE_FAILURE(data, "Should fail to retrieve certificate");
60 BOOST_CHECK_GT(face.sentInterests.size(), 1);
61}
62
63BOOST_AUTO_TEST_CASE(NackedInterests)
64{
65 processInterest = [this] (const Interest& interest) {
66 lp::Nack nack(interest);
67 nack.setReason(lp::NackReason::NO_ROUTE);
68 face.receive(nack);
69 };
70
71 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
72 m_keyChain.sign(data, signingByIdentity(subIdentity));
73
74 VALIDATE_FAILURE(data, "All interests should get NACKed");
Ashlesh Gawande3e39a4d2018-08-30 16:49:13 -050075 // 1 for the first interest, 3 for the retries on nack
76 BOOST_CHECK_EQUAL(face.sentInterests.size(), 4);
Alexander Afanasyev7e721412017-01-11 13:36:08 -080077}
78
79BOOST_AUTO_TEST_CASE(MalformedCert)
80{
81 Data malformedCert = subIdentity.getDefaultKey().getDefaultCertificate();
82 malformedCert.setContentType(tlv::ContentType_Blob);
83 m_keyChain.sign(malformedCert, signingByIdentity(identity));
84 // wrong content type & missing ValidityPeriod
85 BOOST_REQUIRE_THROW(Certificate(malformedCert.wireEncode()), tlv::Error);
86
87 auto originalProcessInterest = processInterest;
88 processInterest = [this, &originalProcessInterest, &malformedCert] (const Interest& interest) {
89 if (interest.getName().isPrefixOf(malformedCert.getName())) {
90 face.receive(malformedCert);
91 }
92 else {
93 originalProcessInterest(interest);
94 }
95 };
96
97 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
98 m_keyChain.sign(data, signingByIdentity(subIdentity));
99
100 VALIDATE_FAILURE(data, "Signed by a malformed certificate");
101 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
102}
103
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800104BOOST_AUTO_TEST_CASE(ExpiredCert)
105{
106 Data expiredCert = subIdentity.getDefaultKey().getDefaultCertificate();
107 SignatureInfo info;
Davide Pesavento0f830802018-01-16 23:58:58 -0500108 info.setValidityPeriod(ValidityPeriod(time::system_clock::now() - 2_h,
109 time::system_clock::now() - 1_h));
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800110 m_keyChain.sign(expiredCert, signingByIdentity(identity).setSignatureInfo(info));
111 BOOST_REQUIRE_NO_THROW(Certificate(expiredCert.wireEncode()));
112
113 auto originalProcessInterest = processInterest;
114 processInterest = [this, &originalProcessInterest, &expiredCert] (const Interest& interest) {
115 if (interest.getName().isPrefixOf(expiredCert.getName())) {
116 face.receive(expiredCert);
117 }
118 else {
Davide Pesaventob88c6bf2017-09-11 20:15:28 -0400119 originalProcessInterest(interest);
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800120 }
121 };
122
123 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
124 m_keyChain.sign(data, signingByIdentity(subIdentity));
125
126 VALIDATE_FAILURE(data, "Signed by an expired certificate");
127 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
128}
129
Alexander Afanasyev6aff0242017-08-29 17:14:44 -0400130BOOST_AUTO_TEST_CASE(ResetAnchors)
131{
132 validator.resetAnchors();
133
134 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
135 m_keyChain.sign(data, signingByIdentity(subIdentity));
136 VALIDATE_FAILURE(data, "Should fail, as no anchors configured");
137}
138
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800139BOOST_AUTO_TEST_CASE(TrustedCertCaching)
140{
141 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
142 m_keyChain.sign(data, signingByIdentity(subIdentity));
143
144 VALIDATE_SUCCESS(data, "Should get accepted, as signed by the policy-compliant cert");
145 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
146 face.sentInterests.clear();
147
148 processInterest = nullptr; // disable data responses from mocked network
149
150 VALIDATE_SUCCESS(data, "Should get accepted, based on the cached trusted cert");
151 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
152 face.sentInterests.clear();
153
Davide Pesavento0f830802018-01-16 23:58:58 -0500154 advanceClocks(1_h, 2); // expire trusted cache
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800155
156 VALIDATE_FAILURE(data, "Should try and fail to retrieve certs");
157 BOOST_CHECK_GT(face.sentInterests.size(), 1);
158 face.sentInterests.clear();
159}
160
Alexander Afanasyev6aff0242017-08-29 17:14:44 -0400161BOOST_AUTO_TEST_CASE(ResetVerifiedCertificates)
162{
163 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
164 m_keyChain.sign(data, signingByIdentity(subIdentity));
165 VALIDATE_SUCCESS(data, "Should get accepted, as signed by the policy-compliant cert");
166
167 // reset anchors
168 validator.resetAnchors();
169 VALIDATE_SUCCESS(data, "Should get accepted, as signed by the cert in trusted cache");
170
171 // reset trusted cache
172 validator.resetVerifiedCertificates();
173 VALIDATE_FAILURE(data, "Should fail, as no trusted cache or anchors");
174}
175
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800176BOOST_AUTO_TEST_CASE(UntrustedCertCaching)
177{
178 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
179 m_keyChain.sign(data, signingByIdentity(subSelfSignedIdentity));
180
181 VALIDATE_FAILURE(data, "Should fail, as signed by the policy-violating cert");
182 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
183 face.sentInterests.clear();
184
185 processInterest = nullptr; // disable data responses from mocked network
186
187 VALIDATE_FAILURE(data, "Should fail again, but no network operations expected");
188 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
189 face.sentInterests.clear();
190
Davide Pesavento0f830802018-01-16 23:58:58 -0500191 advanceClocks(10_min, 2); // expire untrusted cache
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800192
193 VALIDATE_FAILURE(data, "Should try and fail to retrieve certs");
194 BOOST_CHECK_GT(face.sentInterests.size(), 1);
195 face.sentInterests.clear();
196}
197
198class ValidationPolicySimpleHierarchyForInterestOnly : public ValidationPolicySimpleHierarchy
199{
200public:
201 void
202 checkPolicy(const Data& data, const shared_ptr<ValidationState>& state,
203 const ValidationContinuation& continueValidation) override
204 {
205 continueValidation(nullptr, state);
206 }
207};
208
209BOOST_FIXTURE_TEST_CASE(ValidateInterestsButBypassForData,
210 HierarchicalValidatorFixture<ValidationPolicySimpleHierarchyForInterestOnly>)
211{
212 Interest interest("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest");
213 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest");
214
215 VALIDATE_FAILURE(interest, "Unsigned");
216 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
217 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
218 face.sentInterests.clear();
219
220 interest = Interest("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest");
221 m_keyChain.sign(interest, signingWithSha256());
222 m_keyChain.sign(data, signingWithSha256());
223 VALIDATE_FAILURE(interest, "Required KeyLocator/Name missing (not passed to policy)");
224 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
225 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
226 face.sentInterests.clear();
227
228 m_keyChain.sign(interest, signingByIdentity(identity));
229 m_keyChain.sign(data, signingByIdentity(identity));
230 VALIDATE_SUCCESS(interest, "Should get accepted, as signed by the anchor");
231 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
232 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
233 face.sentInterests.clear();
234
235 m_keyChain.sign(interest, signingByIdentity(subIdentity));
236 m_keyChain.sign(data, signingByIdentity(subIdentity));
237 VALIDATE_FAILURE(interest, "Should fail, as policy is not allowed to create new trust anchors");
238 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
239 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
240 face.sentInterests.clear();
241
242 m_keyChain.sign(interest, signingByIdentity(otherIdentity));
243 m_keyChain.sign(data, signingByIdentity(otherIdentity));
244 VALIDATE_FAILURE(interest, "Should fail, as signed by the policy-violating cert");
245 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
246 // no network operations expected, as certificate is not validated by the policy
247 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
248 face.sentInterests.clear();
249
Davide Pesavento0f830802018-01-16 23:58:58 -0500250 advanceClocks(1_h, 2); // expire trusted cache
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800251
252 m_keyChain.sign(interest, signingByIdentity(subSelfSignedIdentity));
253 m_keyChain.sign(data, signingByIdentity(subSelfSignedIdentity));
254 VALIDATE_FAILURE(interest, "Should fail, as policy is not allowed to create new trust anchors");
255 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
256 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
257 face.sentInterests.clear();
258}
259
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800260BOOST_AUTO_TEST_CASE(InfiniteCertChain)
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800261{
262 processInterest = [this] (const Interest& interest) {
263 // create another key for the same identity and sign it properly
264 Key parentKey = m_keyChain.createKey(subIdentity);
265 Key requestedKey = subIdentity.getKey(interest.getName());
266
267 Name certificateName = requestedKey.getName();
268 certificateName
269 .append("looper")
270 .appendVersion();
271 v2::Certificate certificate;
272 certificate.setName(certificateName);
273
274 // set metainfo
275 certificate.setContentType(tlv::ContentType_Key);
Davide Pesavento0f830802018-01-16 23:58:58 -0500276 certificate.setFreshnessPeriod(1_h);
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800277
278 // set content
Davide Pesavento5d0b0102017-10-07 13:43:16 -0400279 certificate.setContent(requestedKey.getPublicKey().data(), requestedKey.getPublicKey().size());
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800280
281 // set signature-info
282 SignatureInfo info;
Davide Pesavento0f830802018-01-16 23:58:58 -0500283 info.setValidityPeriod(security::ValidityPeriod(time::system_clock::now() - 10_days,
284 time::system_clock::now() + 10_days));
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800285
286 m_keyChain.sign(certificate, signingByKey(parentKey).setSignatureInfo(info));
287 face.receive(certificate);
288 };
289
290 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
291 m_keyChain.sign(data, signingByIdentity(subIdentity));
292
293 validator.setMaxDepth(40);
294 BOOST_CHECK_EQUAL(validator.getMaxDepth(), 40);
295 VALIDATE_FAILURE(data, "Should fail, as certificate should be looped");
296 BOOST_CHECK_EQUAL(face.sentInterests.size(), 40);
297 face.sentInterests.clear();
298
Davide Pesavento0f830802018-01-16 23:58:58 -0500299 advanceClocks(1_h, 5); // expire caches
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800300
301 validator.setMaxDepth(30);
302 BOOST_CHECK_EQUAL(validator.getMaxDepth(), 30);
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800303 VALIDATE_FAILURE(data, "Should fail, as certificate chain is infinite");
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800304 BOOST_CHECK_EQUAL(face.sentInterests.size(), 30);
305}
306
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800307BOOST_AUTO_TEST_CASE(LoopedCertChain)
308{
309 auto s1 = addIdentity("/loop");
310 auto k1 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key1")));
311 auto k2 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key2")));
312 auto k3 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key3")));
313
314 auto makeCert = [this] (Key& key, const Key& signer) {
315 v2::Certificate request = key.getDefaultCertificate();
316 request.setName(Name(key.getName()).append("looper").appendVersion());
317
318 SignatureInfo info;
Davide Pesavento0f830802018-01-16 23:58:58 -0500319 info.setValidityPeriod({time::system_clock::now() - 100_days,
320 time::system_clock::now() + 100_days});
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800321 m_keyChain.sign(request, signingByKey(signer).setSignatureInfo(info));
322 m_keyChain.addCertificate(key, request);
323
324 cache.insert(request);
325 };
326
327 makeCert(k1, k2);
328 makeCert(k2, k3);
329 makeCert(k3, k1);
330
331 Data data("/loop/Data");
332 m_keyChain.sign(data, signingByKey(k1));
333 VALIDATE_FAILURE(data, "Should fail, as certificate chain loops");
334 BOOST_CHECK_EQUAL(face.sentInterests.size(), 3);
335}
336
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800337BOOST_AUTO_TEST_SUITE_END() // TestValidator
338BOOST_AUTO_TEST_SUITE_END() // V2
339BOOST_AUTO_TEST_SUITE_END() // Security
340
341} // namespace tests
342} // namespace v2
343} // namespace security
344} // namespace ndn