blob: b55cf69e853d1140f27701948ff83f572c0c7cf1 [file] [log] [blame]
Alexander Afanasyevc169a812014-05-20 20:37:29 -04001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Yingdi Yu48e8c0c2014-03-19 12:01:55 -07002/**
Alexander Afanasyevc169a812014-05-20 20:37:29 -04003 * Copyright (c) 2013-2014 Regents of the University of California.
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07004 *
5 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07006 *
Alexander Afanasyevc169a812014-05-20 20:37:29 -04007 * 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.
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -070020 *
21 * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070022 */
23
24#include "validator-config.hpp"
25#include "certificate-cache-ttl.hpp"
26#include "../util/io.hpp"
27
28#include <boost/filesystem.hpp>
29#include <boost/property_tree/info_parser.hpp>
30#include <boost/algorithm/string.hpp>
31
32namespace ndn {
33
34const shared_ptr<CertificateCache> ValidatorConfig::DEFAULT_CERTIFICATE_CACHE;
Yingdi Yu0f5fb692014-06-10 12:07:28 -070035const time::milliseconds ValidatorConfig::DEFAULT_GRACE_INTERVAL(3000);
36const time::system_clock::Duration ValidatorConfig::DEFAULT_KEY_TIMESTAMP_TTL = time::hours(1);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070037
Yingdi Yu4e9b0692014-11-04 16:13:56 -080038ValidatorConfig::ValidatorConfig(Face* face,
39 const shared_ptr<CertificateCache>& certificateCache,
40 const time::milliseconds& graceInterval,
41 const size_t stepLimit,
42 const size_t maxTrackedKeys,
43 const time::system_clock::Duration& keyTimestampTtl)
44 : Validator(face)
45 , m_shouldValidate(true)
46 , m_stepLimit(stepLimit)
47 , m_certificateCache(certificateCache)
48 , m_graceInterval(graceInterval < time::milliseconds::zero() ?
49 DEFAULT_GRACE_INTERVAL : graceInterval)
50 , m_maxTrackedKeys(maxTrackedKeys)
51 , m_keyTimestampTtl(keyTimestampTtl)
52{
53 if (!static_cast<bool>(m_certificateCache) && face != nullptr)
54 m_certificateCache = make_shared<CertificateCacheTtl>(ref(face->getIoService()));
55}
56
Yingdi Yu96e64062014-04-15 19:57:33 -070057ValidatorConfig::ValidatorConfig(Face& face,
58 const shared_ptr<CertificateCache>& certificateCache,
Yingdi Yu0f5fb692014-06-10 12:07:28 -070059 const time::milliseconds& graceInterval,
60 const size_t stepLimit,
61 const size_t maxTrackedKeys,
62 const time::system_clock::Duration& keyTimestampTtl)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070063 : Validator(face)
Yingdi Yu44d190c2014-04-16 17:05:46 -070064 , m_shouldValidate(true)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070065 , m_stepLimit(stepLimit)
66 , m_certificateCache(certificateCache)
Yingdi Yu0f5fb692014-06-10 12:07:28 -070067 , m_graceInterval(graceInterval < time::milliseconds::zero() ?
68 DEFAULT_GRACE_INTERVAL : graceInterval)
69 , m_maxTrackedKeys(maxTrackedKeys)
70 , m_keyTimestampTtl(keyTimestampTtl)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070071{
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070072 if (!static_cast<bool>(m_certificateCache))
Yingdi Yu4e9b0692014-11-04 16:13:56 -080073 m_certificateCache = make_shared<CertificateCacheTtl>(ref(face.getIoService()));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070074}
75
76void
77ValidatorConfig::load(const std::string& filename)
78{
79 std::ifstream inputFile;
80 inputFile.open(filename.c_str());
81 if (!inputFile.good() || !inputFile.is_open())
82 {
83 std::string msg = "Failed to read configuration file: ";
84 msg += filename;
85 throw security::conf::Error(msg);
86 }
87 load(inputFile, filename);
88 inputFile.close();
89}
90
91void
92ValidatorConfig::load(const std::string& input, const std::string& filename)
93{
94 std::istringstream inputStream(input);
95 load(inputStream, filename);
96}
97
98
99void
100ValidatorConfig::load(std::istream& input, const std::string& filename)
101{
102 security::conf::ConfigSection tree;
103 try
104 {
105 boost::property_tree::read_info(input, tree);
106 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700107 catch (boost::property_tree::info_parser_error& error)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700108 {
109 std::stringstream msg;
110 msg << "Failed to parse configuration file";
111 msg << " " << filename;
112 msg << " " << error.message() << " line " << error.line();
113 throw security::conf::Error(msg.str());
114 }
115
Yingdi Yudfa9d732014-04-09 09:53:01 -0700116 load(tree, filename);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700117}
118
119void
Yingdi Yudfa9d732014-04-09 09:53:01 -0700120ValidatorConfig::load(const security::conf::ConfigSection& configSection,
121 const std::string& filename)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700122{
123 BOOST_ASSERT(!filename.empty());
124
Yingdi Yu58f33712014-04-16 16:57:47 -0700125 reset();
126
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700127 if (configSection.begin() == configSection.end())
128 {
129 std::string msg = "Error processing configuration file";
130 msg += ": ";
131 msg += filename;
132 msg += " no data";
133 throw security::conf::Error(msg);
134 }
135
136 for (security::conf::ConfigSection::const_iterator i = configSection.begin();
137 i != configSection.end(); ++i)
138 {
139 const std::string& sectionName = i->first;
140 const security::conf::ConfigSection& section = i->second;
141
142 if (boost::iequals(sectionName, "rule"))
143 {
144 onConfigRule(section, filename);
145 }
146 else if (boost::iequals(sectionName, "trust-anchor"))
147 {
148 onConfigTrustAnchor(section, filename);
149 }
150 else
151 {
152 std::string msg = "Error processing configuration file";
153 msg += " ";
154 msg += filename;
155 msg += " unrecognized section: " + sectionName;
156 throw security::conf::Error(msg);
157 }
158 }
159}
160
161void
162ValidatorConfig::onConfigRule(const security::conf::ConfigSection& configSection,
163 const std::string& filename)
164{
165 using namespace ndn::security::conf;
166
167 ConfigSection::const_iterator propertyIt = configSection.begin();
168
169 // Get rule.id
170 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "id"))
171 throw Error("Expect <rule.id>!");
172
173 std::string ruleId = propertyIt->second.data();
174 propertyIt++;
175
176 // Get rule.for
177 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first,"for"))
178 throw Error("Expect <rule.for> in rule: " + ruleId + "!");
179
180 std::string usage = propertyIt->second.data();
181 propertyIt++;
182
183 bool isForData;
184 if (boost::iequals(usage, "data"))
185 isForData = true;
186 else if (boost::iequals(usage, "interest"))
187 isForData = false;
188 else
189 throw Error("Unrecognized <rule.for>: " + usage
190 + " in rule: " + ruleId);
191
192 // Get rule.filter(s)
193 std::vector<shared_ptr<Filter> > filters;
194 for (; propertyIt != configSection.end(); propertyIt++)
195 {
196 if (!boost::iequals(propertyIt->first, "filter"))
197 {
198 if (boost::iequals(propertyIt->first, "checker"))
199 break;
200 throw Error("Expect <rule.filter> in rule: " + ruleId);
201 }
202
203 filters.push_back(FilterFactory::create(propertyIt->second));
204 continue;
205 }
206
207 // Get rule.checker(s)
208 std::vector<shared_ptr<Checker> > checkers;
209 for (; propertyIt != configSection.end(); propertyIt++)
210 {
211 if (!boost::iequals(propertyIt->first, "checker"))
212 throw Error("Expect <rule.checker> in rule: " + ruleId);
213
214 checkers.push_back(CheckerFactory::create(propertyIt->second, filename));
215 continue;
216 }
217
218 // Check other stuff
219 if (propertyIt != configSection.end())
220 throw Error("Expect the end of rule: " + ruleId);
221
222 if (checkers.size() == 0)
223 throw Error("No <rule.checker> is specified in rule: " + ruleId);
224
225 if (isForData)
226 {
227 shared_ptr<DataRule> rule(new DataRule(ruleId));
228 for (size_t i = 0; i < filters.size(); i++)
229 rule->addFilter(filters[i]);
230 for (size_t i = 0; i < checkers.size(); i++)
231 rule->addChecker(checkers[i]);
232
233 m_dataRules.push_back(rule);
234 }
235 else
236 {
237 shared_ptr<InterestRule> rule(new InterestRule(ruleId));
238 for (size_t i = 0; i < filters.size(); i++)
239 rule->addFilter(filters[i]);
240 for (size_t i = 0; i < checkers.size(); i++)
241 rule->addChecker(checkers[i]);
242
243 m_interestRules.push_back(rule);
244 }
245}
246
247void
248ValidatorConfig::onConfigTrustAnchor(const security::conf::ConfigSection& configSection,
249 const std::string& filename)
250{
251 using namespace ndn::security::conf;
252 using namespace boost::filesystem;
253
254 ConfigSection::const_iterator propertyIt = configSection.begin();
255
256 // Get trust-anchor.type
257 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type"))
258 throw Error("Expect <trust-anchor.type>!");
259
260 std::string type = propertyIt->second.data();
261 propertyIt++;
262
263 if (boost::iequals(type, "file"))
264 {
265 // Get trust-anchor.file
266 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first,"file-name"))
267 throw Error("Expect <trust-anchor.file-name>!");
268
269 std::string file = propertyIt->second.data();
270 propertyIt++;
271
272 // Check other stuff
273 if (propertyIt != configSection.end())
274 throw Error("Expect the end of trust-anchor!");
275
276 path certfilePath = absolute(file, path(filename).parent_path());
277 shared_ptr<IdentityCertificate> idCert =
278 io::load<IdentityCertificate>(certfilePath.string());
279
280 if (static_cast<bool>(idCert))
281 {
282 BOOST_ASSERT(idCert->getName().size() >= 1);
Yingdi Yub4650652014-04-17 10:19:59 -0700283 m_staticContainer.add(idCert);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700284 m_anchors[idCert->getName().getPrefix(-1)] = idCert;
285 }
286 else
287 throw Error("Cannot read certificate from file: " +
288 certfilePath.native());
289
290 return;
291 }
292 else if (boost::iequals(type, "base64"))
293 {
294 // Get trust-anchor.base64-string
295 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "base64-string"))
296 throw Error("Expect <trust-anchor.base64-string>!");
297
298 std::stringstream ss(propertyIt->second.data());
299 propertyIt++;
300
301 // Check other stuff
302 if (propertyIt != configSection.end())
303 throw Error("Expect the end of trust-anchor!");
304
305 shared_ptr<IdentityCertificate> idCert = io::load<IdentityCertificate>(ss);
306
307 if (static_cast<bool>(idCert))
308 {
309 BOOST_ASSERT(idCert->getName().size() >= 1);
Yingdi Yub4650652014-04-17 10:19:59 -0700310 m_staticContainer.add(idCert);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700311 m_anchors[idCert->getName().getPrefix(-1)] = idCert;
312 }
313 else
314 throw Error("Cannot decode certificate from base64-string");
315
316 return;
317 }
Yingdi Yub4650652014-04-17 10:19:59 -0700318 else if (boost::iequals(type, "dir"))
319 {
320 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "dir"))
321 throw Error("Expect <trust-anchor.dir>!");
322
323 std::string dirString(propertyIt->second.data());
324 propertyIt++;
325
326 if (propertyIt != configSection.end())
327 {
328 if (boost::iequals(propertyIt->first, "refresh"))
329 {
330 using namespace boost::filesystem;
331
332 time::nanoseconds refresh = getRefreshPeriod(propertyIt->second.data());
333 propertyIt++;
334
335 if (propertyIt != configSection.end())
336 throw Error("Expect the end of trust-anchor!");
337
338 path dirPath = absolute(dirString, path(filename).parent_path());
339
340 m_dynamicContainers.push_back(DynamicTrustAnchorContainer(dirPath, true, refresh));
341
342 m_dynamicContainers.rbegin()->setLastRefresh(time::system_clock::now() - refresh);
343
344 return;
345 }
346 else
347 throw Error("Expect <trust-anchor.refresh>!");
348 }
349 else
350 {
351 using namespace boost::filesystem;
352
353 path dirPath = absolute(dirString, path(filename).parent_path());
354
355 directory_iterator end;
356
357 for (directory_iterator it(dirPath); it != end; it++)
358 {
359 shared_ptr<IdentityCertificate> idCert =
360 io::load<IdentityCertificate>(it->path().string());
361
362 if (static_cast<bool>(idCert))
363 m_staticContainer.add(idCert);
364 }
365
366 return;
367 }
368 }
Yingdi Yu44d190c2014-04-16 17:05:46 -0700369 else if (boost::iequals(type, "any"))
370 {
371 m_shouldValidate = false;
372 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700373 else
374 throw Error("Unsupported trust-anchor.type: " + type);
375}
376
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800377void
378ValidatorConfig::reset()
379{
380 if (static_cast<bool>(m_certificateCache))
381 m_certificateCache->reset();
382 m_interestRules.clear();
383 m_dataRules.clear();
384
385 m_anchors.clear();
386
387 m_staticContainer = TrustAnchorContainer();
388
389 m_dynamicContainers.clear();
390}
391
392bool
393ValidatorConfig::isEmpty()
394{
395 if ((!static_cast<bool>(m_certificateCache) || m_certificateCache->isEmpty()) &&
396 m_interestRules.empty() &&
397 m_dataRules.empty() &&
398 m_anchors.empty())
399 return true;
400 return false;
401}
402
Yingdi Yub4650652014-04-17 10:19:59 -0700403time::nanoseconds
404ValidatorConfig::getRefreshPeriod(std::string inputString)
405{
406 char unit = inputString[inputString.size() - 1];
407 std::string refreshString = inputString.substr(0, inputString.size() - 1);
408
409 uint32_t number;
410
411 try
412 {
413 number = boost::lexical_cast<uint32_t>(refreshString);
414 }
415 catch (boost::bad_lexical_cast&)
416 {
417 throw Error("Bad number: " + refreshString);
418 }
419
420 if (number == 0)
421 return getDefaultRefreshPeriod();
422
423 switch (unit)
424 {
425 case 'h':
426 return time::duration_cast<time::nanoseconds>(time::hours(number));
427 case 'm':
428 return time::duration_cast<time::nanoseconds>(time::minutes(number));
429 case 's':
430 return time::duration_cast<time::nanoseconds>(time::seconds(number));
431 default:
432 throw Error(std::string("Wrong time unit: ") + unit);
433 }
434}
435
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800436time::nanoseconds
437ValidatorConfig::getDefaultRefreshPeriod()
438{
439 return time::duration_cast<time::nanoseconds>(time::seconds(3600));
440}
441
Yingdi Yub4650652014-04-17 10:19:59 -0700442void
443ValidatorConfig::refreshAnchors()
444{
445 time::system_clock::TimePoint now = time::system_clock::now();
446
447 bool isRefreshed = false;
448
449 for (DynamicContainers::iterator cIt = m_dynamicContainers.begin();
450 cIt != m_dynamicContainers.end(); cIt++)
451 {
452 if (cIt->getLastRefresh() + cIt->getRefreshPeriod() < now)
453 {
454 isRefreshed = true;
455 cIt->refresh();
456 cIt->setLastRefresh(now);
457 }
458 else
459 break;
460 }
461
462 if (isRefreshed)
463 {
464 m_anchors.clear();
465
466 for (CertificateList::const_iterator it = m_staticContainer.getAll().begin();
467 it != m_staticContainer.getAll().end(); it++)
468 {
469 m_anchors[(*it)->getName().getPrefix(-1)] = (*it);
470 }
471
472 for (DynamicContainers::iterator cIt = m_dynamicContainers.begin();
473 cIt != m_dynamicContainers.end(); cIt++)
474 {
475 const CertificateList& certList = cIt->getAll();
476
477 for (CertificateList::const_iterator it = certList.begin();
478 it != certList.end(); it++)
479 {
480 m_anchors[(*it)->getName().getPrefix(-1)] = (*it);
481 }
482 }
483 m_dynamicContainers.sort(ValidatorConfig::compareDynamicContainer);
484 }
485}
486
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700487void
488ValidatorConfig::checkPolicy(const Data& data,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700489 int nSteps,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700490 const OnDataValidated& onValidated,
491 const OnDataValidationFailed& onValidationFailed,
492 std::vector<shared_ptr<ValidationRequest> >& nextSteps)
493{
Yingdi Yu44d190c2014-04-16 17:05:46 -0700494 if (!m_shouldValidate)
495 return onValidated(data.shared_from_this());
496
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700497 bool isMatched = false;
498 int8_t checkResult = -1;
499
500 for (DataRuleList::iterator it = m_dataRules.begin();
501 it != m_dataRules.end(); it++)
502 {
503 if ((*it)->match(data))
504 {
505 isMatched = true;
506 checkResult = (*it)->check(data, onValidated, onValidationFailed);
507 break;
508 }
509 }
510
511 if (!isMatched)
512 return onValidationFailed(data.shared_from_this(), "No rule matched!");
513
514 if (checkResult == 0)
515 {
516 const Signature& signature = data.getSignature();
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700517 checkSignature(data, signature, nSteps,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700518 onValidated, onValidationFailed, nextSteps);
519 }
520}
521
522void
523ValidatorConfig::checkPolicy(const Interest& interest,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700524 int nSteps,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700525 const OnInterestValidated& onValidated,
526 const OnInterestValidationFailed& onValidationFailed,
527 std::vector<shared_ptr<ValidationRequest> >& nextSteps)
528{
Yingdi Yu44d190c2014-04-16 17:05:46 -0700529 if (!m_shouldValidate)
530 return onValidated(interest.shared_from_this());
531
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700532 // If interestName has less than 4 name components,
533 // it is definitely not a signed interest.
534 if (interest.getName().size() < signed_interest::MIN_LENGTH)
535 return onValidationFailed(interest.shared_from_this(),
536 "Interest is not signed: " + interest.getName().toUri());
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700537
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700538 try
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700539 {
540 const Name& interestName = interest.getName();
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700541 Signature signature(interestName[signed_interest::POS_SIG_INFO].blockFromValue(),
542 interestName[signed_interest::POS_SIG_VALUE].blockFromValue());
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700543
Yingdi Yu4a557052014-07-09 16:40:37 -0700544 if (!signature.hasKeyLocator())
545 return onValidationFailed(interest.shared_from_this(),
546 "No valid KeyLocator");
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700547
Yingdi Yu4a557052014-07-09 16:40:37 -0700548 const KeyLocator& keyLocator = signature.getKeyLocator();
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700549
550 if (keyLocator.getType() != KeyLocator::KeyLocator_Name)
551 return onValidationFailed(interest.shared_from_this(),
552 "Key Locator is not a name");
553
554 Name keyName = IdentityCertificate::certificateNameToPublicKeyName(keyLocator.getName());
555
556 bool isMatched = false;
557 int8_t checkResult = -1;
558
559 for (InterestRuleList::iterator it = m_interestRules.begin();
560 it != m_interestRules.end(); it++)
561 {
562 if ((*it)->match(interest))
563 {
564 isMatched = true;
565 checkResult = (*it)->check(interest,
566 bind(&ValidatorConfig::checkTimestamp, this, _1,
567 keyName, onValidated, onValidationFailed),
568 onValidationFailed);
569 break;
570 }
571 }
572
573 if (!isMatched)
574 return onValidationFailed(interest.shared_from_this(), "No rule matched!");
575
576 if (checkResult == 0)
577 {
578 checkSignature<Interest, OnInterestValidated, OnInterestValidationFailed>
Yingdi Yu4a557052014-07-09 16:40:37 -0700579 (interest, signature, nSteps,
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700580 bind(&ValidatorConfig::checkTimestamp, this, _1,
581 keyName, onValidated, onValidationFailed),
582 onValidationFailed,
583 nextSteps);
584 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700585 }
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700586 catch (Signature::Error& e)
587 {
588 return onValidationFailed(interest.shared_from_this(),
589 "No valid signature");
590 }
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700591 catch (KeyLocator::Error& e)
592 {
593 return onValidationFailed(interest.shared_from_this(),
594 "No valid KeyLocator");
595 }
Junxiao Shic2b8d242014-11-04 08:35:29 -0700596 catch (tlv::Error& e)
597 {
598 return onValidationFailed(interest.shared_from_this(),
599 "Cannot decode signature");
600 }
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700601 catch (IdentityCertificate::Error& e)
602 {
603 return onValidationFailed(interest.shared_from_this(),
604 "Cannot determine the signing key");
605 }
606}
607
608void
609ValidatorConfig::checkTimestamp(const shared_ptr<const Interest>& interest,
610 const Name& keyName,
611 const OnInterestValidated& onValidated,
612 const OnInterestValidationFailed& onValidationFailed)
613{
614 const Name& interestName = interest->getName();
615 time::system_clock::TimePoint interestTime;
616
617 try
618 {
619 interestTime =
620 time::fromUnixTimestamp(
621 time::milliseconds(interestName.get(-signed_interest::MIN_LENGTH).toNumber()));
622 }
Steve DiBenedetto54ce6682014-07-22 13:22:57 -0600623 catch (tlv::Error& e)
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700624 {
625 return onValidationFailed(interest,
626 "Cannot decode signature related TLVs");
627 }
628
629 time::system_clock::TimePoint currentTime = time::system_clock::now();
630
631 LastTimestampMap::iterator timestampIt = m_lastTimestamp.find(keyName);
632 if (timestampIt == m_lastTimestamp.end())
633 {
634 if (!(currentTime - m_graceInterval <= interestTime &&
635 interestTime <= currentTime + m_graceInterval))
636 return onValidationFailed(interest,
637 "The command is not in grace interval: " +
638 interest->getName().toUri());
639 }
640 else
641 {
642 if (interestTime <= timestampIt->second)
643 return onValidationFailed(interest,
644 "The command is outdated: " +
645 interest->getName().toUri());
646 }
647
648 //Update timestamp
649 if (timestampIt == m_lastTimestamp.end())
650 {
651 cleanOldKeys();
652 m_lastTimestamp[keyName] = interestTime;
653 }
654 else
655 {
656 timestampIt->second = interestTime;
657 }
658
659 return onValidated(interest);
660}
661
662void
663ValidatorConfig::cleanOldKeys()
664{
665 if (m_lastTimestamp.size() < m_maxTrackedKeys)
666 return;
667
668 LastTimestampMap::iterator timestampIt = m_lastTimestamp.begin();
669 LastTimestampMap::iterator end = m_lastTimestamp.end();
670
671 time::system_clock::TimePoint now = time::system_clock::now();
672 LastTimestampMap::iterator oldestKeyIt = m_lastTimestamp.begin();
673 time::system_clock::TimePoint oldestTimestamp = oldestKeyIt->second;
674
675 while (timestampIt != end)
676 {
677 if (now - timestampIt->second > m_keyTimestampTtl)
678 {
679 LastTimestampMap::iterator toDelete = timestampIt;
680 timestampIt++;
681 m_lastTimestamp.erase(toDelete);
682 continue;
683 }
684
685 if (timestampIt->second < oldestTimestamp)
686 {
687 oldestTimestamp = timestampIt->second;
688 oldestKeyIt = timestampIt;
689 }
690
691 timestampIt++;
692 }
693
694 if (m_lastTimestamp.size() >= m_maxTrackedKeys)
695 m_lastTimestamp.erase(oldestKeyIt);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700696}
697
Yingdi Yub4650652014-04-17 10:19:59 -0700698void
699ValidatorConfig::DynamicTrustAnchorContainer::refresh()
700{
701 using namespace boost::filesystem;
702
703 m_certificates.clear();
704
705 if (m_isDir)
706 {
707 directory_iterator end;
708
709 for (directory_iterator it(m_path); it != end; it++)
710 {
711 shared_ptr<IdentityCertificate> idCert =
712 io::load<IdentityCertificate>(it->path().string());
713
714 if (static_cast<bool>(idCert))
715 m_certificates.push_back(idCert);
716 }
717 }
718 else
719 {
720 shared_ptr<IdentityCertificate> idCert =
721 io::load<IdentityCertificate>(m_path.string());
722
723 if (static_cast<bool>(idCert))
724 m_certificates.push_back(idCert);
725 }
726}
727
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800728template<class Packet, class OnValidated, class OnFailed>
729void
730ValidatorConfig::checkSignature(const Packet& packet,
731 const Signature& signature,
732 size_t nSteps,
733 const OnValidated& onValidated,
734 const OnFailed& onValidationFailed,
735 std::vector<shared_ptr<ValidationRequest> >& nextSteps)
736{
737 if (signature.getType() == tlv::DigestSha256)
738 {
739 DigestSha256 sigSha256(signature);
740
741 if (verifySignature(packet, sigSha256))
742 return onValidated(packet.shared_from_this());
743 else
744 return onValidationFailed(packet.shared_from_this(),
745 "Sha256 Signature cannot be verified!");
746 }
747
748 try {
749 switch (signature.getType()) {
750 case tlv::SignatureSha256WithRsa:
751 case tlv::SignatureSha256WithEcdsa:
752 {
753 if (!signature.hasKeyLocator()) {
754 return onValidationFailed(packet.shared_from_this(),
755 "Missing KeyLocator in SignatureInfo");
756 }
757 break;
758 }
759 default:
760 return onValidationFailed(packet.shared_from_this(),
761 "Unsupported signature type");
762 }
763 }
764 catch (KeyLocator::Error& e) {
765 return onValidationFailed(packet.shared_from_this(),
766 "Cannot decode KeyLocator in public key signature");
767 }
768 catch (tlv::Error& e) {
769 return onValidationFailed(packet.shared_from_this(),
770 "Cannot decode public key signature");
771 }
772
773
774 if (signature.getKeyLocator().getType() != KeyLocator::KeyLocator_Name) {
775 return onValidationFailed(packet.shared_from_this(), "Unsupported KeyLocator type");
776 }
777
778 const Name& keyLocatorName = signature.getKeyLocator().getName();
779
780 shared_ptr<const Certificate> trustedCert;
781
782 refreshAnchors();
783
784 AnchorList::const_iterator it = m_anchors.find(keyLocatorName);
785 if (m_anchors.end() == it && static_cast<bool>(m_certificateCache))
786 trustedCert = m_certificateCache->getCertificate(keyLocatorName);
787 else
788 trustedCert = it->second;
789
790 if (static_cast<bool>(trustedCert))
791 {
792 if (verifySignature(packet, signature, trustedCert->getPublicKeyInfo()))
793 return onValidated(packet.shared_from_this());
794 else
795 return onValidationFailed(packet.shared_from_this(),
796 "Cannot verify signature");
797 }
798 else
799 {
800 if (m_stepLimit == nSteps)
801 return onValidationFailed(packet.shared_from_this(),
802 "Maximum steps of validation reached");
803
804 OnDataValidated onCertValidated =
805 bind(&ValidatorConfig::onCertValidated<Packet, OnValidated, OnFailed>,
806 this, _1, packet.shared_from_this(), onValidated, onValidationFailed);
807
808 OnDataValidationFailed onCertValidationFailed =
809 bind(&ValidatorConfig::onCertFailed<Packet, OnFailed>,
810 this, _1, _2, packet.shared_from_this(), onValidationFailed);
811
812 Interest certInterest(keyLocatorName);
813
814 shared_ptr<ValidationRequest> nextStep =
815 make_shared<ValidationRequest>(certInterest,
816 onCertValidated,
817 onCertValidationFailed,
818 1, nSteps + 1);
819
820 nextSteps.push_back(nextStep);
821 return;
822 }
823
824 return onValidationFailed(packet.shared_from_this(), "Unsupported Signature Type");
825}
826
827template<class Packet, class OnValidated, class OnFailed>
828void
829ValidatorConfig::onCertValidated(const shared_ptr<const Data>& signCertificate,
830 const shared_ptr<const Packet>& packet,
831 const OnValidated& onValidated,
832 const OnFailed& onValidationFailed)
833{
834 shared_ptr<IdentityCertificate> certificate =
835 make_shared<IdentityCertificate>(*signCertificate);
836
837 if (!certificate->isTooLate() && !certificate->isTooEarly())
838 {
839 if (static_cast<bool>(m_certificateCache))
840 m_certificateCache->insertCertificate(certificate);
841
842 if (verifySignature(*packet, certificate->getPublicKeyInfo()))
843 return onValidated(packet);
844 else
845 return onValidationFailed(packet,
846 "Cannot verify signature: " +
847 packet->getName().toUri());
848 }
849 else
850 {
851 return onValidationFailed(packet,
852 "Signing certificate " +
853 signCertificate->getName().toUri() +
854 " is no longer valid.");
855 }
856}
857
858template<class Packet, class OnFailed>
859void
860ValidatorConfig::onCertFailed(const shared_ptr<const Data>& signCertificate,
861 const std::string& failureInfo,
862 const shared_ptr<const Packet>& packet,
863 const OnFailed& onValidationFailed)
864{
865 onValidationFailed(packet, failureInfo);
866}
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700867
868} // namespace ndn