blob: bb545e162c5540d7bc3de41989ce9dafb43f0071 [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/*
Alexander Afanasyev7e721412017-01-11 13:36:08 -08003 * Copyright (c) 2013-2017 Regents of the University of California.
4 *
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
22#include "security/v2/validator.hpp"
23#include "security/v2/validation-policy-simple-hierarchy.hpp"
24
25#include "boost-test.hpp"
26#include "validator-fixture.hpp"
27
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");
75 BOOST_CHECK_GT(face.sentInterests.size(), 1);
76}
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
96 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
97 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;
107 info.setValidityPeriod(ValidityPeriod(time::system_clock::now() - time::hours(2),
108 time::system_clock::now() - time::hours(1)));
109 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 {
118 processInterest(interest);
119 }
120 };
121
122 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
123 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
133 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
134 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{
140 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
141 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
153 advanceClocks(time::hours(1), 2); // expire trusted cache
154
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{
162 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
163 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{
177 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
178 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
190 advanceClocks(time::minutes(10), 2); // expire untrusted cache
191
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{
211 Interest interest("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest");
212 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest");
213
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
219 interest = Interest("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest");
220 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
249 advanceClocks(time::hours(1), 2); // expire trusted cache
250
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);
275 certificate.setFreshnessPeriod(time::hours(1));
276
277 // set content
278 certificate.setContent(requestedKey.getPublicKey().buf(), requestedKey.getPublicKey().size());
279
280 // set signature-info
281 SignatureInfo info;
282 info.setValidityPeriod(security::ValidityPeriod(time::system_clock::now() - time::days(10),
283 time::system_clock::now() + time::days(10)));
284
285 m_keyChain.sign(certificate, signingByKey(parentKey).setSignatureInfo(info));
286 face.receive(certificate);
287 };
288
289 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
290 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
298 advanceClocks(time::hours(1), 5); // expire caches
299
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;
318 info.setValidityPeriod({time::system_clock::now() - time::days(100),
319 time::system_clock::now() + time::days(100)});
320 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
337BOOST_AUTO_TEST_SUITE_END() // V2
338BOOST_AUTO_TEST_SUITE_END() // Security
339
340} // namespace tests
341} // namespace v2
342} // namespace security
343} // namespace ndn