| /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ |
| /** |
| * Copyright (c) 2013-2014 Regents of the University of California. |
| * |
| * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions). |
| * |
| * ndn-cxx library is free software: you can redistribute it and/or modify it under the |
| * terms of the GNU Lesser General Public License as published by the Free Software |
| * Foundation, either version 3 of the License, or (at your option) any later version. |
| * |
| * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY |
| * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A |
| * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. |
| * |
| * You should have received copies of the GNU General Public License and GNU Lesser |
| * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see |
| * <http://www.gnu.org/licenses/>. |
| * |
| * See AUTHORS.md for complete list of ndn-cxx authors and contributors. |
| * |
| * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/> |
| */ |
| |
| #include "validator-config.hpp" |
| #include "certificate-cache-ttl.hpp" |
| #include "../util/io.hpp" |
| |
| #include <boost/filesystem.hpp> |
| #include <boost/property_tree/info_parser.hpp> |
| #include <boost/algorithm/string.hpp> |
| |
| namespace ndn { |
| |
| const shared_ptr<CertificateCache> ValidatorConfig::DEFAULT_CERTIFICATE_CACHE; |
| const time::milliseconds ValidatorConfig::DEFAULT_GRACE_INTERVAL(3000); |
| const time::system_clock::Duration ValidatorConfig::DEFAULT_KEY_TIMESTAMP_TTL = time::hours(1); |
| |
| ValidatorConfig::ValidatorConfig(Face* face, |
| const shared_ptr<CertificateCache>& certificateCache, |
| const time::milliseconds& graceInterval, |
| const size_t stepLimit, |
| const size_t maxTrackedKeys, |
| const time::system_clock::Duration& keyTimestampTtl) |
| : Validator(face) |
| , m_shouldValidate(true) |
| , m_stepLimit(stepLimit) |
| , m_certificateCache(certificateCache) |
| , m_graceInterval(graceInterval < time::milliseconds::zero() ? |
| DEFAULT_GRACE_INTERVAL : graceInterval) |
| , m_maxTrackedKeys(maxTrackedKeys) |
| , m_keyTimestampTtl(keyTimestampTtl) |
| { |
| if (!static_cast<bool>(m_certificateCache) && face != nullptr) |
| m_certificateCache = make_shared<CertificateCacheTtl>(ref(face->getIoService())); |
| } |
| |
| ValidatorConfig::ValidatorConfig(Face& face, |
| const shared_ptr<CertificateCache>& certificateCache, |
| const time::milliseconds& graceInterval, |
| const size_t stepLimit, |
| const size_t maxTrackedKeys, |
| const time::system_clock::Duration& keyTimestampTtl) |
| : Validator(face) |
| , m_shouldValidate(true) |
| , m_stepLimit(stepLimit) |
| , m_certificateCache(certificateCache) |
| , m_graceInterval(graceInterval < time::milliseconds::zero() ? |
| DEFAULT_GRACE_INTERVAL : graceInterval) |
| , m_maxTrackedKeys(maxTrackedKeys) |
| , m_keyTimestampTtl(keyTimestampTtl) |
| { |
| if (!static_cast<bool>(m_certificateCache)) |
| m_certificateCache = make_shared<CertificateCacheTtl>(ref(face.getIoService())); |
| } |
| |
| void |
| ValidatorConfig::load(const std::string& filename) |
| { |
| std::ifstream inputFile; |
| inputFile.open(filename.c_str()); |
| if (!inputFile.good() || !inputFile.is_open()) |
| { |
| std::string msg = "Failed to read configuration file: "; |
| msg += filename; |
| throw security::conf::Error(msg); |
| } |
| load(inputFile, filename); |
| inputFile.close(); |
| } |
| |
| void |
| ValidatorConfig::load(const std::string& input, const std::string& filename) |
| { |
| std::istringstream inputStream(input); |
| load(inputStream, filename); |
| } |
| |
| |
| void |
| ValidatorConfig::load(std::istream& input, const std::string& filename) |
| { |
| security::conf::ConfigSection tree; |
| try |
| { |
| boost::property_tree::read_info(input, tree); |
| } |
| catch (boost::property_tree::info_parser_error& error) |
| { |
| std::stringstream msg; |
| msg << "Failed to parse configuration file"; |
| msg << " " << filename; |
| msg << " " << error.message() << " line " << error.line(); |
| throw security::conf::Error(msg.str()); |
| } |
| |
| load(tree, filename); |
| } |
| |
| void |
| ValidatorConfig::load(const security::conf::ConfigSection& configSection, |
| const std::string& filename) |
| { |
| BOOST_ASSERT(!filename.empty()); |
| |
| reset(); |
| |
| if (configSection.begin() == configSection.end()) |
| { |
| std::string msg = "Error processing configuration file"; |
| msg += ": "; |
| msg += filename; |
| msg += " no data"; |
| throw security::conf::Error(msg); |
| } |
| |
| for (security::conf::ConfigSection::const_iterator i = configSection.begin(); |
| i != configSection.end(); ++i) |
| { |
| const std::string& sectionName = i->first; |
| const security::conf::ConfigSection& section = i->second; |
| |
| if (boost::iequals(sectionName, "rule")) |
| { |
| onConfigRule(section, filename); |
| } |
| else if (boost::iequals(sectionName, "trust-anchor")) |
| { |
| onConfigTrustAnchor(section, filename); |
| } |
| else |
| { |
| std::string msg = "Error processing configuration file"; |
| msg += " "; |
| msg += filename; |
| msg += " unrecognized section: " + sectionName; |
| throw security::conf::Error(msg); |
| } |
| } |
| } |
| |
| void |
| ValidatorConfig::onConfigRule(const security::conf::ConfigSection& configSection, |
| const std::string& filename) |
| { |
| using namespace ndn::security::conf; |
| |
| ConfigSection::const_iterator propertyIt = configSection.begin(); |
| |
| // Get rule.id |
| if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "id")) |
| throw Error("Expect <rule.id>!"); |
| |
| std::string ruleId = propertyIt->second.data(); |
| propertyIt++; |
| |
| // Get rule.for |
| if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first,"for")) |
| throw Error("Expect <rule.for> in rule: " + ruleId + "!"); |
| |
| std::string usage = propertyIt->second.data(); |
| propertyIt++; |
| |
| bool isForData; |
| if (boost::iequals(usage, "data")) |
| isForData = true; |
| else if (boost::iequals(usage, "interest")) |
| isForData = false; |
| else |
| throw Error("Unrecognized <rule.for>: " + usage |
| + " in rule: " + ruleId); |
| |
| // Get rule.filter(s) |
| std::vector<shared_ptr<Filter> > filters; |
| for (; propertyIt != configSection.end(); propertyIt++) |
| { |
| if (!boost::iequals(propertyIt->first, "filter")) |
| { |
| if (boost::iequals(propertyIt->first, "checker")) |
| break; |
| throw Error("Expect <rule.filter> in rule: " + ruleId); |
| } |
| |
| filters.push_back(FilterFactory::create(propertyIt->second)); |
| continue; |
| } |
| |
| // Get rule.checker(s) |
| std::vector<shared_ptr<Checker> > checkers; |
| for (; propertyIt != configSection.end(); propertyIt++) |
| { |
| if (!boost::iequals(propertyIt->first, "checker")) |
| throw Error("Expect <rule.checker> in rule: " + ruleId); |
| |
| checkers.push_back(CheckerFactory::create(propertyIt->second, filename)); |
| continue; |
| } |
| |
| // Check other stuff |
| if (propertyIt != configSection.end()) |
| throw Error("Expect the end of rule: " + ruleId); |
| |
| if (checkers.size() == 0) |
| throw Error("No <rule.checker> is specified in rule: " + ruleId); |
| |
| if (isForData) |
| { |
| shared_ptr<DataRule> rule(new DataRule(ruleId)); |
| for (size_t i = 0; i < filters.size(); i++) |
| rule->addFilter(filters[i]); |
| for (size_t i = 0; i < checkers.size(); i++) |
| rule->addChecker(checkers[i]); |
| |
| m_dataRules.push_back(rule); |
| } |
| else |
| { |
| shared_ptr<InterestRule> rule(new InterestRule(ruleId)); |
| for (size_t i = 0; i < filters.size(); i++) |
| rule->addFilter(filters[i]); |
| for (size_t i = 0; i < checkers.size(); i++) |
| rule->addChecker(checkers[i]); |
| |
| m_interestRules.push_back(rule); |
| } |
| } |
| |
| void |
| ValidatorConfig::onConfigTrustAnchor(const security::conf::ConfigSection& configSection, |
| const std::string& filename) |
| { |
| using namespace ndn::security::conf; |
| using namespace boost::filesystem; |
| |
| ConfigSection::const_iterator propertyIt = configSection.begin(); |
| |
| // Get trust-anchor.type |
| if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type")) |
| throw Error("Expect <trust-anchor.type>!"); |
| |
| std::string type = propertyIt->second.data(); |
| propertyIt++; |
| |
| if (boost::iequals(type, "file")) |
| { |
| // Get trust-anchor.file |
| if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first,"file-name")) |
| throw Error("Expect <trust-anchor.file-name>!"); |
| |
| std::string file = propertyIt->second.data(); |
| propertyIt++; |
| |
| // Check other stuff |
| if (propertyIt != configSection.end()) |
| throw Error("Expect the end of trust-anchor!"); |
| |
| path certfilePath = absolute(file, path(filename).parent_path()); |
| shared_ptr<IdentityCertificate> idCert = |
| io::load<IdentityCertificate>(certfilePath.string()); |
| |
| if (static_cast<bool>(idCert)) |
| { |
| BOOST_ASSERT(idCert->getName().size() >= 1); |
| m_staticContainer.add(idCert); |
| m_anchors[idCert->getName().getPrefix(-1)] = idCert; |
| } |
| else |
| throw Error("Cannot read certificate from file: " + |
| certfilePath.native()); |
| |
| return; |
| } |
| else if (boost::iequals(type, "base64")) |
| { |
| // Get trust-anchor.base64-string |
| if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "base64-string")) |
| throw Error("Expect <trust-anchor.base64-string>!"); |
| |
| std::stringstream ss(propertyIt->second.data()); |
| propertyIt++; |
| |
| // Check other stuff |
| if (propertyIt != configSection.end()) |
| throw Error("Expect the end of trust-anchor!"); |
| |
| shared_ptr<IdentityCertificate> idCert = io::load<IdentityCertificate>(ss); |
| |
| if (static_cast<bool>(idCert)) |
| { |
| BOOST_ASSERT(idCert->getName().size() >= 1); |
| m_staticContainer.add(idCert); |
| m_anchors[idCert->getName().getPrefix(-1)] = idCert; |
| } |
| else |
| throw Error("Cannot decode certificate from base64-string"); |
| |
| return; |
| } |
| else if (boost::iequals(type, "dir")) |
| { |
| if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "dir")) |
| throw Error("Expect <trust-anchor.dir>!"); |
| |
| std::string dirString(propertyIt->second.data()); |
| propertyIt++; |
| |
| if (propertyIt != configSection.end()) |
| { |
| if (boost::iequals(propertyIt->first, "refresh")) |
| { |
| using namespace boost::filesystem; |
| |
| time::nanoseconds refresh = getRefreshPeriod(propertyIt->second.data()); |
| propertyIt++; |
| |
| if (propertyIt != configSection.end()) |
| throw Error("Expect the end of trust-anchor!"); |
| |
| path dirPath = absolute(dirString, path(filename).parent_path()); |
| |
| m_dynamicContainers.push_back(DynamicTrustAnchorContainer(dirPath, true, refresh)); |
| |
| m_dynamicContainers.rbegin()->setLastRefresh(time::system_clock::now() - refresh); |
| |
| return; |
| } |
| else |
| throw Error("Expect <trust-anchor.refresh>!"); |
| } |
| else |
| { |
| using namespace boost::filesystem; |
| |
| path dirPath = absolute(dirString, path(filename).parent_path()); |
| |
| directory_iterator end; |
| |
| for (directory_iterator it(dirPath); it != end; it++) |
| { |
| shared_ptr<IdentityCertificate> idCert = |
| io::load<IdentityCertificate>(it->path().string()); |
| |
| if (static_cast<bool>(idCert)) |
| m_staticContainer.add(idCert); |
| } |
| |
| return; |
| } |
| } |
| else if (boost::iequals(type, "any")) |
| { |
| m_shouldValidate = false; |
| } |
| else |
| throw Error("Unsupported trust-anchor.type: " + type); |
| } |
| |
| void |
| ValidatorConfig::reset() |
| { |
| if (static_cast<bool>(m_certificateCache)) |
| m_certificateCache->reset(); |
| m_interestRules.clear(); |
| m_dataRules.clear(); |
| |
| m_anchors.clear(); |
| |
| m_staticContainer = TrustAnchorContainer(); |
| |
| m_dynamicContainers.clear(); |
| } |
| |
| bool |
| ValidatorConfig::isEmpty() |
| { |
| if ((!static_cast<bool>(m_certificateCache) || m_certificateCache->isEmpty()) && |
| m_interestRules.empty() && |
| m_dataRules.empty() && |
| m_anchors.empty()) |
| return true; |
| return false; |
| } |
| |
| time::nanoseconds |
| ValidatorConfig::getRefreshPeriod(std::string inputString) |
| { |
| char unit = inputString[inputString.size() - 1]; |
| std::string refreshString = inputString.substr(0, inputString.size() - 1); |
| |
| uint32_t number; |
| |
| try |
| { |
| number = boost::lexical_cast<uint32_t>(refreshString); |
| } |
| catch (boost::bad_lexical_cast&) |
| { |
| throw Error("Bad number: " + refreshString); |
| } |
| |
| if (number == 0) |
| return getDefaultRefreshPeriod(); |
| |
| switch (unit) |
| { |
| case 'h': |
| return time::duration_cast<time::nanoseconds>(time::hours(number)); |
| case 'm': |
| return time::duration_cast<time::nanoseconds>(time::minutes(number)); |
| case 's': |
| return time::duration_cast<time::nanoseconds>(time::seconds(number)); |
| default: |
| throw Error(std::string("Wrong time unit: ") + unit); |
| } |
| } |
| |
| time::nanoseconds |
| ValidatorConfig::getDefaultRefreshPeriod() |
| { |
| return time::duration_cast<time::nanoseconds>(time::seconds(3600)); |
| } |
| |
| void |
| ValidatorConfig::refreshAnchors() |
| { |
| time::system_clock::TimePoint now = time::system_clock::now(); |
| |
| bool isRefreshed = false; |
| |
| for (DynamicContainers::iterator cIt = m_dynamicContainers.begin(); |
| cIt != m_dynamicContainers.end(); cIt++) |
| { |
| if (cIt->getLastRefresh() + cIt->getRefreshPeriod() < now) |
| { |
| isRefreshed = true; |
| cIt->refresh(); |
| cIt->setLastRefresh(now); |
| } |
| else |
| break; |
| } |
| |
| if (isRefreshed) |
| { |
| m_anchors.clear(); |
| |
| for (CertificateList::const_iterator it = m_staticContainer.getAll().begin(); |
| it != m_staticContainer.getAll().end(); it++) |
| { |
| m_anchors[(*it)->getName().getPrefix(-1)] = (*it); |
| } |
| |
| for (DynamicContainers::iterator cIt = m_dynamicContainers.begin(); |
| cIt != m_dynamicContainers.end(); cIt++) |
| { |
| const CertificateList& certList = cIt->getAll(); |
| |
| for (CertificateList::const_iterator it = certList.begin(); |
| it != certList.end(); it++) |
| { |
| m_anchors[(*it)->getName().getPrefix(-1)] = (*it); |
| } |
| } |
| m_dynamicContainers.sort(ValidatorConfig::compareDynamicContainer); |
| } |
| } |
| |
| void |
| ValidatorConfig::checkPolicy(const Data& data, |
| int nSteps, |
| const OnDataValidated& onValidated, |
| const OnDataValidationFailed& onValidationFailed, |
| std::vector<shared_ptr<ValidationRequest> >& nextSteps) |
| { |
| if (!m_shouldValidate) |
| return onValidated(data.shared_from_this()); |
| |
| bool isMatched = false; |
| int8_t checkResult = -1; |
| |
| for (DataRuleList::iterator it = m_dataRules.begin(); |
| it != m_dataRules.end(); it++) |
| { |
| if ((*it)->match(data)) |
| { |
| isMatched = true; |
| checkResult = (*it)->check(data, onValidated, onValidationFailed); |
| break; |
| } |
| } |
| |
| if (!isMatched) |
| return onValidationFailed(data.shared_from_this(), "No rule matched!"); |
| |
| if (checkResult == 0) |
| { |
| const Signature& signature = data.getSignature(); |
| checkSignature(data, signature, nSteps, |
| onValidated, onValidationFailed, nextSteps); |
| } |
| } |
| |
| void |
| ValidatorConfig::checkPolicy(const Interest& interest, |
| int nSteps, |
| const OnInterestValidated& onValidated, |
| const OnInterestValidationFailed& onValidationFailed, |
| std::vector<shared_ptr<ValidationRequest> >& nextSteps) |
| { |
| if (!m_shouldValidate) |
| return onValidated(interest.shared_from_this()); |
| |
| // If interestName has less than 4 name components, |
| // it is definitely not a signed interest. |
| if (interest.getName().size() < signed_interest::MIN_LENGTH) |
| return onValidationFailed(interest.shared_from_this(), |
| "Interest is not signed: " + interest.getName().toUri()); |
| |
| try |
| { |
| const Name& interestName = interest.getName(); |
| Signature signature(interestName[signed_interest::POS_SIG_INFO].blockFromValue(), |
| interestName[signed_interest::POS_SIG_VALUE].blockFromValue()); |
| |
| if (!signature.hasKeyLocator()) |
| return onValidationFailed(interest.shared_from_this(), |
| "No valid KeyLocator"); |
| |
| const KeyLocator& keyLocator = signature.getKeyLocator(); |
| |
| if (keyLocator.getType() != KeyLocator::KeyLocator_Name) |
| return onValidationFailed(interest.shared_from_this(), |
| "Key Locator is not a name"); |
| |
| Name keyName = IdentityCertificate::certificateNameToPublicKeyName(keyLocator.getName()); |
| |
| bool isMatched = false; |
| int8_t checkResult = -1; |
| |
| for (InterestRuleList::iterator it = m_interestRules.begin(); |
| it != m_interestRules.end(); it++) |
| { |
| if ((*it)->match(interest)) |
| { |
| isMatched = true; |
| checkResult = (*it)->check(interest, |
| bind(&ValidatorConfig::checkTimestamp, this, _1, |
| keyName, onValidated, onValidationFailed), |
| onValidationFailed); |
| break; |
| } |
| } |
| |
| if (!isMatched) |
| return onValidationFailed(interest.shared_from_this(), "No rule matched!"); |
| |
| if (checkResult == 0) |
| { |
| checkSignature<Interest, OnInterestValidated, OnInterestValidationFailed> |
| (interest, signature, nSteps, |
| bind(&ValidatorConfig::checkTimestamp, this, _1, |
| keyName, onValidated, onValidationFailed), |
| onValidationFailed, |
| nextSteps); |
| } |
| } |
| catch (Signature::Error& e) |
| { |
| return onValidationFailed(interest.shared_from_this(), |
| "No valid signature"); |
| } |
| catch (KeyLocator::Error& e) |
| { |
| return onValidationFailed(interest.shared_from_this(), |
| "No valid KeyLocator"); |
| } |
| catch (IdentityCertificate::Error& e) |
| { |
| return onValidationFailed(interest.shared_from_this(), |
| "Cannot determine the signing key"); |
| } |
| |
| catch (tlv::Error& e) |
| { |
| return onValidationFailed(interest.shared_from_this(), |
| "Cannot decode signature"); |
| } |
| } |
| |
| void |
| ValidatorConfig::checkTimestamp(const shared_ptr<const Interest>& interest, |
| const Name& keyName, |
| const OnInterestValidated& onValidated, |
| const OnInterestValidationFailed& onValidationFailed) |
| { |
| const Name& interestName = interest->getName(); |
| time::system_clock::TimePoint interestTime; |
| |
| try |
| { |
| interestTime = |
| time::fromUnixTimestamp( |
| time::milliseconds(interestName.get(-signed_interest::MIN_LENGTH).toNumber())); |
| } |
| catch (tlv::Error& e) |
| { |
| return onValidationFailed(interest, |
| "Cannot decode signature related TLVs"); |
| } |
| |
| time::system_clock::TimePoint currentTime = time::system_clock::now(); |
| |
| LastTimestampMap::iterator timestampIt = m_lastTimestamp.find(keyName); |
| if (timestampIt == m_lastTimestamp.end()) |
| { |
| if (!(currentTime - m_graceInterval <= interestTime && |
| interestTime <= currentTime + m_graceInterval)) |
| return onValidationFailed(interest, |
| "The command is not in grace interval: " + |
| interest->getName().toUri()); |
| } |
| else |
| { |
| if (interestTime <= timestampIt->second) |
| return onValidationFailed(interest, |
| "The command is outdated: " + |
| interest->getName().toUri()); |
| } |
| |
| //Update timestamp |
| if (timestampIt == m_lastTimestamp.end()) |
| { |
| cleanOldKeys(); |
| m_lastTimestamp[keyName] = interestTime; |
| } |
| else |
| { |
| timestampIt->second = interestTime; |
| } |
| |
| return onValidated(interest); |
| } |
| |
| void |
| ValidatorConfig::cleanOldKeys() |
| { |
| if (m_lastTimestamp.size() < m_maxTrackedKeys) |
| return; |
| |
| LastTimestampMap::iterator timestampIt = m_lastTimestamp.begin(); |
| LastTimestampMap::iterator end = m_lastTimestamp.end(); |
| |
| time::system_clock::TimePoint now = time::system_clock::now(); |
| LastTimestampMap::iterator oldestKeyIt = m_lastTimestamp.begin(); |
| time::system_clock::TimePoint oldestTimestamp = oldestKeyIt->second; |
| |
| while (timestampIt != end) |
| { |
| if (now - timestampIt->second > m_keyTimestampTtl) |
| { |
| LastTimestampMap::iterator toDelete = timestampIt; |
| timestampIt++; |
| m_lastTimestamp.erase(toDelete); |
| continue; |
| } |
| |
| if (timestampIt->second < oldestTimestamp) |
| { |
| oldestTimestamp = timestampIt->second; |
| oldestKeyIt = timestampIt; |
| } |
| |
| timestampIt++; |
| } |
| |
| if (m_lastTimestamp.size() >= m_maxTrackedKeys) |
| m_lastTimestamp.erase(oldestKeyIt); |
| } |
| |
| void |
| ValidatorConfig::DynamicTrustAnchorContainer::refresh() |
| { |
| using namespace boost::filesystem; |
| |
| m_certificates.clear(); |
| |
| if (m_isDir) |
| { |
| directory_iterator end; |
| |
| for (directory_iterator it(m_path); it != end; it++) |
| { |
| shared_ptr<IdentityCertificate> idCert = |
| io::load<IdentityCertificate>(it->path().string()); |
| |
| if (static_cast<bool>(idCert)) |
| m_certificates.push_back(idCert); |
| } |
| } |
| else |
| { |
| shared_ptr<IdentityCertificate> idCert = |
| io::load<IdentityCertificate>(m_path.string()); |
| |
| if (static_cast<bool>(idCert)) |
| m_certificates.push_back(idCert); |
| } |
| } |
| |
| template<class Packet, class OnValidated, class OnFailed> |
| void |
| ValidatorConfig::checkSignature(const Packet& packet, |
| const Signature& signature, |
| size_t nSteps, |
| const OnValidated& onValidated, |
| const OnFailed& onValidationFailed, |
| std::vector<shared_ptr<ValidationRequest> >& nextSteps) |
| { |
| if (signature.getType() == tlv::DigestSha256) |
| { |
| DigestSha256 sigSha256(signature); |
| |
| if (verifySignature(packet, sigSha256)) |
| return onValidated(packet.shared_from_this()); |
| else |
| return onValidationFailed(packet.shared_from_this(), |
| "Sha256 Signature cannot be verified!"); |
| } |
| |
| try { |
| switch (signature.getType()) { |
| case tlv::SignatureSha256WithRsa: |
| case tlv::SignatureSha256WithEcdsa: |
| { |
| if (!signature.hasKeyLocator()) { |
| return onValidationFailed(packet.shared_from_this(), |
| "Missing KeyLocator in SignatureInfo"); |
| } |
| break; |
| } |
| default: |
| return onValidationFailed(packet.shared_from_this(), |
| "Unsupported signature type"); |
| } |
| } |
| catch (KeyLocator::Error& e) { |
| return onValidationFailed(packet.shared_from_this(), |
| "Cannot decode KeyLocator in public key signature"); |
| } |
| catch (tlv::Error& e) { |
| return onValidationFailed(packet.shared_from_this(), |
| "Cannot decode public key signature"); |
| } |
| |
| |
| if (signature.getKeyLocator().getType() != KeyLocator::KeyLocator_Name) { |
| return onValidationFailed(packet.shared_from_this(), "Unsupported KeyLocator type"); |
| } |
| |
| const Name& keyLocatorName = signature.getKeyLocator().getName(); |
| |
| shared_ptr<const Certificate> trustedCert; |
| |
| refreshAnchors(); |
| |
| AnchorList::const_iterator it = m_anchors.find(keyLocatorName); |
| if (m_anchors.end() == it && static_cast<bool>(m_certificateCache)) |
| trustedCert = m_certificateCache->getCertificate(keyLocatorName); |
| else |
| trustedCert = it->second; |
| |
| if (static_cast<bool>(trustedCert)) |
| { |
| if (verifySignature(packet, signature, trustedCert->getPublicKeyInfo())) |
| return onValidated(packet.shared_from_this()); |
| else |
| return onValidationFailed(packet.shared_from_this(), |
| "Cannot verify signature"); |
| } |
| else |
| { |
| if (m_stepLimit == nSteps) |
| return onValidationFailed(packet.shared_from_this(), |
| "Maximum steps of validation reached"); |
| |
| OnDataValidated onCertValidated = |
| bind(&ValidatorConfig::onCertValidated<Packet, OnValidated, OnFailed>, |
| this, _1, packet.shared_from_this(), onValidated, onValidationFailed); |
| |
| OnDataValidationFailed onCertValidationFailed = |
| bind(&ValidatorConfig::onCertFailed<Packet, OnFailed>, |
| this, _1, _2, packet.shared_from_this(), onValidationFailed); |
| |
| Interest certInterest(keyLocatorName); |
| |
| shared_ptr<ValidationRequest> nextStep = |
| make_shared<ValidationRequest>(certInterest, |
| onCertValidated, |
| onCertValidationFailed, |
| 1, nSteps + 1); |
| |
| nextSteps.push_back(nextStep); |
| return; |
| } |
| |
| return onValidationFailed(packet.shared_from_this(), "Unsupported Signature Type"); |
| } |
| |
| template<class Packet, class OnValidated, class OnFailed> |
| void |
| ValidatorConfig::onCertValidated(const shared_ptr<const Data>& signCertificate, |
| const shared_ptr<const Packet>& packet, |
| const OnValidated& onValidated, |
| const OnFailed& onValidationFailed) |
| { |
| if (signCertificate->getContentType() != tlv::ContentType_Key) |
| return onValidationFailed(packet, |
| "Cannot retrieve signer's cert: " + |
| signCertificate->getName().toUri()); |
| |
| shared_ptr<IdentityCertificate> certificate; |
| try { |
| certificate = make_shared<IdentityCertificate>(*signCertificate); |
| } |
| catch (tlv::Error&) { |
| return onValidationFailed(packet, |
| "Cannot decode signer's cert: " + |
| signCertificate->getName().toUri()); |
| } |
| |
| if (!certificate->isTooLate() && !certificate->isTooEarly()) |
| { |
| if (static_cast<bool>(m_certificateCache)) |
| m_certificateCache->insertCertificate(certificate); |
| |
| if (verifySignature(*packet, certificate->getPublicKeyInfo())) |
| return onValidated(packet); |
| else |
| return onValidationFailed(packet, |
| "Cannot verify signature: " + |
| packet->getName().toUri()); |
| } |
| else |
| { |
| return onValidationFailed(packet, |
| "Signing certificate " + |
| signCertificate->getName().toUri() + |
| " is no longer valid."); |
| } |
| } |
| |
| template<class Packet, class OnFailed> |
| void |
| ValidatorConfig::onCertFailed(const shared_ptr<const Data>& signCertificate, |
| const std::string& failureInfo, |
| const shared_ptr<const Packet>& packet, |
| const OnFailed& onValidationFailed) |
| { |
| onValidationFailed(packet, failureInfo); |
| } |
| |
| } // namespace ndn |