| /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ |
| /** |
| * Copyright (c) 2013-2017 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. |
| */ |
| |
| #include "validator-config.hpp" |
| #include "certificate-cache-ttl.hpp" |
| #include "../util/io.hpp" |
| #include "../lp/tags.hpp" |
| |
| #include <boost/filesystem.hpp> |
| #include <boost/property_tree/info_parser.hpp> |
| #include <boost/algorithm/string.hpp> |
| |
| namespace ndn { |
| namespace security { |
| |
| 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 (m_certificateCache == nullptr && 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 (m_certificateCache == nullptr) |
| 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; |
| BOOST_THROW_EXCEPTION(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 (const boost::property_tree::info_parser_error& error) { |
| std::stringstream msg; |
| msg << "Failed to parse configuration file"; |
| msg << " " << filename; |
| msg << " " << error.message() << " line " << error.line(); |
| BOOST_THROW_EXCEPTION(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"; |
| BOOST_THROW_EXCEPTION(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; |
| BOOST_THROW_EXCEPTION(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")) |
| BOOST_THROW_EXCEPTION(Error("Expect <rule.id>!")); |
| |
| std::string ruleId = propertyIt->second.data(); |
| propertyIt++; |
| |
| // Get rule.for |
| if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first,"for")) |
| BOOST_THROW_EXCEPTION(Error("Expect <rule.for> in rule: " + ruleId + "!")); |
| |
| std::string usage = propertyIt->second.data(); |
| propertyIt++; |
| |
| bool isForData = false; |
| if (boost::iequals(usage, "data")) |
| isForData = true; |
| else if (boost::iequals(usage, "interest")) |
| isForData = false; |
| else |
| BOOST_THROW_EXCEPTION(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; |
| BOOST_THROW_EXCEPTION(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")) |
| BOOST_THROW_EXCEPTION(Error("Expect <rule.checker> in rule: " + ruleId)); |
| |
| checkers.push_back(CheckerFactory::create(propertyIt->second, filename)); |
| continue; |
| } |
| |
| // Check other stuff |
| if (propertyIt != configSection.end()) |
| BOOST_THROW_EXCEPTION(Error("Expect the end of rule: " + ruleId)); |
| |
| if (checkers.empty()) |
| BOOST_THROW_EXCEPTION(Error("No <rule.checker> is specified in rule: " + ruleId)); |
| |
| if (isForData) { |
| shared_ptr<DataRule> rule = make_shared<DataRule>(ruleId); |
| for (const auto& filter : filters) |
| rule->addFilter(filter); |
| for (const auto& checker : checkers) |
| rule->addChecker(checker); |
| |
| m_dataRules.push_back(rule); |
| } |
| else { |
| shared_ptr<InterestRule> rule = make_shared<InterestRule>(ruleId);; |
| for (const auto& filter : filters) |
| rule->addFilter(filter); |
| for (const auto& checker : checkers) |
| rule->addChecker(checker); |
| |
| 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")) |
| BOOST_THROW_EXCEPTION(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")) |
| BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.file-name>!")); |
| |
| std::string file = propertyIt->second.data(); |
| propertyIt++; |
| |
| // Check other stuff |
| if (propertyIt != configSection.end()) |
| BOOST_THROW_EXCEPTION(Error("Expect the end of trust-anchor!")); |
| |
| path certfilePath = absolute(file, path(filename).parent_path()); |
| auto idCert = io::load<v1::IdentityCertificate>(certfilePath.string()); |
| |
| if (idCert != nullptr) { |
| BOOST_ASSERT(idCert->getName().size() >= 1); |
| m_staticContainer.add(idCert); |
| m_anchors[idCert->getName().getPrefix(-1)] = idCert; |
| } |
| else |
| BOOST_THROW_EXCEPTION(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")) |
| BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.base64-string>!")); |
| |
| std::stringstream ss(propertyIt->second.data()); |
| propertyIt++; |
| |
| // Check other stuff |
| if (propertyIt != configSection.end()) |
| BOOST_THROW_EXCEPTION(Error("Expect the end of trust-anchor!")); |
| |
| auto idCert = io::load<v1::IdentityCertificate>(ss); |
| |
| if (idCert != nullptr) { |
| BOOST_ASSERT(idCert->getName().size() >= 1); |
| m_staticContainer.add(idCert); |
| m_anchors[idCert->getName().getPrefix(-1)] = idCert; |
| } |
| else |
| BOOST_THROW_EXCEPTION(Error("Cannot decode certificate from base64-string")); |
| |
| return; |
| } |
| else if (boost::iequals(type, "dir")) { |
| if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "dir")) |
| BOOST_THROW_EXCEPTION(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()) |
| BOOST_THROW_EXCEPTION(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 |
| BOOST_THROW_EXCEPTION(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++) { |
| auto idCert = io::load<v1::IdentityCertificate>(it->path().string()); |
| |
| if (idCert != nullptr) |
| m_staticContainer.add(idCert); |
| } |
| |
| return; |
| } |
| } |
| else if (boost::iequals(type, "any")) { |
| m_shouldValidate = false; |
| } |
| else |
| BOOST_THROW_EXCEPTION(Error("Unsupported trust-anchor.type: " + type)); |
| } |
| |
| void |
| ValidatorConfig::reset() |
| { |
| if (m_certificateCache != nullptr) |
| m_certificateCache->reset(); |
| m_interestRules.clear(); |
| m_dataRules.clear(); |
| |
| m_anchors.clear(); |
| |
| m_staticContainer = TrustAnchorContainer(); |
| |
| m_dynamicContainers.clear(); |
| } |
| |
| bool |
| ValidatorConfig::isEmpty() |
| { |
| return ((m_certificateCache == nullptr || m_certificateCache->isEmpty()) && |
| m_interestRules.empty() && m_dataRules.empty() && m_anchors.empty()); |
| } |
| |
| time::nanoseconds |
| ValidatorConfig::getRefreshPeriod(std::string inputString) |
| { |
| char unit = inputString[inputString.size() - 1]; |
| std::string refreshString = inputString.substr(0, inputString.size() - 1); |
| |
| uint32_t refreshPeriod = 0; |
| |
| try { |
| refreshPeriod = boost::lexical_cast<uint32_t>(refreshString); |
| } |
| catch (const boost::bad_lexical_cast&) { |
| BOOST_THROW_EXCEPTION(Error("Bad number: " + refreshString)); |
| } |
| |
| if (refreshPeriod == 0) |
| return getDefaultRefreshPeriod(); |
| |
| switch (unit) { |
| case 'h': |
| return time::duration_cast<time::nanoseconds>(time::hours(refreshPeriod)); |
| case 'm': |
| return time::duration_cast<time::nanoseconds>(time::minutes(refreshPeriod)); |
| case 's': |
| return time::duration_cast<time::nanoseconds>(time::seconds(refreshPeriod)); |
| default: |
| BOOST_THROW_EXCEPTION(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 (auto cIt = m_dynamicContainers.begin(); |
| cIt != m_dynamicContainers.end() && cIt->getLastRefresh() + cIt->getRefreshPeriod() < now; |
| cIt++) { |
| isRefreshed = true; |
| cIt->refresh(); |
| cIt->setLastRefresh(now); |
| } |
| |
| if (isRefreshed) { |
| m_anchors.clear(); |
| |
| for (const auto& cert : m_staticContainer.getAll()) { |
| m_anchors[cert->getName().getPrefix(-1)] = cert; |
| } |
| |
| for (const auto& container : m_dynamicContainers) { |
| const CertificateList& certList = container.getAll(); |
| |
| for (const auto& cert :certList) { |
| m_anchors[cert->getName().getPrefix(-1)] = cert; |
| } |
| } |
| 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 (const auto& dataRule : m_dataRules) { |
| if (dataRule->match(data)) { |
| isMatched = true; |
| checkResult = dataRule->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 = v1::IdentityCertificate::certificateNameToPublicKeyName(keyLocator.getName()); |
| |
| bool isMatched = false; |
| int8_t checkResult = -1; |
| |
| for (const auto& interestRule : m_interestRules) { |
| if (interestRule->match(interest)) { |
| isMatched = true; |
| checkResult = interestRule->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 (const Signature::Error& e) { |
| return onValidationFailed(interest.shared_from_this(), "No valid signature"); |
| } |
| catch (const KeyLocator::Error& e){ |
| return onValidationFailed(interest.shared_from_this(), "No valid KeyLocator"); |
| } |
| catch (const v1::IdentityCertificate::Error& e){ |
| return onValidationFailed(interest.shared_from_this(), "Cannot determine the signing key"); |
| } |
| catch (const 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 (const 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++) { |
| auto idCert = io::load<v1::IdentityCertificate>(it->path().string()); |
| |
| if (idCert != nullptr) |
| m_certificates.push_back(idCert); |
| } |
| } |
| else { |
| auto idCert = io::load<v1::IdentityCertificate>(m_path.string()); |
| |
| if (idCert != nullptr) |
| 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 (const KeyLocator::Error& e) { |
| return onValidationFailed(packet.shared_from_this(), |
| "Cannot decode KeyLocator in public key signature"); |
| } |
| catch (const 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 v1::Certificate> trustedCert; |
| |
| refreshAnchors(); |
| |
| AnchorList::const_iterator it = m_anchors.find(keyLocatorName); |
| if (m_anchors.end() == it && m_certificateCache != nullptr) |
| trustedCert = m_certificateCache->getCertificate(keyLocatorName); |
| else if (m_anchors.end() != it) |
| trustedCert = it->second; |
| |
| if (trustedCert != nullptr) { |
| 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); |
| |
| uint64_t incomingFaceId = 0; |
| auto incomingFaceIdTag = packet.template getTag<lp::IncomingFaceIdTag>(); |
| if (incomingFaceIdTag != nullptr) { |
| incomingFaceId = incomingFaceIdTag->get(); |
| } |
| auto nextStep = make_shared<ValidationRequest>(certInterest, |
| onCertValidated, |
| onCertValidationFailed, |
| 1, nSteps + 1, |
| incomingFaceId); |
| |
| 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<v1::IdentityCertificate> certificate; |
| try { |
| certificate = make_shared<v1::IdentityCertificate>(*signCertificate); |
| } |
| catch (const tlv::Error&) { |
| return onValidationFailed(packet, |
| "Cannot decode signer's cert: " + |
| signCertificate->getName().toUri()); |
| } |
| |
| if (!certificate->isTooLate() && !certificate->isTooEarly()) { |
| if (m_certificateCache != nullptr) |
| 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 security |
| } // namespace ndn |