blob: 73bd23dcf071a5ac7bda554fdc6630be8272883f [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
39BOOST_AUTO_TEST_CASE(Timeouts)
40{
41 processInterest = nullptr; // no response for all interests
42
43 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
44 m_keyChain.sign(data, signingByIdentity(subIdentity));
45
46 VALIDATE_FAILURE(data, "Should fail to retrieve certificate");
47 BOOST_CHECK_GT(face.sentInterests.size(), 1);
48}
49
50BOOST_AUTO_TEST_CASE(NackedInterests)
51{
52 processInterest = [this] (const Interest& interest) {
53 lp::Nack nack(interest);
54 nack.setReason(lp::NackReason::NO_ROUTE);
55 face.receive(nack);
56 };
57
58 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
59 m_keyChain.sign(data, signingByIdentity(subIdentity));
60
61 VALIDATE_FAILURE(data, "All interests should get NACKed");
62 BOOST_CHECK_GT(face.sentInterests.size(), 1);
63}
64
65BOOST_AUTO_TEST_CASE(MalformedCert)
66{
67 Data malformedCert = subIdentity.getDefaultKey().getDefaultCertificate();
68 malformedCert.setContentType(tlv::ContentType_Blob);
69 m_keyChain.sign(malformedCert, signingByIdentity(identity));
70 // wrong content type & missing ValidityPeriod
71 BOOST_REQUIRE_THROW(Certificate(malformedCert.wireEncode()), tlv::Error);
72
73 auto originalProcessInterest = processInterest;
74 processInterest = [this, &originalProcessInterest, &malformedCert] (const Interest& interest) {
75 if (interest.getName().isPrefixOf(malformedCert.getName())) {
76 face.receive(malformedCert);
77 }
78 else {
79 originalProcessInterest(interest);
80 }
81 };
82
83 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
84 m_keyChain.sign(data, signingByIdentity(subIdentity));
85
86 VALIDATE_FAILURE(data, "Signed by a malformed certificate");
87 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
88}
89
90
91BOOST_AUTO_TEST_CASE(ExpiredCert)
92{
93 Data expiredCert = subIdentity.getDefaultKey().getDefaultCertificate();
94 SignatureInfo info;
95 info.setValidityPeriod(ValidityPeriod(time::system_clock::now() - time::hours(2),
96 time::system_clock::now() - time::hours(1)));
97 m_keyChain.sign(expiredCert, signingByIdentity(identity).setSignatureInfo(info));
98 BOOST_REQUIRE_NO_THROW(Certificate(expiredCert.wireEncode()));
99
100 auto originalProcessInterest = processInterest;
101 processInterest = [this, &originalProcessInterest, &expiredCert] (const Interest& interest) {
102 if (interest.getName().isPrefixOf(expiredCert.getName())) {
103 face.receive(expiredCert);
104 }
105 else {
106 processInterest(interest);
107 }
108 };
109
110 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
111 m_keyChain.sign(data, signingByIdentity(subIdentity));
112
113 VALIDATE_FAILURE(data, "Signed by an expired certificate");
114 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
115}
116
117BOOST_AUTO_TEST_CASE(TrustedCertCaching)
118{
119 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
120 m_keyChain.sign(data, signingByIdentity(subIdentity));
121
122 VALIDATE_SUCCESS(data, "Should get accepted, as signed by the policy-compliant cert");
123 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
124 face.sentInterests.clear();
125
126 processInterest = nullptr; // disable data responses from mocked network
127
128 VALIDATE_SUCCESS(data, "Should get accepted, based on the cached trusted cert");
129 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
130 face.sentInterests.clear();
131
132 advanceClocks(time::hours(1), 2); // expire trusted cache
133
134 VALIDATE_FAILURE(data, "Should try and fail to retrieve certs");
135 BOOST_CHECK_GT(face.sentInterests.size(), 1);
136 face.sentInterests.clear();
137}
138
139BOOST_AUTO_TEST_CASE(UntrustedCertCaching)
140{
141 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
142 m_keyChain.sign(data, signingByIdentity(subSelfSignedIdentity));
143
144 VALIDATE_FAILURE(data, "Should fail, as signed by the policy-violating cert");
145 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
146 face.sentInterests.clear();
147
148 processInterest = nullptr; // disable data responses from mocked network
149
150 VALIDATE_FAILURE(data, "Should fail again, but no network operations expected");
151 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
152 face.sentInterests.clear();
153
154 advanceClocks(time::minutes(10), 2); // expire untrusted cache
155
156 VALIDATE_FAILURE(data, "Should try and fail to retrieve certs");
157 BOOST_CHECK_GT(face.sentInterests.size(), 1);
158 face.sentInterests.clear();
159}
160
161class ValidationPolicySimpleHierarchyForInterestOnly : public ValidationPolicySimpleHierarchy
162{
163public:
164 void
165 checkPolicy(const Data& data, const shared_ptr<ValidationState>& state,
166 const ValidationContinuation& continueValidation) override
167 {
168 continueValidation(nullptr, state);
169 }
170};
171
172BOOST_FIXTURE_TEST_CASE(ValidateInterestsButBypassForData,
173 HierarchicalValidatorFixture<ValidationPolicySimpleHierarchyForInterestOnly>)
174{
175 Interest interest("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest");
176 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest");
177
178 VALIDATE_FAILURE(interest, "Unsigned");
179 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
180 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
181 face.sentInterests.clear();
182
183 interest = Interest("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest");
184 m_keyChain.sign(interest, signingWithSha256());
185 m_keyChain.sign(data, signingWithSha256());
186 VALIDATE_FAILURE(interest, "Required KeyLocator/Name missing (not passed to policy)");
187 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
188 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
189 face.sentInterests.clear();
190
191 m_keyChain.sign(interest, signingByIdentity(identity));
192 m_keyChain.sign(data, signingByIdentity(identity));
193 VALIDATE_SUCCESS(interest, "Should get accepted, as signed by the anchor");
194 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
195 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
196 face.sentInterests.clear();
197
198 m_keyChain.sign(interest, signingByIdentity(subIdentity));
199 m_keyChain.sign(data, signingByIdentity(subIdentity));
200 VALIDATE_FAILURE(interest, "Should fail, as policy is not allowed to create new trust anchors");
201 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
202 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
203 face.sentInterests.clear();
204
205 m_keyChain.sign(interest, signingByIdentity(otherIdentity));
206 m_keyChain.sign(data, signingByIdentity(otherIdentity));
207 VALIDATE_FAILURE(interest, "Should fail, as signed by the policy-violating cert");
208 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
209 // no network operations expected, as certificate is not validated by the policy
210 BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
211 face.sentInterests.clear();
212
213 advanceClocks(time::hours(1), 2); // expire trusted cache
214
215 m_keyChain.sign(interest, signingByIdentity(subSelfSignedIdentity));
216 m_keyChain.sign(data, signingByIdentity(subSelfSignedIdentity));
217 VALIDATE_FAILURE(interest, "Should fail, as policy is not allowed to create new trust anchors");
218 VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
219 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
220 face.sentInterests.clear();
221}
222
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800223BOOST_AUTO_TEST_CASE(InfiniteCertChain)
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800224{
225 processInterest = [this] (const Interest& interest) {
226 // create another key for the same identity and sign it properly
227 Key parentKey = m_keyChain.createKey(subIdentity);
228 Key requestedKey = subIdentity.getKey(interest.getName());
229
230 Name certificateName = requestedKey.getName();
231 certificateName
232 .append("looper")
233 .appendVersion();
234 v2::Certificate certificate;
235 certificate.setName(certificateName);
236
237 // set metainfo
238 certificate.setContentType(tlv::ContentType_Key);
239 certificate.setFreshnessPeriod(time::hours(1));
240
241 // set content
242 certificate.setContent(requestedKey.getPublicKey().buf(), requestedKey.getPublicKey().size());
243
244 // set signature-info
245 SignatureInfo info;
246 info.setValidityPeriod(security::ValidityPeriod(time::system_clock::now() - time::days(10),
247 time::system_clock::now() + time::days(10)));
248
249 m_keyChain.sign(certificate, signingByKey(parentKey).setSignatureInfo(info));
250 face.receive(certificate);
251 };
252
253 Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
254 m_keyChain.sign(data, signingByIdentity(subIdentity));
255
256 validator.setMaxDepth(40);
257 BOOST_CHECK_EQUAL(validator.getMaxDepth(), 40);
258 VALIDATE_FAILURE(data, "Should fail, as certificate should be looped");
259 BOOST_CHECK_EQUAL(face.sentInterests.size(), 40);
260 face.sentInterests.clear();
261
262 advanceClocks(time::hours(1), 5); // expire caches
263
264 validator.setMaxDepth(30);
265 BOOST_CHECK_EQUAL(validator.getMaxDepth(), 30);
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800266 VALIDATE_FAILURE(data, "Should fail, as certificate chain is infinite");
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800267 BOOST_CHECK_EQUAL(face.sentInterests.size(), 30);
268}
269
Alexander Afanasyev5af04a72017-01-30 22:41:23 -0800270BOOST_AUTO_TEST_CASE(LoopedCertChain)
271{
272 auto s1 = addIdentity("/loop");
273 auto k1 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key1")));
274 auto k2 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key2")));
275 auto k3 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key3")));
276
277 auto makeCert = [this] (Key& key, const Key& signer) {
278 v2::Certificate request = key.getDefaultCertificate();
279 request.setName(Name(key.getName()).append("looper").appendVersion());
280
281 SignatureInfo info;
282 info.setValidityPeriod({time::system_clock::now() - time::days(100),
283 time::system_clock::now() + time::days(100)});
284 m_keyChain.sign(request, signingByKey(signer).setSignatureInfo(info));
285 m_keyChain.addCertificate(key, request);
286
287 cache.insert(request);
288 };
289
290 makeCert(k1, k2);
291 makeCert(k2, k3);
292 makeCert(k3, k1);
293
294 Data data("/loop/Data");
295 m_keyChain.sign(data, signingByKey(k1));
296 VALIDATE_FAILURE(data, "Should fail, as certificate chain loops");
297 BOOST_CHECK_EQUAL(face.sentInterests.size(), 3);
298}
299
Alexander Afanasyev7e721412017-01-11 13:36:08 -0800300BOOST_AUTO_TEST_SUITE_END() // TestValidator
301BOOST_AUTO_TEST_SUITE_END() // V2
302BOOST_AUTO_TEST_SUITE_END() // Security
303
304} // namespace tests
305} // namespace v2
306} // namespace security
307} // namespace ndn