blob: c87d34863c23d5ba14b6781d68f83e063e0a9376 [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 Pesavento14c56cd2020-05-21 01:44:03 -04003 * Copyright (c) 2013-2020 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
Alexander Afanasyev09236c22020-06-03 13:42:38 -040022#include "ndn-cxx/security/validator.hpp"
23#include "ndn-cxx/security/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"
Alexander Afanasyev09236c22020-06-03 13:42:38 -040026#include "tests/unit/security/validator-fixture.hpp"
Alexander Afanasyev7e721412017-01-11 13:36:08 -080027
28namespace ndn {
29namespace security {
Alexander Afanasyev09236c22020-06-03 13:42:38 -040030inline namespace v2 {
Alexander Afanasyev7e721412017-01-11 13:36:08 -080031namespace tests {
32
33using namespace ndn::tests;
34
35BOOST_AUTO_TEST_SUITE(Security)
Alexander Afanasyev7e721412017-01-11 13:36:08 -080036BOOST_FIXTURE_TEST_SUITE(TestValidator, HierarchicalValidatorFixture<ValidationPolicySimpleHierarchy>)
37
Alexander Afanasyevb54aa572017-03-21 19:40:49 -050038BOOST_AUTO_TEST_CASE(ConstructorSetValidator)
39{
40 auto middlePolicy = make_unique<ValidationPolicySimpleHierarchy>();
41 auto innerPolicy = make_unique<ValidationPolicySimpleHierarchy>();
42
43 validator.getPolicy().setInnerPolicy(std::move(middlePolicy));
44 validator.getPolicy().setInnerPolicy(std::move(innerPolicy));
45
46 BOOST_CHECK(validator.getPolicy().m_validator != nullptr);
47 BOOST_CHECK(validator.getPolicy().getInnerPolicy().m_validator != nullptr);
48 BOOST_CHECK(validator.getPolicy().getInnerPolicy().getInnerPolicy().m_validator != nullptr);
49}
50
Alexander Afanasyev7e721412017-01-11 13:36:08 -080051BOOST_AUTO_TEST_CASE(Timeouts)
52{
53 processInterest = nullptr; // no response for all interests
54
Alexander Afanasyev09236c22020-06-03 13:42:38 -040055 Data data("/Security/ValidatorFixture/Sub1/Sub2/Data");
Alexander Afanasyev7e721412017-01-11 13:36:08 -080056 m_keyChain.sign(data, signingByIdentity(subIdentity));
57
58 VALIDATE_FAILURE(data, "Should fail to retrieve certificate");
59 BOOST_CHECK_GT(face.sentInterests.size(), 1);
60}
61
62BOOST_AUTO_TEST_CASE(NackedInterests)
63{
64 processInterest = [this] (const Interest& interest) {
65 lp::Nack nack(interest);
66 nack.setReason(lp::NackReason::NO_ROUTE);
67 face.receive(nack);
68 };
69
Alexander Afanasyev09236c22020-06-03 13:42:38 -040070 Data data("/Security/ValidatorFixture/Sub1/Sub2/Data");
Alexander Afanasyev7e721412017-01-11 13:36:08 -080071 m_keyChain.sign(data, signingByIdentity(subIdentity));
72
73 VALIDATE_FAILURE(data, "All interests should get NACKed");
Ashlesh Gawande3e39a4d2018-08-30 16:49:13 -050074 // 1 for the first interest, 3 for the retries on nack
75 BOOST_CHECK_EQUAL(face.sentInterests.size(), 4);
Alexander Afanasyev7e721412017-01-11 13:36:08 -080076}
77
78BOOST_AUTO_TEST_CASE(MalformedCert)
79{
80 Data malformedCert = subIdentity.getDefaultKey().getDefaultCertificate();
81 malformedCert.setContentType(tlv::ContentType_Blob);
82 m_keyChain.sign(malformedCert, signingByIdentity(identity));
83 // wrong content type & missing ValidityPeriod
84 BOOST_REQUIRE_THROW(Certificate(malformedCert.wireEncode()), tlv::Error);
85
86 auto originalProcessInterest = processInterest;
87 processInterest = [this, &originalProcessInterest, &malformedCert] (const Interest& interest) {
88 if (interest.getName().isPrefixOf(malformedCert.getName())) {
89 face.receive(malformedCert);
90 }
91 else {
92 originalProcessInterest(interest);
93 }
94 };
95
Alexander Afanasyev09236c22020-06-03 13:42:38 -040096 Data data("/Security/ValidatorFixture/Sub1/Sub2/Data");
Alexander Afanasyev7e721412017-01-11 13:36:08 -080097 m_keyChain.sign(data, signingByIdentity(subIdentity));
98
99 VALIDATE_FAILURE(data, "Signed by a malformed certificate");
100 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
101}
102
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800103BOOST_AUTO_TEST_CASE(ExpiredCert)
104{
105 Data expiredCert = subIdentity.getDefaultKey().getDefaultCertificate();
106 SignatureInfo info;
Davide Pesavento0f830802018-01-16 23:58:58 -0500107 info.setValidityPeriod(ValidityPeriod(time::system_clock::now() - 2_h,
108 time::system_clock::now() - 1_h));
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800109 m_keyChain.sign(expiredCert, signingByIdentity(identity).setSignatureInfo(info));
110 BOOST_REQUIRE_NO_THROW(Certificate(expiredCert.wireEncode()));
111
112 auto originalProcessInterest = processInterest;
113 processInterest = [this, &originalProcessInterest, &expiredCert] (const Interest& interest) {
114 if (interest.getName().isPrefixOf(expiredCert.getName())) {
115 face.receive(expiredCert);
116 }
117 else {
Davide Pesaventob88c6bf2017-09-11 20:15:28 -0400118 originalProcessInterest(interest);
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800119 }
120 };
121
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400122 Data data("/Security/ValidatorFixture/Sub1/Sub2/Data");
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800123 m_keyChain.sign(data, signingByIdentity(subIdentity));
124
125 VALIDATE_FAILURE(data, "Signed by an expired certificate");
126 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
127}
128
Alexander Afanasyev6aff0242017-08-29 17:14:44 -0400129BOOST_AUTO_TEST_CASE(ResetAnchors)
130{
131 validator.resetAnchors();
132
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400133 Data data("/Security/ValidatorFixture/Sub1/Sub2/Data");
Alexander Afanasyev6aff0242017-08-29 17:14:44 -0400134 m_keyChain.sign(data, signingByIdentity(subIdentity));
135 VALIDATE_FAILURE(data, "Should fail, as no anchors configured");
136}
137
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800138BOOST_AUTO_TEST_CASE(TrustedCertCaching)
139{
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400140 Data data("/Security/ValidatorFixture/Sub1/Sub2/Data");
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800141 m_keyChain.sign(data, signingByIdentity(subIdentity));
142
143 VALIDATE_SUCCESS(data, "Should get accepted, as signed by the policy-compliant cert");
144 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
145 face.sentInterests.clear();
146
147 processInterest = nullptr; // disable data responses from mocked network
148
149 VALIDATE_SUCCESS(data, "Should get accepted, based on the cached trusted cert");
150 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
151 face.sentInterests.clear();
152
Davide Pesavento0f830802018-01-16 23:58:58 -0500153 advanceClocks(1_h, 2); // expire trusted cache
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800154
155 VALIDATE_FAILURE(data, "Should try and fail to retrieve certs");
156 BOOST_CHECK_GT(face.sentInterests.size(), 1);
157 face.sentInterests.clear();
158}
159
Alexander Afanasyev6aff0242017-08-29 17:14:44 -0400160BOOST_AUTO_TEST_CASE(ResetVerifiedCertificates)
161{
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400162 Data data("/Security/ValidatorFixture/Sub1/Sub2/Data");
Alexander Afanasyev6aff0242017-08-29 17:14:44 -0400163 m_keyChain.sign(data, signingByIdentity(subIdentity));
164 VALIDATE_SUCCESS(data, "Should get accepted, as signed by the policy-compliant cert");
165
166 // reset anchors
167 validator.resetAnchors();
168 VALIDATE_SUCCESS(data, "Should get accepted, as signed by the cert in trusted cache");
169
170 // reset trusted cache
171 validator.resetVerifiedCertificates();
172 VALIDATE_FAILURE(data, "Should fail, as no trusted cache or anchors");
173}
174
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800175BOOST_AUTO_TEST_CASE(UntrustedCertCaching)
176{
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400177 Data data("/Security/ValidatorFixture/Sub1/Sub2/Data");
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800178 m_keyChain.sign(data, signingByIdentity(subSelfSignedIdentity));
179
180 VALIDATE_FAILURE(data, "Should fail, as signed by the policy-violating cert");
181 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
182 face.sentInterests.clear();
183
184 processInterest = nullptr; // disable data responses from mocked network
185
186 VALIDATE_FAILURE(data, "Should fail again, but no network operations expected");
187 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
188 face.sentInterests.clear();
189
Davide Pesavento0f830802018-01-16 23:58:58 -0500190 advanceClocks(10_min, 2); // expire untrusted cache
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800191
192 VALIDATE_FAILURE(data, "Should try and fail to retrieve certs");
193 BOOST_CHECK_GT(face.sentInterests.size(), 1);
194 face.sentInterests.clear();
195}
196
197class ValidationPolicySimpleHierarchyForInterestOnly : public ValidationPolicySimpleHierarchy
198{
199public:
200 void
201 checkPolicy(const Data& data, const shared_ptr<ValidationState>& state,
202 const ValidationContinuation& continueValidation) override
203 {
204 continueValidation(nullptr, state);
205 }
206};
207
208BOOST_FIXTURE_TEST_CASE(ValidateInterestsButBypassForData,
209 HierarchicalValidatorFixture<ValidationPolicySimpleHierarchyForInterestOnly>)
210{
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400211 Interest interest("/Security/ValidatorFixture/Sub1/Sub2/Interest");
212 Data data("/Security/ValidatorFixture/Sub1/Sub2/Interest");
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800213
214 VALIDATE_FAILURE(interest, "Unsigned");
215 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
216 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
217 face.sentInterests.clear();
218
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400219 interest = Interest("/Security/ValidatorFixture/Sub1/Sub2/Interest");
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800220 m_keyChain.sign(interest, signingWithSha256());
221 m_keyChain.sign(data, signingWithSha256());
222 VALIDATE_FAILURE(interest, "Required KeyLocator/Name missing (not passed to policy)");
223 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
224 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
225 face.sentInterests.clear();
226
227 m_keyChain.sign(interest, signingByIdentity(identity));
228 m_keyChain.sign(data, signingByIdentity(identity));
229 VALIDATE_SUCCESS(interest, "Should get accepted, as signed by the anchor");
230 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
231 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
232 face.sentInterests.clear();
233
234 m_keyChain.sign(interest, signingByIdentity(subIdentity));
235 m_keyChain.sign(data, signingByIdentity(subIdentity));
236 VALIDATE_FAILURE(interest, "Should fail, as policy is not allowed to create new trust anchors");
237 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
238 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
239 face.sentInterests.clear();
240
241 m_keyChain.sign(interest, signingByIdentity(otherIdentity));
242 m_keyChain.sign(data, signingByIdentity(otherIdentity));
243 VALIDATE_FAILURE(interest, "Should fail, as signed by the policy-violating cert");
244 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
245 // no network operations expected, as certificate is not validated by the policy
246 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
247 face.sentInterests.clear();
248
Davide Pesavento0f830802018-01-16 23:58:58 -0500249 advanceClocks(1_h, 2); // expire trusted cache
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800250
251 m_keyChain.sign(interest, signingByIdentity(subSelfSignedIdentity));
252 m_keyChain.sign(data, signingByIdentity(subSelfSignedIdentity));
253 VALIDATE_FAILURE(interest, "Should fail, as policy is not allowed to create new trust anchors");
254 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
255 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
256 face.sentInterests.clear();
257}
258
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800259BOOST_AUTO_TEST_CASE(InfiniteCertChain)
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800260{
261 processInterest = [this] (const Interest& interest) {
262 // create another key for the same identity and sign it properly
263 Key parentKey = m_keyChain.createKey(subIdentity);
264 Key requestedKey = subIdentity.getKey(interest.getName());
265
266 Name certificateName = requestedKey.getName();
267 certificateName
268 .append("looper")
269 .appendVersion();
270 v2::Certificate certificate;
271 certificate.setName(certificateName);
272
273 // set metainfo
274 certificate.setContentType(tlv::ContentType_Key);
Davide Pesavento0f830802018-01-16 23:58:58 -0500275 certificate.setFreshnessPeriod(1_h);
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800276
277 // set content
Davide Pesavento5d0b0102017-10-07 13:43:16 -0400278 certificate.setContent(requestedKey.getPublicKey().data(), requestedKey.getPublicKey().size());
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800279
280 // set signature-info
281 SignatureInfo info;
Davide Pesavento0f830802018-01-16 23:58:58 -0500282 info.setValidityPeriod(security::ValidityPeriod(time::system_clock::now() - 10_days,
283 time::system_clock::now() + 10_days));
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800284
285 m_keyChain.sign(certificate, signingByKey(parentKey).setSignatureInfo(info));
286 face.receive(certificate);
287 };
288
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400289 Data data("/Security/ValidatorFixture/Sub1/Sub2/Data");
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800290 m_keyChain.sign(data, signingByIdentity(subIdentity));
291
292 validator.setMaxDepth(40);
293 BOOST_CHECK_EQUAL(validator.getMaxDepth(), 40);
294 VALIDATE_FAILURE(data, "Should fail, as certificate should be looped");
295 BOOST_CHECK_EQUAL(face.sentInterests.size(), 40);
296 face.sentInterests.clear();
297
Davide Pesavento0f830802018-01-16 23:58:58 -0500298 advanceClocks(1_h, 5); // expire caches
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800299
300 validator.setMaxDepth(30);
301 BOOST_CHECK_EQUAL(validator.getMaxDepth(), 30);
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800302 VALIDATE_FAILURE(data, "Should fail, as certificate chain is infinite");
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800303 BOOST_CHECK_EQUAL(face.sentInterests.size(), 30);
304}
305
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800306BOOST_AUTO_TEST_CASE(LoopedCertChain)
307{
308 auto s1 = addIdentity("/loop");
309 auto k1 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key1")));
310 auto k2 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key2")));
311 auto k3 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key3")));
312
313 auto makeCert = [this] (Key& key, const Key& signer) {
314 v2::Certificate request = key.getDefaultCertificate();
315 request.setName(Name(key.getName()).append("looper").appendVersion());
316
317 SignatureInfo info;
Davide Pesavento14c56cd2020-05-21 01:44:03 -0400318 info.setValidityPeriod(ValidityPeriod(time::system_clock::now() - 100_days,
319 time::system_clock::now() + 100_days));
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800320 m_keyChain.sign(request, signingByKey(signer).setSignatureInfo(info));
321 m_keyChain.addCertificate(key, request);
322
323 cache.insert(request);
324 };
325
326 makeCert(k1, k2);
327 makeCert(k2, k3);
328 makeCert(k3, k1);
329
330 Data data("/loop/Data");
331 m_keyChain.sign(data, signingByKey(k1));
332 VALIDATE_FAILURE(data, "Should fail, as certificate chain loops");
333 BOOST_CHECK_EQUAL(face.sentInterests.size(), 3);
334}
335
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800336BOOST_AUTO_TEST_SUITE_END() // TestValidator
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800337BOOST_AUTO_TEST_SUITE_END() // Security
338
339} // namespace tests
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400340} // inline namespace v2
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800341} // namespace security
342} // namespace ndn