blob: faecf5666d53ff4556171a1e933cfe8039d43f4b [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");
Eric Newberryb74bbda2020-06-18 19:33:58 -0700212 interest.setCanBePrefix(false);
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400213 Data data("/Security/ValidatorFixture/Sub1/Sub2/Interest");
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800214
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
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400220 interest = Interest("/Security/ValidatorFixture/Sub1/Sub2/Interest");
Eric Newberryb74bbda2020-06-18 19:33:58 -0700221 interest.setCanBePrefix(false);
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800222 m_keyChain.sign(interest, signingWithSha256());
223 m_keyChain.sign(data, signingWithSha256());
224 VALIDATE_FAILURE(interest, "Required KeyLocator/Name missing (not passed to policy)");
225 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
226 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
227 face.sentInterests.clear();
228
229 m_keyChain.sign(interest, signingByIdentity(identity));
230 m_keyChain.sign(data, signingByIdentity(identity));
231 VALIDATE_SUCCESS(interest, "Should get accepted, as signed by the anchor");
232 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
233 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
234 face.sentInterests.clear();
235
236 m_keyChain.sign(interest, signingByIdentity(subIdentity));
237 m_keyChain.sign(data, signingByIdentity(subIdentity));
238 VALIDATE_FAILURE(interest, "Should fail, as policy is not allowed to create new trust anchors");
239 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
240 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
241 face.sentInterests.clear();
242
243 m_keyChain.sign(interest, signingByIdentity(otherIdentity));
244 m_keyChain.sign(data, signingByIdentity(otherIdentity));
245 VALIDATE_FAILURE(interest, "Should fail, as signed by the policy-violating cert");
246 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
247 // no network operations expected, as certificate is not validated by the policy
248 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
249 face.sentInterests.clear();
250
Davide Pesavento0f830802018-01-16 23:58:58 -0500251 advanceClocks(1_h, 2); // expire trusted cache
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800252
253 m_keyChain.sign(interest, signingByIdentity(subSelfSignedIdentity));
254 m_keyChain.sign(data, signingByIdentity(subSelfSignedIdentity));
255 VALIDATE_FAILURE(interest, "Should fail, as policy is not allowed to create new trust anchors");
256 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
257 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
258 face.sentInterests.clear();
259}
260
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800261BOOST_AUTO_TEST_CASE(InfiniteCertChain)
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800262{
263 processInterest = [this] (const Interest& interest) {
264 // create another key for the same identity and sign it properly
265 Key parentKey = m_keyChain.createKey(subIdentity);
266 Key requestedKey = subIdentity.getKey(interest.getName());
267
268 Name certificateName = requestedKey.getName();
269 certificateName
270 .append("looper")
271 .appendVersion();
272 v2::Certificate certificate;
273 certificate.setName(certificateName);
274
275 // set metainfo
276 certificate.setContentType(tlv::ContentType_Key);
Davide Pesavento0f830802018-01-16 23:58:58 -0500277 certificate.setFreshnessPeriod(1_h);
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800278
279 // set content
Davide Pesavento5d0b0102017-10-07 13:43:16 -0400280 certificate.setContent(requestedKey.getPublicKey().data(), requestedKey.getPublicKey().size());
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800281
282 // set signature-info
283 SignatureInfo info;
Davide Pesavento0f830802018-01-16 23:58:58 -0500284 info.setValidityPeriod(security::ValidityPeriod(time::system_clock::now() - 10_days,
285 time::system_clock::now() + 10_days));
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800286
287 m_keyChain.sign(certificate, signingByKey(parentKey).setSignatureInfo(info));
288 face.receive(certificate);
289 };
290
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400291 Data data("/Security/ValidatorFixture/Sub1/Sub2/Data");
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800292 m_keyChain.sign(data, signingByIdentity(subIdentity));
293
294 validator.setMaxDepth(40);
295 BOOST_CHECK_EQUAL(validator.getMaxDepth(), 40);
296 VALIDATE_FAILURE(data, "Should fail, as certificate should be looped");
297 BOOST_CHECK_EQUAL(face.sentInterests.size(), 40);
298 face.sentInterests.clear();
299
Davide Pesavento0f830802018-01-16 23:58:58 -0500300 advanceClocks(1_h, 5); // expire caches
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800301
302 validator.setMaxDepth(30);
303 BOOST_CHECK_EQUAL(validator.getMaxDepth(), 30);
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800304 VALIDATE_FAILURE(data, "Should fail, as certificate chain is infinite");
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800305 BOOST_CHECK_EQUAL(face.sentInterests.size(), 30);
306}
307
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800308BOOST_AUTO_TEST_CASE(LoopedCertChain)
309{
310 auto s1 = addIdentity("/loop");
311 auto k1 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key1")));
312 auto k2 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key2")));
313 auto k3 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key3")));
314
315 auto makeCert = [this] (Key& key, const Key& signer) {
316 v2::Certificate request = key.getDefaultCertificate();
317 request.setName(Name(key.getName()).append("looper").appendVersion());
318
319 SignatureInfo info;
Davide Pesavento14c56cd2020-05-21 01:44:03 -0400320 info.setValidityPeriod(ValidityPeriod(time::system_clock::now() - 100_days,
321 time::system_clock::now() + 100_days));
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800322 m_keyChain.sign(request, signingByKey(signer).setSignatureInfo(info));
323 m_keyChain.addCertificate(key, request);
324
325 cache.insert(request);
326 };
327
328 makeCert(k1, k2);
329 makeCert(k2, k3);
330 makeCert(k3, k1);
331
332 Data data("/loop/Data");
333 m_keyChain.sign(data, signingByKey(k1));
334 VALIDATE_FAILURE(data, "Should fail, as certificate chain loops");
335 BOOST_CHECK_EQUAL(face.sentInterests.size(), 3);
336}
337
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800338BOOST_AUTO_TEST_SUITE_END() // TestValidator
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800339BOOST_AUTO_TEST_SUITE_END() // Security
340
341} // namespace tests
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400342} // inline namespace v2
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800343} // namespace security
344} // namespace ndn