blob: 4acbe4d8f02b7e4332e02c28cf56487521b83db3 [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 Pesaventoaee2ada2022-02-18 14:43:02 -05003 * Copyright (c) 2013-2022 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");
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());
Junxiao Shi9ee770b2022-04-25 23:33:33 +0000265
266 SignatureInfo sigInfo;
267 sigInfo.setKeyLocator(parentKey.getName());
268 auto si = signingByKey(parentKey).setSignatureInfo(sigInfo);
269
270 auto cert = m_keyChain.makeCertificate(requestedKey, si);
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -0500271 face.receive(cert);
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800272 };
273
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400274 Data data("/Security/ValidatorFixture/Sub1/Sub2/Data");
Junxiao Shi7d728682022-04-01 01:21:13 +0000275 m_keyChain.sign(data, signingByIdentity(subIdentity).setSignatureInfo(
276 SignatureInfo().setKeyLocator(subIdentity.getDefaultKey().getName())));
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800277
278 validator.setMaxDepth(40);
279 BOOST_CHECK_EQUAL(validator.getMaxDepth(), 40);
280 VALIDATE_FAILURE(data, "Should fail, as certificate should be looped");
281 BOOST_CHECK_EQUAL(face.sentInterests.size(), 40);
282 face.sentInterests.clear();
283
Davide Pesavento0f830802018-01-16 23:58:58 -0500284 advanceClocks(1_h, 5); // expire caches
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800285
286 validator.setMaxDepth(30);
287 BOOST_CHECK_EQUAL(validator.getMaxDepth(), 30);
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800288 VALIDATE_FAILURE(data, "Should fail, as certificate chain is infinite");
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800289 BOOST_CHECK_EQUAL(face.sentInterests.size(), 30);
290}
291
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800292BOOST_AUTO_TEST_CASE(LoopedCertChain)
293{
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -0500294 auto s1 = m_keyChain.createIdentity("/loop");
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800295 auto k1 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key1")));
296 auto k2 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key2")));
297 auto k3 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key3")));
298
Junxiao Shi7d728682022-04-01 01:21:13 +0000299 auto makeLoopCert = [this] (Key& key, const Key& signer) {
Junxiao Shi9ee770b2022-04-25 23:33:33 +0000300 SignatureInfo sigInfo;
301 sigInfo.setKeyLocator(signer.getName());
302 auto si = signingByKey(signer).setSignatureInfo(sigInfo);
303
304 auto cert = m_keyChain.makeCertificate(key, si);
Junxiao Shi7d728682022-04-01 01:21:13 +0000305 m_keyChain.setDefaultCertificate(key, cert);
306 cache.insert(cert);
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800307 };
308
Junxiao Shi7d728682022-04-01 01:21:13 +0000309 makeLoopCert(k1, k2);
310 makeLoopCert(k2, k3);
311 makeLoopCert(k3, k1);
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800312
313 Data data("/loop/Data");
314 m_keyChain.sign(data, signingByKey(k1));
315 VALIDATE_FAILURE(data, "Should fail, as certificate chain loops");
Junxiao Shi7d728682022-04-01 01:21:13 +0000316 BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 3);
317 BOOST_CHECK_EQUAL(face.sentInterests[0].getName(), k1.getDefaultCertificate().getName());
318 BOOST_CHECK_EQUAL(face.sentInterests[1].getName(), k2.getName());
319 BOOST_CHECK_EQUAL(face.sentInterests[2].getName(), k3.getName());
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800320}
321
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800322BOOST_AUTO_TEST_SUITE_END() // TestValidator
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800323BOOST_AUTO_TEST_SUITE_END() // Security
324
325} // namespace tests
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400326} // inline namespace v2
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800327} // namespace security
328} // namespace ndn