blob: 42bcf2a22dbb24d3464379333da196830426d8a7 [file] [log] [blame]
Alexander Afanasyev7e721412017-01-11 13:36:08 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * 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
103
104BOOST_AUTO_TEST_CASE(ExpiredCert)
105{
106 Data expiredCert = subIdentity.getDefaultKey().getDefaultCertificate();
107 SignatureInfo info;
108 info.setValidityPeriod(ValidityPeriod(time::system_clock::now() - time::hours(2),
109 time::system_clock::now() - time::hours(1)));
110 m_keyChain.sign(expiredCert, signingByIdentity(identity).setSignatureInfo(info));
111 BOOST_REQUIRE_NO_THROW(Certificate(expiredCert.wireEncode()));
112
113 auto originalProcessInterest = processInterest;
114 processInterest = [this, &originalProcessInterest, &expiredCert] (const Interest& interest) {
115 if (interest.getName().isPrefixOf(expiredCert.getName())) {
116 face.receive(expiredCert);
117 }
118 else {
119 processInterest(interest);
120 }
121 };
122
123 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
124 m_keyChain.sign(data, signingByIdentity(subIdentity));
125
126 VALIDATE_FAILURE(data, "Signed by an expired certificate");
127 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
128}
129
130BOOST_AUTO_TEST_CASE(TrustedCertCaching)
131{
132 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
133 m_keyChain.sign(data, signingByIdentity(subIdentity));
134
135 VALIDATE_SUCCESS(data, "Should get accepted, as signed by the policy-compliant cert");
136 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
137 face.sentInterests.clear();
138
139 processInterest = nullptr; // disable data responses from mocked network
140
141 VALIDATE_SUCCESS(data, "Should get accepted, based on the cached trusted cert");
142 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
143 face.sentInterests.clear();
144
145 advanceClocks(time::hours(1), 2); // expire trusted cache
146
147 VALIDATE_FAILURE(data, "Should try and fail to retrieve certs");
148 BOOST_CHECK_GT(face.sentInterests.size(), 1);
149 face.sentInterests.clear();
150}
151
152BOOST_AUTO_TEST_CASE(UntrustedCertCaching)
153{
154 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
155 m_keyChain.sign(data, signingByIdentity(subSelfSignedIdentity));
156
157 VALIDATE_FAILURE(data, "Should fail, as signed by the policy-violating cert");
158 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
159 face.sentInterests.clear();
160
161 processInterest = nullptr; // disable data responses from mocked network
162
163 VALIDATE_FAILURE(data, "Should fail again, but no network operations expected");
164 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
165 face.sentInterests.clear();
166
167 advanceClocks(time::minutes(10), 2); // expire untrusted cache
168
169 VALIDATE_FAILURE(data, "Should try and fail to retrieve certs");
170 BOOST_CHECK_GT(face.sentInterests.size(), 1);
171 face.sentInterests.clear();
172}
173
174class ValidationPolicySimpleHierarchyForInterestOnly : public ValidationPolicySimpleHierarchy
175{
176public:
177 void
178 checkPolicy(const Data& data, const shared_ptr<ValidationState>& state,
179 const ValidationContinuation& continueValidation) override
180 {
181 continueValidation(nullptr, state);
182 }
183};
184
185BOOST_FIXTURE_TEST_CASE(ValidateInterestsButBypassForData,
186 HierarchicalValidatorFixture<ValidationPolicySimpleHierarchyForInterestOnly>)
187{
188 Interest interest("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest");
189 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest");
190
191 VALIDATE_FAILURE(interest, "Unsigned");
192 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
193 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
194 face.sentInterests.clear();
195
196 interest = Interest("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest");
197 m_keyChain.sign(interest, signingWithSha256());
198 m_keyChain.sign(data, signingWithSha256());
199 VALIDATE_FAILURE(interest, "Required KeyLocator/Name missing (not passed to policy)");
200 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
201 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
202 face.sentInterests.clear();
203
204 m_keyChain.sign(interest, signingByIdentity(identity));
205 m_keyChain.sign(data, signingByIdentity(identity));
206 VALIDATE_SUCCESS(interest, "Should get accepted, as signed by the anchor");
207 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
208 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
209 face.sentInterests.clear();
210
211 m_keyChain.sign(interest, signingByIdentity(subIdentity));
212 m_keyChain.sign(data, signingByIdentity(subIdentity));
213 VALIDATE_FAILURE(interest, "Should fail, as policy is not allowed to create new trust anchors");
214 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
215 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
216 face.sentInterests.clear();
217
218 m_keyChain.sign(interest, signingByIdentity(otherIdentity));
219 m_keyChain.sign(data, signingByIdentity(otherIdentity));
220 VALIDATE_FAILURE(interest, "Should fail, as signed by the policy-violating cert");
221 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
222 // no network operations expected, as certificate is not validated by the policy
223 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
224 face.sentInterests.clear();
225
226 advanceClocks(time::hours(1), 2); // expire trusted cache
227
228 m_keyChain.sign(interest, signingByIdentity(subSelfSignedIdentity));
229 m_keyChain.sign(data, signingByIdentity(subSelfSignedIdentity));
230 VALIDATE_FAILURE(interest, "Should fail, as policy is not allowed to create new trust anchors");
231 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
232 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
233 face.sentInterests.clear();
234}
235
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800236BOOST_AUTO_TEST_CASE(InfiniteCertChain)
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800237{
238 processInterest = [this] (const Interest& interest) {
239 // create another key for the same identity and sign it properly
240 Key parentKey = m_keyChain.createKey(subIdentity);
241 Key requestedKey = subIdentity.getKey(interest.getName());
242
243 Name certificateName = requestedKey.getName();
244 certificateName
245 .append("looper")
246 .appendVersion();
247 v2::Certificate certificate;
248 certificate.setName(certificateName);
249
250 // set metainfo
251 certificate.setContentType(tlv::ContentType_Key);
252 certificate.setFreshnessPeriod(time::hours(1));
253
254 // set content
255 certificate.setContent(requestedKey.getPublicKey().buf(), requestedKey.getPublicKey().size());
256
257 // set signature-info
258 SignatureInfo info;
259 info.setValidityPeriod(security::ValidityPeriod(time::system_clock::now() - time::days(10),
260 time::system_clock::now() + time::days(10)));
261
262 m_keyChain.sign(certificate, signingByKey(parentKey).setSignatureInfo(info));
263 face.receive(certificate);
264 };
265
266 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
267 m_keyChain.sign(data, signingByIdentity(subIdentity));
268
269 validator.setMaxDepth(40);
270 BOOST_CHECK_EQUAL(validator.getMaxDepth(), 40);
271 VALIDATE_FAILURE(data, "Should fail, as certificate should be looped");
272 BOOST_CHECK_EQUAL(face.sentInterests.size(), 40);
273 face.sentInterests.clear();
274
275 advanceClocks(time::hours(1), 5); // expire caches
276
277 validator.setMaxDepth(30);
278 BOOST_CHECK_EQUAL(validator.getMaxDepth(), 30);
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800279 VALIDATE_FAILURE(data, "Should fail, as certificate chain is infinite");
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800280 BOOST_CHECK_EQUAL(face.sentInterests.size(), 30);
281}
282
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800283BOOST_AUTO_TEST_CASE(LoopedCertChain)
284{
285 auto s1 = addIdentity("/loop");
286 auto k1 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key1")));
287 auto k2 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key2")));
288 auto k3 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key3")));
289
290 auto makeCert = [this] (Key& key, const Key& signer) {
291 v2::Certificate request = key.getDefaultCertificate();
292 request.setName(Name(key.getName()).append("looper").appendVersion());
293
294 SignatureInfo info;
295 info.setValidityPeriod({time::system_clock::now() - time::days(100),
296 time::system_clock::now() + time::days(100)});
297 m_keyChain.sign(request, signingByKey(signer).setSignatureInfo(info));
298 m_keyChain.addCertificate(key, request);
299
300 cache.insert(request);
301 };
302
303 makeCert(k1, k2);
304 makeCert(k2, k3);
305 makeCert(k3, k1);
306
307 Data data("/loop/Data");
308 m_keyChain.sign(data, signingByKey(k1));
309 VALIDATE_FAILURE(data, "Should fail, as certificate chain loops");
310 BOOST_CHECK_EQUAL(face.sentInterests.size(), 3);
311}
312
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800313BOOST_AUTO_TEST_SUITE_END() // TestValidator
314BOOST_AUTO_TEST_SUITE_END() // V2
315BOOST_AUTO_TEST_SUITE_END() // Security
316
317} // namespace tests
318} // namespace v2
319} // namespace security
320} // namespace ndn