add Validator

Change-Id: Ib8ce9023aad0782f934e8e6e559840b559d68208
diff --git a/log4cxx.properties.sample.in b/log4cxx.properties.sample.in
new file mode 100644
index 0000000..15a36d7
--- /dev/null
+++ b/log4cxx.properties.sample.in
@@ -0,0 +1,9 @@
+log4j.rootLogger=TRACE, A1
+log4j.appender.A1=org.apache.log4j.ConsoleAppender
+log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+
+# Print the date in ISO 8601 format
+log4j.appender.A1.layout.ConversionPattern=%-5p %-15c - %m%n
+
+# Print only messages of level WARN or above in the package com.foo.
+#log4j.logger.com.foo=WARN
\ No newline at end of file
diff --git a/src/validator.cpp b/src/validator.cpp
new file mode 100644
index 0000000..721b311
--- /dev/null
+++ b/src/validator.cpp
@@ -0,0 +1,125 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014, Regents of the University of California.
+ *
+ * This file is part of NDNS (Named Data Networking Domain Name Service).
+ * See AUTHORS.md for complete list of NDNS authors and contributors.
+ *
+ * NDNS is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NDNS 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NDNS, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "logger.hpp"
+#include "config.hpp"
+#include "validator.hpp"
+
+#include "ndn-cxx/data.hpp"
+#include <ndn-cxx/security/validator-config.hpp>
+
+
+namespace ndn {
+namespace ndns {
+NDNS_LOG_INIT("validator");
+
+std::string Validator::VALIDATOR_CONF_FILE = DEFAULT_CONFIG_PATH "/" "validator.conf";
+
+Validator::Validator(Face& face, const std::string& confFile /* = VALIDATOR_CONF_FILE */)
+  : ValidatorConfig(face)
+{
+  try {
+    this->load(confFile);
+    NDNS_LOG_TRACE("Validator loads configuration: " << confFile);
+  }
+  catch (std::exception&) {
+    std::string config =
+      "rule                                                                       \n"
+      "{                                                                          \n"
+      "  id \"NDNS Validator\"                                                    \n"
+      "  for data                                                                 \n"
+      "  checker                                                                  \n"
+      "  {                                                                        \n"
+      "    type customized                                                        \n"
+      "    sig-type rsa-sha256                                                    \n"
+      "    key-locator                                                            \n"
+      "    {                                                                      \n"
+      "      type name                                                            \n"
+      "      hyper-relation                                                       \n"
+      "      {                                                                    \n"
+      "        k-regex ^(<>*)<KEY>(<>*)<><ID-CERT>$                               \n"
+      "        k-expand \\\\1\\\\2                                                \n"
+      "        h-relation is-prefix-of                                            \n"
+      "        p-regex ^(<>*)[<KEY><NDNS>](<>*)<><>$                              \n"
+      "        p-expand \\\\1\\\\2                                                \n"
+      "      }                                                                    \n"
+      "    }                                                                      \n"
+      "  }                                                                        \n"
+      "}                                                                          \n"
+      "                                                                           \n"
+      "                                                                           \n"
+      "trust-anchor                                                               \n"
+      "{                                                                          \n"
+      "  type file                                                                \n"
+      "  file-name \""
+      ;
+
+    config += "anchors/root.cert";
+
+    config +=
+      "\"                                                                         \n"
+      "}                                                                          \n"
+      "                                                                           \n"
+      ;
+
+    this->load(config, "embededConf");
+    NDNS_LOG_TRACE("Validator loads embedded configuration with anchors path: anchors/root.cert");
+  }
+
+}
+
+void
+Validator::validate(const Data& data,
+                    const OnDataValidated& onValidated,
+                    const OnDataValidationFailed& onValidationFailed)
+{
+  NDNS_LOG_TRACE("[* ?? *] verify data: " << data.getName() << ". KeyLocator: "
+                 << data.getSignature().getKeyLocator().getName());
+  ValidatorConfig::validate(data,
+                            [this, onValidated](const shared_ptr<const Data>& data)
+                            // onValidated here cannot use reference, since this is non-block
+                            {
+                              onValidated(data);
+                              this->onDataValidated(data);
+                            },
+                            [this, onValidationFailed](const shared_ptr<const Data>& data,
+                                                       const std::string& str)
+                            {
+                              onValidationFailed(data, str);
+                              this->onDataValidationFailed(data, str);
+                            }
+                            );
+}
+
+void
+Validator::onDataValidated(const shared_ptr<const Data>& data)
+{
+  NDNS_LOG_TRACE("[* VV *] pass validation: " << data->getName() << ". KeyLocator = "
+                 << data->getSignature().getKeyLocator().getName());
+}
+
+void
+Validator::onDataValidationFailed(const shared_ptr<const Data>& data, const std::string& str)
+{
+  NDNS_LOG_WARN("[* XX *] fail validation: " << data->getName() << ". due to: " << str
+                << ". KeyLocator = " << data->getSignature().getKeyLocator().getName());
+}
+
+} // namespace ndns
+} // namespace ndn
diff --git a/src/validator.hpp b/src/validator.hpp
new file mode 100644
index 0000000..dc7f656
--- /dev/null
+++ b/src/validator.hpp
@@ -0,0 +1,87 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014, Regents of the University of California.
+ *
+ * This file is part of NDNS (Named Data Networking Domain Name Service).
+ * See AUTHORS.md for complete list of NDNS authors and contributors.
+ *
+ * NDNS is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NDNS 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NDNS, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NDNS_VALIDATOR_HPP
+#define NDNS_VALIDATOR_HPP
+
+#include "config.hpp"
+
+#include "ndn-cxx/data.hpp"
+#include <ndn-cxx/security/validator-config.hpp>
+
+
+namespace ndn {
+namespace ndns {
+
+/**
+ * @brief NDNS validator, which validates Data with hierarchical way. Validator is used in three
+ * scenarios:
+ * 1) Dig client gets the final response Data;
+ * 2) Authoritative name server receives update request;
+ * 3) Update client gets the result of update request.
+ *
+ * @note Compared to its parent class, ValidatorConfig, the class provides is customized according
+ * to config file and the above working scenarios:
+ * 1) give the default path of config file;
+ * 2) default rule is the given path if not valid or the content is wrong.
+ *    Validator rule is must for NDNS, the daemon/dig/update must work even without manually edit
+ * 3) some wrapper provides default behavior when verification succeeds or fails
+ */
+class Validator : public ValidatorConfig
+{
+
+public:
+  static std::string VALIDATOR_CONF_FILE;
+
+  /**
+   * @brief the callback function which is called after validation finishes
+   * @param[in] callback The function is called after validation finishes, no matter validation
+   * succeeds or fails
+   */
+  explicit
+  Validator(Face& face, const std::string& confFile = VALIDATOR_CONF_FILE);
+
+  /**
+   * @brief validate the Data
+   */
+  virtual void
+  validate(const Data& data,
+           const OnDataValidated& onValidated,
+           const OnDataValidationFailed& onValidationFailed);
+
+private:
+  /**
+   * @brief the default callback function on data validated
+   */
+  void
+  onDataValidated(const shared_ptr<const Data>& data);
+
+  /**
+   * @brief the default callback function on data validation failed
+   */
+  void
+  onDataValidationFailed(const shared_ptr<const Data>& data, const std::string& str);
+
+};
+
+
+} // namespace ndns
+} // namespace ndn
+
+#endif // NDNS_VALIDATOR_HPP
diff --git a/tests/unit/dummy-client-face.hpp b/tests/unit/dummy-client-face.hpp
new file mode 100644
index 0000000..649e714
--- /dev/null
+++ b/tests/unit/dummy-client-face.hpp
@@ -0,0 +1,236 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014, Regents of the University of California.
+ *
+ * This file is part of NDNS (Named Data Networking Domain Name Service).
+ * See AUTHORS.md for complete list of NDNS authors and contributors.
+ *
+ * NDNS is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NDNS 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NDNS, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Copyright (c) 2013-2014 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDNS_TESTS_UNIT_DUMMY_CLIENT_FACE_HPP
+#define NDNS_TESTS_UNIT_DUMMY_CLIENT_FACE_HPP
+
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/transport/transport.hpp>
+#include <ndn-cxx/management/nfd-controller.hpp>
+#include <ndn-cxx/management/nfd-control-response.hpp>
+#include <ndn-cxx/util/event-emitter.hpp>
+
+
+namespace ndn {
+namespace tests {
+
+class DummyClientTransport : public ndn::Transport
+{
+public:
+  void
+  receive(const Block& block)
+  {
+    if (static_cast<bool>(m_receiveCallback))
+      m_receiveCallback(block);
+  }
+
+  virtual void
+  close()
+  {
+  }
+
+  virtual void
+  pause()
+  {
+  }
+
+  virtual void
+  resume()
+  {
+  }
+
+  virtual void
+  send(const Block& wire)
+  {
+    if (wire.type() == tlv::Interest) {
+      shared_ptr<Interest> interest = make_shared<Interest>(wire);
+      (*m_onInterest)(*interest, this);
+    }
+    else if (wire.type() == tlv::Data) {
+      shared_ptr<Data> data = make_shared<Data>(wire);
+      (*m_onData)(*data, this);
+    }
+  }
+
+  virtual void
+  send(const Block& header, const Block& payload)
+  {
+    this->send(payload);
+  }
+
+  boost::asio::io_service&
+  getIoService()
+  {
+    return *m_ioService;
+  }
+
+private:
+  friend class DummyClientFace;
+  util::EventEmitter<Interest, DummyClientTransport*>* m_onInterest;
+  util::EventEmitter<Data, DummyClientTransport*>* m_onData;
+};
+
+
+/** \brief Callback to connect
+ */
+inline void
+replyNfdRibCommands(const Interest& interest, DummyClientTransport* transport)
+{
+  static const Name localhostRegistration("/localhost/nfd/rib");
+  if (localhostRegistration.isPrefixOf(interest.getName())) {
+    shared_ptr<Data> okResponse = make_shared<Data>(interest.getName());
+    nfd::ControlParameters params(interest.getName().get(-5).blockFromValue());
+    params.setFaceId(1);
+    params.setOrigin(0);
+    if (interest.getName().get(3) == name::Component("register")) {
+      params.setCost(0);
+    }
+    nfd::ControlResponse resp;
+    resp.setCode(200);
+    resp.setBody(params.wireEncode());
+    okResponse->setContent(resp.wireEncode());
+    KeyChain keyChain;
+    keyChain.signWithSha256(*okResponse);
+
+    transport->getIoService().post(bind(&DummyClientTransport::receive, transport,
+                                        okResponse->wireEncode()));
+  }
+}
+
+/** \brief a client-side face for unit testing
+ */
+class DummyClientFace : public ndn::Face
+{
+public:
+  explicit
+  DummyClientFace(shared_ptr<DummyClientTransport> transport)
+    : Face(transport)
+    , m_transport(transport)
+  {
+    m_transport->m_onInterest = &onInterest;
+    m_transport->m_onData     = &onData;
+
+    enablePacketLogging();
+  }
+
+  DummyClientFace(shared_ptr<DummyClientTransport> transport, boost::asio::io_service& ioService)
+    : Face(transport, ioService)
+    , m_transport(transport)
+  {
+    m_transport->m_onInterest = &onInterest;
+    m_transport->m_onData     = &onData;
+
+    enablePacketLogging();
+  }
+
+  /** \brief cause the Face to receive a packet
+   */
+  template<typename Packet>
+  void
+  receive(const Packet& packet)
+  {
+    m_transport->receive(packet.wireEncode());
+  }
+
+  void
+  enablePacketLogging()
+  {
+    // @todo Replace with C++11 lambdas
+
+    onInterest += bind(static_cast<void(std::vector<Interest>::*)(const Interest&)>(
+                         &std::vector<Interest>::push_back),
+                       &m_sentInterests, _1);
+
+    onData += bind(static_cast<void(std::vector<Data>::*)(const Data&)>(
+                     &std::vector<Data>::push_back),
+                   &m_sentDatas, _1);
+  }
+
+  void
+  enableRegistrationReply()
+  {
+    onInterest += &replyNfdRibCommands;
+  }
+
+public:
+  /** \brief sent Interests
+   *  \note After .expressInterest, .processEvents must be called before
+   *        the Interest would show up here.
+   */
+  std::vector<Interest> m_sentInterests;
+  /** \brief sent Datas
+   *  \note After .put, .processEvents must be called before
+   *        the Interest would show up here.
+   */
+  std::vector<Data>     m_sentDatas;
+
+public:
+  /** \brief Event to be called whenever an Interest is received
+   *  \note After .expressInterest, .processEvents must be called before
+   *        the Interest would show up here.
+   */
+  util::EventEmitter<Interest, DummyClientTransport*> onInterest;
+
+  /** \brief Event to be called whenever a Data packet is received
+   *  \note After .put, .processEvents must be called before
+   *        the Interest would show up here.
+   */
+  util::EventEmitter<Data, DummyClientTransport*> onData;
+
+private:
+  shared_ptr<DummyClientTransport> m_transport;
+};
+
+inline shared_ptr<DummyClientFace>
+makeDummyClientFace()
+{
+  return make_shared<DummyClientFace>(make_shared<DummyClientTransport>());
+}
+
+inline shared_ptr<DummyClientFace>
+makeDummyClientFace(boost::asio::io_service& ioService)
+{
+  return make_shared<DummyClientFace>(make_shared<DummyClientTransport>(), ref(ioService));
+}
+
+
+} // namespace tests
+} // namespace ndn
+
+#endif // NDNS_TESTS_UNIT_DUMMY_CLIENT_FACE_HPP
diff --git a/tests/unit/validator.cpp b/tests/unit/validator.cpp
new file mode 100644
index 0000000..26c25ac
--- /dev/null
+++ b/tests/unit/validator.cpp
@@ -0,0 +1,264 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014, Regents of the University of California.
+ *
+ * This file is part of NDNS (Named Data Networking Domain Name Service).
+ * See AUTHORS.md for complete list of NDNS authors and contributors.
+ *
+ * NDNS is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NDNS 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NDNS, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "validator.hpp"
+#include "dummy-client-face.hpp"
+#include <ndn-cxx/security/key-chain.hpp>
+#include "../boost-test.hpp"
+
+namespace ndn {
+namespace ndns {
+namespace tests {
+NDNS_LOG_INIT("ValidatorTest");
+
+BOOST_AUTO_TEST_SUITE(Validator)
+
+class Fixture
+{
+public:
+  Fixture()
+    : m_testId1("/test02")
+    , m_testId2("/test02/ndn")
+    , m_testId3("/test02/ndn/edu")
+    , m_randomId("/test03")
+    , m_version(name::Component::fromVersion(0))
+    , m_face(::ndn::tests::makeDummyClientFace())
+  {
+    m_keyChain.deleteIdentity(m_testId1);
+    m_keyChain.deleteIdentity(m_testId2);
+    m_keyChain.deleteIdentity(m_testId3);
+    m_keyChain.deleteIdentity(m_randomId);
+
+    m_randomDsk = createRoot(m_randomId); // generate a root cert
+
+    m_dsk1 = createRoot(m_testId1); // replace to root cert
+    m_dsk2 = createIdentity(m_testId2, m_dsk1);
+    m_dsk3 = createIdentity(m_testId3, m_dsk2);
+
+    m_selfSignCert = m_keyChain.generateRsaKeyPair(m_testId3, false);
+    shared_ptr<IdentityCertificate> cert = m_keyChain.selfSign(m_selfSignCert);
+    m_selfSignCert = cert->getName();
+    m_keyChain.addCertificate(*cert);
+    NDNS_LOG_TRACE("add cert: " << cert->getName() << " to KeyChain");
+
+    m_face->onInterest += bind(&Fixture::respondInterest, this, _1, _2);
+  }
+
+  ~Fixture()
+  {
+    m_face->getIoService().stop();
+    m_face->shutdown();
+    m_keyChain.deleteIdentity(m_testId1);
+    m_keyChain.deleteIdentity(m_testId2);
+    m_keyChain.deleteIdentity(m_testId3);
+    m_keyChain.deleteIdentity(m_randomId);
+  }
+
+  const Name
+  createIdentity(const Name& id, const Name& parentCertName)
+  {
+    Name kskCertName = m_keyChain.createIdentity(id);
+    Name kskName = m_keyChain.getDefaultKeyNameForIdentity(id);
+    m_keyChain.deleteCertificate(kskCertName);
+    auto kskCert = createCertificate(kskName, parentCertName);
+
+    Name dskName = m_keyChain.generateRsaKeyPair(id, false);
+    auto dskCert = createCertificate(dskName, kskCert);
+    return dskCert;
+  }
+
+  const Name
+  createRoot(const Name& root)
+  {
+    m_rootCert = m_keyChain.createIdentity(root);
+    ndn::io::save(*(m_keyChain.getCertificate(m_rootCert)), TEST_CONFIG_PATH "/anchors/root.cert");
+    NDNS_LOG_TRACE("save root cert "<< m_rootCert <<
+                  " to: " << TEST_CONFIG_PATH "/anchors/root.cert");
+    Name dsk = m_keyChain.generateRsaKeyPair(root, false);
+    auto cert = createCertificate(dsk, m_rootCert);
+    return cert;
+  }
+
+
+  const Name
+  createCertificate(const Name& keyName, const Name& parentCertName)
+  {
+    std::vector<CertificateSubjectDescription> desc;
+    time::system_clock::TimePoint notBefore = time::system_clock::now();
+    time::system_clock::TimePoint notAfter = notBefore + time::days(365);
+    desc.push_back(CertificateSubjectDescription(oid::ATTRIBUTE_NAME,
+                                                 "Signer: " + parentCertName.toUri()));
+    shared_ptr<IdentityCertificate> cert =
+      m_keyChain.prepareUnsignedIdentityCertificate(keyName, parentCertName,
+                                                    notBefore, notAfter, desc);
+
+    Name tmp = cert->getName().getPrefix(-1).append(m_version);
+    cert->setName(tmp);
+    m_keyChain.sign(*cert, parentCertName);
+    m_keyChain.addCertificateAsKeyDefault(*cert);
+    NDNS_LOG_TRACE("add cert: " << cert->getName() << " to KeyChain");
+    return cert->getName();
+  }
+
+
+  void
+  respondInterest(const Interest& interest, ndn::tests::DummyClientTransport* transport)
+  {
+    Name certName = interest.getName();
+    if (certName.isPrefixOf(m_selfSignCert)) {
+      // self-sign cert's version number is not m_version
+      certName = m_selfSignCert;
+    } else {
+      certName.append(m_version);
+    }
+    NDNS_LOG_TRACE("validator needs: " << certName);
+    BOOST_CHECK_EQUAL(m_keyChain.doesCertificateExist(certName), true);
+    auto cert = m_keyChain.getCertificate(certName);
+    transport->receive(cert->wireEncode());
+  }
+
+public:
+  Name m_testId1;
+  Name m_testId2;
+  Name m_testId3;
+  Name m_randomId;
+
+  Name m_rootCert;
+
+  KeyChain m_keyChain;
+
+  Name m_dsk1;
+  Name m_dsk2;
+  Name m_dsk3;
+
+  Name m_selfSignCert;
+
+  Name m_randomDsk;
+
+  name::Component m_version;
+
+  shared_ptr<ndn::tests::DummyClientFace> m_face;
+};
+
+
+BOOST_FIXTURE_TEST_CASE(Basic, Fixture)
+{
+  // validator must be created after root key is saved to the target
+  ndns::Validator validator(*m_face, TEST_CONFIG_PATH "/" "validator.conf");
+
+  Name dataName(m_testId3);
+  dataName.append("NDNS")
+    .append("rrLabel")
+    .append("rrType")
+    .appendVersion();
+  shared_ptr<Data> data = make_shared<Data>(dataName);
+  m_keyChain.sign(*data, m_dsk3);
+
+  bool hasValidated = false;
+  validator.validate(*data,
+                     [&] (const shared_ptr<const Data>& data) {
+                       hasValidated = true;
+                       BOOST_CHECK(true);
+                     },
+                     [&] (const shared_ptr<const Data>& data, const std::string& str) {
+                       hasValidated = true;
+                       BOOST_CHECK(false);
+                     });
+
+  m_face->processEvents(time::milliseconds(-1));
+
+  BOOST_CHECK_EQUAL(hasValidated, true);
+
+
+  dataName = m_testId2;
+  dataName.append("KEY")
+    .append("rrLabel")
+    .append("ID-CERT")
+    .appendVersion();
+  data = make_shared<Data>(dataName);
+  m_keyChain.sign(*data, m_dsk3); // key's owner's name is longer than data owner's
+
+  hasValidated = false;
+  validator.validate(*data,
+                     [&] (const shared_ptr<const Data>& data) {
+                       hasValidated = true;
+                       BOOST_CHECK(false);
+                     },
+                     [&] (const shared_ptr<const Data>& data, const std::string& str) {
+                       hasValidated = true;
+                       BOOST_CHECK(true);
+                     });
+
+  m_face->processEvents(time::milliseconds(-1));
+  // cannot pass verification due to key's owner's name is longer than data owner's
+  BOOST_CHECK_EQUAL(hasValidated, true);
+
+
+  dataName = m_testId3;
+  dataName.append("KEY")
+    .append("rrLabel")
+    .append("ID-CERT")
+    .appendVersion();
+  data = make_shared<Data>(dataName);
+  m_keyChain.sign(*data, m_selfSignCert);
+
+  hasValidated = false;
+  validator.validate(*data,
+                     [&] (const shared_ptr<const Data>& data) {
+                       hasValidated = true;
+                       BOOST_CHECK(false);
+                     },
+                     [&] (const shared_ptr<const Data>& data, const std::string& str) {
+                       hasValidated = true;
+                       BOOST_CHECK(true);
+                     });
+
+  m_face->processEvents(time::milliseconds(-1));
+  // cannot pass due to self-sign cert is used
+  BOOST_CHECK_EQUAL(hasValidated, true);
+
+  dataName = m_testId2;
+  dataName.append("KEY")
+    .append("rrLabel")
+    .append("ID-CERT")
+    .appendVersion();
+  data = make_shared<Data>(dataName);
+  m_keyChain.sign(*data, m_randomDsk);
+
+  hasValidated = false;
+  validator.validate(*data,
+                     [&] (const shared_ptr<const Data>& data) {
+                       hasValidated = true;
+                       BOOST_CHECK(false);
+                     },
+                     [&] (const shared_ptr<const Data>& data, const std::string& str) {
+                       hasValidated = true;
+                       BOOST_CHECK(true);
+                     });
+
+  m_face->processEvents(time::milliseconds(-1));
+  // cannot pass due to a totally mismatched key
+  BOOST_CHECK_EQUAL(hasValidated, true);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace ndns
+} // namespace ndn
diff --git a/tests/wscript b/tests/wscript
index 0407962..fa36e78 100644
--- a/tests/wscript
+++ b/tests/wscript
@@ -5,12 +5,33 @@
 top = '..'
 
 def build(bld):
-    if bld.env['WITH_TESTS']:
-        unit_tests = bld.program(
-            target='../unit-tests',
-            features='cxx cxxprogram',
-            source=bld.path.ant_glob(['**/*.cpp']),
-            use='ndns-objects',
-            install_path=None,
-            defines="BUILDDIR=\"%s\"" % Context.out_dir,
-          )
+    if not bld.env['WITH_TESTS']:
+        return
+    dst = bld.bldnode.make_node("conf-test/anchors")
+    dst.mkdir()
+
+    bld(features = "subst",
+        name = 'test-validator-conf',
+        source = '../validator.conf.sample.in',
+        target = '../conf-test/validator.conf',
+        use = 'validator-sample',
+        ANCHORPATH='\"anchors/root.cert\"',
+        RELATION='is-prefix-of',
+    )
+
+    bld(features = "subst",
+        name = 'test-logger-conf',
+        source = '../log4cxx.properties.sample.in',
+        target = '../conf-test/log4cxx.properties.sample',
+        is_copy = True,
+        use = 'log4cxx-sample',
+    )
+
+    unit_tests = bld.program(
+        target='../unit-tests',
+        features='cxx cxxprogram',
+        source=bld.path.ant_glob(['**/*.cpp']),
+        use='ndns-objects',
+        install_path=None,
+        defines='TEST_CONFIG_PATH=\"%s/conf-test\"' %(bld.bldnode)
+    )
diff --git a/tools/wscript b/tools/wscript
index 78b50ca..bea6079 100644
--- a/tools/wscript
+++ b/tools/wscript
@@ -1,9 +1,11 @@
 # -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
+import os.path
+
 top = '..'
 
 def build(bld):
-    for app in bld.path.ant_glob('tools/*', dir=True):
+    for app in bld.path.ant_glob('**/*', dir=True):
         if os.path.isdir(app.abspath()):
             bld(features=['cxx', 'cxxprogram'],
                 target = '../bin/%s' % (str(app)),
@@ -11,9 +13,9 @@
                 use = 'ndns-objects',
                 )
 
-    for app in bld.path.ant_glob('tools/*.cpp'):
-        bld(features=['cxxprogram'],
-            target = 'bin/%s' % (str(app.change_ext('','.cpp'))),
+    for app in bld.path.ant_glob('**/*.cpp'):
+        bld(features=['cxx', 'cxxprogram'],
+            target = '../bin/%s' % (str(app.change_ext('','.cpp'))),
             source = app,
             use = 'ndns-objects',
             )
diff --git a/validator.conf.sample.in b/validator.conf.sample.in
new file mode 100644
index 0000000..8aaf58c
--- /dev/null
+++ b/validator.conf.sample.in
@@ -0,0 +1,28 @@
+rule
+{
+  id "NDNS Validator"
+  for data
+  checker
+  {
+    type customized
+    sig-type rsa-sha256
+    key-locator
+    {
+      type name
+      hyper-relation
+      {
+        k-regex ^(<>*)<KEY>(<>*)<><ID-CERT>$
+        k-expand \\1\\2
+        h-relation @RELATION@ ; data is only allowed to be signed by the zone key
+        p-regex ^(<>*)[<KEY><NDNS>](<>*)<><>$
+        p-expand \\1\\2
+      }
+    }
+  }
+}
+
+trust-anchor
+{
+  type file
+  file-name @ANCHORPATH@
+}
diff --git a/wscript b/wscript
index 8963265..6a3c29b 100644
--- a/wscript
+++ b/wscript
@@ -32,8 +32,10 @@
 
     conf.check_sqlite3(mandatory=True)
 
+
     if conf.options.with_tests:
         conf.env['WITH_TESTS'] = True
+        conf.define('NDNS_HAVE_TESTS', 1)
 
     USED_BOOST_LIBS = ['system', 'filesystem']
     if conf.env['WITH_TESTS']:
@@ -43,8 +45,8 @@
     if not conf.options.with_sqlite_locking:
         conf.define('DISABLE_SQLITE3_FS_LOCKING', 1)
 
-    conf.define("DEFAULT_CONFIG_PATH", "%s/ndn" % conf.env['SYSCONFDIR'])
-    conf.define("DEFAULT_DATABASE_PATH", "%s/ndn/ndns" % conf.env['LOCALSTATEDIR'])
+    conf.define("DEFAULT_CONFIG_PATH", "%s/ndns" % conf.env['SYSCONFDIR'])
+    conf.define("DEFAULT_DATABASE_PATH", "%s/ndns" % conf.env['LOCALSTATEDIR'])
 
     conf.write_config_header('src/config.hpp')
 
@@ -70,7 +72,7 @@
         features='cxx',
         name='ndns-objects',
         source=bld.path.ant_glob(['src/**/*.cpp'],
-                                 excl=['src/main.cpp']),
+                                 excl=['src/main.cpp',]),
         use='version NDN_CXX LOG4CXX BOOST',
         includes='src',
         export_includes='src',
@@ -88,7 +90,25 @@
     bld.recurse('tests')
 
     bld.recurse('tools')
-    # bld.install_files('${SYSCONFDIR}/ndn', 'ndns.conf.sample')
+
+    bld(features='subst',
+        source='validator.conf.sample.in',
+        target='validator.conf',
+        install_path='${SYSCONFDIR}/ndns',
+        name='validator-sample',
+        ANCHORPATH='anchors/root.cert',
+        RELATION='is-prefix-of',
+        help='the validator configuration of ndns',
+    )
+
+    bld(features='subst',
+        source='log4cxx.properties.sample.in',
+        target='log4cxx.properties.sample',
+        install_path='${SYSCONFDIR}/ndns',
+        is_copy = True,
+        name='log4cxx-sample',
+        help='the log4cxx configration file',
+    )
 
 def docs(bld):
     from waflib import Options