security: Convert ValidatorConfig to ValidationPolicyConfig
The security API also provides a convenience ValidatorConfig helper.
Change-Id: Ic86dec4904b917361cb4740204de4b6710d2a386
Refs: #3920
diff --git a/src/security/v2/trust-anchor-group.cpp b/src/security/v2/trust-anchor-group.cpp
index 24f5b9e..6d702ab 100644
--- a/src/security/v2/trust-anchor-group.cpp
+++ b/src/security/v2/trust-anchor-group.cpp
@@ -1,5 +1,5 @@
/* -*- 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).
@@ -96,6 +96,8 @@
BOOST_THROW_EXCEPTION(std::runtime_error("Refresh period for the dynamic group must be positive"));
}
+ NDN_LOG_TRACE("Create dynamic trust anchor group " << id << " for file/dir " << path
+ << " with refresh time " << refreshPeriod);
refresh();
}
diff --git a/src/security/v2/validation-policy-config.cpp b/src/security/v2/validation-policy-config.cpp
new file mode 100644
index 0000000..ba87a23
--- /dev/null
+++ b/src/security/v2/validation-policy-config.cpp
@@ -0,0 +1,314 @@
+/* -*- 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 "validation-policy-config.hpp"
+#include "validator.hpp"
+#include "../../util/io.hpp"
+
+#include <boost/algorithm/string.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/property_tree/info_parser.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+
+ValidationPolicyConfig::ValidationPolicyConfig()
+ : m_shouldBypass(false)
+ , m_isConfigured(false)
+{
+}
+
+void
+ValidationPolicyConfig::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(Error(msg));
+ }
+ load(inputFile, filename);
+ inputFile.close();
+}
+
+void
+ValidationPolicyConfig::load(const std::string& input, const std::string& filename)
+{
+ std::istringstream inputStream(input);
+ load(inputStream, filename);
+}
+
+void
+ValidationPolicyConfig::load(std::istream& input, const std::string& filename)
+{
+ 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(Error(msg.str()));
+ }
+
+ load(tree, filename);
+}
+
+void
+ValidationPolicyConfig::load(const ConfigSection& configSection,
+ const std::string& filename)
+{
+ if (m_isConfigured) {
+ BOOST_THROW_EXCEPTION(std::logic_error("ValidationPolicyConfig can be configured only once"));
+ }
+ m_isConfigured = true;
+
+ BOOST_ASSERT(!filename.empty());
+
+ if (configSection.begin() == configSection.end()) {
+ std::string msg = "Error processing configuration file";
+ msg += ": ";
+ msg += filename;
+ msg += " no data";
+ BOOST_THROW_EXCEPTION(Error(msg));
+ }
+
+ for (const auto& subSection : configSection) {
+ const std::string& sectionName = subSection.first;
+ const ConfigSection& section = subSection.second;
+
+ if (boost::iequals(sectionName, "rule")) {
+ auto rule = Rule::create(section, filename);
+ if (rule->getPktType() == tlv::Data) {
+ m_dataRules.push_back(std::move(rule));
+ }
+ else if (rule->getPktType() == tlv::Interest) {
+ m_interestRules.push_back(std::move(rule));
+ }
+ }
+ else if (boost::iequals(sectionName, "trust-anchor")) {
+ processConfigTrustAnchor(section, filename);
+ }
+ else {
+ std::string msg = "Error processing configuration file";
+ msg += " ";
+ msg += filename;
+ msg += " unrecognized section: " + sectionName;
+ BOOST_THROW_EXCEPTION(Error(msg));
+ }
+ }
+}
+
+void
+ValidationPolicyConfig::processConfigTrustAnchor(const ConfigSection& configSection, const std::string& filename)
+{
+ 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("Expecting <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("Expecting <trust-anchor.file-name>"));
+ }
+
+ std::string file = propertyIt->second.data();
+ propertyIt++;
+
+ time::nanoseconds refresh = getRefreshPeriod(propertyIt, configSection.end());
+ if (propertyIt != configSection.end()) {
+ BOOST_THROW_EXCEPTION(Error("Expect the end of trust-anchor!"));
+ }
+
+ m_validator->loadAnchor(filename, absolute(file, path(filename).parent_path()).string(),
+ refresh, false);
+ 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("Expecting <trust-anchor.base64-string>"));
+
+ std::stringstream ss(propertyIt->second.data());
+ propertyIt++;
+
+ // Check other stuff
+ if (propertyIt != configSection.end())
+ BOOST_THROW_EXCEPTION(Error("Expecting the end of trust-anchor"));
+
+ auto idCert = io::load<Certificate>(ss);
+ if (idCert != nullptr) {
+ m_validator->loadAnchor("", std::move(*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++;
+
+ time::nanoseconds refresh = getRefreshPeriod(propertyIt, configSection.end());
+ if (propertyIt != configSection.end()) {
+ BOOST_THROW_EXCEPTION(Error("Expecting the end of trust-anchor"));
+ }
+
+ path dirPath = absolute(dirString, path(filename).parent_path());
+ m_validator->loadAnchor(dirString, dirPath.string(), refresh, true);
+ return;
+ }
+ else if (boost::iequals(type, "any")) {
+ m_shouldBypass = true;
+ }
+ else {
+ BOOST_THROW_EXCEPTION(Error("Unsupported trust-anchor.type: " + type));
+ }
+}
+
+time::nanoseconds
+ValidationPolicyConfig::getRefreshPeriod(ConfigSection::const_iterator& it,
+ const ConfigSection::const_iterator& end)
+{
+ time::nanoseconds refresh = time::nanoseconds::max();
+ if (it == end) {
+ return refresh;
+ }
+
+ if (!boost::iequals(it->first, "refresh")) {
+ BOOST_THROW_EXCEPTION(Error("Expecting <trust-anchor.refresh>"));
+ }
+
+ std::string inputString = it->second.data();
+ ++it;
+
+ 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
+ValidationPolicyConfig::getDefaultRefreshPeriod()
+{
+ return time::duration_cast<time::nanoseconds>(time::seconds(3600));
+}
+
+void
+ValidationPolicyConfig::checkPolicy(const Data& data, const shared_ptr<ValidationState>& state,
+ const ValidationContinuation& continueValidation)
+{
+ BOOST_ASSERT_MSG(!hasInnerPolicy(), "ValidationPolicyConfig must be a terminal inner policy");
+
+ if (m_shouldBypass) {
+ return continueValidation(nullptr, state);
+ }
+
+ Name klName = getKeyLocatorName(data, *state);
+ if (!state->getOutcome()) { // already failed
+ return;
+ }
+
+ for (const auto& rule : m_dataRules) {
+ if (rule->match(tlv::Data, data.getName())) {
+ if (rule->check(tlv::Data, data.getName(), klName, state)) {
+ return continueValidation(make_shared<CertificateRequest>(Interest(klName)), state);
+ }
+ // rule->check calls state->fail(...) if the check fails
+ return;
+ }
+ }
+
+ return state->fail({ValidationError::POLICY_ERROR, "No rule matched for data `" + data.getName().toUri() + "`"});
+}
+
+void
+ValidationPolicyConfig::checkPolicy(const Interest& interest, const shared_ptr<ValidationState>& state,
+ const ValidationContinuation& continueValidation)
+{
+ BOOST_ASSERT_MSG(!hasInnerPolicy(), "ValidationPolicyConfig must be a terminal inner policy");
+
+ if (m_shouldBypass) {
+ return continueValidation(nullptr, state);
+ }
+
+ Name klName = getKeyLocatorName(interest, *state);
+ if (!state->getOutcome()) { // already failed
+ return;
+ }
+
+ for (const auto& rule : m_interestRules) {
+ if (rule->match(tlv::Interest, interest.getName())) {
+ if (rule->check(tlv::Interest, interest.getName(), klName, state)) {
+ return continueValidation(make_shared<CertificateRequest>(Interest(klName)), state);
+ }
+ // rule->check calls state->fail(...) if the check fails
+ return;
+ }
+ }
+
+ return state->fail({ValidationError::POLICY_ERROR, "No rule matched for interest `" + interest.getName().toUri() + "`"});
+}
+
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/src/security/v2/validation-policy-config.hpp b/src/security/v2/validation-policy-config.hpp
new file mode 100644
index 0000000..7690cc1
--- /dev/null
+++ b/src/security/v2/validation-policy-config.hpp
@@ -0,0 +1,99 @@
+/* -*- 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.
+ */
+
+#ifndef NDN_SECURITY_V2_VALIDATION_POLICY_CONFIG_HPP
+#define NDN_SECURITY_V2_VALIDATION_POLICY_CONFIG_HPP
+
+#include "validation-policy.hpp"
+#include "validator-config/rule.hpp"
+#include "validator-config/common.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+
+/**
+ * @brief The validator which can be set up via a configuration file.
+ *
+ * @note For command Interest validation, this policy must be combined with
+ * @p ValidationPolicyCommandInterest, in order to guard against replay attacks.
+ * @note This policy does not support inner policies (a sole policy or a terminal inner policy)
+ * @sa https://named-data.net/doc/ndn-cxx/current/tutorials/security-validator-config.html
+ */
+class ValidationPolicyConfig : public ValidationPolicy
+{
+public:
+ ValidationPolicyConfig();
+
+ void
+ load(const std::string& filename);
+
+ void
+ load(const std::string& input, const std::string& filename);
+
+ void
+ load(std::istream& input, const std::string& filename);
+
+ void
+ load(const ConfigSection& configSection, const std::string& filename);
+
+protected:
+ void
+ checkPolicy(const Data& data, const shared_ptr<ValidationState>& state,
+ const ValidationContinuation& continueValidation) override;
+
+ void
+ checkPolicy(const Interest& interest, const shared_ptr<ValidationState>& state,
+ const ValidationContinuation& continueValidation) override;
+
+private:
+ void
+ processConfigTrustAnchor(const ConfigSection& section, const std::string& filename);
+
+ time::nanoseconds
+ getRefreshPeriod(ConfigSection::const_iterator& it, const ConfigSection::const_iterator& end);
+
+ time::nanoseconds
+ getDefaultRefreshPeriod();
+
+NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+ /** @brief whether to always bypass validation
+ *
+ * This is set to true when 'any' is specified as a trust anchor.
+ * It causes all packets to bypass validation.
+ */
+ bool m_shouldBypass;
+ bool m_isConfigured;
+
+ std::vector<unique_ptr<Rule>> m_dataRules;
+ std::vector<unique_ptr<Rule>> m_interestRules;
+};
+
+} // namespace validator_config
+
+using validator_config::ValidationPolicyConfig;
+
+} // namespace v2
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_SECURITY_V2_VALIDATION_POLICY_CONFIG_HPP
diff --git a/src/security/v2/validation-policy.hpp b/src/security/v2/validation-policy.hpp
index 351ffed..d7a261b 100644
--- a/src/security/v2/validation-policy.hpp
+++ b/src/security/v2/validation-policy.hpp
@@ -1,5 +1,5 @@
/* -*- 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).
@@ -65,6 +65,20 @@
void
setInnerPolicy(unique_ptr<ValidationPolicy> innerPolicy);
+ /**
+ * @brief Check if inner policy is set
+ */
+ bool
+ hasInnerPolicy() const
+ {
+ return m_innerPolicy != nullptr;
+ }
+
+ /**
+ * @brief Return the inner policy
+ *
+ * If the inner policy was not set, behavior is undefined.
+ */
ValidationPolicy&
getInnerPolicy();
diff --git a/src/security/v2/validator-config/checker.cpp b/src/security/v2/validator-config/checker.cpp
new file mode 100644
index 0000000..47415af
--- /dev/null
+++ b/src/security/v2/validator-config/checker.cpp
@@ -0,0 +1,334 @@
+/* -*- 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 "checker.hpp"
+#include "security/v2/validation-state.hpp"
+#include "security/verification-helpers.hpp"
+#include "security/pib/key.hpp"
+#include "util/logger.hpp"
+
+#include <boost/algorithm/string.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+
+bool
+Checker::check(uint32_t pktType, const Name& pktName, const Name& klName, const shared_ptr<ValidationState>& state)
+{
+ BOOST_ASSERT(pktType == tlv::Interest || pktType == tlv::Data);
+
+ if (pktType == tlv::Interest) {
+ if (pktName.size() < signed_interest::MIN_SIZE)
+ return false;
+
+ return checkNames(pktName.getPrefix(-signed_interest::MIN_SIZE), klName, state);
+ }
+ else {
+ return checkNames(pktName, klName, state);
+ }
+}
+
+NameRelationChecker::NameRelationChecker(const Name& name, const NameRelation& relation)
+ : m_name(name)
+ , m_relation(relation)
+{
+}
+
+bool
+NameRelationChecker::checkNames(const Name& pktName, const Name& klName, const shared_ptr<ValidationState>& state)
+{
+ // pktName not used in this check
+ Name identity = extractIdentityFromKeyName(klName);
+ bool result = checkNameRelation(m_relation, m_name, identity);
+ if (!result) {
+ std::ostringstream os;
+ os << "KeyLocator check failed: name relation " << m_name << " " << m_relation
+ << " for packet " << pktName << " is invalid"
+ << " (KeyLocator=" << klName << ", identity=" << identity << ")";
+ state->fail({ValidationError::POLICY_ERROR, os.str()});
+ }
+ return result;
+}
+
+RegexChecker::RegexChecker(const Regex& regex)
+ : m_regex(regex)
+{
+}
+
+bool
+RegexChecker::checkNames(const Name& pktName, const Name& klName, const shared_ptr<ValidationState>& state)
+{
+ // pktName not used in this check
+ Name identity = extractIdentityFromKeyName(klName);
+ bool result = m_regex.match(identity);
+ if (!result) {
+ std::ostringstream os;
+ os << "KeyLocator check failed: regex " << m_regex << " for packet " << pktName << " is invalid"
+ << " (KeyLocator=" << klName << ", identity=" << identity << ")";
+ state->fail({ValidationError::POLICY_ERROR, os.str()});
+ }
+
+ return result;
+}
+
+HyperRelationChecker::HyperRelationChecker(const std::string& pktNameExpr, const std::string pktNameExpand,
+ const std::string& klNameExpr, const std::string klNameExpand,
+ const NameRelation& hyperRelation)
+ : m_hyperPRegex(pktNameExpr, pktNameExpand)
+ , m_hyperKRegex(klNameExpr, klNameExpand)
+ , m_hyperRelation(hyperRelation)
+{
+}
+
+bool
+HyperRelationChecker::checkNames(const Name& pktName, const Name& klName, const shared_ptr<ValidationState>& state)
+{
+ if (!m_hyperPRegex.match(pktName) || !m_hyperKRegex.match(klName)) {
+ std::ostringstream os;
+ os << "Packet " << pktName << " (" << "KeyLocator=" << klName << ") does not match "
+ << "the hyper relation rule pkt=" << m_hyperPRegex << ", key=" << m_hyperKRegex;
+ state->fail({ValidationError::POLICY_ERROR, os.str()});
+ return false;
+ }
+
+ bool result = checkNameRelation(m_hyperRelation, m_hyperKRegex.expand(), m_hyperPRegex.expand());
+ if (!result) {
+ std::ostringstream os;
+ os << "KeyLocator check failed: hyper relation " << m_hyperRelation
+ << " pkt=" << m_hyperPRegex << ", key=" << m_hyperKRegex
+ << " of packet " << pktName << " (KeyLocator=" << klName << ") is invalid";
+ state->fail({ValidationError::POLICY_ERROR, os.str()});
+ }
+ return result;
+}
+
+unique_ptr<Checker>
+Checker::create(const ConfigSection& configSection, const std::string& configFilename)
+{
+ auto propertyIt = configSection.begin();
+
+ // Get checker.type
+ if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type")) {
+ BOOST_THROW_EXCEPTION(Error("Expect <checker.type>"));
+ }
+
+ std::string type = propertyIt->second.data();
+
+ if (boost::iequals(type, "customized")) {
+ return createCustomizedChecker(configSection, configFilename);
+ }
+ else if (boost::iequals(type, "hierarchical")) {
+ return createHierarchicalChecker(configSection, configFilename);
+ }
+ else {
+ BOOST_THROW_EXCEPTION(Error("Unsupported checker type: " + type));
+ }
+}
+
+unique_ptr<Checker>
+Checker::createCustomizedChecker(const ConfigSection& configSection,
+ const std::string& configFilename)
+{
+ auto propertyIt = configSection.begin();
+ propertyIt++;
+
+ // TODO implement restrictions based on signature type (outside this checker)
+
+ if (propertyIt != configSection.end() && boost::iequals(propertyIt->first, "sig-type")) {
+ // ignore sig-type
+ propertyIt++;
+ }
+
+ // Get checker.key-locator
+ if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "key-locator")) {
+ BOOST_THROW_EXCEPTION(Error("Expect <checker.key-locator>"));
+ }
+
+ auto checker = createKeyLocatorChecker(propertyIt->second, configFilename);
+ propertyIt++;
+
+ if (propertyIt != configSection.end()) {
+ BOOST_THROW_EXCEPTION(Error("Expect the end of checker"));
+ }
+
+ return checker;
+}
+
+unique_ptr<Checker>
+Checker::createHierarchicalChecker(const ConfigSection& configSection,
+ const std::string& configFilename)
+{
+ auto propertyIt = configSection.begin();
+ propertyIt++;
+
+ // TODO implement restrictions based on signature type (outside this checker)
+
+ if (propertyIt != configSection.end() && boost::iequals(propertyIt->first, "sig-type")) {
+ // ignore sig-type
+ propertyIt++;
+ }
+
+ if (propertyIt != configSection.end()) {
+ BOOST_THROW_EXCEPTION(Error("Expect the end of checker"));
+ }
+
+ return make_unique<HyperRelationChecker>("^(<>*)$", "\\1",
+ "^(<>*)<KEY><>$", "\\1",
+ NameRelation::IS_PREFIX_OF);
+}
+
+///
+
+unique_ptr<Checker>
+Checker::createKeyLocatorChecker(const ConfigSection& configSection, const std::string& configFilename)
+{
+ auto propertyIt = configSection.begin();
+
+ // Get checker.key-locator.type
+ if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type"))
+ BOOST_THROW_EXCEPTION(Error("Expect <checker.key-locator.type>"));
+
+ std::string type = propertyIt->second.data();
+
+ if (boost::iequals(type, "name"))
+ return createKeyLocatorNameChecker(configSection, configFilename);
+ else
+ BOOST_THROW_EXCEPTION(Error("Unsupported checker.key-locator.type: " + type));
+}
+
+unique_ptr<Checker>
+Checker::createKeyLocatorNameChecker(const ConfigSection& configSection, const std::string& configFilename)
+{
+ auto propertyIt = configSection.begin();
+ propertyIt++;
+
+ if (propertyIt == configSection.end())
+ BOOST_THROW_EXCEPTION(Error("Expect more checker.key-locator properties"));
+
+ if (boost::iequals(propertyIt->first, "name")) {
+ Name name;
+ try {
+ name = Name(propertyIt->second.data());
+ }
+ catch (const Name::Error& e) {
+ BOOST_THROW_EXCEPTION(Error("Invalid checker.key-locator.name: " + propertyIt->second.data()));
+ }
+ propertyIt++;
+
+ if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "relation")) {
+ BOOST_THROW_EXCEPTION(Error("Expect <checker.key-locator.relation>"));
+ }
+
+ std::string relationString = propertyIt->second.data();
+ propertyIt++;
+
+ NameRelation relation = getNameRelationFromString(relationString);
+
+ if (propertyIt != configSection.end()) {
+ BOOST_THROW_EXCEPTION(Error("Expect the end of checker.key-locator"));
+ }
+ return make_unique<NameRelationChecker>(name, relation);
+ }
+ else if (boost::iequals(propertyIt->first, "regex")) {
+ std::string regexString = propertyIt->second.data();
+ propertyIt++;
+
+ if (propertyIt != configSection.end()) {
+ BOOST_THROW_EXCEPTION(Error("Expect the end of checker.key-locator"));
+ }
+
+ try {
+ return make_unique<RegexChecker>(regexString);
+ }
+ catch (const Regex::Error& e) {
+ BOOST_THROW_EXCEPTION(Error("Invalid checker.key-locator.regex: " + regexString));
+ }
+ }
+ else if (boost::iequals(propertyIt->first, "hyper-relation")) {
+ const ConfigSection& hSection = propertyIt->second;
+
+ ConfigSection::const_iterator hPropertyIt = hSection.begin();
+
+ // Get k-regex
+ if (hPropertyIt == hSection.end() || !boost::iequals(hPropertyIt->first, "k-regex")) {
+ BOOST_THROW_EXCEPTION(Error("Expect <checker.key-locator.hyper-relation.k-regex>"));
+ }
+
+ std::string kRegex = hPropertyIt->second.data();
+ hPropertyIt++;
+
+ // Get k-expand
+ if (hPropertyIt == hSection.end() || !boost::iequals(hPropertyIt->first, "k-expand")) {
+ BOOST_THROW_EXCEPTION(Error("Expect <checker.key-locator.hyper-relation.k-expand>"));
+ }
+
+ std::string kExpand = hPropertyIt->second.data();
+ hPropertyIt++;
+
+ // Get h-relation
+ if (hPropertyIt == hSection.end() || !boost::iequals(hPropertyIt->first, "h-relation")) {
+ BOOST_THROW_EXCEPTION(Error("Expect <checker.key-locator.hyper-relation.h-relation>"));
+ }
+
+ std::string hRelation = hPropertyIt->second.data();
+ hPropertyIt++;
+
+ // Get p-regex
+ if (hPropertyIt == hSection.end() || !boost::iequals(hPropertyIt->first, "p-regex")) {
+ BOOST_THROW_EXCEPTION(Error("Expect <checker.key-locator.hyper-relation.p-regex>"));
+ }
+
+ std::string pRegex = hPropertyIt->second.data();
+ hPropertyIt++;
+
+ // Get p-expand
+ if (hPropertyIt == hSection.end() || !boost::iequals(hPropertyIt->first, "p-expand")) {
+ BOOST_THROW_EXCEPTION(Error("Expect <checker.key-locator.hyper-relation.p-expand>"));
+ }
+
+ std::string pExpand = hPropertyIt->second.data();
+ hPropertyIt++;
+
+ if (hPropertyIt != hSection.end()) {
+ BOOST_THROW_EXCEPTION(Error("Expect the end of checker.key-locator.hyper-relation"));
+ }
+
+ NameRelation relation = getNameRelationFromString(hRelation);
+ try {
+ return make_unique<HyperRelationChecker>(pRegex, pExpand, kRegex, kExpand, relation);
+ }
+ catch (const Regex::Error& e) {
+ BOOST_THROW_EXCEPTION(Error("Invalid regex for key-locator.hyper-relation"));
+ }
+ }
+ else {
+ BOOST_THROW_EXCEPTION(Error("Unsupported checker.key-locator"));
+ }
+}
+
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/src/security/v2/validator-config/checker.hpp b/src/security/v2/validator-config/checker.hpp
new file mode 100644
index 0000000..390dc08
--- /dev/null
+++ b/src/security/v2/validator-config/checker.hpp
@@ -0,0 +1,136 @@
+/* -*- 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.
+ */
+
+#ifndef NDN_SECURITY_V2_VALIDATOR_CONFIG_CHECKER_HPP
+#define NDN_SECURITY_V2_VALIDATOR_CONFIG_CHECKER_HPP
+
+#include "common.hpp"
+#include "name-relation.hpp"
+#include "../../../name.hpp"
+#include "../../../util/regex.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+
+class ValidationState;
+
+namespace validator_config {
+
+class Checker : noncopyable
+{
+public:
+ virtual
+ ~Checker() = default;
+
+ /**
+ * @brief Check if packet name ane KeyLocator satisfy the checker's conditions
+ *
+ * @param pktType tlv::Interest or tlv::Data
+ * @param pktName packet's name
+ * @param klName KeyLocator's name
+ * @param state Validation state
+ *
+ * @retval false data is immediately invalid. Will call state::fail() with proper code and message.
+ * @retval true further signature verification is needed.
+ */
+ bool
+ check(uint32_t pktType, const Name& pktName, const Name& klName, const shared_ptr<ValidationState>& state);
+
+ /**
+ * @brief create a checker from configuration section
+ *
+ * @param configSection The section containing the definition of checker.
+ * @param configFilename The configuration file name.
+ * @return a checker created from configuration
+ */
+ static unique_ptr<Checker>
+ create(const ConfigSection& configSection, const std::string& configFilename);
+
+private:
+ static unique_ptr<Checker>
+ createCustomizedChecker(const ConfigSection& configSection, const std::string& configFilename);
+
+ static unique_ptr<Checker>
+ createHierarchicalChecker(const ConfigSection& configSection, const std::string& configFilename);
+
+ static unique_ptr<Checker>
+ createKeyLocatorChecker(const ConfigSection& configSection, const std::string& configFilename);
+
+ static unique_ptr<Checker>
+ createKeyLocatorNameChecker(const ConfigSection& configSection, const std::string& configFilename);
+
+protected:
+ virtual bool
+ checkNames(const Name& pktName, const Name& klName, const shared_ptr<ValidationState>& state) = 0;
+};
+
+class NameRelationChecker : public Checker
+{
+public:
+ NameRelationChecker(const Name& name, const NameRelation& relation);
+
+protected:
+ bool
+ checkNames(const Name& pktName, const Name& klName, const shared_ptr<ValidationState>& state) override;
+
+private:
+ Name m_name;
+ NameRelation m_relation;
+};
+
+class RegexChecker : public Checker
+{
+public:
+ explicit
+ RegexChecker(const Regex& regex);
+
+protected:
+ bool
+ checkNames(const Name& pktName, const Name& klName, const shared_ptr<ValidationState>& state) override;
+
+private:
+ Regex m_regex;
+};
+
+class HyperRelationChecker : public Checker
+{
+public:
+ HyperRelationChecker(const std::string& pktNameExpr, const std::string pktNameExpand,
+ const std::string& klNameExpr, const std::string klNameExpand,
+ const NameRelation& hyperRelation);
+
+protected:
+ bool
+ checkNames(const Name& pktName, const Name& klName, const shared_ptr<ValidationState>& state) override;
+
+private:
+ Regex m_hyperPRegex;
+ Regex m_hyperKRegex;
+ NameRelation m_hyperRelation;
+};
+
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_SECURITY_V2_VALIDATOR_CONFIG_CHECKER_HPP
diff --git a/src/security/v2/validator-config/common.hpp b/src/security/v2/validator-config/common.hpp
new file mode 100644
index 0000000..1663e5a
--- /dev/null
+++ b/src/security/v2/validator-config/common.hpp
@@ -0,0 +1,48 @@
+/* -*- 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.
+ *
+ * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>
+ */
+
+#ifndef NDN_SECURITY_V2_VALIDATOR_CONFIG_COMMON_HPP
+#define NDN_SECURITY_V2_VALIDATOR_CONFIG_COMMON_HPP
+
+#include "../../../common.hpp"
+#include <boost/property_tree/ptree.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+
+typedef boost::property_tree::ptree ConfigSection;
+
+class Error : public std::runtime_error
+{
+public:
+ using std::runtime_error::runtime_error;
+};
+
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_SECURITY_V2_VALIDATOR_CONFIG_COMMON_HPP
diff --git a/src/security/v2/validator-config/filter.cpp b/src/security/v2/validator-config/filter.cpp
new file mode 100644
index 0000000..baeba1d
--- /dev/null
+++ b/src/security/v2/validator-config/filter.cpp
@@ -0,0 +1,149 @@
+/* -*- 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 "filter.hpp"
+
+#include "data.hpp"
+#include "interest.hpp"
+#include "util/regex.hpp"
+#include "security/security-common.hpp"
+
+#include <boost/algorithm/string.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+
+bool
+Filter::match(uint32_t pktType, const Name& pktName)
+{
+ BOOST_ASSERT(pktType == tlv::Interest || pktType == tlv::Data);
+
+ if (pktType == tlv::Interest) {
+ if (pktName.size() < signed_interest::MIN_SIZE)
+ return false;
+
+ return matchName(pktName.getPrefix(-signed_interest::MIN_SIZE));
+ }
+ else {
+ return matchName(pktName);
+ }
+}
+
+RelationNameFilter::RelationNameFilter(const Name& name, NameRelation relation)
+ : m_name(name)
+ , m_relation(relation)
+{
+}
+
+bool
+RelationNameFilter::matchName(const Name& name)
+{
+ return checkNameRelation(m_relation, m_name, name);
+}
+
+RegexNameFilter::RegexNameFilter(const Regex& regex)
+ : m_regex(regex)
+{
+}
+
+bool
+RegexNameFilter::matchName(const Name& name)
+{
+ return m_regex.match(name);
+}
+
+unique_ptr<Filter>
+Filter::create(const ConfigSection& configSection, const std::string& configFilename)
+{
+ auto propertyIt = configSection.begin();
+
+ if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type")) {
+ BOOST_THROW_EXCEPTION(Error("Expect <filter.type>"));
+ }
+
+ std::string type = propertyIt->second.data();
+
+ if (boost::iequals(type, "name"))
+ return createNameFilter(configSection, configFilename);
+ else
+ BOOST_THROW_EXCEPTION(Error("Unsupported filter.type: " + type));
+}
+
+unique_ptr<Filter>
+Filter::createNameFilter(const ConfigSection& configSection, const std::string& configFilename)
+{
+ auto propertyIt = configSection.begin();
+ propertyIt++;
+
+ if (propertyIt == configSection.end()) {
+ BOOST_THROW_EXCEPTION(Error("Expect more properties for filter(name)"));
+ }
+
+ if (boost::iequals(propertyIt->first, "name")) {
+ // Get filter.name
+ Name name;
+ try {
+ name = Name(propertyIt->second.data());
+ }
+ catch (const Name::Error& e) {
+ BOOST_THROW_EXCEPTION(Error("Wrong filter.name: " + propertyIt->second.data()));
+ }
+
+ propertyIt++;
+
+ // Get filter.relation
+ if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "relation")) {
+ BOOST_THROW_EXCEPTION(Error("Expect <filter.relation>"));
+ }
+
+ NameRelation relation = getNameRelationFromString(propertyIt->second.data());
+ propertyIt++;
+
+ if (propertyIt != configSection.end())
+ BOOST_THROW_EXCEPTION(Error("Expect the end of filter"));
+
+ return make_unique<RelationNameFilter>(name, relation);
+ }
+ else if (boost::iequals(propertyIt->first, "regex")) {
+ std::string regexString = propertyIt->second.data();
+ propertyIt++;
+
+ if (propertyIt != configSection.end())
+ BOOST_THROW_EXCEPTION(Error("Expect the end of filter"));
+
+ try {
+ return make_unique<RegexNameFilter>(regexString);
+ }
+ catch (const Regex::Error& e) {
+ BOOST_THROW_EXCEPTION(Error("Wrong filter.regex: " + regexString));
+ }
+ }
+ else {
+ BOOST_THROW_EXCEPTION(Error("Wrong filter(name) properties"));
+ }
+}
+
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/src/security/v2/validator-config/filter.hpp b/src/security/v2/validator-config/filter.hpp
new file mode 100644
index 0000000..cbd5275
--- /dev/null
+++ b/src/security/v2/validator-config/filter.hpp
@@ -0,0 +1,142 @@
+/* -*- 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.
+ */
+
+#ifndef NDN_SECURITY_V2_VALIDATOR_CONFIG_FILTER_HPP
+#define NDN_SECURITY_V2_VALIDATOR_CONFIG_FILTER_HPP
+
+#include "common.hpp"
+#include "name-relation.hpp"
+#include "../../../interest.hpp"
+#include "../../../data.hpp"
+#include "../../../util/regex.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+
+/**
+ * @brief Filter is one of the classes used by ValidatorConfig.
+ *
+ * The ValidatorConfig class consists of a set of rules.
+ * The Filter class is a part of a rule and is used to match packet.
+ * Matched packets will be checked against the checkers defined in the rule.
+ */
+class Filter : noncopyable
+{
+public:
+ virtual
+ ~Filter() = default;
+
+ bool
+ match(uint32_t pktType, const Name& pktName);
+
+public:
+ /**
+ * @brief Create a filter from the configuration section
+ *
+ * @param configSection The section containing the definition of filter.
+ * @param configFilename The configuration file name.
+ * @return a filter created from configuration
+ */
+ static unique_ptr<Filter>
+ create(const ConfigSection& configSection, const std::string& configFilename);
+
+private:
+ static unique_ptr<Filter>
+ createNameFilter(const ConfigSection& configSection, const std::string& configFilename);
+
+private:
+ virtual bool
+ matchName(const Name& pktName) = 0;
+};
+
+/**
+ * @brief Check that name is in relation to the packet name
+ *
+ * The following configuration
+ * @code
+ * filter
+ * {
+ * type name
+ * name /example
+ * relation is-prefix-of
+ * }
+ * @endcode
+ *
+ * creates
+ * @code
+ * RelationNameFilter("/example", RelationNameFilter::RELATION_IS_PREFIX_OF);
+ * @endcode
+ */
+class RelationNameFilter : public Filter
+{
+public:
+ RelationNameFilter(const Name& name, NameRelation relation);
+
+private:
+ bool
+ matchName(const Name& pktName) override;
+
+private:
+ Name m_name;
+ NameRelation m_relation;
+};
+
+/**
+ * @brief Filter to check that packet name matches the specified regular expression
+ *
+ * The following configuration
+ * @code
+ * filter
+ * {
+ * type name
+ * regex ^[^<KEY>]*<KEY><>*<ksk-.*>$
+ * }
+ * @endcode
+ *
+ * creates
+ * @code
+ * RegexNameFilter("^[^<KEY>]*<KEY><>*<ksk-.*>$");
+ * @endcode
+ *
+ * @sa Regex
+ */
+class RegexNameFilter : public Filter
+{
+public:
+ explicit
+ RegexNameFilter(const Regex& regex);
+
+private:
+ bool
+ matchName(const Name& pktName) override;
+
+private:
+ Regex m_regex;
+};
+
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_SECURITY_V2_VALIDATOR_CONFIG_FILTER_HPP
diff --git a/src/security/v2/validator-config/name-relation.cpp b/src/security/v2/validator-config/name-relation.cpp
new file mode 100644
index 0000000..ad65e9a
--- /dev/null
+++ b/src/security/v2/validator-config/name-relation.cpp
@@ -0,0 +1,79 @@
+/* -*- 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 "name-relation.hpp"
+
+#include <boost/algorithm/string.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+
+std::ostream&
+operator<<(std::ostream& os, NameRelation relation)
+{
+ switch (relation) {
+ case NameRelation::EQUAL:
+ return os << "equal";
+ case NameRelation::IS_PREFIX_OF:
+ return os << "is-prefix-of";
+ case NameRelation::IS_STRICT_PREFIX_OF:
+ return os << "is-strict-prefix-of";
+ }
+ return os;
+}
+
+bool
+checkNameRelation(NameRelation relation, const Name& name1, const Name& name2)
+{
+ switch (relation) {
+ case NameRelation::EQUAL:
+ return name1 == name2;
+ case NameRelation::IS_PREFIX_OF:
+ return name1.isPrefixOf(name2);
+ case NameRelation::IS_STRICT_PREFIX_OF:
+ return name1.isPrefixOf(name2) && name1.size() < name2.size();
+ }
+ return false;
+}
+
+NameRelation
+getNameRelationFromString(const std::string& relationString)
+{
+ if (boost::iequals(relationString, "equal")) {
+ return NameRelation::EQUAL;
+ }
+ else if (boost::iequals(relationString, "is-prefix-of")) {
+ return NameRelation::IS_PREFIX_OF;
+ }
+ else if (boost::iequals(relationString, "is-strict-prefix-of")) {
+ return NameRelation::IS_STRICT_PREFIX_OF;
+ }
+ else {
+ BOOST_THROW_EXCEPTION(Error("Unsupported relation: " + relationString));
+ }
+}
+
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/src/security/v2/validator-config/name-relation.hpp b/src/security/v2/validator-config/name-relation.hpp
new file mode 100644
index 0000000..5aa0dcf
--- /dev/null
+++ b/src/security/v2/validator-config/name-relation.hpp
@@ -0,0 +1,60 @@
+/* -*- 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.
+ */
+
+#ifndef NDN_SECURITY_V2_VALIDATOR_CONFIG_NAME_RELATION_HPP
+#define NDN_SECURITY_V2_VALIDATOR_CONFIG_NAME_RELATION_HPP
+
+#include "common.hpp"
+#include "../../../name.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+
+enum class NameRelation {
+ EQUAL,
+ IS_PREFIX_OF,
+ IS_STRICT_PREFIX_OF
+};
+
+std::ostream&
+operator<<(std::ostream& os, NameRelation relation);
+
+/**
+ * @brief check whether @p name1 and @p name2 satisfies @p relation
+ */
+bool
+checkNameRelation(NameRelation relation, const Name& name1, const Name& name2);
+
+/**
+ * @brief convert @p relationString to NameRelation
+ * @throw Error if @p relationString cannot be converted
+ */
+NameRelation
+getNameRelationFromString(const std::string& relationString);
+
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_SECURITY_V2_VALIDATOR_CONFIG_NAME_RELATION_HPP
diff --git a/src/security/v2/validator-config/rule.cpp b/src/security/v2/validator-config/rule.cpp
new file mode 100644
index 0000000..93b8df4
--- /dev/null
+++ b/src/security/v2/validator-config/rule.cpp
@@ -0,0 +1,169 @@
+/* -*- 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 "rule.hpp"
+#include "util/logger.hpp"
+
+#include <boost/algorithm/string/predicate.hpp>
+
+NDN_LOG_INIT(ndn.security.validator_config.Rule);
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+
+Rule::Rule(const std::string& id, uint32_t pktType)
+ : m_id(id)
+ , m_pktType(pktType)
+{
+}
+
+void
+Rule::addFilter(unique_ptr<Filter> filter)
+{
+ m_filters.push_back(std::move(filter));
+}
+
+void
+Rule::addChecker(unique_ptr<Checker> checker)
+{
+ m_checkers.push_back(std::move(checker));
+}
+
+bool
+Rule::match(uint32_t pktType, const Name& pktName) const
+{
+ NDN_LOG_TRACE("Trying to match " << pktName);
+ if (pktType != m_pktType) {
+ BOOST_THROW_EXCEPTION(Error("Invalid packet type supplied (" +
+ to_string(pktType) + " != " + to_string(m_pktType) + ")"));
+ }
+
+ if (m_filters.empty()) {
+ return true;
+ }
+
+ bool retval = false;
+ for (const auto& filter : m_filters) {
+ retval |= filter->match(pktType, pktName);
+ if (retval) {
+ break;
+ }
+ }
+ return retval;
+}
+
+bool
+Rule::check(uint32_t pktType, const Name& pktName, const Name& klName, const shared_ptr<ValidationState>& state) const
+{
+ NDN_LOG_TRACE("Trying to check " << pktName << " with keyLocator " << klName);
+
+ if (pktType != m_pktType) {
+ BOOST_THROW_EXCEPTION(Error("Invalid packet type supplied (" +
+ to_string(pktType) + " != " + to_string(m_pktType) + ")"));
+ }
+
+ bool hasPendingResult = false;
+ for (const auto& checker : m_checkers) {
+ bool result = checker->check(pktType, pktName, klName, state);
+ if (!result) {
+ return result;
+ }
+ hasPendingResult = true;
+ }
+
+ return hasPendingResult;
+}
+
+unique_ptr<Rule>
+Rule::create(const ConfigSection& configSection, const std::string& configFilename)
+{
+ auto propertyIt = configSection.begin();
+
+ // Get rule.id
+ if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "id")) {
+ BOOST_THROW_EXCEPTION(Error("Expecting <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("Expecting <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));
+ }
+
+ auto rule = make_unique<Rule>(ruleId, isForData ? tlv::Data : tlv::Interest);
+
+ // Get rule.filter(s)
+ for (; propertyIt != configSection.end(); propertyIt++) {
+ if (!boost::iequals(propertyIt->first, "filter")) {
+ if (boost::iequals(propertyIt->first, "checker")) {
+ break;
+ }
+ BOOST_THROW_EXCEPTION(Error("Expecting <rule.filter> in rule: " + ruleId));
+ }
+
+ rule->addFilter(Filter::create(propertyIt->second, configFilename));
+ }
+
+ // Get rule.checker(s)
+ bool hasCheckers = false;
+ for (; propertyIt != configSection.end(); propertyIt++) {
+ if (!boost::iequals(propertyIt->first, "checker")) {
+ BOOST_THROW_EXCEPTION(Error("Expecting <rule.checker> in rule: " + ruleId));
+ }
+
+ rule->addChecker(Checker::create(propertyIt->second, configFilename));
+ hasCheckers = true;
+ }
+
+ // Check other stuff
+ if (propertyIt != configSection.end()) {
+ BOOST_THROW_EXCEPTION(Error("Expecting the end of rule: " + ruleId));
+ }
+
+ if (!hasCheckers) {
+ BOOST_THROW_EXCEPTION(Error("No <rule.checker> is specified in rule: " + ruleId));
+ }
+
+ return rule;
+}
+
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/src/security/v2/validator-config/rule.hpp b/src/security/v2/validator-config/rule.hpp
new file mode 100644
index 0000000..66d6d6a
--- /dev/null
+++ b/src/security/v2/validator-config/rule.hpp
@@ -0,0 +1,113 @@
+/* -*- 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.
+ */
+
+#ifndef NDN_SECURITY_V2_VALIDATOR_CONFIG_RULE_HPP
+#define NDN_SECURITY_V2_VALIDATOR_CONFIG_RULE_HPP
+
+#include "filter.hpp"
+#include "checker.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+
+class ValidationState;
+
+namespace validator_config {
+
+class Rule : noncopyable
+{
+public:
+ Rule(const std::string& id, uint32_t pktType);
+
+ const std::string&
+ getId() const
+ {
+ return m_id;
+ }
+
+ uint32_t
+ getPktType() const
+ {
+ return m_pktType;
+ }
+
+ void
+ addFilter(unique_ptr<Filter> filter);
+
+ void
+ addChecker(unique_ptr<Checker> checker);
+
+ /**
+ * @brief check if the packet name matches rule's filter
+ *
+ * If no filters were added, the rule matches everything.
+ *
+ * @param pktType tlv::Interest or tlv::Data
+ * @param pktName packet name, for signed Interests the last two components are not removed
+ * @retval true If at least one filter matches @p pktName
+ * @retval false If none of the filters match @p pktName
+ *
+ * @throw Error the supplied pktType doesn't match one for which the rule is designed
+ */
+ bool
+ match(uint32_t pktType, const Name& pktName) const;
+
+ /**
+ * @brief check if packet satisfies rule's condition
+ *
+ * @param pktType tlv::Interest or tlv::Data
+ * @param pktName packet name, for signed Interests the last two components are not removed
+ * @param klName KeyLocator name
+ * @param state Validation state
+ *
+ * @retval false packet violates at least one checker. Will call state::fail() with proper code and message.
+ * @retval true packet satisfies all checkers, further validation is needed
+ *
+ * @throw Error the supplied pktType doesn't match one for which the rule is designed
+ */
+ bool
+ check(uint32_t pktType, const Name& pktName, const Name& klName, const shared_ptr<ValidationState>& state) const;
+
+public:
+ /**
+ * @brief create a rule from configuration section
+ *
+ * @param configSection The section containing the definition of checker.
+ * @param configFilename The configuration file name.
+ * @return a rule created from configuration
+ */
+ static unique_ptr<Rule>
+ create(const ConfigSection& configSection, const std::string& configFilename);
+
+NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+ std::string m_id;
+ uint32_t m_pktType;
+ std::vector<unique_ptr<Filter>> m_filters;
+ std::vector<unique_ptr<Checker>> m_checkers;
+};
+
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_SECURITY_V2_VALIDATOR_CONFIG_RULE_HPP