blob: adf72d32e528edd0b594368fa533c95af3158803 [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
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -0500201 checkPolicy(const Data&, const shared_ptr<ValidationState>& state,
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800202 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());
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -0500267 auto cert = makeCert(requestedKey, "looper", parentKey);
268 face.receive(cert);
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800269 };
270
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400271 Data data("/Security/ValidatorFixture/Sub1/Sub2/Data");
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800272 m_keyChain.sign(data, signingByIdentity(subIdentity));
273
274 validator.setMaxDepth(40);
275 BOOST_CHECK_EQUAL(validator.getMaxDepth(), 40);
276 VALIDATE_FAILURE(data, "Should fail, as certificate should be looped");
277 BOOST_CHECK_EQUAL(face.sentInterests.size(), 40);
278 face.sentInterests.clear();
279
Davide Pesavento0f830802018-01-16 23:58:58 -0500280 advanceClocks(1_h, 5); // expire caches
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800281
282 validator.setMaxDepth(30);
283 BOOST_CHECK_EQUAL(validator.getMaxDepth(), 30);
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800284 VALIDATE_FAILURE(data, "Should fail, as certificate chain is infinite");
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800285 BOOST_CHECK_EQUAL(face.sentInterests.size(), 30);
286}
287
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800288BOOST_AUTO_TEST_CASE(LoopedCertChain)
289{
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -0500290 auto s1 = m_keyChain.createIdentity("/loop");
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800291 auto k1 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key1")));
292 auto k2 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key2")));
293 auto k3 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key3")));
294
295 auto makeCert = [this] (Key& key, const Key& signer) {
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -0500296 Certificate request = key.getDefaultCertificate();
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800297 request.setName(Name(key.getName()).append("looper").appendVersion());
298
299 SignatureInfo info;
Davide Pesavento14c56cd2020-05-21 01:44:03 -0400300 info.setValidityPeriod(ValidityPeriod(time::system_clock::now() - 100_days,
301 time::system_clock::now() + 100_days));
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800302 m_keyChain.sign(request, signingByKey(signer).setSignatureInfo(info));
303 m_keyChain.addCertificate(key, request);
304
305 cache.insert(request);
306 };
307
308 makeCert(k1, k2);
309 makeCert(k2, k3);
310 makeCert(k3, k1);
311
312 Data data("/loop/Data");
313 m_keyChain.sign(data, signingByKey(k1));
314 VALIDATE_FAILURE(data, "Should fail, as certificate chain loops");
315 BOOST_CHECK_EQUAL(face.sentInterests.size(), 3);
316}
317
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800318BOOST_AUTO_TEST_SUITE_END() // TestValidator
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800319BOOST_AUTO_TEST_SUITE_END() // Security
320
321} // namespace tests
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400322} // inline namespace v2
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800323} // namespace security
324} // namespace ndn