security: Add configuration based validator

configuration file format can be found at: http://redmine.named-data.net/projects/ndn-cpp-dev/wiki/CommandValidatorConf

Change-Id: Icc2725f349aed7513f35f2cccdcd4463fadeef31
diff --git a/src/security/conf/checker.hpp b/src/security/conf/checker.hpp
new file mode 100644
index 0000000..1ec2323
--- /dev/null
+++ b/src/security/conf/checker.hpp
@@ -0,0 +1,481 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/**
+ * Copyright (C) 2013 Regents of the University of California.
+ * @author: Yingdi Yu <yingdi@cs.ucla.edu>
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NDN_SECURITY_CONF_CHECKER_HPP
+#define NDN_SECURITY_CONF_CHECKER_HPP
+
+#include "key-locator-checker.hpp"
+#include "../../util/io.hpp"
+#include <boost/algorithm/string.hpp>
+
+#include "common.hpp"
+
+namespace ndn {
+namespace security {
+namespace conf {
+
+class Checker
+{
+public:
+  typedef function<void(const shared_ptr<const Interest>&)> OnInterestChecked;
+  typedef function<void(const shared_ptr<const Interest>&, const std::string&)> OnInterestCheckFailed;
+  typedef function<void(const shared_ptr<const Data>&)> OnDataChecked;
+  typedef function<void(const shared_ptr<const Data>&, const std::string&)> OnDataCheckFailed;
+
+
+  virtual
+  ~Checker()
+  {
+  }
+
+  /**
+   * @brief check if data satisfies condition defined in the specific checker implementation
+   *
+   * @param data Data packet
+   * @param onValidated Callback function which is called when data is immediately valid
+   * @param onValidationFailed Call function which is called when data is immediately invalid
+   * @return -1 if data is immediately invalid (onValidationFailed has been called)
+   *          1 if data is immediately valid (onValidated has been called)
+   *          0 if further signature verification is needed.
+   */
+  virtual int8_t
+  check(const Data& data,
+        const OnDataChecked& onValidated,
+        const OnDataCheckFailed& onValidationFailed) = 0;
+
+  /**
+   * @brief check if interest satisfies condition defined in the specific checker implementation
+   *
+   * @param interest Interest packet
+   * @param onValidated Callback function which is called when interest is immediately valid
+   * @param onValidationFailed Call function which is called when interest is immediately invalid
+   * @return -1 if interest is immediately invalid (onValidationFailed has been called)
+   *          1 if interest is immediately valid (onValidated has been called)
+   *          0 if further signature verification is needed.
+   */
+  virtual int8_t
+  check(const Interest& interest,
+        const OnInterestChecked& onValidated,
+        const OnInterestCheckFailed& onValidationFailed) = 0;
+};
+
+class CustomizedChecker : public Checker
+{
+  enum
+    {
+      INTEREST_SIG_VALUE = -1,
+      INTEREST_SIG_INFO = -2,
+    };
+
+public:
+  CustomizedChecker(uint32_t sigType,
+                    shared_ptr<KeyLocatorChecker> keyLocatorChecker)
+    : m_sigType(sigType)
+    , m_keyLocatorChecker(keyLocatorChecker)
+  {
+    if (m_sigType == Signature::Sha256WithRsa && !static_cast<bool>(m_keyLocatorChecker))
+      throw Error("Strong signature requires KeyLocatorChecker");
+  }
+
+  virtual int8_t
+  check(const Data& data,
+        const OnDataChecked& onValidated,
+        const OnDataCheckFailed& onValidationFailed)
+  {
+    return check(data, data.getSignature(), onValidated, onValidationFailed);
+  }
+
+  virtual int8_t
+  check(const Interest& interest,
+        const OnInterestChecked& onValidated,
+        const OnInterestCheckFailed& onValidationFailed)
+  {
+    const Name& interestName = interest.getName();
+    Signature signature(interestName[INTEREST_SIG_INFO].blockFromValue(),
+                        interestName[INTEREST_SIG_VALUE].blockFromValue());
+    return check(interest, signature, onValidated, onValidationFailed);
+  }
+
+private:
+  template<class Packet, class OnValidated, class OnFailed>
+  int8_t
+  check(const Packet& packet, const Signature& signature,
+        const OnValidated& onValidated,
+        const OnFailed& onValidationFailed)
+  {
+    if (m_sigType != signature.getType())
+      {
+        onValidationFailed(packet.shared_from_this(),
+                           "Signature type does not match: " +
+                           boost::lexical_cast<std::string>(m_sigType) +
+                           "!=" +
+                           boost::lexical_cast<std::string>(signature.getType()));
+        return -1;
+      }
+
+    switch (signature.getType())
+      {
+      case Signature::Sha256WithRsa:
+        {
+          try
+            {
+              SignatureSha256WithRsa sig(signature);
+
+              std::string failInfo;
+              if (m_keyLocatorChecker->check(packet, sig.getKeyLocator(), failInfo))
+                return 0;
+              else
+                {
+                  onValidationFailed(packet.shared_from_this(), failInfo);
+                  return -1;
+                }
+            }
+          catch (const SignatureSha256WithRsa::Error& e)
+            {
+              onValidationFailed(packet.shared_from_this(),
+                                 "Cannot decode Sha256WithRsa signature!");
+              return -1;
+            }
+        }
+      case Signature::Sha256:
+        return 0;
+      default:
+        {
+          onValidationFailed(packet.shared_from_this(),
+                             "Unsupported signature type: " +
+                             boost::lexical_cast<std::string>(signature.getType()));
+          return -1;
+        }
+      }
+  }
+
+private:
+  uint32_t m_sigType;
+  shared_ptr<KeyLocatorChecker> m_keyLocatorChecker;
+};
+
+class HierarchicalChecker : public CustomizedChecker
+{
+public:
+  HierarchicalChecker(uint32_t sigType)
+    : CustomizedChecker(sigType,
+        make_shared<HyperKeyLocatorNameChecker>("^(<>*)$", "\\1",
+                                                "^([^<KEY>]*)<KEY>(<>*)<ksk-.*><ID-CERT>$",
+                                                "\\1\\2",
+                                                KeyLocatorChecker::RELATION_IS_PREFIX_OF))
+  {
+  }
+};
+
+class FixedSignerChecker : public Checker
+{
+  enum
+    {
+      INTEREST_SIG_VALUE = -1,
+      INTEREST_SIG_INFO = -2,
+    };
+public:
+  FixedSignerChecker(uint32_t sigType,
+                     const std::vector<shared_ptr<IdentityCertificate> >& signers)
+    : m_sigType(sigType)
+  {
+    for (std::vector<shared_ptr<IdentityCertificate> >::const_iterator it = signers.begin();
+         it != signers.end(); it++)
+      m_signers[(*it)->getName().getPrefix(-1)] = (*it);
+  }
+
+  virtual int8_t
+  check(const Data& data,
+        const OnDataChecked& onValidated,
+        const OnDataCheckFailed& onValidationFailed)
+  {
+    return check(data, data.getSignature(), onValidated, onValidationFailed);
+  }
+
+  virtual int8_t
+  check(const Interest& interest,
+        const OnInterestChecked& onValidated,
+        const OnInterestCheckFailed& onValidationFailed)
+  {
+    const Name& interestName = interest.getName();
+    Signature signature(interestName[INTEREST_SIG_INFO].blockFromValue(),
+                        interestName[INTEREST_SIG_VALUE].blockFromValue());
+    return check(interest, signature, onValidated, onValidationFailed);
+  }
+
+private:
+  template<class Packet, class OnValidated, class OnFailed>
+  int8_t
+  check(const Packet& packet, const Signature& signature,
+        const OnValidated& onValidated,
+        const OnFailed& onValidationFailed)
+  {
+    if (m_sigType != signature.getType())
+      {
+        onValidationFailed(packet.shared_from_this(),
+                           "Signature type does not match: "
+                           + boost::lexical_cast<std::string>(m_sigType)
+                           + "!="
+                           + boost::lexical_cast<std::string>(signature.getType()));
+        return -1;
+      }
+
+    switch(signature.getType())
+      {
+      case Signature::Sha256WithRsa:
+        {
+          try
+            {
+              SignatureSha256WithRsa sig(signature);
+
+              const Name& keyLocatorName = sig.getKeyLocator().getName();
+              if (m_signers.find(keyLocatorName) == m_signers.end())
+                {
+                  onValidationFailed(packet.shared_from_this(),
+                                     "Signer is not in the fixed signer list: "
+                                     + keyLocatorName.toUri());
+                  return -1;
+                }
+
+              if (Validator::verifySignature(packet, sig,
+                                             m_signers[keyLocatorName]->getPublicKeyInfo()))
+                {
+                  onValidated(packet.shared_from_this());
+                  return 1;
+                }
+              else
+                {
+                  onValidationFailed(packet.shared_from_this(),
+                                     "Signature cannot be validated!");
+                  return -1;
+                }
+            }
+          catch (const KeyLocator::Error& e)
+            {
+              onValidationFailed(packet.shared_from_this(),
+                                 "KeyLocator does not have name!");
+              return -1;
+            }
+          catch (const SignatureSha256WithRsa::Error& e)
+            {
+              onValidationFailed(packet.shared_from_this(),
+                                 "Cannot decode signature!");
+              return -1;
+            }
+        }
+      case Signature::Sha256:
+        {
+          onValidationFailed(packet.shared_from_this(),
+                             "FixedSigner does not allow Sha256 signature type!");
+          return -1;
+        }
+      default:
+        {
+          onValidationFailed(packet.shared_from_this(),
+                             "Unsupported signature type: "
+                             + boost::lexical_cast<std::string>(signature.getType()));
+          return -1;
+        }
+      }
+  }
+
+private:
+  typedef std::map<Name, shared_ptr<IdentityCertificate> > SignerList;
+
+  uint32_t m_sigType;
+  SignerList m_signers;
+};
+
+class CheckerFactory
+{
+public:
+  /**
+   * @brief create a checker from configuration file.
+   *
+   * @param configSection The section containing the definition of checker.
+   * @param configFilename The configuration file name.
+   * @return a shared pointer to the created checker.
+   */
+  static shared_ptr<Checker>
+  create(const ConfigSection& configSection, const std::string& configFilename)
+  {
+    ConfigSection::const_iterator propertyIt = configSection.begin();
+
+    // Get checker.type
+    if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type"))
+      throw 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 if (boost::iequals(type, "fixed-signer"))
+      return createFixedSignerChecker(configSection, configFilename);
+    else
+      throw Error("Unsupported checker type: " + type);
+  }
+
+private:
+  static shared_ptr<Checker>
+  createCustomizedChecker(const ConfigSection& configSection,
+                          const std::string& configFilename)
+  {
+    ConfigSection::const_iterator propertyIt = configSection.begin();
+    propertyIt++;
+
+    // Get checker.sig-type
+    if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "sig-type"))
+      throw Error("Expect <checker.sig-type>!");
+
+    std::string sigType = propertyIt->second.data();
+    propertyIt++;
+
+    // Get checker.key-locator
+    if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "key-locator"))
+      throw Error("Expect <checker.key-locator>!");
+
+    shared_ptr<KeyLocatorChecker> keyLocatorChecker =
+      KeyLocatorCheckerFactory::create(propertyIt->second, configFilename);
+    propertyIt++;
+
+    if (propertyIt != configSection.end())
+      throw Error("Expect the end of checker!");
+
+    return make_shared<CustomizedChecker>(boost::cref(getSigType(sigType)),
+                                          boost::cref(keyLocatorChecker));
+  }
+
+  static shared_ptr<Checker>
+  createHierarchicalChecker(const ConfigSection& configSection,
+                            const std::string& configFilename)
+  {
+    ConfigSection::const_iterator propertyIt = configSection.begin();
+    propertyIt++;
+
+    // Get checker.sig-type
+    if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "sig-type"))
+      throw Error("Expect <checker.sig-type>!");
+
+    std::string sigType = propertyIt->second.data();
+    propertyIt++;
+
+    if (propertyIt != configSection.end())
+      throw Error("Expect the end of checker!");
+
+    return make_shared<HierarchicalChecker>(boost::cref(getSigType(sigType)));
+  }
+
+  static shared_ptr<Checker>
+  createFixedSignerChecker(const ConfigSection& configSection,
+                           const std::string& configFilename)
+  {
+    ConfigSection::const_iterator propertyIt = configSection.begin();
+    propertyIt++;
+
+    // Get checker.sig-type
+    if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "sig-type"))
+      throw Error("Expect <checker.sig-type>!");
+
+    std::string sigType = propertyIt->second.data();
+    propertyIt++;
+
+    std::vector<shared_ptr<IdentityCertificate> > signers;
+    for (; propertyIt != configSection.end(); propertyIt++)
+      {
+        if (!boost::iequals(propertyIt->first, "signer"))
+          throw Error("Expect <checker.signer> but get <checker."
+                      + propertyIt->first + ">");
+
+        signers.push_back(getSigner(propertyIt->second, configFilename));
+      }
+
+    if (propertyIt != configSection.end())
+      throw Error("Expect the end of checker!");
+
+    return shared_ptr<FixedSignerChecker>(new FixedSignerChecker(getSigType(sigType),
+                                                                 signers));
+  }
+
+  static shared_ptr<IdentityCertificate>
+  getSigner(const ConfigSection& configSection, const std::string& configFilename)
+  {
+    using namespace boost::filesystem;
+
+    ConfigSection::const_iterator propertyIt = configSection.begin();
+
+    // Get checker.signer.type
+    if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type"))
+      throw Error("Expect <checker.signer.type>!");
+
+    std::string type = propertyIt->second.data();
+    propertyIt++;
+
+    if (boost::iequals(type, "file"))
+      {
+        // Get checker.signer.file-name
+        if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "file-name"))
+          throw Error("Expect <checker.signer.file-name>!");
+
+        path certfilePath = absolute(propertyIt->second.data(),
+                                     path(configFilename).parent_path());
+        propertyIt++;
+
+        if (propertyIt != configSection.end())
+          throw Error("Expect the end of checker.signer");
+
+        shared_ptr<IdentityCertificate> idCert
+          = io::load<IdentityCertificate>(certfilePath.c_str());
+
+        if (static_cast<bool>(idCert))
+          return idCert;
+        else
+          throw Error("Cannot read certificate from file: "
+                      + certfilePath.native());
+      }
+    else if (boost::iequals(type, "base64"))
+      {
+        // Get checker.signer.base64-string
+        if (propertyIt == configSection.end() ||
+            !boost::iequals(propertyIt->first, "base64-string"))
+          throw Error("Expect <checker.signer.base64-string>!");
+
+        std::stringstream ss(propertyIt->second.data());
+        propertyIt++;
+
+        if (propertyIt != configSection.end())
+          throw Error("Expect the end of checker.signer");
+
+        shared_ptr<IdentityCertificate> idCert = io::load<IdentityCertificate>(ss);
+
+        if (static_cast<bool>(idCert))
+          return idCert;
+        else
+          throw Error("Cannot decode certificate from string");
+      }
+    else
+      throw Error("Unsupported checker.signer type: " + type);
+  }
+
+  static int32_t
+  getSigType(const std::string& sigType)
+  {
+    if (boost::iequals(sigType, "rsa-sha256"))
+      return Signature::Sha256WithRsa;
+    else if (boost::iequals(sigType, "sha256"))
+      return Signature::Sha256;
+    else
+      return -1;
+  }
+};
+
+} // namespace conf
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_SECURITY_SEC_CONF_RULE_SIGNER_HPP