mgmt: management tools

refs #2034

Change-Id: I43ff11e0aa7e67f6207e75f139b7417779590efc
diff --git a/src/daemon/db-mgr.cpp b/src/daemon/db-mgr.cpp
index 35af5de..53d3822 100644
--- a/src/daemon/db-mgr.cpp
+++ b/src/daemon/db-mgr.cpp
@@ -174,6 +174,31 @@
   return zone.getId() != 0;
 }
 
+std::vector<Zone>
+DbMgr::listZones()
+{
+  sqlite3_stmt* stmt;
+  const char* sql = "SELECT id, name, ttl FROM zones";
+  int rc = sqlite3_prepare_v2(m_conn, sql, -1, &stmt, 0);
+  if (rc != SQLITE_OK) {
+    throw PrepareError(sql);
+  }
+
+  std::vector<Zone> vec;
+
+  while (sqlite3_step(stmt) == SQLITE_ROW) {
+    vec.emplace_back();
+    Zone& zone = vec.back();
+    zone.setId(sqlite3_column_int64(stmt, 0));
+    zone.setTtl(time::seconds(sqlite3_column_int(stmt, 2)));
+    zone.setName(Name(Block(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 1)),
+                            sqlite3_column_bytes(stmt, 1))));
+  }
+  sqlite3_finalize(stmt);
+
+  return vec;
+}
+
 void
 DbMgr::remove(Zone& zone)
 {
diff --git a/src/daemon/db-mgr.hpp b/src/daemon/db-mgr.hpp
index ca83691..336b330 100644
--- a/src/daemon/db-mgr.hpp
+++ b/src/daemon/db-mgr.hpp
@@ -113,6 +113,12 @@
   find(Zone& zone);
 
   /**
+   * @brief get all zones in the database
+   */
+  std::vector<Zone>
+  listZones();
+
+  /**
    * @brief remove the zone
    * @pre m_zone.getId() > 0
    * @post m_zone.getId() == 0
diff --git a/src/mgmt/management-tool.cpp b/src/mgmt/management-tool.cpp
new file mode 100644
index 0000000..619d1c0
--- /dev/null
+++ b/src/mgmt/management-tool.cpp
@@ -0,0 +1,642 @@
+/* -*- 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 "management-tool.hpp"
+#include "logger.hpp"
+#include "ndns-label.hpp"
+#include "ndns-tlv.hpp"
+
+#include <string>
+#include <iomanip>
+
+#include <boost/filesystem/operations.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/algorithm/string/replace.hpp>
+
+#include <ndn-cxx/util/io.hpp>
+#include <ndn-cxx/util/regex.hpp>
+#include <ndn-cxx/encoding/oid.hpp>
+#include <ndn-cxx/security/cryptopp.hpp>
+
+namespace ndn {
+namespace ndns {
+
+NDNS_LOG_INIT("ManagementTool");
+
+ManagementTool::ManagementTool(const std::string& dbFile)
+  : m_dbMgr(dbFile)
+{
+}
+
+void
+ManagementTool::createZone(const Name &zoneName,
+                           const Name& parentZoneName,
+                           const time::seconds& cacheTtl,
+                           const time::seconds& certTtl,
+                           const Name& kskCertName,
+                           const Name& dskCertName)
+{
+  bool isRoot = zoneName == ROOT_ZONE;
+
+  //check preconditions
+  Zone zone(zoneName, cacheTtl);
+  if (m_dbMgr.find(zone)) {
+    throw Error(zoneName.toUri() + " is already presented in the NDNS db");
+  }
+
+  if (!isRoot && parentZoneName.equals(zoneName)) {
+    throw Error("Parent zone name can not be the zone itself");
+  }
+
+  if (!isRoot && !parentZoneName.isPrefixOf(zoneName)) {
+    throw Error(parentZoneName.toUri() + " is not a prefix of " + zoneName.toUri());
+  }
+
+  if (kskCertName != DEFAULT_CERT) {
+    if (!matchCertificate(kskCertName, zoneName)) {
+      throw Error("Cannot verify KSK certificate");
+    }
+  }
+
+  if (dskCertName != DEFAULT_CERT) {
+    if (!matchCertificate(dskCertName, zoneName)) {
+      throw Error("Cannot verify DSK certificate");
+    }
+  }
+
+  if (kskCertName == DEFAULT_CERT && isRoot) {
+    throw Error("Cannot generate KSK for root zone");
+  }
+
+  //first generate KSK and DSK to the keyChain system, and add DSK as default
+  NDNS_LOG_INFO("Start generating KSK and DSK and their corresponding certificates");
+  time::system_clock::TimePoint notBefore = time::system_clock::now();
+  time::system_clock::TimePoint notAfter = notBefore + certTtl;
+  shared_ptr<IdentityCertificate> kskCert;
+
+  if (kskCertName == DEFAULT_CERT) {
+    //create KSK's certificate
+    Name kskName = m_keyChain.generateRsaKeyPair(zoneName, true);
+    std::vector<CertificateSubjectDescription> kskDesc;
+    kskCert = m_keyChain.prepareUnsignedIdentityCertificate(kskName, zoneName, notBefore, notAfter,
+                                                            kskDesc);
+    //prepare the correct name for the ksk certificate
+    Name newScertName = parentZoneName;
+    newScertName.append(label::NDNS_CERT_QUERY);
+    newScertName.append(zoneName.getSubName(parentZoneName.size()));
+    //remove the zone prefix and KEY
+    newScertName.append(kskCert->getName().getSubName(zoneName.size()+1));
+    kskCert->setName(newScertName);
+
+    m_keyChain.selfSign(*kskCert);
+    m_keyChain.addCertificate(*kskCert);
+    NDNS_LOG_INFO("Generated KSK: " << kskCert->getName().toUri());
+  }
+  else {
+    kskCert = m_keyChain.getCertificate(kskCertName);
+  }
+
+  Name dskName;
+  shared_ptr<IdentityCertificate> dskCert;
+  if (dskCertName == DEFAULT_CERT) {
+    dskName = m_keyChain.generateRsaKeyPairAsDefault(zoneName, false);
+    //create DSK's certificate
+    std::vector<CertificateSubjectDescription> dskDesc;
+    dskCert = m_keyChain.prepareUnsignedIdentityCertificate(dskName, zoneName, notBefore, notAfter,
+                                                            dskDesc);
+    m_keyChain.sign(*dskCert, kskCert->getName());
+    m_keyChain.addCertificateAsKeyDefault(*dskCert);
+    NDNS_LOG_INFO("Generated DSK: " << dskCert->getName().toUri());
+  }
+  else {
+    dskCert = m_keyChain.getCertificate(dskCertName);
+    dskName = dskCert->getPublicKeyName();
+    m_keyChain.setDefaultKeyNameForIdentity(dskName);
+    m_keyChain.setDefaultCertificateNameForKey(dskCert->getName());
+  }
+
+  //second add zone to the database
+  NDNS_LOG_INFO("Start adding new zone to data base");
+  addZone(zone);
+
+  //third create ID-cert
+  NDNS_LOG_INFO("Start creating DSK's ID-CERT");
+  addIdCert(zone, dskCert, cacheTtl);
+}
+
+void
+ManagementTool::deleteZone(const Name& zoneName)
+{
+  //check pre-conditions
+  Zone zone(zoneName);
+  if (!m_dbMgr.find(zone)) {
+    throw Error(zoneName.toUri() + " is not presented in the NDNS db");
+  }
+
+  //first remove all rrsets of this zone from local ndns database
+  std::vector<Rrset> rrsets = m_dbMgr.findRrsets(zone);
+  for (Rrset& rrset : rrsets) {
+    m_dbMgr.remove(rrset);
+  }
+
+  //second remove zone from local ndns database
+  removeZone(zone);
+
+  //third remove identity
+  m_keyChain.deleteIdentity(zoneName);
+}
+
+void
+ManagementTool::exportCertificate(const Name& certName, const std::string& outFile)
+{
+  //search for the certificate, start from KeyChain then local NDNS database
+  shared_ptr<IdentityCertificate> cert;
+  if (m_keyChain.doesCertificateExist(certName)) {
+    cert = m_keyChain.getCertificate(certName);
+  }
+  else {
+    shared_ptr<Regex> regex = make_shared<Regex>("(<>*)<KEY>(<>+)<ID-CERT><>");
+    if (regex->match(certName) != true) {
+      throw Error("Certificate name is illegal");
+    }
+    Name zoneName = regex->expand("\\1");
+    Name label = regex->expand("\\2");
+
+    Zone zone(zoneName);
+    Rrset rrset(&zone);
+    rrset.setLabel(label);
+    rrset.setType(label::CERT_RR_TYPE);
+    if (m_dbMgr.find(rrset)) {
+      Data data(rrset.getData());
+      cert = make_shared<IdentityCertificate>(data);
+    }
+    else {
+      throw Error("Cannot find the cert: " + certName.toUri());
+    }
+  }
+
+  if (outFile == DEFAULT_IO) {
+    ndn::io::save(*cert, std::cout);
+  }
+  else {
+    ndn::io::save(*cert, outFile);
+    NDNS_LOG_INFO("save cert to file: " << outFile);
+  }
+}
+
+void
+ManagementTool::addRrSet(const Name& zoneName,
+                         const Name& label,
+                         const name::Component& type,
+                         NdnsType ndnsType,
+                         const uint64_t version,
+                         const std::vector<std::string>& contents,
+                         const Name& inputDskCertName,
+                         const time::seconds& ttl)
+{
+  // check pre-condition
+  Zone zone(zoneName);
+  if (!m_dbMgr.find(zone)) {
+    throw Error(zoneName.toUri() + " is not presented in the NDNS db");
+  }
+
+  if (ndnsType == NDNS_UNKNOWN) {
+    throw Error("The ndns type is unknown");
+  }
+
+  if (type == label::CERT_RR_TYPE) {
+    throw Error("It cannot handle ID-CERT rrset type");
+  }
+
+  // check strange rr type and ndns type combination
+  if (type == label::NS_RR_TYPE && ndnsType == NDNS_RAW) {
+    throw Error("NS cannot be of the type NDNS_RAW");
+  }
+
+  if (type == label::TXT_RR_TYPE && ndnsType != NDNS_RESP) {
+    throw Error("TXT cannot be of the type NDNS_RAW or NDNS_AUTH");
+  }
+
+  if (ndnsType == NDNS_RAW && contents.size() != 1) {
+    throw Error("NDNS_RAW must contain a single content element");
+  }
+
+  Name dskName;
+  Name dskCertName = inputDskCertName;
+  if (dskCertName == DEFAULT_CERT) {
+    dskName = m_keyChain.getDefaultKeyNameForIdentity(zoneName);
+    dskCertName = m_keyChain.getDefaultCertificateNameForKey(dskName);
+  }
+  else {
+    if (!matchCertificate(dskCertName, zoneName)) {
+      throw Error("Cannot verify certificate");
+    }
+  }
+
+  // set rrset
+  Rrset rrset(&zone);
+  rrset.setLabel(label);
+  rrset.setType(type);
+  if (ttl == DEFAULT_RR_TTL)
+    rrset.setTtl(zone.getTtl());
+  else
+    rrset.setTtl(ttl);
+
+  // set response
+  Response re;
+  re.setZone(zoneName);
+  re.setQueryType(label::NDNS_ITERATIVE_QUERY);
+  re.setRrLabel(label);
+  re.setRrType(type);
+  re.setNdnsType(ndnsType);
+
+  //set content according to ndns type
+  if (ndnsType == NDNS_RAW) {
+    Block tmp = ndn::dataBlock(ndn::tlv::Content, contents[0].c_str(), contents[0].length());
+    re.setAppContent(tmp);
+  }
+  else if (ndnsType != NDNS_AUTH) {
+    if (contents.empty()) {
+      re.addRr("");
+    }
+    else {
+      for (const auto& item : contents) {
+        re.addRr(item);
+      }
+    }
+  }
+
+  shared_ptr<Data> data = re.toData();
+  if (version != VERSION_USE_UNIX_TIMESTAMP) {
+    name::Component tmp = name::Component::fromVersion(version);
+    re.setVersion(tmp);
+  }
+  m_keyChain.sign(*data, dskCertName);
+
+  rrset.setVersion(re.getVersion());
+  rrset.setData(data->wireEncode());
+
+  if (m_dbMgr.find(rrset)) {
+    throw Error("Rrset with label=" + label.toUri() + " is already in local NDNS databse");
+  }
+  NDNS_LOG_INFO("Add rrset with zone-id: " << zone.getId() << " label: " << label << " type: "
+                << type);
+  m_dbMgr.insert(rrset);
+}
+
+void
+ManagementTool::addRrSet(const Name& zoneName,
+                         const std::string& inFile,
+                         const time::seconds& ttl,
+                         const Name& inputDskCertName)
+{
+  //check precondition
+  Zone zone(zoneName);
+  if (!m_dbMgr.find(zone)) {
+    throw Error(zoneName.toUri() + " is not presented in the NDNS db");
+  }
+
+  Name dskName;
+  Name dskCertName = inputDskCertName;
+  if (dskCertName == DEFAULT_CERT) {
+    dskName = m_keyChain.getDefaultKeyNameForIdentity(zoneName);
+    dskCertName = m_keyChain.getDefaultCertificateNameForKey(dskName);
+  }
+  else {
+    if (!matchCertificate(dskCertName, zoneName)) {
+      throw Error("Cannot verify certificate");
+    }
+  }
+
+  if (inFile != DEFAULT_IO) {
+    boost::filesystem::path dir = boost::filesystem::path(inFile);
+    if (!boost::filesystem::exists(dir) || boost::filesystem::is_directory(dir)) {
+      throw Error("Data: " + inFile + " does not exist");
+    }
+  }
+
+  //first load the data
+  shared_ptr<Data> data;
+  if (inFile == DEFAULT_IO)
+    data = ndn::io::load<ndn::Data>(std::cin);
+  else
+    data = ndn::io::load<ndn::Data>(inFile);
+
+  //determine whether the data is a self-signed certificate
+  shared_ptr<Regex> regex1 = make_shared<Regex>("(<>*)<KEY>(<>+)<ID-CERT><>");
+  if (regex1->match(data->getName())) {
+    IdentityCertificate scert(*data);
+    Name keyName = scert.getPublicKeyName();
+    Name keyLocator = scert.getSignature().getKeyLocator().getName();
+
+    //if it is, extract the content and name from the data, and resign it using the dsk.
+    shared_ptr<Regex> regex2 = make_shared<Regex>("(<>*)<KEY>(<>+)<ID-CERT>");
+    BOOST_VERIFY(regex2->match(keyLocator) == true);
+    if (keyName == regex2->expand("\\1\\2")) {
+      shared_ptr<Data> pre = data;
+      Name name = pre->getName();
+      //check whether the name is legal or not. if not converting it to a legal name
+      if (zoneName != regex1->expand("\\1")) {
+        Name comp1 = regex1->expand("\\1").getSubName(zoneName.size());
+        Name comp2 = regex1->expand("\\2");
+        name = zoneName;
+        name.append("KEY");
+        name.append(comp1);
+        name.append(comp2);
+        name.append("ID-CERT");
+        name.append(pre->getName().get(-1));
+      }
+
+      data = make_shared<Data>();
+      data->setName(name);
+      data->setContent(pre->getContent());
+
+      m_keyChain.sign(*data, dskCertName);
+    }
+  }
+
+  // create response for the input data
+  Response re;
+  Name hint;
+  re.fromData(hint, zoneName, *data);
+  Name label = re.getRrLabel();
+  name::Component type = re.getRrType();
+
+  Rrset rrset(&zone);
+  rrset.setLabel(label);
+  rrset.setType(type);
+  if (ttl == DEFAULT_RR_TTL)
+    rrset.setTtl(zone.getTtl());
+  else
+    rrset.setTtl(ttl);
+  rrset.setVersion(re.getVersion());
+  rrset.setData(data->wireEncode());
+
+  if (m_dbMgr.find(rrset)) {
+    throw Error("Rrset with label=" + label.toUri() + " is already in local NDNS databse");
+  }
+  NDNS_LOG_INFO("Add rrset with zone-id: " << zone.getId() << " label: " << label << " type: "
+                << type);
+  m_dbMgr.insert(rrset);
+}
+
+void
+ManagementTool::listZone(const Name& zoneName, std::ostream& os, const bool printRaw) {
+  Zone zone(zoneName);
+  if (!m_dbMgr.find(zone)) {
+    os << "No record is found" << std::endl;
+    return;
+  }
+
+  //first output the zone name
+  os << "; Zone " << zoneName.toUri() << std::endl << std::endl;
+
+  //second output all rrsets
+  std::vector<Rrset> rrsets = m_dbMgr.findRrsets(zone);
+
+  //set width for different columns
+  size_t labelWidth = 0;
+  size_t ttlWidth = 0;
+  size_t typeWidth = 0;
+  for (Rrset& rrset : rrsets) {
+    Data data(rrset.getData());
+    Response re;
+    Name hint;
+    re.fromData(hint, zoneName, data);
+
+    if (rrset.getLabel().toUri().size() > labelWidth)
+      labelWidth = rrset.getLabel().toUri().size();
+
+    std::stringstream seconds;
+    seconds << rrset.getTtl().count();
+    if (seconds.str().size() > ttlWidth)
+      ttlWidth = seconds.str().size();
+
+    if (rrset.getType().toUri().size() > typeWidth)
+      typeWidth = rrset.getType().toUri().size();
+  }
+
+  //output
+  for (Rrset& rrset : rrsets) {
+    Data data(rrset.getData());
+    Response re;
+    Name hint;
+    re.fromData(hint, zoneName, data);
+    int iteration = re.getNdnsType() == NDNS_RAW || re.getNdnsType() == NDNS_AUTH ?
+                      1 : re.getRrs().size();
+    const std::vector<Block> &rrs = re.getRrs();
+
+    if (re.getNdnsType() != NDNS_RAW && re.getNdnsType() != NDNS_AUTH) {
+      os << "; rrset=" << rrset.getLabel().toUri()
+         << " type=" << rrset.getType().toUri()
+         << " version=" << rrset.getVersion().toUri()
+         << " signed-by=" << data.getSignature().getKeyLocator().getName().toUri()
+         << std::endl;
+    }
+
+    for (int i = 0; i < iteration; i++) {
+      os.setf(os.left);
+      os.width(labelWidth + 2);
+      os << rrset.getLabel().toUri();
+
+      os.width(ttlWidth + 2);
+      os << rrset.getTtl().count();
+
+      os.width(typeWidth + 2);
+      os << rrset.getType().toUri();
+
+      if (re.getNdnsType() != NDNS_RAW && re.getNdnsType() != NDNS_AUTH) {
+        using namespace CryptoPP;
+        if (rrset.getType() == label::TXT_RR_TYPE) {
+          os.write(reinterpret_cast<const char*>(rrs[i].value()), rrs[i].value_size());
+          os << std::endl;
+        }
+        else if (rrset.getType() == label::NS_RR_TYPE) {
+          //TODO output the NS data once we have it
+          os << std::endl;
+        }
+        else {
+          StringSource ss(rrs[i].wire(), rrs[i].size(), true,
+                          new Base64Encoder(new FileSink(os), true, 64));
+        }
+      }
+    }
+
+    if (re.getNdnsType() == NDNS_RAW || re.getNdnsType() == NDNS_AUTH) {
+      os.width();
+      os << "; content-type=" << re.getNdnsType()
+         << " version=" << rrset.getVersion().toUri()
+         << " signed-by=" << data.getSignature().getKeyLocator().getName().toUri();
+      os << std::endl;
+
+      if (printRaw && re.getNdnsType() == NDNS_RAW) {
+        using namespace CryptoPP;
+        std::stringstream sstream;
+        StringSource ss(re.getAppContent().wire(), re.getAppContent().size(), true,
+                        new Base64Encoder(new FileSink(sstream), true, 64));
+
+        std::string content = sstream.str();
+        std::string delimiter = "\n";
+        size_t pos = 0;
+        std::string token;
+        while ((pos = content.find(delimiter)) != std::string::npos) {
+            token = content.substr(0, pos);
+            os << "; " << token << std::endl;
+            content.erase(0, pos + delimiter.length());
+        }
+
+        os << std::endl;
+      }
+    }
+    else {
+      os << std::endl;
+    }
+  }
+}
+
+void
+ManagementTool::listAllZones(std::ostream& os) {
+  std::vector<Zone> zones = m_dbMgr.listZones();
+
+  size_t nameWidth = 0;
+  for (const Zone& zone : zones) {
+    if (zone.getName().toUri().size() > nameWidth)
+      nameWidth = zone.getName().toUri().size();
+  }
+
+  for (const Zone& zone : zones) {
+    os.setf(os.left);
+    os.width(nameWidth + 2);
+    os << zone.getName().toUri();
+
+    os << "; default-ttl=" << zone.getTtl().count();
+    os << " default-key=" << m_keyChain.getDefaultKeyNameForIdentity(zone.getName());
+    os << " default-certificate="
+       << m_keyChain.getDefaultCertificateNameForIdentity(zone.getName());
+    os << std::endl;
+  }
+}
+
+void
+ManagementTool::removeRrSet(const Name& zoneName, const Name& label, const name::Component& type)
+{
+  Zone zone(zoneName);
+  Rrset rrset(&zone);
+  rrset.setLabel(label);
+  rrset.setType(type);
+
+  if (!m_dbMgr.find(rrset)) {
+    return;
+  }
+  NDNS_LOG_INFO("Remove rrset with zone-id: " << zone.getId() << " label: " << label << " type: "
+                << type);
+  m_dbMgr.remove(rrset);
+}
+
+void
+ManagementTool::getRrSet(const Name& zoneName,
+                         const Name& label,
+                         const name::Component& type,
+                         std::ostream& os)
+{
+  Zone zone(zoneName);
+  Rrset rrset(&zone);
+  rrset.setLabel(label);
+  rrset.setType(type);
+
+  if (!m_dbMgr.find(rrset)) {
+    os << "No record is found" << std::endl;
+    return;
+  }
+
+  using namespace CryptoPP;
+  StringSource ss(rrset.getData().wire(), rrset.getData().size(), true,
+                  new Base64Encoder(new FileSink(os), true, 64));
+}
+
+void
+ManagementTool::addIdCert(Zone& zone, shared_ptr<IdentityCertificate> cert,
+                          const time::seconds& ttl)
+{
+  Rrset rrset(&zone);
+  size_t size = zone.getName().size();
+  Name label = cert->getName().getSubName(size + 1, cert->getName().size() - size - 3);
+  rrset.setLabel(label);
+  rrset.setType(label::CERT_RR_TYPE);
+  rrset.setTtl(ttl);
+  rrset.setVersion(cert->getName().get(-1));
+  rrset.setData(cert->wireEncode());
+
+  if (m_dbMgr.find(rrset)) {
+    throw Error("ID-CERT with label=" + label.toUri() +
+                " is already presented in local NDNS databse");
+  }
+  NDNS_LOG_INFO("Add rrset with zone-id: " << zone.getId() << " label: " << label << " type: "
+                << label::CERT_RR_TYPE);
+  m_dbMgr.insert(rrset);
+}
+
+void
+ManagementTool::addZone(Zone& zone)
+{
+  if (m_dbMgr.find(zone)) {
+    throw Error("Zone with Name=" + zone.getName().toUri() +
+                " is already presented in local NDNS databse");
+  }
+  NDNS_LOG_INFO("Add zone with Name: " << zone.getName().toUri());
+  m_dbMgr.insert(zone);
+}
+
+void
+ManagementTool::removeZone(Zone& zone)
+{
+  if (!m_dbMgr.find(zone)) {
+    return;
+  }
+  NDNS_LOG_INFO("Remove zone with Name: " << zone.getName().toUri());
+  m_dbMgr.remove(zone);
+}
+
+bool
+ManagementTool::matchCertificate(const Name& certName, const Name& identity)
+{
+  if (!m_keyChain.doesCertificateExist(certName)) {
+    NDNS_LOG_WARN(certName.toUri() << " is not presented in KeyChain");
+    return false;
+  }
+
+  //check its public key information
+  shared_ptr<IdentityCertificate> cert = m_keyChain.getCertificate(certName);
+  Name keyName = cert->getPublicKeyName();
+
+  if (!identity.isPrefixOf(keyName) || identity.size()!=keyName.size()-1) {
+    NDNS_LOG_WARN(keyName.toUri() << " is not a key of " << identity.toUri());
+    return false;
+  }
+
+  if (!m_keyChain.doesKeyExistInTpm(keyName, KEY_CLASS_PRIVATE)) {
+    NDNS_LOG_WARN("Private key: " << keyName.toUri() << " is not presented in KeyChain");
+    return false;
+  }
+
+  return true;
+}
+
+} // namespace ndns
+} // namespace ndn
diff --git a/src/mgmt/management-tool.hpp b/src/mgmt/management-tool.hpp
new file mode 100644
index 0000000..f962b11
--- /dev/null
+++ b/src/mgmt/management-tool.hpp
@@ -0,0 +1,233 @@
+/* -*- 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_MGMT_MANAGEMENT_TOOL_HPP
+#define NDNS_MGMT_MANAGEMENT_TOOL_HPP
+
+#include "config.hpp"
+#include "ndns-enum.hpp"
+#include "./daemon/zone.hpp"
+#include "./daemon/db-mgr.hpp"
+#include "./daemon/rrset.hpp"
+#include "./clients/response.hpp"
+
+#include <stdexcept>
+#include <ndn-cxx/common.hpp>
+#include <ndn-cxx/security/identity-certificate.hpp>
+#include <ndn-cxx/security/key-chain.hpp>
+
+namespace ndn {
+namespace ndns {
+
+static const Name DEFAULT_CERT;
+static const Name ROOT_ZONE;
+static const time::seconds DEFAULT_CACHE_TTL = time::seconds(3600);
+static const time::seconds DEFAULT_CERT_TTL = time::days(365);
+static const std::vector<std::string> DEFAULT_CONTENTS;
+static const std::string DEFAULT_IO = "-";
+static const time::seconds DEFAULT_RR_TTL = time::seconds(0);
+static constexpr uint64_t VERSION_USE_UNIX_TIMESTAMP = std::numeric_limits<uint64_t>::max();
+
+/**
+ * @brief provides management tools to the NDNS system, such as zone creation, zone delegation, DSK
+ * generation and root zone creation.
+ */
+class ManagementTool : noncopyable
+{
+public:
+  /** @brief Represents an error might be thrown during runtime
+   */
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what) : std::runtime_error(what)
+    {
+    }
+  };
+
+  /** @param certDir Path to the directory to store certificates
+   *  @param dbFile Path to the local database
+   */
+  explicit
+  ManagementTool(const std::string& dbFile);
+
+  /** @brief Create a Zone according to a given name.
+   *
+   *  Specifically, It will generate a KSK and a DSK (and their certificates) to the following
+   *  places:
+   *  1) Local NDNS database: a new zone is added.
+   *  2) Local NDNS database: an ID-CERT of the DSK is added.
+   *  3) KeyChain: an identity named with zone name is added.
+   *  4) KeyChain: a KSK and its self-signed certificate is added. The ownership of the KSK is the
+   *  parent zone.
+   *  5) KeyChain: a DSK and its KSK signed certificate is added.
+   *
+   *  -SS.cert (self-signed)
+   *  -SKS.cert (self's Key signed)
+   *  -PKS.cert (parent's Key Signed)
+   *
+   *  @attention
+   *  1) to create root zone, supply zoneName and parentZoneName both with ROOT_ZONE
+   *
+   *  @param zoneName zone's name
+   *  @param parentZoneName parent zone's name
+   *  @param ttl ttl for the created zone
+   *  @param kskCertName if given, a zone will be created with this ksk certificate and its key
+   *  @param kskCertName if given, a zone will be created with this dsk certificate and its key
+   */
+  void
+  createZone(const Name& zoneName,
+             const Name& parentZoneName,
+             const time::seconds& cacheTtl = DEFAULT_CACHE_TTL,
+             const time::seconds& certTtl = DEFAULT_CERT_TTL,
+             const Name& kskCertName = DEFAULT_CERT,
+             const Name& dskCertName = DEFAULT_CERT);
+
+  /** @brief Delete a Zone according to a given name.
+   *
+   *  Specifically, It will do the following things:
+   *  1) KeyChain System: delete the Identity with zone name and all its keys/certificates
+   *  2) Local NDNS database: delete the zone record
+   *  3) Local NDNS database: delete the ID-CERT of the zone's DSK
+   */
+  void
+  deleteZone(const Name& zoneName);
+
+  /** @brief Export the certificate to file system
+   *
+   *  @param certName the name of the certificate to be exported
+   *  @param output the path to output to-be exported file, including the file name
+   */
+  void
+  exportCertificate(const Name& certName, const std::string& outFile = DEFAULT_IO);
+
+  /** @brief add rrset to the NDNS local database
+   *
+   *  This one is only capable of adding NS type including NDNS_RESP and NDNS_AUTH and user defined
+   *  type with string content.
+   *  Other complicated situations can be handled by the other addRrSet() overload function.
+   *
+   *  @param zoneName the name of the zone to hold the rrset
+   *  @param label the rrset label
+   *  @param type the rrset type
+   *  @param ndnsType the ndnsType of the response, for user-defined type, just set it NDNS_RAW
+   *  @param version the version of the response and rrset, default is Unix Timestamp
+   *  @param content the content of the response
+   *  @param dskName the DSK to signed the response, default is the zone's DSK
+   *  @param ttl the ttl of the rrset
+    */
+  void
+  addRrSet(const Name& zoneName,
+           const Name& label,
+           const name::Component& type,
+           NdnsType ndnsType,
+           const uint64_t version = VERSION_USE_UNIX_TIMESTAMP,
+           const std::vector<std::string>& contents = DEFAULT_CONTENTS,
+           const Name& dskCertName = DEFAULT_CERT,
+           const time::seconds& ttl = DEFAULT_RR_TTL);
+
+  /** @brief add rrset to the NDNS local database
+   *
+   *  This overload is capable of adding any data to the rrset as long as the supplied data is
+   *  valid.
+   *  A special case is to add the ID-CERT of KSK to the parent zone. At this case, the SS cert
+   *  should be supplied, and therefore it will use the parent zone's DSK to resign the certificate.
+   *  For other cases, the data will be added directly without any modification.
+   *
+   *  @param zoneName the name of the zone to hold the rrset
+   *  @param dataPath the path to the supplied data
+   *  @param ttl the ttl of the rrset
+   *  @param dskName the DSK to signed the special case, default is the zone's DSK
+    */
+  void
+  addRrSet(const Name& zoneName,
+           const std::string& inFile = DEFAULT_IO,
+           const time::seconds& ttl = DEFAULT_RR_TTL,
+           const Name& dskCertName = DEFAULT_CERT);
+
+  /** @brief remove rrset from the NDNS local database
+   *
+   *  @param zonName the name of the zone holding the rrset
+   *  @param label rrset's label
+   *  @param type rrset's type
+    */
+  void
+  removeRrSet(const Name& zoneName, const Name& label, const name::Component& type);
+
+  /** @brief output the raw data of the selected rrset
+   *
+   *  @param zonName the name of the zone holding the rrset
+   *  @param label rrset's label
+   *  @param type rrset's type
+   *  @param os the ostream to print information to
+   *  @param isPP indicate pretty print
+    */
+  void
+  getRrSet(const Name& zoneName,
+           const Name& label,
+           const name::Component& type,
+           std::ostream& os);
+
+  /** @brief generates an output like DNS zone file. Reference:
+   *  http://en.wikipedia.org/wiki/Zone_file
+   *
+   *  @param zoneName the name of the zone to investigate
+   *  @param os the ostream to print information to
+   *  @param printRaw set to print content of ndns-raw rrset
+    */
+  void
+  listZone(const Name& zoneName, std::ostream& os, const bool printRaw = false);
+
+  /** @brief lists all existing zones within this name server.
+   *
+   *  @param os the ostream to print information to
+    */
+  void
+  listAllZones(std::ostream& os);
+
+private:
+  /** @brief add ID-CERT to the NDNS local database
+    */
+  void
+  addIdCert(Zone& zone, shared_ptr<IdentityCertificate> cert, const time::seconds& ttl);
+
+  /** @brief add zone to the NDNS local database
+    */
+  void
+  addZone(Zone& zone);
+
+  /** @brief remove zone from the NDNS local database
+    */
+  void
+  removeZone(Zone& zone);
+
+  /** @brief determine whether a certificate matches with both the identity and key type
+   */
+  bool
+  matchCertificate(const Name& certName, const Name& identity);
+
+private:
+  KeyChain m_keyChain;
+  DbMgr m_dbMgr;
+};
+
+} // namespace ndns
+} // namespace ndn
+#endif // NDNS_MGMT_MANAGEMENT_TOOL_HPP