Move pib service from ndn-cxx to ndn-tools

Code is copied from ndn-cxx:commit:e1c8c4cfffb33dca13c8ca7d7ef04c7401a7cbef,
(gerrit change-id Ieea485c0ebdce9fb9c876cad005cb95fd8e0c899)
with minor changes for changing include paths and building script.

Change-Id: I77b94fe69b20f04c338e7be7387125f709fa9e1a
Refs: #3018
diff --git a/AUTHORS.md b/AUTHORS.md
index 3d40a6f..0915877 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -12,3 +12,4 @@
 * Junxiao Shi           <http://www.cs.arizona.edu/people/shijunxiao/>
 * Eric Newberry         <http://ericnewberry.com/>
 * Xiaoke Jiang          <http://netarchlab.tsinghua.edu.cn/~shock/>
+* Yingdi Yu             <http://irl.cs.ucla.edu/~yingdi/>
diff --git a/manpages/conf.py b/manpages/conf.py
index 3c0f6bb..d86777a 100644
--- a/manpages/conf.py
+++ b/manpages/conf.py
@@ -12,4 +12,5 @@
     ('ndnpingserver', 'ndnpingserver', 'reachability testing server', None, 1),
     ('ndndump', 'ndndump', 'traffic analysis tool', None, 8),
     ('ndn-dissect', 'ndn-dissect', 'NDN packet format inspector', None, 1),
+    ('ndn-pib', 'ndn-pib',  'NDN PIB service', None, 1),
 ]
diff --git a/manpages/ndn-pib.rst b/manpages/ndn-pib.rst
new file mode 100644
index 0000000..e94a7d2
--- /dev/null
+++ b/manpages/ndn-pib.rst
@@ -0,0 +1,35 @@
+ndn-pib
+========
+
+``ndn-pib`` is a public key and certificate management and publishing service.
+
+Usage
+-----
+
+::
+
+    ndn-pib [-h] -o owner -d database_dir -t tpm_locator
+
+Description
+-----------
+
+This command will start a PIB service process which serves signing key lookup/management for local
+applications and also public the public key certificate of signing keys.  The lookup/management
+interface listens on a prefix "/localhost/pib/[OwnerName]" and accept five types of command (get,
+default, list, update, delete). More details can be found at `Public key Info Base (PIB) Service
+<http://redmine.named-data.net/projects/ndn-cxx/wiki/PublicKey_Info_Base>`__
+
+Since PIB service is queried through Interest/Data exchange, NFD is required to run before this
+command is executed.
+
+This command requires three arguments: ``owner`` specify the owner of the pib service, which is also
+the third name component in the local lookup/management prefix; ``database_dir`` is the path to the
+database where all the public key, certificate, identity are stored and managed; ``tpm_locator`` is
+locator of a TPM which stores the corresponding private key of the public keys in PIB.
+
+Example
+-------
+
+::
+
+    $ ndn-pib -o alice -d /var/pib -t tpm-file:/var/ndn/tpm
diff --git a/tests/pib/cert-publicher.t.cpp b/tests/pib/cert-publicher.t.cpp
new file mode 100644
index 0000000..f33f325
--- /dev/null
+++ b/tests/pib/cert-publicher.t.cpp
@@ -0,0 +1,132 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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 "tools/pib/cert-publisher.hpp"
+#include "identity-management-time-fixture.hpp"
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
+#include <boost/filesystem.hpp>
+
+#include "tests/test-common.hpp"
+
+namespace ndn {
+namespace pib {
+namespace tests {
+
+class CertPublisherFixture : public ndn::security::IdentityManagementTimeFixture
+{
+public:
+  CertPublisherFixture()
+    : tmpPath(boost::filesystem::path(TMP_TESTS_PATH) / "DbTest")
+    , db(tmpPath.c_str())
+    , face(util::makeDummyClientFace(io, {true, true}))
+  {
+  }
+
+  ~CertPublisherFixture()
+  {
+    boost::filesystem::remove_all(tmpPath);
+  }
+
+  boost::asio::io_service io;
+  boost::filesystem::path tmpPath;
+  PibDb db;
+  shared_ptr<util::DummyClientFace> face;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestCertPublisher, CertPublisherFixture)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  // Initialize id1
+  Name id1("/test/identity");
+  addIdentity(id1);
+  Name certName111 = m_keyChain.getDefaultCertificateNameForIdentity(id1);
+  shared_ptr<IdentityCertificate> cert111 = m_keyChain.getCertificate(certName111);
+  Name keyName11 = cert111->getPublicKeyName();
+
+  advanceClocks(io, time::milliseconds(100));
+  shared_ptr<IdentityCertificate> cert112 = m_keyChain.selfSign(keyName11);
+  Name certName112 = cert112->getName();
+
+  CertPublisher certPublisher(*face, db);
+
+  // Add a certificate
+  db.addCertificate(*cert111);
+  advanceClocks(io, time::milliseconds(2), 50);
+
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 0);
+  auto interest111 = make_shared<Interest>(cert111->getName().getPrefix(-1));
+  face->receive(*interest111);
+  advanceClocks(io, time::milliseconds(2), 50);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  BOOST_CHECK(face->sentDatas[0].wireEncode() == cert111->wireEncode());
+  face->sentDatas.clear();
+
+  // Add another certificate
+  db.addCertificate(*cert112);
+  advanceClocks(io, time::milliseconds(2), 50);
+
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 0);
+  auto interest112 = make_shared<Interest>(cert112->getName().getPrefix(-1));
+  face->receive(*interest112);
+  advanceClocks(io, time::milliseconds(2), 50);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  BOOST_CHECK(face->sentDatas[0].wireEncode() == cert111->wireEncode());
+  face->sentDatas.clear();
+
+  Exclude exclude;
+  exclude.excludeOne(cert111->getName().get(-1));
+  interest112->setExclude(exclude);
+  face->receive(*interest112);
+  advanceClocks(io, time::milliseconds(2), 50);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  BOOST_CHECK(face->sentDatas[0].wireEncode() == cert112->wireEncode());
+  face->sentDatas.clear();
+
+  // delete a certificate
+  db.deleteCertificate(certName112);
+  face->receive(*interest112);
+  advanceClocks(io, time::milliseconds(2), 50);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 0);
+
+  face->receive(*interest111);
+  advanceClocks(io, time::milliseconds(2), 50);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  BOOST_CHECK(face->sentDatas[0].wireEncode() == cert111->wireEncode());
+  face->sentDatas.clear();
+
+  // delete another certificate
+  db.deleteCertificate(certName111);
+  face->receive(*interest112);
+  advanceClocks(io, time::milliseconds(2), 50);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 0);
+
+  face->receive(*interest111);
+  advanceClocks(io, time::milliseconds(2), 50);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace pib
+} // namespace ndn
diff --git a/tests/pib/identity-management-time-fixture.cpp b/tests/pib/identity-management-time-fixture.cpp
new file mode 100644
index 0000000..526a2e6
--- /dev/null
+++ b/tests/pib/identity-management-time-fixture.cpp
@@ -0,0 +1,58 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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 "identity-management-time-fixture.hpp"
+
+namespace ndn {
+namespace security {
+
+IdentityManagementTimeFixture::IdentityManagementTimeFixture()
+  : m_keyChainTmpPath(boost::filesystem::path(TMP_TESTS_PATH) / "PibIdMgmtTimeTest")
+  , m_keyChain(std::string("pib-sqlite3:").append(m_keyChainTmpPath.string()),
+               std::string("tpm-file:").append(m_keyChainTmpPath.string()))
+{
+}
+
+IdentityManagementTimeFixture::~IdentityManagementTimeFixture()
+{
+  for (const auto& identity : m_identities) {
+    m_keyChain.deleteIdentity(identity);
+  }
+
+  boost::filesystem::remove_all(m_keyChainTmpPath);
+}
+
+bool
+IdentityManagementTimeFixture::addIdentity(const Name& identity, const KeyParams& params)
+{
+  try {
+    m_keyChain.createIdentity(identity, params);
+    m_identities.push_back(identity);
+    return true;
+  }
+  catch (std::runtime_error&) {
+    return false;
+  }
+}
+
+
+} // namespace security
+} // namespace ndn
diff --git a/tests/pib/identity-management-time-fixture.hpp b/tests/pib/identity-management-time-fixture.hpp
new file mode 100644
index 0000000..4ac2e63
--- /dev/null
+++ b/tests/pib/identity-management-time-fixture.hpp
@@ -0,0 +1,61 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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_TESTS_IDENTITY_MANAGEMENT_TIME_FIXTURE_HPP
+#define NDN_TESTS_IDENTITY_MANAGEMENT_TIME_FIXTURE_HPP
+
+#include <ndn-cxx/security/key-chain.hpp>
+#include <vector>
+#include <boost/filesystem.hpp>
+
+#include "tests/test-common.hpp"
+
+namespace ndn {
+namespace security {
+
+/**
+ * @brief IdentityManagementTimeFixture is a test suite level fixture.
+ * Test cases in the suite can use this fixture to create identities.
+ * Identities added via addIdentity method are automatically deleted
+ * during test teardown.
+ */
+class IdentityManagementTimeFixture : public tests::UnitTestTimeFixture
+{
+public:
+  IdentityManagementTimeFixture();
+
+  ~IdentityManagementTimeFixture();
+
+  /// @brief add identity, return true if succeed.
+  bool
+  addIdentity(const Name& identity, const KeyParams& params = KeyChain::DEFAULT_KEY_PARAMS);
+
+protected:
+  boost::filesystem::path m_keyChainTmpPath;
+
+  KeyChain m_keyChain;
+  std::vector<Name> m_identities;
+};
+
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_TESTS_IDENTITY_MANAGEMENT_TIME_FIXTURE_HPP
diff --git a/tests/pib/key-cache.t.cpp b/tests/pib/key-cache.t.cpp
new file mode 100644
index 0000000..936c132
--- /dev/null
+++ b/tests/pib/key-cache.t.cpp
@@ -0,0 +1,75 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * 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.
+ */
+
+#include "tools/pib/key-cache.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace ndn {
+namespace pib {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(TestKeyCache)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  KeyCache keyCache(3);
+
+  Name name1("/1");
+  Name name2("/2");
+  Name name3("/3");
+  Name name4("/4");
+
+  auto key1 = make_shared<PublicKey>();
+  auto key2 = make_shared<PublicKey>();
+  auto key3 = make_shared<PublicKey>();
+  auto key4 = make_shared<PublicKey>();
+
+  keyCache.insert(name1, key1);
+  keyCache.insert(name2, key2);
+  keyCache.insert(name3, key3);
+
+  BOOST_CHECK_EQUAL(keyCache.size(), 3);
+  BOOST_CHECK(static_cast<bool>(keyCache.find(name1)));
+  BOOST_CHECK(static_cast<bool>(keyCache.find(name2)));
+  BOOST_CHECK(static_cast<bool>(keyCache.find(name3)));
+  BOOST_CHECK(!static_cast<bool>(keyCache.find(name4)));
+
+  keyCache.insert(name1, key1);
+  keyCache.insert(name4, key4);
+  BOOST_CHECK_EQUAL(keyCache.size(), 3);
+  BOOST_CHECK(static_cast<bool>(keyCache.find(name1)));
+  BOOST_CHECK(!static_cast<bool>(keyCache.find(name2)));
+  BOOST_CHECK(static_cast<bool>(keyCache.find(name3)));
+  BOOST_CHECK(static_cast<bool>(keyCache.find(name4)));
+
+  keyCache.erase(name1);
+  BOOST_CHECK_EQUAL(keyCache.size(), 2);
+  BOOST_CHECK(!static_cast<bool>(keyCache.find(name1)));
+  BOOST_CHECK(static_cast<bool>(keyCache.find(name3)));
+  BOOST_CHECK(static_cast<bool>(keyCache.find(name4)));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace pib
+} // namespace ndn
diff --git a/tests/pib/pib-db.t.cpp b/tests/pib/pib-db.t.cpp
new file mode 100644
index 0000000..6d826d2
--- /dev/null
+++ b/tests/pib/pib-db.t.cpp
@@ -0,0 +1,474 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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 "tools/pib/pib-db.hpp"
+#include "identity-management-time-fixture.hpp"
+
+#include <boost/filesystem.hpp>
+
+#include "tests/test-common.hpp"
+
+namespace ndn {
+namespace pib {
+namespace tests {
+
+class PibDbTestFixture : public ndn::security::IdentityManagementTimeFixture
+{
+public:
+  PibDbTestFixture()
+    : tmpPath(boost::filesystem::path(TMP_TESTS_PATH) / "DbTest")
+    , db(tmpPath.c_str())
+  {
+  }
+
+  ~PibDbTestFixture()
+  {
+    boost::filesystem::remove_all(tmpPath);
+  }
+
+  boost::asio::io_service io;
+  boost::filesystem::path tmpPath;
+  PibDb db;
+  std::vector<Name> deletedIds;
+  std::vector<Name> deletedKeys;
+  std::vector<Name> deletedCerts;
+  std::vector<Name> insertedCerts;
+};
+
+
+BOOST_FIXTURE_TEST_SUITE(TestPibDb, PibDbTestFixture)
+
+BOOST_AUTO_TEST_CASE(MgmtTest)
+{
+  Name testUser("/localhost/pib/test/mgmt");
+  addIdentity(testUser);
+  Name testUserCertName = m_keyChain.getDefaultCertificateNameForIdentity(testUser);
+  shared_ptr<IdentityCertificate> testUserCert = m_keyChain.getCertificate(testUserCertName);
+
+
+  BOOST_CHECK_EQUAL(db.getOwnerName(), "");
+  BOOST_CHECK(db.getMgmtCertificate() == nullptr);
+
+  db.updateMgmtCertificate(*testUserCert);
+  BOOST_CHECK_EQUAL(db.getOwnerName(), "test");
+  BOOST_REQUIRE(db.getMgmtCertificate() != nullptr);
+  BOOST_CHECK_EQUAL(db.getMgmtCertificate()->getName(), testUserCertName);
+
+  db.setTpmLocator("tpmLocator");
+  BOOST_CHECK_EQUAL(db.getTpmLocator(), "tpmLocator");
+
+  Name testUser2("/localhost/pib/test2/mgmt");
+  addIdentity(testUser2);
+  Name testUser2CertName = m_keyChain.getDefaultCertificateNameForIdentity(testUser2);
+  shared_ptr<IdentityCertificate> testUser2Cert = m_keyChain.getCertificate(testUser2CertName);
+
+  BOOST_CHECK_THROW(db.updateMgmtCertificate(*testUser2Cert), PibDb::Error);
+
+  Name testUserKeyName2 = m_keyChain.generateRsaKeyPairAsDefault(testUser);
+  shared_ptr<IdentityCertificate> testUserCert2 = m_keyChain.selfSign(testUserKeyName2);
+
+  BOOST_CHECK_NO_THROW(db.updateMgmtCertificate(*testUserCert2));
+  BOOST_REQUIRE(db.getMgmtCertificate() != nullptr);
+  BOOST_CHECK_EQUAL(db.getMgmtCertificate()->getName(),
+                    testUserCert2->getName());
+}
+
+BOOST_AUTO_TEST_CASE(IdentityTest)
+{
+  db.identityDeleted.connect([this] (const Name& id) {
+      this->deletedIds.push_back(id);
+    });
+
+  Name identity("/test/identity");
+  Name identity2("/test/identity2");
+
+  // Add an identity: /test/identity
+  // Since there is no default identity,
+  // the new added identity will be set as the default identity.
+  BOOST_CHECK_EQUAL(db.getDefaultIdentity(), PibDb::NON_EXISTING_IDENTITY);
+  db.addIdentity(identity);
+  BOOST_CHECK(db.hasIdentity(identity));
+  BOOST_CHECK_EQUAL(db.getDefaultIdentity(), identity);
+
+  // Add the second identity: /test/identity2
+  // Since the default identity exists,
+  // the new added identity will not be set as the default identity.
+  db.addIdentity(identity2);
+  BOOST_CHECK_EQUAL(db.hasIdentity(identity2), true);
+  BOOST_CHECK_EQUAL(db.getDefaultIdentity(), identity);
+
+  // Set the second identity: /test/identity2 as default explicitly
+  db.setDefaultIdentity(identity2);
+  BOOST_CHECK_EQUAL(db.getDefaultIdentity(), identity2);
+
+  // Delete identity /test/identity2, which is also the default one
+  // This will trigger the identityDeleted signal
+  // and also causes no default identity.
+  db.deleteIdentity(identity2);
+  BOOST_CHECK_EQUAL(db.hasIdentity(identity2), false);
+  BOOST_CHECK_EQUAL(db.hasIdentity(identity), true);
+  BOOST_CHECK_EQUAL(deletedIds.size(), 1);
+  BOOST_CHECK_EQUAL(deletedIds[0], identity2);
+  BOOST_CHECK_EQUAL(db.getDefaultIdentity(), PibDb::NON_EXISTING_IDENTITY);
+  deletedIds.clear();
+
+  // Add the second identity back
+  // Since there is no default identity (though another identity still exists),
+  // the second identity will be set as default.
+  db.addIdentity(identity2);
+  BOOST_CHECK_EQUAL(db.hasIdentity(identity2), true);
+  BOOST_CHECK_EQUAL(db.getDefaultIdentity(), identity2);
+
+  // Delete identity /test/identity
+  db.deleteIdentity(identity);
+  BOOST_CHECK_EQUAL(db.hasIdentity(identity), false);
+  BOOST_CHECK_EQUAL(db.hasIdentity(identity2), true);
+  BOOST_CHECK_EQUAL(deletedIds.size(), 1);
+  BOOST_CHECK_EQUAL(deletedIds[0], identity);
+  deletedIds.clear();
+
+  // Delete identity /test/identity2
+  db.deleteIdentity(identity2);
+  BOOST_CHECK_EQUAL(db.hasIdentity(identity), false);
+  BOOST_CHECK_EQUAL(db.hasIdentity(identity2), false);
+  BOOST_CHECK_EQUAL(deletedIds.size(), 1);
+  BOOST_CHECK_EQUAL(deletedIds[0], identity2);
+  deletedIds.clear();
+}
+
+
+BOOST_AUTO_TEST_CASE(KeyTest)
+{
+  db.identityDeleted.connect([this] (const Name& id) {
+      this->deletedIds.push_back(id);
+    });
+
+  db.keyDeleted.connect([this] (const Name& key) {
+      this->deletedKeys.push_back(key);
+    });
+
+  // Initialize id1
+  Name id1("/test/identity");
+  addIdentity(id1);
+  Name certName111 = m_keyChain.getDefaultCertificateNameForIdentity(id1);
+  shared_ptr<IdentityCertificate> cert111 = m_keyChain.getCertificate(certName111);
+  Name keyName11 = cert111->getPublicKeyName();
+  PublicKey& key11 = cert111->getPublicKeyInfo();
+
+  advanceClocks(io, time::milliseconds(100));
+  Name keyName12 = m_keyChain.generateRsaKeyPairAsDefault(id1);
+  shared_ptr<IdentityCertificate> cert121 = m_keyChain.selfSign(keyName12);
+  PublicKey& key12 = cert121->getPublicKeyInfo();
+
+  // Initialize id2
+  advanceClocks(io, time::milliseconds(100));
+  Name id2("/test/identity2");
+  addIdentity(id2);
+  Name certName211 = m_keyChain.getDefaultCertificateNameForIdentity(id2);
+  shared_ptr<IdentityCertificate> cert211 = m_keyChain.getCertificate(certName211);
+  Name keyName21 = cert211->getPublicKeyName();
+  PublicKey& key21 = cert211->getPublicKeyInfo();
+
+  advanceClocks(io, time::milliseconds(100));
+  Name keyName22 = m_keyChain.generateRsaKeyPairAsDefault(id2);
+  shared_ptr<IdentityCertificate> cert221 = m_keyChain.selfSign(keyName22);
+  PublicKey& key22 = cert221->getPublicKeyInfo();
+
+  // Add a key, the corresponding identity should be added as well
+  // Since the PIB does not have any default identity set before,
+  // the added identity will be set as default.
+  // Since there is no default key for the identity,
+  // the added key will be set as default.
+  BOOST_CHECK_EQUAL(db.getDefaultKeyNameOfIdentity(id1), PibDb::NON_EXISTING_KEY);
+  BOOST_CHECK(db.getKey(keyName11) == nullptr);
+  db.addKey(keyName11, key11);
+  BOOST_CHECK(db.hasIdentity(id1));
+  BOOST_CHECK(db.getKey(keyName11) != nullptr);
+  BOOST_CHECK_EQUAL(db.getDefaultIdentity(), id1);
+  BOOST_CHECK_EQUAL(db.getDefaultKeyNameOfIdentity(id1), keyName11);
+
+  // Add the second key of /test/identity.
+  // Since the default key of /test/identity has been set,
+  // The new added key will not be set as default.
+  db.addKey(keyName12, key12);
+  BOOST_CHECK(db.getKey(keyName12) != nullptr);
+  BOOST_CHECK_EQUAL(db.getDefaultKeyNameOfIdentity(id1), keyName11);
+
+  // Explicitly set the second key as the default key of /test/identity
+  db.setDefaultKeyNameOfIdentity(keyName12);
+  BOOST_CHECK_EQUAL(db.getDefaultKeyNameOfIdentity(id1), keyName12);
+
+  // Delete the second key which is also the default key.
+  // This will trigger the keyDeleted signal.
+  // This will also cause no default key for /test/identity
+  db.deleteKey(keyName12);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName12), false);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName11), true);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName22), false);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName21), false);
+  BOOST_CHECK_EQUAL(deletedKeys.size(), 1);
+  BOOST_CHECK_EQUAL(deletedKeys[0], keyName12);
+  deletedKeys.clear();
+
+  // Add the second key back.
+  // Since there is no default key of /test/identity (although another key still exists)
+  // The second key will be set as the default key of /test/identity
+  BOOST_CHECK_EQUAL(db.getDefaultKeyNameOfIdentity(id1), PibDb::NON_EXISTING_KEY);
+  db.addKey(keyName12, key12);
+  BOOST_CHECK_EQUAL(db.getDefaultKeyNameOfIdentity(id1), keyName12);
+
+  // Prepare test for identity deletion
+  db.addKey(keyName21, key21);
+  db.addKey(keyName22, key22);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName12), true);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName22), true);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName21), true);
+  BOOST_CHECK_EQUAL(db.hasIdentity(id2), true);
+
+  // Delete the identity.
+  // All keys of the identity should also be deleted,
+  // and the keyDeleted signal should be triggered twice.
+  db.deleteIdentity(id1);
+  BOOST_CHECK_EQUAL(db.hasIdentity(id1), false);
+  BOOST_CHECK_EQUAL(db.hasIdentity(id2), true);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName11), false);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName12), false);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName21), true);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName22), true);
+  BOOST_CHECK_EQUAL(deletedKeys.size(), 2);
+  BOOST_CHECK(std::find(deletedKeys.begin(), deletedKeys.end(), keyName11) !=
+              deletedKeys.end());
+  BOOST_CHECK(std::find(deletedKeys.begin(), deletedKeys.end(), keyName12) !=
+              deletedKeys.end());
+  BOOST_CHECK_EQUAL(deletedIds.size(), 1);
+  BOOST_CHECK_EQUAL(deletedIds[0], id1);
+}
+
+BOOST_AUTO_TEST_CASE(CertTest)
+{
+  db.identityDeleted.connect([this] (const Name& id) {
+      this->deletedIds.push_back(id);
+    });
+
+  db.keyDeleted.connect([this] (const Name& key) {
+      this->deletedKeys.push_back(key);
+    });
+
+  db.certificateDeleted.connect([this] (const Name& certificate) {
+      this->deletedCerts.push_back(certificate);
+    });
+
+  db.certificateInserted.connect([this] (const Name& certificate) {
+      this->insertedCerts.push_back(certificate);
+    });
+
+  // Initialize id1
+  Name id1("/test/identity");
+  addIdentity(id1);
+  Name certName111 = m_keyChain.getDefaultCertificateNameForIdentity(id1);
+  shared_ptr<IdentityCertificate> cert111 = m_keyChain.getCertificate(certName111);
+  Name keyName11 = cert111->getPublicKeyName();
+
+  advanceClocks(io, time::milliseconds(100));
+  shared_ptr<IdentityCertificate> cert112 = m_keyChain.selfSign(keyName11);
+  Name certName112 = cert112->getName();
+
+  advanceClocks(io, time::milliseconds(100));
+  Name keyName12 = m_keyChain.generateRsaKeyPairAsDefault(id1);
+  shared_ptr<IdentityCertificate> cert121 = m_keyChain.selfSign(keyName12);
+  Name certName121 = cert121->getName();
+
+  advanceClocks(io, time::milliseconds(100));
+  shared_ptr<IdentityCertificate> cert122 = m_keyChain.selfSign(keyName12);
+  Name certName122 = cert122->getName();
+
+  // Initialize id2
+  advanceClocks(io, time::milliseconds(100));
+  Name id2("/test/identity2");
+  addIdentity(id2);
+  Name certName211 = m_keyChain.getDefaultCertificateNameForIdentity(id2);
+  shared_ptr<IdentityCertificate> cert211 = m_keyChain.getCertificate(certName211);
+  Name keyName21 = cert211->getPublicKeyName();
+
+  advanceClocks(io, time::milliseconds(100));
+  shared_ptr<IdentityCertificate> cert212 = m_keyChain.selfSign(keyName21);
+  Name certName212 = cert212->getName();
+
+  advanceClocks(io, time::milliseconds(100));
+  Name keyName22 = m_keyChain.generateRsaKeyPairAsDefault(id2);
+  shared_ptr<IdentityCertificate> cert221 = m_keyChain.selfSign(keyName22);
+  Name certName221 = cert221->getName();
+
+  advanceClocks(io, time::milliseconds(100));
+  shared_ptr<IdentityCertificate> cert222 = m_keyChain.selfSign(keyName22);
+  Name certName222 = cert222->getName();
+
+  // Add a certificate
+  // This will also add the corresponding key and identity.
+  // Since there is no default setting before,
+  // The certificate will be set as the default one of the key, and so be the key and identity
+  BOOST_CHECK_EQUAL(db.hasCertificate(certName111), false);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName11), false);
+  BOOST_CHECK_EQUAL(db.hasIdentity(id1), false);
+  BOOST_CHECK_EQUAL(db.getDefaultCertNameOfKey(keyName11), PibDb::NON_EXISTING_CERTIFICATE);
+  db.addCertificate(*cert111);
+  BOOST_CHECK_EQUAL(db.hasCertificate(certName111), true);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName11), true);
+  BOOST_CHECK_EQUAL(db.hasIdentity(id1), true);
+  BOOST_CHECK_EQUAL(db.getDefaultIdentity(), id1);
+  BOOST_CHECK_EQUAL(db.getDefaultKeyNameOfIdentity(id1), keyName11);
+  BOOST_CHECK_EQUAL(db.getDefaultCertNameOfKey(keyName11), certName111);
+  BOOST_CHECK_EQUAL(insertedCerts.size(), 1);
+  BOOST_CHECK(std::find(insertedCerts.begin(), insertedCerts.end(), certName111) !=
+              insertedCerts.end());
+  insertedCerts.clear();
+
+  // Add the second certificate of the same key
+  // Since default certificate already exists, no default setting changes.
+  BOOST_CHECK(db.getCertificate(certName112) == nullptr);
+  db.addCertificate(*cert112);
+  BOOST_CHECK(db.getCertificate(certName112) != nullptr);
+  BOOST_CHECK_EQUAL(db.getDefaultCertNameOfKey(keyName11), certName111);
+  BOOST_CHECK_EQUAL(insertedCerts.size(), 1);
+  BOOST_CHECK(std::find(insertedCerts.begin(), insertedCerts.end(), certName112) !=
+              insertedCerts.end());
+  insertedCerts.clear();
+
+  // Explicitly set the second certificate as the default one of the key.
+  db.setDefaultCertNameOfKey(certName112);
+  BOOST_CHECK_EQUAL(db.getDefaultCertNameOfKey(keyName11), certName112);
+
+  // Delete the default certificate
+  // This will trigger certificateDeleted signal
+  // and also causes no default certificate for the key.
+  db.deleteCertificate(certName112);
+  BOOST_CHECK_EQUAL(db.hasCertificate(certName112), false);
+  BOOST_CHECK_EQUAL(db.hasCertificate(certName111), true);
+  BOOST_CHECK_EQUAL(deletedCerts.size(), 1);
+  BOOST_CHECK_EQUAL(deletedCerts[0], certName112);
+  BOOST_CHECK_EQUAL(db.getDefaultCertNameOfKey(keyName11), PibDb::NON_EXISTING_CERTIFICATE);
+  deletedCerts.clear();
+
+  // Add the second certificate back
+  // Since there is no default certificate of the key (though another certificate still exists),
+  // the new added certificate will be set as default
+  db.addCertificate(*cert112);
+  BOOST_CHECK(db.getCertificate(certName112) != nullptr);
+  BOOST_CHECK_EQUAL(db.getDefaultCertNameOfKey(keyName11), certName112);
+  insertedCerts.clear();
+
+  // Add entries for delete tests
+  db.addCertificate(*cert111); // already exists no certInserted signal emitted
+  db.addCertificate(*cert112); // already exists no certInserted signal emitted
+  db.addCertificate(*cert121);
+  db.addCertificate(*cert122);
+  db.addCertificate(*cert211);
+  db.addCertificate(*cert212);
+  db.addCertificate(*cert221);
+  db.addCertificate(*cert222);
+  BOOST_CHECK_EQUAL(insertedCerts.size(), 6);
+  BOOST_CHECK(std::find(insertedCerts.begin(), insertedCerts.end(), certName121) !=
+              insertedCerts.end());
+  BOOST_CHECK(std::find(insertedCerts.begin(), insertedCerts.end(), certName122) !=
+              insertedCerts.end());
+  BOOST_CHECK(std::find(insertedCerts.begin(), insertedCerts.end(), certName211) !=
+              insertedCerts.end());
+  BOOST_CHECK(std::find(insertedCerts.begin(), insertedCerts.end(), certName212) !=
+              insertedCerts.end());
+  BOOST_CHECK(std::find(insertedCerts.begin(), insertedCerts.end(), certName221) !=
+              insertedCerts.end());
+  BOOST_CHECK(std::find(insertedCerts.begin(), insertedCerts.end(), certName222) !=
+              insertedCerts.end());
+  insertedCerts.clear();
+
+  // Delete the key.
+  // All the related certificates will be deleted as well.
+  db.deleteKey(keyName11);
+  BOOST_CHECK_EQUAL(db.hasCertificate(certName112), false);
+  BOOST_CHECK_EQUAL(db.hasCertificate(certName111), false);
+  BOOST_CHECK_EQUAL(db.hasCertificate(certName122), true);
+  BOOST_CHECK_EQUAL(db.hasCertificate(certName121), true);
+  BOOST_CHECK_EQUAL(db.hasCertificate(certName212), true);
+  BOOST_CHECK_EQUAL(db.hasCertificate(certName211), true);
+  BOOST_CHECK_EQUAL(db.hasCertificate(certName222), true);
+  BOOST_CHECK_EQUAL(db.hasCertificate(certName221), true);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName11), false);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName12), true);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName21), true);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName22), true);
+  BOOST_CHECK_EQUAL(deletedCerts.size(), 2);
+  BOOST_CHECK(std::find(deletedCerts.begin(), deletedCerts.end(), certName111) !=
+              deletedCerts.end());
+  BOOST_CHECK(std::find(deletedCerts.begin(), deletedCerts.end(), certName112) !=
+              deletedCerts.end());
+  BOOST_CHECK_EQUAL(deletedKeys.size(), 1);
+  BOOST_CHECK_EQUAL(deletedKeys[0], keyName11);
+  deletedCerts.clear();
+  deletedKeys.clear();
+
+  // Recover deleted entries
+  db.addCertificate(*cert111);
+  db.addCertificate(*cert112);
+
+  // Delete the identity
+  // All the related certificates and keys will be deleted as well.
+  BOOST_CHECK_EQUAL(db.hasCertificate(certName111), true);
+  BOOST_CHECK_EQUAL(db.hasCertificate(certName112), true);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName11), true);
+  BOOST_CHECK_EQUAL(db.hasIdentity(id1), true);
+  db.deleteIdentity(id1);
+  BOOST_CHECK_EQUAL(db.hasCertificate(certName112), false);
+  BOOST_CHECK_EQUAL(db.hasCertificate(certName111), false);
+  BOOST_CHECK_EQUAL(db.hasCertificate(certName122), false);
+  BOOST_CHECK_EQUAL(db.hasCertificate(certName121), false);
+  BOOST_CHECK_EQUAL(db.hasCertificate(certName212), true);
+  BOOST_CHECK_EQUAL(db.hasCertificate(certName211), true);
+  BOOST_CHECK_EQUAL(db.hasCertificate(certName222), true);
+  BOOST_CHECK_EQUAL(db.hasCertificate(certName221), true);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName11), false);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName12), false);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName21), true);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName22), true);
+  BOOST_CHECK_EQUAL(db.hasIdentity(id1), false);
+  BOOST_CHECK_EQUAL(db.hasIdentity(id2), true);
+  BOOST_CHECK_EQUAL(deletedCerts.size(), 4);
+  BOOST_CHECK(std::find(deletedCerts.begin(), deletedCerts.end(), certName111) !=
+              deletedCerts.end());
+  BOOST_CHECK(std::find(deletedCerts.begin(), deletedCerts.end(), certName112) !=
+              deletedCerts.end());
+  BOOST_CHECK(std::find(deletedCerts.begin(), deletedCerts.end(), certName121) !=
+              deletedCerts.end());
+  BOOST_CHECK(std::find(deletedCerts.begin(), deletedCerts.end(), certName122) !=
+              deletedCerts.end());
+  BOOST_CHECK_EQUAL(deletedKeys.size(), 2);
+  BOOST_CHECK(std::find(deletedKeys.begin(), deletedKeys.end(), keyName11) !=
+              deletedKeys.end());
+  BOOST_CHECK(std::find(deletedKeys.begin(), deletedKeys.end(), keyName12) !=
+              deletedCerts.end());
+  BOOST_CHECK_EQUAL(deletedIds.size(), 1);
+  BOOST_CHECK_EQUAL(deletedIds[0], id1);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace pib
+} // namespace ndn
diff --git a/tests/pib/pib-validator.t.cpp b/tests/pib/pib-validator.t.cpp
new file mode 100644
index 0000000..06fd373
--- /dev/null
+++ b/tests/pib/pib-validator.t.cpp
@@ -0,0 +1,137 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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 "tools/pib/pib-validator.hpp"
+#include "tools/pib/encoding/update-param.hpp"
+#include "tools/pib/encoding/delete-param.hpp"
+#include <ndn-cxx/security/key-chain.hpp>
+
+#include "identity-management-time-fixture.hpp"
+#include <boost/filesystem.hpp>
+#include "tests/test-common.hpp"
+
+namespace ndn {
+namespace pib {
+namespace tests {
+
+class PibValidatorFixture : public ndn::security::IdentityManagementTimeFixture
+{
+public:
+  PibValidatorFixture()
+    : tmpPath(boost::filesystem::path(TMP_TESTS_PATH) / "DbTest")
+    , db(tmpPath.c_str())
+  {
+  }
+
+  ~PibValidatorFixture()
+  {
+    boost::filesystem::remove_all(tmpPath);
+  }
+
+  boost::asio::io_service io;
+  boost::filesystem::path tmpPath;
+  PibDb db;
+  bool isProcessed;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestPibValidator, PibValidatorFixture)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  PibValidator validator(db);
+
+  Name testUser("/localhost/pib/test/mgmt");
+  BOOST_REQUIRE(addIdentity(testUser, RsaKeyParams()));
+  Name testUserCertName = m_keyChain.getDefaultCertificateNameForIdentity(testUser);
+  shared_ptr<IdentityCertificate> testUserCert = m_keyChain.getCertificate(testUserCertName);
+
+  advanceClocks(io, time::milliseconds(100));
+  Name testUser2("/localhost/pib/test2/mgmt");
+  BOOST_REQUIRE(addIdentity(testUser2, RsaKeyParams()));
+
+  db.updateMgmtCertificate(*testUserCert);
+
+  advanceClocks(io, time::milliseconds(100));
+  Name normalId("/normal/id");
+  BOOST_REQUIRE(addIdentity(normalId, RsaKeyParams()));
+  Name normalIdCertName = m_keyChain.getDefaultCertificateNameForIdentity(normalId);
+  shared_ptr<IdentityCertificate> normalIdCert = m_keyChain.getCertificate(normalIdCertName);
+
+  db.addIdentity(normalId);
+  db.addKey(normalIdCert->getPublicKeyName(), normalIdCert->getPublicKeyInfo());
+  db.addCertificate(*normalIdCert);
+
+  Name command1("/localhost/pib/test/verb/param");
+  shared_ptr<Interest> interest1 = make_shared<Interest>(command1);
+  m_keyChain.signByIdentity(*interest1, testUser);
+  // "test" user is trusted for any command about itself, OK.
+  isProcessed = false;
+  validator.validate(*interest1,
+    [this] (const shared_ptr<const Interest>&) {
+      isProcessed = true;
+      BOOST_CHECK(true);
+    },
+    [this] (const shared_ptr<const Interest>&, const std::string&) {
+      isProcessed = true;
+      BOOST_CHECK(false);
+    });
+  BOOST_CHECK(isProcessed);
+
+  Name command2("/localhost/pib/test/verb/param");
+  shared_ptr<Interest> interest2 = make_shared<Interest>(command2);
+  m_keyChain.signByIdentity(*interest2, testUser2);
+  // "test2" user is NOT trusted for any command about other user, MUST fail
+  isProcessed = false;
+  validator.validate(*interest2,
+    [this] (const shared_ptr<const Interest>&) {
+      isProcessed = true;
+      BOOST_CHECK(false);
+    },
+    [this] (const shared_ptr<const Interest>&, const std::string&) {
+      isProcessed = true;
+      BOOST_CHECK(true);
+    });
+  BOOST_CHECK(isProcessed);
+
+  Name command3("/localhost/pib/test/verb/param");
+  shared_ptr<Interest> interest3 = make_shared<Interest>(command3);
+  m_keyChain.signByIdentity(*interest3, normalId);
+  // "normalId" is in "test" pib, can be trusted for some commands about "test".
+  // Detail checking is needed, but it is not the job of Validator, OK.
+  isProcessed = false;
+  validator.validate(*interest3,
+    [this] (const shared_ptr<const Interest>&) {
+      isProcessed = true;
+      BOOST_CHECK(true);
+    },
+    [this] (const shared_ptr<const Interest>&, const std::string&) {
+      isProcessed = true;
+      BOOST_CHECK(false);
+    });
+  BOOST_CHECK(isProcessed);
+
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace pib
+} // namespace ndn
diff --git a/tests/pib/pib.t.cpp b/tests/pib/pib.t.cpp
new file mode 100644
index 0000000..f4c4e6f
--- /dev/null
+++ b/tests/pib/pib.t.cpp
@@ -0,0 +1,1389 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * 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.
+ */
+
+#include "tools/pib/pib.hpp"
+#include "identity-management-time-fixture.hpp"
+#include <ndn-cxx/security/sec-tpm-file.hpp>
+#include "tools/pib/encoding/pib-encoding.hpp"
+#include <ndn-cxx/util/io.hpp>
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
+#include <boost/filesystem.hpp>
+
+#include "tests/test-common.hpp"
+
+namespace ndn {
+namespace pib {
+namespace tests {
+
+class PibTestFixture : public ndn::security::IdentityManagementTimeFixture
+{
+public:
+  PibTestFixture()
+    : tmpPath(boost::filesystem::path(TMP_TESTS_PATH) / "PibTest")
+    , face(util::makeDummyClientFace(io, {true, true}))
+  {
+  }
+
+  ~PibTestFixture()
+  {
+    boost::filesystem::remove_all(tmpPath);
+  }
+
+  template<class Param>
+  shared_ptr<Interest>
+  generateUnsignedInterest(Param& param, const std::string& user)
+  {
+    Name command("/localhost/pib");
+    command.append(user).append(Param::VERB).append(param.wireEncode());
+    shared_ptr<Interest> interest = make_shared<Interest>(command);
+
+    return interest;
+  }
+
+  template<class Param>
+  shared_ptr<Interest>
+  generateSignedInterest(Param& param, const std::string& user, const Name& certName)
+  {
+    shared_ptr<Interest> interest = generateUnsignedInterest(param, user);
+    m_keyChain.sign(*interest, certName);
+
+    return interest;
+  }
+
+  boost::asio::io_service io;
+  std::string owner;
+  boost::filesystem::path tmpPath;
+  shared_ptr<util::DummyClientFace> face;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestPib, PibTestFixture)
+
+BOOST_AUTO_TEST_CASE(InitCertTest1)
+{
+  // Create a PIB with full parameters
+  owner = "testUser";
+
+  Pib pib(*face,
+          tmpPath.string(),
+          m_keyChain.getTpm().getTpmLocator(),
+          owner);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_CHECK_EQUAL(pib.getOwner(), owner);
+  BOOST_CHECK_EQUAL(pib.getDb().getOwnerName(), owner);
+
+  auto mgmtCert = pib.getMgmtCert();
+  BOOST_CHECK_EQUAL(mgmtCert.getName().getPrefix(-3),
+                    Name("/localhost/pib/testUser/mgmt/KEY"));
+  BOOST_CHECK_EQUAL(mgmtCert.getName().get(5).toUri().substr(0, 4), "dsk-");
+
+  auto mgmtCert2 = pib.getDb().getMgmtCertificate();
+  BOOST_REQUIRE(mgmtCert2 != nullptr);
+  BOOST_CHECK(mgmtCert.wireEncode() == mgmtCert2->wireEncode());
+
+  BOOST_CHECK_EQUAL(pib.getDb().getTpmLocator(), m_keyChain.getTpm().getTpmLocator());
+
+  GetParam param01;
+  shared_ptr<Interest> interest01 = generateUnsignedInterest(param01, owner);
+
+  face->receive(*interest01);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibUser result01;
+  BOOST_REQUIRE_NO_THROW(result01.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK(result01.getMgmtCert().wireEncode() == mgmtCert.wireEncode());
+  BOOST_CHECK_EQUAL(result01.getTpmLocator(), m_keyChain.getTpm().getTpmLocator());
+}
+
+BOOST_AUTO_TEST_CASE(InitCertTest2)
+{
+  // Create a PIB from a database (assume that the database is configured)
+  std::string dbDir = tmpPath.string();
+  std::string tpmLocator = m_keyChain.getTpm().getTpmLocator();
+  owner = "testUser";
+
+  Name testUser("/localhost/pib/testUser/mgmt");
+
+  addIdentity(testUser);
+  Name testUserCertName = m_keyChain.getDefaultCertificateNameForIdentity(testUser);
+  shared_ptr<IdentityCertificate> testUserCert = m_keyChain.getCertificate(testUserCertName);
+
+  PibDb db(tmpPath.string());
+  BOOST_CHECK_NO_THROW(Pib(*face, dbDir, tpmLocator, owner));
+
+  db.updateMgmtCertificate(*testUserCert);
+  BOOST_CHECK_NO_THROW(Pib(*face, dbDir, tpmLocator, owner));
+  BOOST_CHECK_THROW(Pib(*face, dbDir, tpmLocator, "wrongUser"), Pib::Error);
+
+  db.setTpmLocator(m_keyChain.getTpm().getTpmLocator());
+  BOOST_CHECK_NO_THROW(Pib(*face, dbDir, tpmLocator, owner));
+  BOOST_CHECK_THROW(Pib(*face, dbDir, "tpm-file:wrong", owner), Pib::Error);
+
+  advanceClocks(io, time::milliseconds(10));
+  m_keyChain.deleteIdentity(testUser);
+  BOOST_CHECK_NO_THROW(Pib(*face, dbDir, tpmLocator, owner));
+}
+
+BOOST_AUTO_TEST_CASE(InitCertTest3)
+{
+  std::string dbDir = tmpPath.string();
+  std::string tpmLocator = m_keyChain.getTpm().getTpmLocator();
+  owner = "testUser";
+
+  Name testUser("/localhost/pib/testUser/mgmt");
+  addIdentity(testUser);
+  Name testUserCertName = m_keyChain.getDefaultCertificateNameForIdentity(testUser);
+  shared_ptr<IdentityCertificate> testUserCert = m_keyChain.getCertificate(testUserCertName);
+
+  Pib pib1(*face, dbDir, tpmLocator, owner);
+  BOOST_CHECK_EQUAL(pib1.getMgmtCert().getName().getPrefix(-3),
+                    Name("/localhost/pib/testUser/mgmt/KEY"));
+
+  PibDb db(tmpPath.string());
+  db.updateMgmtCertificate(*testUserCert);
+  Pib pib2(*face, dbDir, tpmLocator, owner);
+  BOOST_CHECK_EQUAL(pib2.getMgmtCert().getName(), testUserCertName);
+
+  advanceClocks(io, time::milliseconds(10));
+  m_keyChain.deleteIdentity(testUser);
+  Pib pib3(*face, dbDir, tpmLocator, owner);
+  BOOST_CHECK(pib3.getMgmtCert().getName() != testUserCertName);
+  BOOST_CHECK_EQUAL(pib3.getMgmtCert().getName().getPrefix(-3),
+                    Name("/localhost/pib/testUser/mgmt/KEY"));
+}
+
+BOOST_AUTO_TEST_CASE(GetCommandTest)
+{
+  owner = "alice";
+
+  Pib pib(*face,
+          tmpPath.string(),
+          m_keyChain.getTpm().getTpmLocator(),
+          owner);
+  advanceClocks(io, time::milliseconds(10), 10);
+  util::InMemoryStoragePersistent& cache = pib.getResponseCache();
+  auto ownerMgmtCert = pib.getMgmtCert();
+  m_keyChain.addCertificate(ownerMgmtCert);
+
+  PibDb db(tmpPath.string());
+
+  Name testId("/test/identity");
+  Name testIdCertName00 = m_keyChain.createIdentity(testId);
+  shared_ptr<IdentityCertificate> cert00 = m_keyChain.getCertificate(testIdCertName00);
+  Name testIdKeyName0 = cert00->getPublicKeyName();
+  advanceClocks(io, time::milliseconds(100));
+  shared_ptr<IdentityCertificate> cert01 = m_keyChain.selfSign(testIdKeyName0);
+  Name testIdCertName01 = cert01->getName();
+
+  advanceClocks(io, time::milliseconds(100));
+  Name testIdKeyName1 = m_keyChain.generateRsaKeyPair(testId);
+  shared_ptr<IdentityCertificate> cert10 = m_keyChain.selfSign(testIdKeyName1);
+  Name testIdCertName10 = cert10->getName();
+  advanceClocks(io, time::milliseconds(100));
+  shared_ptr<IdentityCertificate> cert11 = m_keyChain.selfSign(testIdKeyName1);
+  Name testIdCertName11 = cert11->getName();
+
+  BOOST_CHECK_EQUAL(db.hasIdentity(testId), false);
+  BOOST_CHECK_EQUAL(db.hasKey(testIdKeyName0), false);
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName00), false);
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName01), false);
+  BOOST_CHECK_EQUAL(db.hasKey(testIdKeyName1), false);
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName10), false);
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName11), false);
+
+  db.addCertificate(*cert00);
+  db.addCertificate(*cert01);
+  db.addCertificate(*cert10);
+  db.addCertificate(*cert11);
+  db.setDefaultIdentity(testId);
+  db.setDefaultKeyNameOfIdentity(testIdKeyName0);
+  db.setDefaultCertNameOfKey(testIdCertName00);
+
+  BOOST_CHECK_EQUAL(db.hasIdentity(testId), true);
+  BOOST_CHECK_EQUAL(db.hasKey(testIdKeyName0), true);
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName00), true);
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName01), true);
+  BOOST_CHECK_EQUAL(db.hasKey(testIdKeyName1), true);
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName10), true);
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName11), true);
+
+  // Get Param
+  GetParam param01;
+  shared_ptr<Interest> interest01 = generateUnsignedInterest(param01, owner);
+
+  face->sentDatas.clear();
+  face->receive(*interest01);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_REQUIRE(cache.find(interest01->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibUser result01;
+  BOOST_REQUIRE_NO_THROW(result01.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK(result01.getMgmtCert().wireEncode() == ownerMgmtCert.wireEncode());
+
+
+  GetParam param02;
+  shared_ptr<Interest> interest02 = generateUnsignedInterest(param02, "non-existing");
+
+  face->sentDatas.clear();
+  face->receive(*interest02);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_CHECK(cache.find(interest02->getName()) == nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 0);
+
+
+  GetParam param03(TYPE_ID, testId);
+  shared_ptr<Interest> interest03 = generateUnsignedInterest(param03, owner);
+
+  face->sentDatas.clear();
+  face->receive(*interest03);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_REQUIRE(cache.find(interest03->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibIdentity result03;
+  BOOST_REQUIRE_NO_THROW(result03.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result03.getIdentity(), testId);
+
+
+  Name wrongId("/wrong/id");
+  GetParam param04(TYPE_ID, wrongId);
+  shared_ptr<Interest> interest04 = generateUnsignedInterest(param04, owner);
+
+  face->sentDatas.clear();
+  face->receive(*interest04);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_REQUIRE(cache.find(interest04->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibError result04;
+  BOOST_REQUIRE_NO_THROW(result04.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result04.getErrorCode(), ERR_NON_EXISTING_ID);
+
+
+  GetParam param05(TYPE_KEY, testIdKeyName1);
+  shared_ptr<Interest> interest05 = generateUnsignedInterest(param05, owner);
+
+  face->sentDatas.clear();
+  face->receive(*interest05);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_REQUIRE(cache.find(interest05->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibPublicKey result05;
+  BOOST_REQUIRE_NO_THROW(result05.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result05.getKeyName(), testIdKeyName1);
+
+
+  Name wrongKeyName1("/wrong/key/name1");
+  GetParam param06(TYPE_KEY, wrongKeyName1);
+  shared_ptr<Interest> interest06 = generateUnsignedInterest(param06, owner);
+
+  face->sentDatas.clear();
+  face->receive(*interest06);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_REQUIRE(cache.find(interest06->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibError result06;
+  BOOST_REQUIRE_NO_THROW(result06.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result06.getErrorCode(), ERR_NON_EXISTING_KEY);
+
+
+  GetParam param07(TYPE_CERT, testIdCertName00);
+  shared_ptr<Interest> interest07 = generateUnsignedInterest(param07, owner);
+
+  face->sentDatas.clear();
+  face->receive(*interest07);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_REQUIRE(cache.find(interest07->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibCertificate result07;
+  BOOST_REQUIRE_NO_THROW(result07.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result07.getCertificate().getName(), testIdCertName00);
+
+
+  Name wrongCertName1("/wrong/cert/name1");
+  GetParam param08(TYPE_CERT, wrongCertName1);
+  shared_ptr<Interest> interest08 = generateUnsignedInterest(param08, owner);
+
+  face->sentDatas.clear();
+  face->receive(*interest08);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_REQUIRE(cache.find(interest08->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibError result08;
+  BOOST_REQUIRE_NO_THROW(result08.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result08.getErrorCode(), ERR_NON_EXISTING_CERT);
+
+
+  Name wrongKeyName2;
+  GetParam param09(TYPE_KEY, wrongKeyName2);
+  shared_ptr<Interest> interest09 = generateUnsignedInterest(param09, owner);
+
+  face->sentDatas.clear();
+  face->receive(*interest09);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_REQUIRE(cache.find(interest09->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibError result09;
+  BOOST_REQUIRE_NO_THROW(result09.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result09.getErrorCode(), ERR_WRONG_PARAM);
+}
+
+BOOST_AUTO_TEST_CASE(DefaultCommandTest)
+{
+  owner = "alice";
+
+  Pib pib(*face,
+          tmpPath.string(),
+          m_keyChain.getTpm().getTpmLocator(),
+          owner);
+  advanceClocks(io, time::milliseconds(10), 10);
+  util::InMemoryStoragePersistent& cache = pib.getResponseCache();
+  auto ownerMgmtCert = pib.getMgmtCert();
+  m_keyChain.addCertificate(ownerMgmtCert);
+
+  PibDb db(tmpPath.string());
+
+  Name testId("/test/identity");
+  Name testIdCertName00 = m_keyChain.createIdentity(testId);
+  shared_ptr<IdentityCertificate> cert00 = m_keyChain.getCertificate(testIdCertName00);
+  Name testIdKeyName0 = cert00->getPublicKeyName();
+  advanceClocks(io, time::milliseconds(100));
+  shared_ptr<IdentityCertificate> cert01 = m_keyChain.selfSign(testIdKeyName0);
+  Name testIdCertName01 = cert01->getName();
+
+  advanceClocks(io, time::milliseconds(100));
+  Name testIdKeyName1 = m_keyChain.generateRsaKeyPair(testId);
+  shared_ptr<IdentityCertificate> cert10 = m_keyChain.selfSign(testIdKeyName1);
+  Name testIdCertName10 = cert10->getName();
+  advanceClocks(io, time::milliseconds(100));
+  shared_ptr<IdentityCertificate> cert11 = m_keyChain.selfSign(testIdKeyName1);
+  Name testIdCertName11 = cert11->getName();
+
+  BOOST_CHECK_EQUAL(db.hasIdentity(testId), false);
+  BOOST_CHECK_EQUAL(db.hasKey(testIdKeyName0), false);
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName00), false);
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName01), false);
+  BOOST_CHECK_EQUAL(db.hasKey(testIdKeyName1), false);
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName10), false);
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName11), false);
+
+  db.addCertificate(*cert00);
+  db.addCertificate(*cert01);
+  db.addCertificate(*cert10);
+  db.addCertificate(*cert11);
+  db.setDefaultIdentity(testId);
+  db.setDefaultKeyNameOfIdentity(testIdKeyName0);
+  db.setDefaultCertNameOfKey(testIdCertName00);
+
+  BOOST_CHECK_EQUAL(db.hasIdentity(testId), true);
+  BOOST_CHECK_EQUAL(db.hasKey(testIdKeyName0), true);
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName00), true);
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName01), true);
+  BOOST_CHECK_EQUAL(db.hasKey(testIdKeyName1), true);
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName10), true);
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName11), true);
+
+  // Default Param
+  DefaultParam param11(TYPE_ID, TYPE_USER);
+  shared_ptr<Interest> interest11 = generateUnsignedInterest(param11, owner);
+
+  face->sentDatas.clear();
+  face->receive(*interest11);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_REQUIRE(cache.find(interest11->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibIdentity result11;
+  BOOST_REQUIRE_NO_THROW(result11.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result11.getIdentity(), testId);
+
+
+  DefaultParam param13(TYPE_ID, TYPE_ID);
+  shared_ptr<Interest> interest13 = generateUnsignedInterest(param13, owner);
+
+  face->sentDatas.clear();
+  face->receive(*interest13);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_REQUIRE(cache.find(interest13->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibError result13;
+  BOOST_REQUIRE_NO_THROW(result13.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result13.getErrorCode(), ERR_WRONG_PARAM);
+
+
+  DefaultParam param14(TYPE_KEY, TYPE_ID, testId);
+  shared_ptr<Interest> interest14 = generateUnsignedInterest(param14, owner);
+
+  face->sentDatas.clear();
+  face->receive(*interest14);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_REQUIRE(cache.find(interest14->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibPublicKey result14;
+  BOOST_REQUIRE_NO_THROW(result14.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result14.getKeyName(), testIdKeyName0);
+
+
+  DefaultParam param15(TYPE_CERT, TYPE_ID, testId);
+  shared_ptr<Interest> interest15 = generateUnsignedInterest(param15, owner);
+
+  face->sentDatas.clear();
+  face->receive(*interest15);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_REQUIRE(cache.find(interest15->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibCertificate result15;
+  BOOST_REQUIRE_NO_THROW(result15.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result15.getCertificate().getName(), testIdCertName00);
+
+
+  DefaultParam param16(TYPE_CERT, TYPE_USER);
+  shared_ptr<Interest> interest16 = generateUnsignedInterest(param16, owner);
+
+  face->sentDatas.clear();
+  face->receive(*interest16);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_REQUIRE(cache.find(interest16->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibCertificate result16;
+  BOOST_REQUIRE_NO_THROW(result16.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result16.getCertificate().getName(), testIdCertName00);
+
+
+  DefaultParam param17(TYPE_CERT, TYPE_KEY, testIdKeyName1);
+  shared_ptr<Interest> interest17 = generateUnsignedInterest(param17, owner);
+
+  face->sentDatas.clear();
+  face->receive(*interest17);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_REQUIRE(cache.find(interest17->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibCertificate result17;
+  BOOST_REQUIRE_NO_THROW(result17.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result17.getCertificate().getName(), testIdCertName10);
+}
+
+BOOST_AUTO_TEST_CASE(ListCommandTest)
+{
+  owner = "alice";
+
+  Pib pib(*face,
+          tmpPath.string(),
+          m_keyChain.getTpm().getTpmLocator(),
+          owner);
+  advanceClocks(io, time::milliseconds(10), 10);
+  util::InMemoryStoragePersistent& cache = pib.getResponseCache();
+  auto ownerMgmtCert = pib.getMgmtCert();
+  m_keyChain.addCertificate(ownerMgmtCert);
+
+  PibDb db(tmpPath.string());
+
+  Name testId("/test/identity");
+  Name testIdCertName00 = m_keyChain.createIdentity(testId);
+  shared_ptr<IdentityCertificate> cert00 = m_keyChain.getCertificate(testIdCertName00);
+  Name testIdKeyName0 = cert00->getPublicKeyName();
+  advanceClocks(io, time::milliseconds(100));
+  shared_ptr<IdentityCertificate> cert01 = m_keyChain.selfSign(testIdKeyName0);
+  Name testIdCertName01 = cert01->getName();
+
+  advanceClocks(io, time::milliseconds(100));
+  Name testIdKeyName1 = m_keyChain.generateRsaKeyPair(testId);
+  shared_ptr<IdentityCertificate> cert10 = m_keyChain.selfSign(testIdKeyName1);
+  Name testIdCertName10 = cert10->getName();
+  advanceClocks(io, time::milliseconds(100));
+  shared_ptr<IdentityCertificate> cert11 = m_keyChain.selfSign(testIdKeyName1);
+  Name testIdCertName11 = cert11->getName();
+
+  BOOST_CHECK_EQUAL(db.hasIdentity(testId), false);
+  BOOST_CHECK_EQUAL(db.hasKey(testIdKeyName0), false);
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName00), false);
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName01), false);
+  BOOST_CHECK_EQUAL(db.hasKey(testIdKeyName1), false);
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName10), false);
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName11), false);
+
+  db.addCertificate(*cert00);
+  db.addCertificate(*cert01);
+  db.addCertificate(*cert10);
+  db.addCertificate(*cert11);
+  db.setDefaultIdentity(testId);
+  db.setDefaultKeyNameOfIdentity(testIdKeyName0);
+  db.setDefaultCertNameOfKey(testIdCertName00);
+
+  BOOST_CHECK_EQUAL(db.hasIdentity(testId), true);
+  BOOST_CHECK_EQUAL(db.hasKey(testIdKeyName0), true);
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName00), true);
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName01), true);
+  BOOST_CHECK_EQUAL(db.hasKey(testIdKeyName1), true);
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName10), true);
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName11), true);
+
+  Name wrongId("/wrong/id");
+
+  // List Param
+  ListParam param21;
+  shared_ptr<Interest> interest21 = generateUnsignedInterest(param21, owner);
+
+  face->sentDatas.clear();
+  face->receive(*interest21);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_REQUIRE(cache.find(interest21->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibNameList result21;
+  BOOST_REQUIRE_NO_THROW(result21.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result21.getNameList().size(), 1);
+
+
+  ListParam param22(TYPE_ID, testId);
+  shared_ptr<Interest> interest22 = generateUnsignedInterest(param22, owner);
+
+  face->sentDatas.clear();
+  face->receive(*interest22);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_REQUIRE(cache.find(interest22->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibNameList result22;
+  BOOST_REQUIRE_NO_THROW(result22.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result22.getNameList().size(), 2);
+
+
+  ListParam param23(TYPE_ID, wrongId);
+  shared_ptr<Interest> interest23 = generateUnsignedInterest(param23, owner);
+
+  face->sentDatas.clear();
+  face->receive(*interest23);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_REQUIRE(cache.find(interest23->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibNameList result23;
+  BOOST_REQUIRE_NO_THROW(result23.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result23.getNameList().size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(IsUpdateAllowedTest1)
+{
+  // This test case is to check the access control of local management key
+  owner = "alice";
+
+  Pib pib(*face,
+          tmpPath.string(),
+          m_keyChain.getTpm().getTpmLocator(),
+          owner);
+
+  UpdateQueryProcessor& pro = pib.m_updateProcessor;
+
+  Name target01("/localhost/pib");
+  Name target02("/localhost/pib/alice/mgmt");
+  Name target03("/localhost/pib/alice/mgmt/ok");
+  Name target04("/localhost/pib/alice");
+  Name target05("/test/id");
+  Name target06("/test/id/ksk-123");
+  Name target07("/test/id/KEY/ksk-123/ID-CERT/version");
+  Name signer01 = pib.getMgmtCert().getName().getPrefix(-1);
+  Name signer02("/localhost/pib/bob/mgmt/KEY/ksk-1234/ID-CERT");
+
+  // TYPE_USER is handled separately, isUpdatedAllowed simply returns false
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_USER, target02, signer01, DEFAULT_OPT_NO), false);
+
+  // Test access control of local management key
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_ID, target01, signer01, DEFAULT_OPT_NO), false);
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_ID, target02, signer01, DEFAULT_OPT_NO), false);
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_ID, target03, signer01, DEFAULT_OPT_NO), false);
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_ID, target04, signer01, DEFAULT_OPT_NO), false);
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_ID, target05, signer01, DEFAULT_OPT_NO), true);
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_ID, target05, signer02, DEFAULT_OPT_NO), false);
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_KEY, target06, signer01, DEFAULT_OPT_NO), true);
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_KEY, target06, signer02, DEFAULT_OPT_NO), false);
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_CERT, target07, signer01, DEFAULT_OPT_NO), true);
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_CERT, target07, signer02, DEFAULT_OPT_NO), false);
+}
+
+BOOST_AUTO_TEST_CASE(IsUpdateAllowedTest2)
+{
+  // This test case is to check the access control of regular key
+
+  owner = "alice";
+
+  Pib pib(*face,
+          tmpPath.string(),
+          m_keyChain.getTpm().getTpmLocator(),
+          owner);
+  PibDb db(tmpPath.string());
+
+  UpdateQueryProcessor& pro = pib.m_updateProcessor;
+
+  Name parent("/test");
+  addIdentity(parent);
+  Name parentCertName = m_keyChain.getDefaultCertificateNameForIdentity(parent);
+  shared_ptr<IdentityCertificate> parentCert = m_keyChain.getCertificate(parentCertName);
+  Name parentSigner = parentCertName.getPrefix(-1);
+
+  advanceClocks(io, time::milliseconds(100));
+  Name parentKeyName2 = m_keyChain.generateRsaKeyPair(parent);
+  shared_ptr<IdentityCertificate> parentCert2 = m_keyChain.selfSign(parentKeyName2);
+  Name parentSigner2 = parentCert2->getName().getPrefix(-1);
+
+  db.addIdentity(parent);
+  db.addKey(parentCert->getPublicKeyName(), parentCert->getPublicKeyInfo());
+  db.addKey(parentCert2->getPublicKeyName(), parentCert2->getPublicKeyInfo());
+  db.setDefaultKeyNameOfIdentity(parentCert->getPublicKeyName());
+  db.addCertificate(*parentCert);
+  db.setDefaultCertNameOfKey(parentCert->getName());
+  db.addCertificate(*parentCert2);
+  db.setDefaultCertNameOfKey(parentCert2->getName());
+
+  Name testId("/test/id");
+  addIdentity(testId);
+  Name certName = m_keyChain.getDefaultCertificateNameForIdentity(testId);
+  shared_ptr<IdentityCertificate> testCert = m_keyChain.getCertificate(certName);
+  Name testKeyName = testCert->getPublicKeyName();
+  Name testSigner = certName.getPrefix(-1);
+
+  advanceClocks(io, time::milliseconds(100));
+  Name secondKeyName = m_keyChain.generateRsaKeyPair(testId);
+  shared_ptr<IdentityCertificate> secondCert = m_keyChain.selfSign(secondKeyName);
+  Name secondCertName = secondCert->getName();
+  Name secondSigner = secondCertName.getPrefix(-1);
+
+  db.addIdentity(testId);
+  db.addKey(testKeyName, testCert->getPublicKeyInfo());
+  db.addKey(secondKeyName, secondCert->getPublicKeyInfo());
+  db.setDefaultKeyNameOfIdentity(testKeyName);
+  db.addCertificate(*testCert);
+  db.setDefaultCertNameOfKey(testCert->getName());
+  db.addCertificate(*secondCert);
+  db.setDefaultCertNameOfKey(secondCert->getName());
+
+  Name nonSigner("/non-signer/KEY/ksk-123/ID-CERT");
+
+  // for target type = TYPE_ID
+  // one cannot add non-child
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_ID, testId, nonSigner, DEFAULT_OPT_NO), false);
+  // parent can add child
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_ID, testId, parentSigner, DEFAULT_OPT_NO), true);
+  // non-default parent key cannot add a child
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_ID, testId, parentSigner2, DEFAULT_OPT_NO), false);
+  // only DEFAULT_OPT_NO is allowed if target type is TYPE_ID
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_ID, testId, parentSigner, DEFAULT_OPT_ID), false);
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_ID, testId, parentSigner, DEFAULT_OPT_KEY), false);
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_ID, testId, parentSigner, DEFAULT_OPT_USER), false);
+
+  // for target type = TYPE_KEY
+  // one can add its own key
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_KEY, testKeyName, testSigner, DEFAULT_OPT_NO),
+                    true);
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_KEY, secondKeyName, testSigner, DEFAULT_OPT_NO),
+                    true);
+  // one can set its default key
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_KEY, testKeyName, testSigner, DEFAULT_OPT_ID),
+                    true);
+  // non-default key cannot add its own key
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_KEY, secondKeyName, secondSigner, DEFAULT_OPT_NO),
+                    false);
+  // non-default key cannot set its default key
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_KEY, testKeyName, secondSigner, DEFAULT_OPT_ID),
+                    false);
+  // one can add its child's key
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_KEY, secondKeyName, parentSigner, DEFAULT_OPT_NO),
+                    true);
+  // one can set its child's default key
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_KEY, testKeyName, parentSigner, DEFAULT_OPT_ID),
+                    true);
+  // non-default key cannot add its child's key
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_KEY, secondKeyName, parentSigner2, DEFAULT_OPT_NO),
+                    false);
+  // non-default parent key cannot set its child's default key
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_KEY, testKeyName, parentSigner2, DEFAULT_OPT_ID),
+                    false);
+  // DEFAULT_OPT_KEY is not allowed if target type is TYPE_KEY
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_KEY, testKeyName, testSigner, DEFAULT_OPT_KEY),
+                    false);
+  // DEFAULT_OPT_USER is not allowed if signer is no local management key
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_KEY, testKeyName, testSigner, DEFAULT_OPT_USER),
+                    false);
+
+  // for target type = TYPE_CERT
+  // one can add its own certificate
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_CERT, certName, testSigner, DEFAULT_OPT_NO),
+                    true);
+  // one can set its own default certificate
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_CERT, certName, testSigner, DEFAULT_OPT_ID),
+                    true);
+  // one can set its own key's default certificate
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_CERT, certName, testSigner, DEFAULT_OPT_KEY),
+                    true);
+  // DEFAULT_OPT_USER is not allowed if signer is no local management key
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_CERT, certName, testSigner, DEFAULT_OPT_USER),
+                    false);
+  // non-default key can add other key's certificate
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_CERT, certName, secondSigner, DEFAULT_OPT_NO),
+                    false);
+  // non-default key can add its own certificate
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_CERT, secondCertName, secondSigner, DEFAULT_OPT_NO),
+                    true);
+  // one can add its child's certificate
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_CERT, certName, parentSigner, DEFAULT_OPT_NO),
+                    true);
+  // non-default key cannot add its child's certificate
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_CERT, certName, parentSigner2, DEFAULT_OPT_NO),
+                    false);
+  // non-default key cannot set add its identity default certificate
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_CERT, secondCertName, secondSigner, DEFAULT_OPT_ID),
+                    false);
+  // non-default key can set add its own default certificate
+  BOOST_CHECK_EQUAL(pro.isUpdateAllowed(TYPE_CERT, secondCertName, secondSigner, DEFAULT_OPT_KEY),
+                    true);
+}
+
+BOOST_AUTO_TEST_CASE(IsDeleteAllowedTest1)
+{
+  // This test case is to check the access control of local management key
+
+  owner = "alice";
+
+  Pib pib(*face,
+          tmpPath.string(),
+          m_keyChain.getTpm().getTpmLocator(),
+          owner);
+
+  DeleteQueryProcessor& pro = pib.m_deleteProcessor;
+
+  Name target01("/localhost/pib");
+  Name target02("/localhost/pib/alice/Mgmt");
+  Name target03("/localhost/pib/alice/Mgmt/ok");
+  Name target04("/localhost/pib/alice");
+  Name target05("/test/id");
+  Name target06("/test/id/ksk-123");
+  Name target07("/test/id/KEY/ksk-123/ID-CERT/version");
+  Name signer01 = pib.getMgmtCert().getName().getPrefix(-1);
+  Name signer02("/localhost/pib/bob/Mgmt/KEY/ksk-1234/ID-CERT");
+
+  // TYPE_USER is handled separately
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_USER, target02, signer01), false);
+
+  // Test access control of local management key
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_ID, target01, signer01), false);
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_ID, target02, signer01), false);
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_ID, target03, signer01), false);
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_ID, target04, signer01), false);
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_ID, target05, signer01), true);
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_KEY, target06, signer01), true);
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_CERT, target07, signer01), true);
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_ID, target05, signer02), false);
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_KEY, target06, signer02), false);
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_CERT, target07, signer02), false);
+}
+
+BOOST_AUTO_TEST_CASE(IsDeleteAllowedTest2)
+{
+  // This test case is to check the access control of regular key
+  owner = "alice";
+
+  Pib pib(*face,
+          tmpPath.string(),
+          m_keyChain.getTpm().getTpmLocator(),
+          owner);
+  PibDb db(tmpPath.string());
+  DeleteQueryProcessor& pro = pib.m_deleteProcessor;
+
+  Name parent("/test");
+  addIdentity(parent);
+  Name parentCertName = m_keyChain.getDefaultCertificateNameForIdentity(parent);
+  shared_ptr<IdentityCertificate> parentCert = m_keyChain.getCertificate(parentCertName);
+  Name parentSigner = parentCertName.getPrefix(-1);
+
+  advanceClocks(io, time::milliseconds(100));
+  Name parentKeyName2 = m_keyChain.generateRsaKeyPair(parent);
+  shared_ptr<IdentityCertificate> parentCert2 = m_keyChain.selfSign(parentKeyName2);
+  Name parentSigner2 = parentCert2->getName().getPrefix(-1);
+
+  db.addIdentity(parent);
+  db.addKey(parentCert->getPublicKeyName(), parentCert->getPublicKeyInfo());
+  db.addKey(parentCert2->getPublicKeyName(), parentCert2->getPublicKeyInfo());
+  db.setDefaultKeyNameOfIdentity(parentCert->getPublicKeyName());
+  db.addCertificate(*parentCert);
+  db.setDefaultCertNameOfKey(parentCert->getName());
+  db.addCertificate(*parentCert2);
+  db.setDefaultCertNameOfKey(parentCert2->getName());
+
+  Name testId("/test/id");
+  addIdentity(testId);
+  Name certName = m_keyChain.getDefaultCertificateNameForIdentity(testId);
+  shared_ptr<IdentityCertificate> testCert = m_keyChain.getCertificate(certName);
+  Name testKeyName = testCert->getPublicKeyName();
+  Name testSigner = certName.getPrefix(-1);
+
+  advanceClocks(io, time::milliseconds(100));
+  Name secondKeyName = m_keyChain.generateRsaKeyPair(testId);
+  shared_ptr<IdentityCertificate> secondCert = m_keyChain.selfSign(secondKeyName);
+  Name secondCertName = secondCert->getName();
+  Name secondSigner = secondCertName.getPrefix(-1);
+
+  db.addIdentity(testId);
+  db.addKey(testKeyName, testCert->getPublicKeyInfo());
+  db.addKey(secondKeyName, secondCert->getPublicKeyInfo());
+  db.setDefaultKeyNameOfIdentity(testKeyName);
+  db.addCertificate(*testCert);
+  db.setDefaultCertNameOfKey(testCert->getName());
+  db.addCertificate(*secondCert);
+  db.setDefaultCertNameOfKey(secondCert->getName());
+
+  Name nonSigner("/non-signer/KEY/ksk-123/ID-CERT");
+
+  // one can delete itself
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_ID, testId, testSigner), true);
+  // parent can delete its child
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_ID, testId, parentSigner), true);
+  // non-default key cannot delete its identity
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_ID, testId, secondSigner), false);
+  // non-default key cannot delete its child
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_ID, testId, parentSigner2), false);
+  // one cannot delete its parent
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_ID, parent, testSigner), false);
+
+  // one can delete its own key
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_KEY, testKeyName, testSigner), true);
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_KEY, secondKeyName, testSigner), true);
+  // parent can delete its child's key
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_KEY, testKeyName, parentSigner), true);
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_KEY, secondKeyName, parentSigner), true);
+  // non-default key cannot delete other key
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_KEY, testKeyName, secondSigner), false);
+  // non-default key can delete itself
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_KEY, secondKeyName, secondSigner), true);
+  // non-default key cannot delete its child's key
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_KEY, testKeyName, parentSigner2), false);
+
+  // one can delete its own certificate
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_CERT, certName, testSigner), true);
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_CERT, secondCertName, testSigner), true);
+  // non-default key cannot delete other's certificate
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_CERT, certName, secondSigner), false);
+  // non-default key can delete its own certificate
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_CERT, secondCertName, secondSigner), true);
+  // parent can delete its child's certificate
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_CERT, certName, parentSigner), true);
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_CERT, secondCertName, parentSigner), true);
+  // non-default parent cannot delete its child's certificate
+  BOOST_CHECK_EQUAL(pro.isDeleteAllowed(TYPE_CERT, certName, parentSigner2), false);
+}
+
+
+BOOST_AUTO_TEST_CASE(UpdateUserTest)
+{
+  owner = "alice";
+
+  Pib pib(*face,
+          tmpPath.string(),
+          m_keyChain.getTpm().getTpmLocator(),
+          owner);
+
+  advanceClocks(io, time::milliseconds(10), 10);
+  util::InMemoryStoragePersistent& cache = pib.getResponseCache();
+
+  m_keyChain.addCertificate(pib.getMgmtCert());
+
+  PibDb db(tmpPath.string());
+
+  Name bob("/localhost/pib/bob/mgmt");
+  addIdentity(bob);
+  Name bobCertName = m_keyChain.getDefaultCertificateNameForIdentity(bob);
+  shared_ptr<IdentityCertificate> bobCert = m_keyChain.getCertificate(bobCertName);
+
+  // signer is correct, but user name is wrong, should fall
+  PibUser pibUser1;
+  pibUser1.setMgmtCert(*bobCert);
+  UpdateParam param1(pibUser1);
+  auto interest1 = generateSignedInterest(param1, owner, db.getMgmtCertificate()->getName());
+
+  face->sentDatas.clear();
+  face->receive(*interest1);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_REQUIRE(cache.find(interest1->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibError result;
+  BOOST_REQUIRE_NO_THROW(result.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result.getErrorCode(), ERR_WRONG_PARAM);
+
+  // user name is correct, but signer is wrong, should fail
+  PibUser pibUser2;
+  pibUser2.setMgmtCert(pib.getMgmtCert());
+  UpdateParam param2(pibUser2);
+  auto interest2 = generateSignedInterest(param2, owner, bobCertName);
+
+  face->sentDatas.clear();
+  face->receive(*interest2);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_CHECK(cache.find(interest2->getName()) == nullptr); // verification should fail, no response
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 0);
+
+  // update an existing user with a new mgmt key, signed by the old mgmt key.
+  advanceClocks(io, time::milliseconds(100));
+  Name ownerSecondKeyName =
+    m_keyChain.generateRsaKeyPair(Name("/localhost/pib/alice/mgmt"), false);
+  shared_ptr<IdentityCertificate> ownerSecondCert = m_keyChain.selfSign(ownerSecondKeyName);
+  m_keyChain.addCertificate(*ownerSecondCert);
+
+  PibUser pibUser3;
+  pibUser3.setMgmtCert(*ownerSecondCert);
+  UpdateParam param3(pibUser3);
+  auto interest3 = generateSignedInterest(param3, owner, db.getMgmtCertificate()->getName());
+
+  face->sentDatas.clear();
+  face->receive(*interest3);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_REQUIRE(cache.find(interest3->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibError result3;
+  BOOST_REQUIRE_NO_THROW(result3.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result3.getErrorCode(), ERR_SUCCESS);
+  BOOST_CHECK(db.getMgmtCertificate()->wireEncode() == ownerSecondCert->wireEncode());
+
+  // Add an cert and set it as user default cert.
+  Name testId("/test/id");
+  Name testIdCertName = m_keyChain.createIdentity(testId);
+  shared_ptr<IdentityCertificate> testIdCert = m_keyChain.getCertificate(testIdCertName);
+  Name testIdKeyName = testIdCert->getPublicKeyName();
+  UpdateParam updateParam(*testIdCert, DEFAULT_OPT_USER);
+  auto interest4 = generateSignedInterest(updateParam, owner, ownerSecondCert->getName());
+
+  face->sentDatas.clear();
+  face->receive(*interest4);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_REQUIRE(cache.find(interest4->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibError result4;
+  BOOST_REQUIRE_NO_THROW(result4.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result4.getErrorCode(), ERR_SUCCESS);
+
+  BOOST_CHECK(pib.getDb().hasCertificate(testIdCertName));
+  BOOST_CHECK(pib.getDb().hasKey(testIdKeyName));
+  BOOST_CHECK(pib.getDb().hasIdentity(testId));
+
+  BOOST_REQUIRE_NO_THROW(pib.getDb().getDefaultCertNameOfKey(testIdKeyName));
+  BOOST_REQUIRE_NO_THROW(pib.getDb().getDefaultKeyNameOfIdentity(testId));
+  BOOST_REQUIRE_NO_THROW(pib.getDb().getDefaultIdentity());
+
+  BOOST_CHECK_EQUAL(pib.getDb().getDefaultCertNameOfKey(testIdKeyName), testIdCertName);
+  BOOST_CHECK_EQUAL(pib.getDb().getDefaultKeyNameOfIdentity(testId), testIdKeyName);
+  BOOST_CHECK_EQUAL(pib.getDb().getDefaultIdentity(), testId);
+}
+
+BOOST_AUTO_TEST_CASE(UpdateRegularKeyTest)
+{
+  owner = "alice";
+
+  Pib pib(*face,
+          tmpPath.string(),
+          m_keyChain.getTpm().getTpmLocator(),
+          owner);
+
+  advanceClocks(io, time::milliseconds(10), 10);
+  util::InMemoryStoragePersistent& cache = pib.getResponseCache();
+  auto ownerMgmtCert = pib.getMgmtCert();
+  m_keyChain.addCertificate(ownerMgmtCert);
+
+  PibDb db(tmpPath.string());
+
+  Name id0("/test/identity0");
+  Name certName000 = m_keyChain.createIdentity(id0);
+  shared_ptr<IdentityCertificate> cert000 = m_keyChain.getCertificate(certName000);
+  Name keyName00 = cert000->getPublicKeyName();
+  advanceClocks(io, time::milliseconds(100));
+  shared_ptr<IdentityCertificate> cert001 = m_keyChain.selfSign(keyName00);
+  Name certName001 = cert001->getName();
+
+  advanceClocks(io, time::milliseconds(100));
+  Name keyName01 = m_keyChain.generateRsaKeyPair(id0);
+  shared_ptr<IdentityCertificate> cert010 = m_keyChain.selfSign(keyName01);
+  Name certName010 = cert010->getName();
+  advanceClocks(io, time::milliseconds(100));
+  shared_ptr<IdentityCertificate> cert011 = m_keyChain.selfSign(keyName01);
+  Name certName011 = cert011->getName();
+  m_keyChain.addCertificate(*cert010);
+
+  advanceClocks(io, time::milliseconds(100));
+  Name id1("/test/identity1");
+  Name certName100 = m_keyChain.createIdentity(id1);
+  shared_ptr<IdentityCertificate> cert100 = m_keyChain.getCertificate(certName100);
+  Name keyName10 = cert100->getPublicKeyName();
+  advanceClocks(io, time::milliseconds(100));
+  shared_ptr<IdentityCertificate> cert101 = m_keyChain.selfSign(keyName10);
+  Name certName101 = cert101->getName();
+
+  advanceClocks(io, time::milliseconds(100));
+  Name keyName11 = m_keyChain.generateRsaKeyPair(id1);
+  shared_ptr<IdentityCertificate> cert110 = m_keyChain.selfSign(keyName11);
+  Name certName110 = cert110->getName();
+  advanceClocks(io, time::milliseconds(100));
+  shared_ptr<IdentityCertificate> cert111 = m_keyChain.selfSign(keyName11);
+  Name certName111 = cert111->getName();
+  m_keyChain.addCertificate(*cert111);
+
+
+  // Add a cert
+  BOOST_CHECK_EQUAL(db.hasIdentity(id0), false);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName00), false);
+  BOOST_CHECK_EQUAL(db.hasCertificate(certName000), false);
+  UpdateParam param1(*cert000);
+  auto interest1 = generateSignedInterest(param1, owner, ownerMgmtCert.getName());
+
+  face->sentDatas.clear();
+  face->receive(*interest1);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_CHECK(cache.find(interest1->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibError result1;
+  BOOST_REQUIRE_NO_THROW(result1.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result1.getErrorCode(), ERR_SUCCESS);
+  BOOST_CHECK_EQUAL(db.hasIdentity(id0), true);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName00), true);
+  BOOST_CHECK_EQUAL(db.hasCertificate(certName000), true);
+
+  db.addCertificate(*cert100);
+  BOOST_CHECK_EQUAL(db.hasIdentity(id1), true);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName10), true);
+  BOOST_CHECK_EQUAL(db.hasCertificate(certName100), true);
+
+  // Set default
+  BOOST_CHECK_EQUAL(db.getDefaultIdentity(), id0);
+  BOOST_CHECK_EQUAL(db.getDefaultKeyNameOfIdentity(id0), keyName00);
+  BOOST_CHECK_EQUAL(db.getDefaultCertNameOfKey(keyName00), certName000);
+
+  UpdateParam param2(id1, DEFAULT_OPT_USER);
+  auto interest2 = generateSignedInterest(param2, owner, ownerMgmtCert.getName());
+
+  face->sentDatas.clear();
+  face->receive(*interest2);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_CHECK(cache.find(interest2->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibError result2;
+  BOOST_REQUIRE_NO_THROW(result2.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result2.getErrorCode(), ERR_SUCCESS);
+  BOOST_CHECK_EQUAL(db.getDefaultIdentity(), id1);
+
+  db.addCertificate(*cert010);
+  UpdateParam param3(keyName01, cert010->getPublicKeyInfo(), DEFAULT_OPT_ID);
+  auto interest3 = generateSignedInterest(param3, owner, ownerMgmtCert.getName());
+
+  face->sentDatas.clear();
+  face->receive(*interest3);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_CHECK(cache.find(interest3->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibError result3;
+  BOOST_REQUIRE_NO_THROW(result3.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result3.getErrorCode(), ERR_SUCCESS);
+  BOOST_CHECK_EQUAL(db.getDefaultKeyNameOfIdentity(id0), keyName01);
+
+  db.addCertificate(*cert011);
+  UpdateParam param4(*cert011, DEFAULT_OPT_KEY);
+  auto interest4 = generateSignedInterest(param4, owner, ownerMgmtCert.getName());
+
+  face->sentDatas.clear();
+  face->receive(*interest4);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_CHECK(cache.find(interest4->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibError result4;
+  BOOST_REQUIRE_NO_THROW(result4.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result4.getErrorCode(), ERR_SUCCESS);
+  BOOST_CHECK_EQUAL(db.getDefaultCertNameOfKey(keyName01), certName011);
+
+  // add key and certificate using regular keys.
+  BOOST_CHECK_EQUAL(db.hasKey(keyName11), false);
+  UpdateParam param5(keyName11, cert110->getPublicKeyInfo());
+  auto interest5 = generateSignedInterest(param5, owner, cert100->getName());
+
+  face->sentDatas.clear();
+  face->receive(*interest5);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_CHECK(cache.find(interest5->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibError result5;
+  BOOST_REQUIRE_NO_THROW(result5.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result5.getErrorCode(), ERR_SUCCESS);
+  BOOST_CHECK_EQUAL(db.hasKey(keyName11), true);
+
+  // add cert using its own key which has been added before
+  BOOST_CHECK_EQUAL(db.hasCertificate(cert101->getName()), false);
+  UpdateParam param6(*cert101);
+  auto interest6 = generateSignedInterest(param6, owner, cert100->getName());
+
+  face->sentDatas.clear();
+  face->receive(*interest6);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_CHECK(cache.find(interest6->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibError result6;
+  BOOST_REQUIRE_NO_THROW(result6.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result6.getErrorCode(), ERR_SUCCESS);
+  BOOST_CHECK_EQUAL(db.hasCertificate(cert101->getName()), true);
+}
+
+BOOST_AUTO_TEST_CASE(DeleteUserTest)
+{
+  owner = "alice";
+
+  Pib pib(*face,
+          tmpPath.string(),
+          m_keyChain.getTpm().getTpmLocator(),
+          owner);
+  advanceClocks(io, time::milliseconds(10), 10);
+  util::InMemoryStoragePersistent& cache = pib.getResponseCache();
+  auto ownerMgmtCert = pib.getMgmtCert();
+  m_keyChain.addCertificate(ownerMgmtCert);
+
+  PibDb db(tmpPath.string());
+
+  // Delete user should fail
+  DeleteParam param(Name(), TYPE_USER);
+  auto interest = generateSignedInterest(param, owner, ownerMgmtCert.getName());
+
+  face->receive(*interest);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_CHECK(cache.find(interest->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibError result;
+  BOOST_REQUIRE_NO_THROW(result.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result.getErrorCode(), ERR_WRONG_PARAM);
+}
+
+BOOST_AUTO_TEST_CASE(DeleteRegularKeyTest)
+{
+  owner = "alice";
+
+  Pib pib(*face,
+          tmpPath.string(),
+          m_keyChain.getTpm().getTpmLocator(),
+          owner);
+  advanceClocks(io, time::milliseconds(10), 10);
+  util::InMemoryStoragePersistent& cache = pib.getResponseCache();
+  auto ownerMgmtCert = pib.getMgmtCert();
+  m_keyChain.addCertificate(ownerMgmtCert);
+
+  PibDb& db = pib.getDb();
+
+  Name testId("/test/identity");
+  Name testIdCertName00 = m_keyChain.createIdentity(testId);
+  shared_ptr<IdentityCertificate> cert00 = m_keyChain.getCertificate(testIdCertName00);
+  Name testIdKeyName0 = cert00->getPublicKeyName();
+  advanceClocks(io, time::milliseconds(100));
+  shared_ptr<IdentityCertificate> cert01 = m_keyChain.selfSign(testIdKeyName0);
+  Name testIdCertName01 = cert01->getName();
+
+  advanceClocks(io, time::milliseconds(100));
+  Name testIdKeyName1 = m_keyChain.generateRsaKeyPair(testId);
+  shared_ptr<IdentityCertificate> cert10 = m_keyChain.selfSign(testIdKeyName1);
+  Name testIdCertName10 = cert10->getName();
+  advanceClocks(io, time::milliseconds(100));
+  shared_ptr<IdentityCertificate> cert11 = m_keyChain.selfSign(testIdKeyName1);
+  Name testIdCertName11 = cert11->getName();
+  m_keyChain.addCertificate(*cert11);
+
+  db.addCertificate(*cert00);
+  db.addCertificate(*cert01);
+  db.addCertificate(*cert10);
+  db.addCertificate(*cert11);
+  db.setDefaultIdentity(testId);
+  db.setDefaultKeyNameOfIdentity(testIdKeyName0);
+  db.setDefaultCertNameOfKey(testIdCertName00);
+  db.setDefaultCertNameOfKey(testIdCertName10);
+
+  // delete a certificate itself
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName11), true);
+  DeleteParam param1(testIdCertName11, TYPE_CERT);
+  auto interest1 = generateSignedInterest(param1, owner, testIdCertName11);
+
+  face->sentDatas.clear();
+  face->receive(*interest1);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_CHECK(cache.find(interest1->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibError result1;
+  BOOST_REQUIRE_NO_THROW(result1.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result1.getErrorCode(), ERR_SUCCESS);
+  BOOST_CHECK_EQUAL(db.hasCertificate(testIdCertName11), false);
+
+  // delete a key itself
+  BOOST_CHECK_EQUAL(db.hasKey(testIdKeyName1), true);
+  DeleteParam param2(testIdKeyName1, TYPE_KEY);
+  auto interest2 = generateSignedInterest(param2, owner, testIdCertName11);
+
+  face->sentDatas.clear();
+  face->receive(*interest2);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_CHECK(cache.find(interest2->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibError result2;
+  BOOST_REQUIRE_NO_THROW(result2.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result2.getErrorCode(), ERR_SUCCESS);
+  BOOST_CHECK_EQUAL(db.hasKey(testIdKeyName1), false);
+
+  // delete an identity using non-default key, should fail
+  db.addCertificate(*cert11);
+  BOOST_CHECK_EQUAL(db.hasIdentity(testId), true);
+  DeleteParam param3(testId, TYPE_ID);
+  auto interest3 = generateSignedInterest(param3, owner, testIdCertName11);
+
+  face->sentDatas.clear();
+  face->receive(*interest3);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_CHECK(cache.find(interest3->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibError result3;
+  BOOST_REQUIRE_NO_THROW(result3.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result3.getErrorCode(), ERR_WRONG_SIGNER);
+  BOOST_CHECK_EQUAL(db.hasIdentity(testId), true);
+
+  // delete an identity using identity default key, should succeed
+  DeleteParam param4(testId, TYPE_ID);
+  auto interest4 = generateSignedInterest(param4, owner, testIdCertName00);
+
+  face->sentDatas.clear();
+  face->receive(*interest4);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  BOOST_CHECK(cache.find(interest4->getName()) != nullptr);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  PibError result4;
+  BOOST_REQUIRE_NO_THROW(result4.wireDecode(face->sentDatas[0].getContent().blockFromValue()));
+  BOOST_CHECK_EQUAL(result4.getErrorCode(), ERR_SUCCESS);
+  BOOST_CHECK_EQUAL(db.hasIdentity(testId), false);
+}
+
+BOOST_AUTO_TEST_CASE(ReadCommandTest2)
+{
+  // Read Certificates;
+  owner = "alice";
+
+  Pib pib(*face,
+          tmpPath.string(),
+          m_keyChain.getTpm().getTpmLocator(),
+          owner);
+
+  advanceClocks(io, time::milliseconds(10), 100);
+  auto ownerMgmtCert = pib.getMgmtCert();
+  m_keyChain.addCertificate(ownerMgmtCert);
+
+  Name testId("/test/identity");
+  Name testIdCertName00 = m_keyChain.createIdentity(testId);
+  shared_ptr<IdentityCertificate> cert00 = m_keyChain.getCertificate(testIdCertName00);
+  Name testIdKeyName0 = cert00->getPublicKeyName();
+  advanceClocks(io, time::milliseconds(100));
+  shared_ptr<IdentityCertificate> cert01 = m_keyChain.selfSign(testIdKeyName0);
+  Name testIdCertName01 = cert01->getName();
+
+  advanceClocks(io, time::milliseconds(100));
+  Name testIdKeyName1 = m_keyChain.generateRsaKeyPair(testId);
+  shared_ptr<IdentityCertificate> cert10 = m_keyChain.selfSign(testIdKeyName1);
+  Name testIdCertName10 = cert10->getName();
+  advanceClocks(io, time::milliseconds(100));
+  shared_ptr<IdentityCertificate> cert11 = m_keyChain.selfSign(testIdKeyName1);
+  Name testIdCertName11 = cert11->getName();
+
+
+  UpdateParam param00(*cert00);
+  UpdateParam param01(*cert01);
+  UpdateParam param10(*cert10);
+  UpdateParam param11(*cert11);
+  auto interest00 = generateSignedInterest(param00, owner, ownerMgmtCert.getName());
+  auto interest01 = generateSignedInterest(param01, owner, ownerMgmtCert.getName());
+  auto interest10 = generateSignedInterest(param10, owner, ownerMgmtCert.getName());
+  auto interest11 = generateSignedInterest(param11, owner, ownerMgmtCert.getName());
+
+  face->sentDatas.clear();
+  face->receive(*interest00);
+  advanceClocks(io, time::milliseconds(10), 10);
+  face->receive(*interest01);
+  advanceClocks(io, time::milliseconds(10), 10);
+  face->receive(*interest10);
+  advanceClocks(io, time::milliseconds(10), 10);
+  face->receive(*interest11);
+  advanceClocks(io, time::milliseconds(10), 10);
+
+  auto interest1 = make_shared<Interest>(testIdCertName11);
+  face->sentDatas.clear();
+  face->receive(*interest1);
+  advanceClocks(io, time::milliseconds(10), 10);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  BOOST_CHECK(face->sentDatas[0].wireEncode() == cert11->wireEncode());
+
+  auto interest2 = make_shared<Interest>(testIdCertName11.getPrefix(-1));
+  face->sentDatas.clear();
+  face->receive(*interest2);
+  advanceClocks(io, time::milliseconds(10), 10);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  BOOST_CHECK_EQUAL(face->sentDatas[0].getName().getPrefix(-1),
+                    cert11->getName().getPrefix(-1));
+
+  auto interest3 = make_shared<Interest>(testIdCertName11.getPrefix(-1));
+  pib.getDb().deleteCertificate(testIdCertName11);
+  face->sentDatas.clear();
+  face->receive(*interest3);
+  advanceClocks(io, time::milliseconds(10), 10);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  BOOST_CHECK(face->sentDatas[0].wireEncode() == cert10->wireEncode());
+
+  auto interest4 = make_shared<Interest>(testIdCertName11);
+  face->sentDatas.clear();
+  face->receive(*interest4);
+  advanceClocks(io, time::milliseconds(10), 10);
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace pib
+} // namespace ndn
diff --git a/tests/pib/response-cache.t.cpp b/tests/pib/response-cache.t.cpp
new file mode 100644
index 0000000..2653579
--- /dev/null
+++ b/tests/pib/response-cache.t.cpp
@@ -0,0 +1,64 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * 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.
+ */
+
+#include "tools/pib/response-cache.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace ndn {
+namespace pib {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(TestResponseCache)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  ResponseCache cache;
+
+  Name dataName("/test/data");
+  dataName.appendVersion();
+  shared_ptr<Data> data = make_shared<Data>(dataName);
+
+  Name dataNameNoVersion("/test/data");
+  Name anotherDataName("/test/another");
+
+  BOOST_CHECK_EQUAL(static_cast<bool>(cache.find(dataNameNoVersion)), false);
+  BOOST_CHECK_EQUAL(static_cast<bool>(cache.find(dataName, true)), false);
+
+  cache.insert(*data);
+
+  BOOST_CHECK(static_cast<bool>(cache.find(dataNameNoVersion)));
+  BOOST_CHECK(static_cast<bool>(cache.find(dataName, true)));
+  BOOST_CHECK_EQUAL(static_cast<bool>(cache.find(anotherDataName)), false);
+  BOOST_CHECK_EQUAL(static_cast<bool>(cache.find(anotherDataName, true)), false);
+
+  cache.erase(dataNameNoVersion);
+
+  BOOST_CHECK_EQUAL(static_cast<bool>(cache.find(dataNameNoVersion)), false);
+  BOOST_CHECK_EQUAL(static_cast<bool>(cache.find(dataName, true)), false);
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace pib
+} // namespace ndn
diff --git a/tests/wscript b/tests/wscript
index c67d252..e44f2a4 100644
--- a/tests/wscript
+++ b/tests/wscript
@@ -10,4 +10,5 @@
         source=bld.path.ant_glob(['*.cpp'] + ['%s/**/*.cpp' % tool for tool in bld.env['BUILD_TOOLS']]),
         use=['core-objects'] + ['%s-objects' % tool for tool in bld.env['BUILD_TOOLS']],
         headers='../common.hpp boost-test.hpp',
+        defines='TMP_TESTS_PATH=\"%s/tmp-tests\"' % bld.bldnode,
         )
diff --git a/tools/pib/cert-publisher.cpp b/tools/pib/cert-publisher.cpp
new file mode 100644
index 0000000..abcd5a2
--- /dev/null
+++ b/tools/pib/cert-publisher.cpp
@@ -0,0 +1,128 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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 "cert-publisher.hpp"
+
+namespace ndn {
+namespace pib {
+
+CertPublisher::CertPublisher(Face& face, PibDb& db)
+  : m_face(face)
+  , m_db(db)
+{
+  startPublishAll();
+
+  m_certDeletedConnection =
+    m_db.certificateDeleted.connect(bind(&CertPublisher::stopPublish, this, _1));
+  m_certInsertedConnection =
+    m_db.certificateInserted.connect(bind(&CertPublisher::startPublish, this, _1));
+}
+
+CertPublisher::~CertPublisher()
+{
+  for (const auto& it : m_registeredPrefixes)
+    m_face.unsetInterestFilter(it.second);
+}
+
+void
+CertPublisher::startPublishAll()
+{
+  // For now we have to register the prefix of certificates separately.
+  // The reason is that certificates do not share the same prefix that
+  // can aggregate the certificates publishing without attracting interests
+  // for non-PIB data.
+
+  std::vector<Name> identities = m_db.listIdentities();
+
+  for (const auto& identity : identities) {
+    std::vector<Name> keyNames = m_db.listKeyNamesOfIdentity(identity);
+
+    for (const auto& key : keyNames) {
+      std::vector<Name> certNames = m_db.listCertNamesOfKey(key);
+
+      for (const auto& certName : certNames)
+        startPublish(certName);
+    }
+  }
+}
+
+void
+CertPublisher::registerCertPrefix(const Name& certName)
+{
+  BOOST_ASSERT(!certName.empty());
+
+  const Name& prefix = certName.getPrefix(-1);
+
+  if (m_registeredPrefixes.find(prefix) == m_registeredPrefixes.end()) {
+    m_registeredPrefixes[prefix] =
+      m_face.setInterestFilter(certName.getPrefix(-1),
+        bind(&CertPublisher::processInterest, this, _1, _2),
+        [] (const Name& name) {},
+        [=] (const Name& name, const std::string& msg) {
+          if (m_registeredPrefixes.erase(prefix) == 0) {
+            // registration no longer needed - certificates deleted
+            return;
+          }
+          // retry
+          registerCertPrefix(certName);
+        });
+
+  }
+}
+
+void
+CertPublisher::processInterest(const InterestFilter& interestFilter,
+                               const Interest& interest)
+{
+  shared_ptr<const Data> certificate = m_responseCache.find(interest);
+  if (certificate != nullptr) {
+    m_face.put(*certificate);
+  }
+}
+
+void
+CertPublisher::startPublish(const Name& certName)
+{
+  m_responseCache.insert(*m_db.getCertificate(certName));
+  registerCertPrefix(certName);
+}
+
+void
+CertPublisher::stopPublish(const Name& certName)
+{
+  BOOST_ASSERT(!certName.empty());
+
+  m_responseCache.erase(certName);
+
+  // clear the listener if this is the only cert using the prefix
+  const Name& prefix = certName.getPrefix(-1); // strip version component
+
+  if (m_responseCache.find(prefix) != nullptr)
+    return;
+
+  auto it = m_registeredPrefixes.find(prefix);
+  BOOST_ASSERT(it != m_registeredPrefixes.end());
+  m_face.unsetInterestFilter(it->second);
+  m_registeredPrefixes.erase(it);
+}
+
+} // namespace pib
+} // namespace ndn
diff --git a/tools/pib/cert-publisher.hpp b/tools/pib/cert-publisher.hpp
new file mode 100644
index 0000000..052895c
--- /dev/null
+++ b/tools/pib/cert-publisher.hpp
@@ -0,0 +1,99 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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_PIB_CERT_PUBLISHER_HPP
+#define NDN_PIB_CERT_PUBLISHER_HPP
+
+#include "pib-db.hpp"
+
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/util/in-memory-storage-persistent.hpp>
+
+namespace ndn {
+namespace pib {
+
+/// @brief implements the certificate publisher
+class CertPublisher : noncopyable
+{
+public:
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+  /**
+   * @brief Constructor
+   *
+   * @param face The face pib used to receive queries and serve certificates.
+   * @param pibDb Database which holds the certificates.
+   */
+  CertPublisher(Face& face, PibDb& pibDb);
+
+  ~CertPublisher();
+
+private:
+  void
+  startPublishAll();
+
+  /**
+   * @brief add an interest filter for the certificate
+   */
+  void
+  registerCertPrefix(const Name& certName);
+
+  void
+  processInterest(const InterestFilter& interestFilter,
+                  const Interest& interest);
+
+  void
+  startPublish(const Name& certName);
+
+  /**
+   * @brief callback when a certificate is deleted
+   *
+   * The method will remove the cert from in-memory storage
+   * and also unset interest filter if the removed cert
+   * is the only one with the registered prefix.
+   *
+   * @param certName removed certificate name
+   */
+  void
+  stopPublish(const Name& certName);
+
+private:
+  Face& m_face;
+  PibDb& m_db;
+  util::InMemoryStoragePersistent m_responseCache;
+  std::map<Name, const RegisteredPrefixId*> m_registeredPrefixes;
+
+  util::signal::ScopedConnection m_certDeletedConnection;
+  util::signal::ScopedConnection m_certInsertedConnection;
+};
+
+} // namespace pib
+} // namespace ndn
+
+#endif // NDN_PIB_CERT_PUBLISHER_HPP
diff --git a/tools/pib/default-query-processor.cpp b/tools/pib/default-query-processor.cpp
new file mode 100644
index 0000000..4909ea2
--- /dev/null
+++ b/tools/pib/default-query-processor.cpp
@@ -0,0 +1,199 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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 "default-query-processor.hpp"
+#include "encoding/pib-encoding.hpp"
+
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace pib {
+
+using std::string;
+
+const size_t DefaultQueryProcessor::DEFAULT_QUERY_LENGTH = 5;
+
+DefaultQueryProcessor::DefaultQueryProcessor(PibDb& db)
+  : m_db(db)
+{
+}
+
+std::pair<bool, Block>
+DefaultQueryProcessor::operator()(const Interest& interest)
+{
+  const Name& interestName = interest.getName();
+
+  // handle pib query: /localhost/pib/[UserName]/default/param
+  if (interestName.size() != DEFAULT_QUERY_LENGTH) {
+    // malformed interest, discard
+    return std::make_pair(false, Block());
+  }
+
+  DefaultParam param;
+
+  try {
+    param.wireDecode(interestName.get(OFFSET_PARAM).blockFromValue());
+  }
+  catch (const tlv::Error& e) {
+    PibError error(ERR_WRONG_PARAM, "error in parsing param: " + string(e.what()));
+    return std::make_pair(true, error.wireEncode());
+  }
+
+  switch (param.getTargetType()) {
+  case TYPE_ID:
+    return processDefaultIdQuery(param);
+  case TYPE_KEY:
+    return processDefaultKeyQuery(param);
+  case TYPE_CERT:
+    return processDefaultCertQuery(param);
+  default:
+    {
+      PibError error(ERR_WRONG_PARAM,
+                     "target type is not supported: " +
+                     boost::lexical_cast<string>(param.getTargetType()));
+      return std::make_pair(true, error.wireEncode());
+    }
+  }
+}
+
+std::pair<bool, Block>
+DefaultQueryProcessor::processDefaultIdQuery(const DefaultParam& param)
+{
+  if (param.getOriginType() == TYPE_USER) {
+    try {
+      PibIdentity result(m_db.getDefaultIdentity());
+      return std::make_pair(true, result.wireEncode());
+    }
+    catch (PibDb::Error&) {
+      PibError error(ERR_NO_DEFAULT_ID, "default identity does not exist");
+      return std::make_pair(true, error.wireEncode());
+    }
+  }
+  else {
+    PibError error(ERR_WRONG_PARAM,
+                   "origin type of id target must be USER(0), but gets: " +
+                   boost::lexical_cast<string>(param.getOriginType()));
+    return std::make_pair(true, error.wireEncode());
+  }
+}
+
+std::pair<bool, Block>
+DefaultQueryProcessor::processDefaultKeyQuery(const DefaultParam& param)
+{
+  Name identity;
+  if (param.getOriginType() == TYPE_ID)
+    identity = param.getOriginName();
+  else if (param.getOriginType() == TYPE_USER) {
+    try {
+      identity = m_db.getDefaultIdentity();
+    }
+    catch (PibDb::Error&) {
+      PibError error(ERR_NO_DEFAULT_ID, "default identity does not exist");
+      return std::make_pair(true, error.wireEncode());
+    }
+  }
+  else {
+    PibError error(ERR_WRONG_PARAM,
+                   "origin type of key target must be ID(1) or USER(0), but gets: " +
+                   boost::lexical_cast<string>(param.getOriginType()));
+    return std::make_pair(true, error.wireEncode());
+  }
+
+  try {
+    Name keyName = m_db.getDefaultKeyNameOfIdentity(identity);
+    shared_ptr<PublicKey> key = m_db.getKey(keyName);
+
+    if (key == nullptr) {
+      PibError error(ERR_NO_DEFAULT_KEY, "default key does not exist");
+      return std::make_pair(true, error.wireEncode());
+    }
+
+    PibPublicKey result(keyName, *key);
+    return std::make_pair(true, result.wireEncode());
+  }
+  catch (PibDb::Error& e) {
+    PibError error(ERR_NO_DEFAULT_KEY,
+                   "default key does not exist: " + string(e.what()));
+    return std::make_pair(true, error.wireEncode());
+  }
+}
+
+std::pair<bool, Block>
+DefaultQueryProcessor::processDefaultCertQuery(const DefaultParam& param)
+{
+  Name identity;
+  if (param.getOriginType() == TYPE_USER) {
+    try {
+      identity = m_db.getDefaultIdentity();
+    }
+    catch (PibDb::Error&) {
+      PibError error(ERR_NO_DEFAULT_ID, "default identity does not exist");
+      return std::make_pair(true, error.wireEncode());
+    }
+  }
+  else if (param.getOriginType() == TYPE_ID)
+    identity = param.getOriginName();
+  else if (param.getOriginType() != TYPE_KEY) {
+    PibError error(ERR_WRONG_PARAM,
+                   "origin type of cert target must be KEY(2), ID(1) or USER(0), but gets: " +
+                   boost::lexical_cast<string>(param.getOriginType()));
+    return std::make_pair(true, error.wireEncode());
+  }
+
+  Name keyName;
+  if (param.getOriginType() == TYPE_KEY) {
+    keyName = param.getOriginName();
+    if (keyName.size() < 1) {
+      PibError error(ERR_WRONG_PARAM,
+                     "key name must contain key id component");
+      return std::make_pair(true, error.wireEncode());
+    }
+  }
+  else {
+    try {
+      keyName = m_db.getDefaultKeyNameOfIdentity(identity);
+    }
+    catch (PibDb::Error&) {
+      PibError error(ERR_NO_DEFAULT_KEY, "default key does not exist");
+      return std::make_pair(true, error.wireEncode());
+    }
+  }
+
+  try {
+    Name certName = m_db.getDefaultCertNameOfKey(keyName);
+    shared_ptr<IdentityCertificate> certificate = m_db.getCertificate(certName);
+
+    if (certificate == nullptr) {
+      PibError error (ERR_NO_DEFAULT_CERT, "default cert does not exist");
+      return std::make_pair(true, error.wireEncode());
+    }
+
+    PibCertificate result(*certificate);
+    return std::make_pair(true, result.wireEncode());
+  }
+  catch (PibDb::Error&) {
+    PibError error(ERR_NO_DEFAULT_CERT, "default cert does not exist");
+    return std::make_pair(true, error.wireEncode());
+  }
+}
+
+} // namespace pib
+} // namespace ndn
diff --git a/tools/pib/default-query-processor.hpp b/tools/pib/default-query-processor.hpp
new file mode 100644
index 0000000..1938074
--- /dev/null
+++ b/tools/pib/default-query-processor.hpp
@@ -0,0 +1,76 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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_PIB_DEFAULT_QUERY_PROCESSOR_HPP
+#define NDN_PIB_DEFAULT_QUERY_PROCESSOR_HPP
+
+#include "pib-db.hpp"
+#include "encoding/default-param.hpp"
+#include <ndn-cxx/interest.hpp>
+#include <utility>
+
+namespace ndn {
+namespace pib {
+
+class DefaultQueryProcessor : noncopyable
+{
+public:
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+  /**
+   * @brief Constructor
+   *
+   * @param db The pib database.
+   */
+  explicit
+  DefaultQueryProcessor(PibDb& db);
+
+  std::pair<bool, Block>
+  operator()(const Interest& interest);
+
+private:
+  std::pair<bool, Block>
+  processDefaultIdQuery(const DefaultParam& param);
+
+  std::pair<bool, Block>
+  processDefaultKeyQuery(const DefaultParam& param);
+
+  std::pair<bool, Block>
+  processDefaultCertQuery(const DefaultParam& param);
+
+private:
+  static const size_t DEFAULT_QUERY_LENGTH;
+
+  const PibDb&  m_db;
+};
+
+} // namespace pib
+} // namespace ndn
+
+#endif // NDN_PIB_DEFAULT_QUERY_PROCESSOR_HPP
diff --git a/tools/pib/delete-query-processor.cpp b/tools/pib/delete-query-processor.cpp
new file mode 100644
index 0000000..9174784
--- /dev/null
+++ b/tools/pib/delete-query-processor.cpp
@@ -0,0 +1,205 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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 "delete-query-processor.hpp"
+#include "encoding/pib-encoding.hpp"
+#include "pib.hpp"
+
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace pib {
+
+using std::string;
+
+const size_t DeleteQueryProcessor::DELETE_QUERY_LENGTH = 9;
+const Name DeleteQueryProcessor::PIB_PREFIX("/localhost/pib");
+
+DeleteQueryProcessor::DeleteQueryProcessor(PibDb& db)
+  : m_db(db)
+{
+}
+
+std::pair<bool, Block>
+DeleteQueryProcessor::operator()(const Interest& interest)
+{
+  const Name& interestName = interest.getName();
+
+  // handle pib query: /localhost/pib/[UserName]/delete/param/<signed_interest_related_components>
+  if (interestName.size() != DELETE_QUERY_LENGTH) {
+    // malformed interest, discard
+    return std::make_pair(false, Block());
+  }
+
+  try {
+    DeleteParam param;
+    param.wireDecode(interestName.get(OFFSET_PARAM).blockFromValue());
+
+    SignatureInfo sigInfo;
+    sigInfo.wireDecode(interestName.get(OFFSET_SIG_INFO).blockFromValue());
+
+    // sigInfo must have KeyLocator.Name if the interest passed validation.
+    Name signerName;
+    signerName = sigInfo.getKeyLocator().getName();
+
+    switch (param.getTargetType()) {
+    case TYPE_USER:
+      return std::make_pair(true, PibError(ERR_WRONG_PARAM, "not allowed to delete user").wireEncode());
+    case TYPE_ID:
+      return processDeleteIdQuery(param, signerName);
+    case TYPE_KEY:
+      return processDeleteKeyQuery(param, signerName);
+    case TYPE_CERT:
+      return processDeleteCertQuery(param, signerName);
+    default:
+      {
+        PibError error(ERR_WRONG_PARAM,
+                       "target type is not supported: " +
+                       boost::lexical_cast<string>(param.getTargetType()));
+        return std::make_pair(true, error.wireEncode());
+      }
+    }
+  }
+  catch (const PibDb::Error& e) {
+    PibError error(ERR_INTERNAL_ERROR, e.what());
+    return std::make_pair(true, error.wireEncode());
+  }
+  catch (const tlv::Error& e) {
+    PibError error(ERR_WRONG_PARAM, "error in parsing param: " + string(e.what()));
+    return std::make_pair(true, error.wireEncode());
+  }
+}
+
+std::pair<bool, Block>
+DeleteQueryProcessor::processDeleteIdQuery(const DeleteParam& param, const Name& signerName)
+{
+  Name identity = param.getTargetName();
+  if (!isDeleteAllowed(TYPE_ID, identity, signerName)) {
+    PibError error(ERR_WRONG_SIGNER, "signer is not trusted for this command");
+    return std::make_pair(true, error.wireEncode());
+  }
+
+  m_db.deleteIdentity(identity);
+
+  return std::make_pair(true, PibError(ERR_SUCCESS).wireEncode());
+}
+
+std::pair<bool, Block>
+DeleteQueryProcessor::processDeleteKeyQuery(const DeleteParam& param, const Name& signerName)
+{
+  const Name& keyName = param.getTargetName();
+  if (!isDeleteAllowed(TYPE_KEY, keyName, signerName)) {
+    PibError error(ERR_WRONG_SIGNER, "signer is not trusted for this command");
+    return std::make_pair(true, error.wireEncode());
+  }
+
+  m_db.deleteKey(keyName);
+
+  return std::make_pair(true, PibError(ERR_SUCCESS).wireEncode());
+}
+
+std::pair<bool, Block>
+DeleteQueryProcessor::processDeleteCertQuery(const DeleteParam& param, const Name& signerName)
+{
+  const Name& certName = param.getTargetName();
+  if (!isDeleteAllowed(TYPE_CERT, certName, signerName)) {
+    PibError error(ERR_WRONG_SIGNER, "signer is not trusted for this command");
+    return std::make_pair(true, error.wireEncode());
+  }
+
+  m_db.deleteCertificate(certName);
+
+  return std::make_pair(true, PibError(ERR_SUCCESS).wireEncode());
+}
+
+bool
+DeleteQueryProcessor::isDeleteAllowed(const pib::Type targetType,
+                                      const Name& targetName,
+                                      const Name& signer) const
+{
+  // sanity checking, user cannot be deleted.
+  if (targetType == TYPE_USER)
+    return false;
+
+  // Any identity with prefix /localhost/pib is reserved. Any operation with a targetName under
+  // that prefix will be rejected.
+  if (PIB_PREFIX.isPrefixOf(targetName))
+    return false;
+
+  // Rules for other items:
+  //
+  //    signer                   | deleting privilege
+  //    =========================+===============================================
+  //    local mgmt key           | any id, key, and cert
+  //    -------------------------+-----------------------------------------------
+  //    default key of an id     | any id, key, and cert under the id's namespace
+  //    -------------------------+-----------------------------------------------
+  //    non-default key of an id | any cert of the key
+
+  const Name& signerKeyName = IdentityCertificate::certificateNameToPublicKeyName(signer);
+  const Name& signerId = signerKeyName.getPrefix(-1);
+
+  bool hasSignerDefaultKey = true;
+  Name signerDefaultKeyName;
+  try {
+    signerDefaultKeyName = m_db.getDefaultKeyNameOfIdentity(signerId);
+  }
+  catch (PibDb::Error&) {
+    hasSignerDefaultKey = false;
+  }
+
+  Name mgmtCertName;
+  try {
+    mgmtCertName = m_db.getMgmtCertificate()->getName().getPrefix(-1);
+  }
+  catch (PibDb::Error&) {
+    return false;
+  }
+
+  if (signer == mgmtCertName) {
+    // signer is current management key, anything is allowed.
+    return true;
+  }
+  else if (hasSignerDefaultKey && signerDefaultKeyName == signerKeyName) {
+    // signer is an identity's default key
+    if (!signerId.isPrefixOf(targetName))
+      return false;
+    else
+      return true;
+  }
+  else {
+    // non-default key
+    if (targetType == TYPE_CERT) {
+      // check if it is for the key's cert
+      if (IdentityCertificate::certificateNameToPublicKeyName(targetName) == signerKeyName)
+        return true;
+    }
+    else if (targetType == TYPE_KEY) {
+      // check if it is for the key itself
+      if (targetName == signerKeyName)
+        return true;
+    }
+    return false;
+  }
+}
+
+} // namespace pib
+} // namespace ndn
diff --git a/tools/pib/delete-query-processor.hpp b/tools/pib/delete-query-processor.hpp
new file mode 100644
index 0000000..3336deb
--- /dev/null
+++ b/tools/pib/delete-query-processor.hpp
@@ -0,0 +1,93 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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_PIB_DELETE_QUERY_PROCESSOR_HPP
+#define NDN_PIB_DELETE_QUERY_PROCESSOR_HPP
+
+#include "pib-db.hpp"
+#include "encoding/delete-param.hpp"
+#include <ndn-cxx/interest.hpp>
+#include <utility>
+
+namespace ndn {
+namespace pib {
+
+class Pib;
+
+/// @brief Processing unit for PIB delete query
+class DeleteQueryProcessor : noncopyable
+{
+public:
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+  /**
+   * @brief Constructor
+   *
+   * @param db The pib database.
+   */
+  explicit
+  DeleteQueryProcessor(PibDb& db);
+
+  std::pair<bool, Block>
+  operator()(const Interest& interest);
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  /**
+   * @brief Determine if a delete command is allowed.
+   *
+   * @param targetType The type of the target that will be deleted.
+   * @param targetName The name of the target that will be deleted.
+   * @param signer     The name of the command signer.
+   */
+  bool
+  isDeleteAllowed(const pib::Type targetType,
+                  const Name& targetName,
+                  const Name& signer) const;
+
+private:
+  std::pair<bool, Block>
+  processDeleteIdQuery(const DeleteParam& param, const Name& signerName);
+
+  std::pair<bool, Block>
+  processDeleteKeyQuery(const DeleteParam& param, const Name& signerName);
+
+  std::pair<bool, Block>
+  processDeleteCertQuery(const DeleteParam& param, const Name& signerName);
+
+private:
+  static const size_t DELETE_QUERY_LENGTH;
+  static const Name PIB_PREFIX;
+
+  PibDb&  m_db;
+};
+
+} // namespace pib
+} // namespace ndn
+
+#endif // NDN_PIB_DELETE_QUERY_PROCESSOR_HPP
diff --git a/tools/pib/encoding/default-param.cpp b/tools/pib/encoding/default-param.cpp
new file mode 100644
index 0000000..015bf19
--- /dev/null
+++ b/tools/pib/encoding/default-param.cpp
@@ -0,0 +1,168 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * 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.
+ */
+
+#include "default-param.hpp"
+#include <ndn-cxx/encoding/block-helpers.hpp>
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace pib {
+
+static_assert(std::is_base_of<tlv::Error, DefaultParam::Error>::value,
+              "DefaultParam::Error must inherit from tlv::Error");
+
+const std::string DefaultParam::VERB("default");
+
+DefaultParam::DefaultParam()
+  : m_targetType(TYPE_DEFAULT)
+  , m_originType(TYPE_DEFAULT)
+{
+}
+
+DefaultParam::DefaultParam(const pib::Type targetType,
+                           const pib::Type originType,
+                           const Name& originName)
+  : m_targetType(targetType)
+  , m_originType(originType)
+  , m_originName(originName)
+{
+}
+
+DefaultParam::DefaultParam(const Block& wire)
+{
+  wireDecode(wire);
+}
+
+const Name&
+DefaultParam::getOriginName() const
+{
+  if (m_originType == TYPE_ID || m_originType == TYPE_KEY || m_originType == TYPE_CERT)
+    return m_originName;
+  else
+    throw Error("DefaultParam::getOriginName: origin name does not exist");
+}
+
+template<bool T>
+size_t
+DefaultParam::wireEncode(EncodingImpl<T>& block) const
+{
+  size_t totalLength = 0;
+
+  switch (m_originType) {
+  case TYPE_ID:
+  case TYPE_KEY:
+  case TYPE_CERT:
+    {
+      totalLength += m_originName.wireEncode(block);
+      break;
+    }
+  case TYPE_USER:
+    break;
+  default:
+    throw Error("DefaultParam::wireEncode: unsupported PibType: " +
+                boost::lexical_cast<std::string>(m_originType));
+  }
+
+  totalLength += prependNonNegativeIntegerBlock(block, tlv::pib::Type, m_originType);
+  totalLength += prependNonNegativeIntegerBlock(block, tlv::pib::Type, m_targetType);
+  totalLength += block.prependVarNumber(totalLength);
+  totalLength += block.prependVarNumber(tlv::pib::DefaultParam);
+
+  return totalLength;
+}
+
+template size_t
+DefaultParam::wireEncode<true>(EncodingImpl<true>& block) const;
+
+template size_t
+DefaultParam::wireEncode<false>(EncodingImpl<false>& block) const;
+
+const Block&
+DefaultParam::wireEncode() const
+{
+  if (m_wire.hasWire())
+    return m_wire;
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  return m_wire;
+}
+
+void
+DefaultParam::wireDecode(const Block& wire)
+{
+  if (!wire.hasWire()) {
+    throw Error("The supplied block does not contain wire format");
+  }
+
+  m_wire = wire;
+  m_wire.parse();
+
+  if (m_wire.type() != tlv::pib::DefaultParam)
+    throw Error("Unexpected TLV type when decoding DefaultParam");
+
+  Block::element_const_iterator it = m_wire.elements_begin();
+
+  // the first block must be PibType
+  if (it != m_wire.elements_end() && it->type() == tlv::pib::Type) {
+    m_targetType = static_cast<pib::Type>(readNonNegativeInteger(*it));
+    it++;
+  }
+  else
+    throw Error("DefaultParam requires the first sub-TLV to be PibType");
+
+  // the second block must be PibType
+  if (it != m_wire.elements_end() && it->type() == tlv::pib::Type) {
+    m_originType = static_cast<pib::Type>(readNonNegativeInteger(*it));
+    it++;
+  }
+  else
+    throw Error("DefaultParam requires the second sub-TLV to be PibType");
+
+  switch (m_originType) {
+  case TYPE_ID:
+  case TYPE_KEY:
+  case TYPE_CERT:
+    {
+      if (it != m_wire.elements_end()) {
+        // the third block, if exists, must be Name
+        m_originName.wireDecode(*it);
+        return;
+      }
+      else {
+        throw Error("DefaultParam requires the third sub-TLV to be Name");
+      }
+    }
+  case TYPE_USER:
+    return;
+  default:
+    throw Error("DefaultParam::wireDecode: unsupported PibType: " +
+                boost::lexical_cast<std::string>(m_originType));
+  }
+}
+
+} // namespace pib
+} // namespace ndn
diff --git a/tools/pib/encoding/default-param.hpp b/tools/pib/encoding/default-param.hpp
new file mode 100644
index 0000000..1906954
--- /dev/null
+++ b/tools/pib/encoding/default-param.hpp
@@ -0,0 +1,128 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * 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 NDN_PIB_DEFAULT_PARAM_HPP
+#define NDN_PIB_DEFAULT_PARAM_HPP
+
+#include "pib-common.hpp"
+#include <ndn-cxx/name.hpp>
+
+namespace ndn {
+namespace pib {
+
+/**
+ * @brief DefaultParam is the abstraction of PIB Default parameter.
+ *
+ *  PibDefaultParam := PIB-DEFAULT-PARAM-TYPE TLV-LENGTH
+ *                     PibType    // target type
+ *                     PibType    // origin type
+ *                     Name?      // origin name
+ *
+ * @sa http://redmine.named-data.net/projects/ndn-cxx/wiki/PublicKey_Info_Base#Default-Parameters
+ */
+
+class DefaultParam : noncopyable
+{
+public:
+  class Error : public tlv::Error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : tlv::Error(what)
+    {
+    }
+  };
+
+  DefaultParam();
+
+  DefaultParam(const pib::Type targetType,
+               const pib::Type originType,
+               const Name& originName = Name());
+
+  explicit
+  DefaultParam(const Block& wire);
+
+  tlv::pib::ParamType
+  getParamType() const
+  {
+    return tlv::pib::DefaultParam;
+  }
+
+  pib::Type
+  getTargetType() const
+  {
+    return m_targetType;
+  }
+
+  pib::Type
+  getOriginType() const
+  {
+    return m_originType;
+  }
+
+  /**
+   * @brief Get target name
+   *
+   * @throws Error if origin name does not exist
+   */
+  const Name&
+  getOriginName() const;
+
+  /// @brief Encode to a wire format or estimate wire format
+  template<bool T>
+  size_t
+  wireEncode(EncodingImpl<T>& block) const;
+
+  /**
+   * @brief Encode to a wire format
+   *
+   * @throws Error if encoding fails
+   */
+  const Block&
+  wireEncode() const;
+
+  /**
+   * @brief Decode GetParam from a wire encoded block
+   *
+   * @throws Error if decoding fails
+   */
+  void
+  wireDecode(const Block& wire);
+
+public:
+  static const std::string VERB;
+
+private:
+  pib::Type m_targetType;
+  pib::Type m_originType;
+  Name m_originName;
+
+  mutable Block m_wire;
+};
+
+} // namespace pib
+} // namespace ndn
+
+
+
+#endif // NDN_PIB_DEFAULT_PARAM_HPP
diff --git a/tools/pib/encoding/delete-param.cpp b/tools/pib/encoding/delete-param.cpp
new file mode 100644
index 0000000..41c8f6a
--- /dev/null
+++ b/tools/pib/encoding/delete-param.cpp
@@ -0,0 +1,129 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * 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.
+ */
+
+#include "delete-param.hpp"
+#include <ndn-cxx/encoding/block-helpers.hpp>
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace pib {
+
+static_assert(std::is_base_of<tlv::Error, DeleteParam::Error>::value,
+              "DeleteParam::Error must inherit from tlv::Error");
+
+const std::string DeleteParam::VERB("delete");
+
+DeleteParam::DeleteParam()
+  : m_targetType(TYPE_DEFAULT)
+{
+}
+
+DeleteParam::DeleteParam(const Name& name, pib::Type type)
+  : m_targetType(type)
+  , m_targetName(name)
+{
+}
+
+DeleteParam::DeleteParam(const Block& wire)
+{
+  wireDecode(wire);
+}
+
+template<bool T>
+size_t
+DeleteParam::wireEncode(EncodingImpl<T>& block) const
+{
+  if (m_targetType != TYPE_ID &&
+      m_targetType != TYPE_KEY &&
+      m_targetType != TYPE_CERT &&
+      m_targetType != TYPE_USER)
+    throw Error("DeleteParam::wireEncode: Wrong Type value: " +
+                boost::lexical_cast<std::string>(m_targetType));
+
+  size_t totalLength = 0;
+
+  totalLength += m_targetName.wireEncode(block);
+  totalLength += prependNonNegativeIntegerBlock(block, tlv::pib::Type, m_targetType);
+  totalLength += block.prependVarNumber(totalLength);
+  totalLength += block.prependVarNumber(tlv::pib::DeleteParam);
+
+  return totalLength;
+}
+
+template size_t
+DeleteParam::wireEncode<true>(EncodingImpl<true>& block) const;
+
+template size_t
+DeleteParam::wireEncode<false>(EncodingImpl<false>& block) const;
+
+const Block&
+DeleteParam::wireEncode() const
+{
+  if (m_wire.hasWire())
+    return m_wire;
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  return m_wire;
+}
+
+void
+DeleteParam::wireDecode(const Block& wire)
+{
+  if (!wire.hasWire()) {
+    throw Error("The supplied block does not contain wire format");
+  }
+
+  m_wire = wire;
+  m_wire.parse();
+
+  if (m_wire.type() != tlv::pib::DeleteParam)
+    throw Error("Unexpected TLV type when decoding DeleteParam");
+
+  Block::element_const_iterator it = m_wire.elements_begin();
+
+  // the first block must be Type
+  if (it != m_wire.elements_end() && it->type() == tlv::pib::Type) {
+    m_targetType = static_cast<pib::Type>(readNonNegativeInteger(*it));
+    it++;
+  }
+  else
+    throw Error("DeleteParam requires the first sub-TLV to be Type");
+
+  // the second block must be Name
+  if (it != m_wire.elements_end() && it->type() == tlv::Name) {
+    m_targetName.wireDecode(*it);
+    it++;
+  }
+  else
+    throw Error("DeleteParam requires the second sub-TLV to be Name");
+
+  if (it != m_wire.elements_end())
+    throw Error("DeleteParam must not contain more than two sub-TLVs");
+}
+
+} // namespace pib
+} // namespace ndn
diff --git a/tools/pib/encoding/delete-param.hpp b/tools/pib/encoding/delete-param.hpp
new file mode 100644
index 0000000..14d8392
--- /dev/null
+++ b/tools/pib/encoding/delete-param.hpp
@@ -0,0 +1,114 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * 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 NDN_PIB_DELETE_PARAM_HPP
+#define NDN_PIB_DELETE_PARAM_HPP
+
+#include "pib-common.hpp"
+#include <ndn-cxx/name.hpp>
+
+namespace ndn {
+namespace pib {
+
+/**
+ * @brief DeleteParam is the abstraction of PIB Delete parameter.
+ *
+ * PibDeleteParam := PIB-DELETE-PARAM-TYPE TLV-LENGTH
+ *                   PibType
+ *                   Name
+ *
+ * @sa http://redmine.named-data.net/projects/ndn-cxx/wiki/PublicKey_Info_Base#Delete-Parameters
+ */
+
+class DeleteParam : noncopyable
+{
+public:
+  class Error : public tlv::Error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : tlv::Error(what)
+    {
+    }
+  };
+
+  DeleteParam();
+
+  explicit
+  DeleteParam(const Name& name, const pib::Type type = TYPE_ID);
+
+  explicit
+  DeleteParam(const Block& wire);
+
+  tlv::pib::ParamType
+  getParamType() const
+  {
+    return tlv::pib::DeleteParam;
+  }
+
+  pib::Type
+  getTargetType() const
+  {
+    return m_targetType;
+  }
+
+  const Name&
+  getTargetName() const
+  {
+    return m_targetName;
+  }
+
+  /// @brief Encode to a wire format or estimate wire format
+  template<bool T>
+  size_t
+  wireEncode(EncodingImpl<T>& block) const;
+
+  /**
+   * @brief Encode to a wire format
+   *
+   * @throws Error if encoding fails
+   */
+  const Block&
+  wireEncode() const;
+
+  /**
+   * @brief Decode GetParam from a wire encoded block
+   *
+   * @throws Error if decoding fails
+   */
+  void
+  wireDecode(const Block& wire);
+
+public:
+  static const std::string VERB;
+
+private:
+  pib::Type m_targetType;
+  Name m_targetName;
+
+  mutable Block m_wire;
+};
+
+} // namespace pib
+} // namespace ndn
+
+#endif // NDN_PIB_DELETE_PARAM_HPP
diff --git a/tools/pib/encoding/get-param.cpp b/tools/pib/encoding/get-param.cpp
new file mode 100644
index 0000000..ea076f9
--- /dev/null
+++ b/tools/pib/encoding/get-param.cpp
@@ -0,0 +1,156 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * 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.
+ */
+
+#include "get-param.hpp"
+#include <boost/lexical_cast.hpp>
+#include <ndn-cxx/encoding/block-helpers.hpp>
+
+namespace ndn {
+namespace pib {
+
+static_assert(std::is_base_of<tlv::Error, GetParam::Error>::value,
+              "GetParam::Error must inherit from tlv::Error");
+
+const std::string GetParam::VERB("get");
+
+GetParam::GetParam()
+  : m_targetType(TYPE_USER)
+{
+}
+
+GetParam::GetParam(const pib::Type targetType, const Name& targetName)
+  : m_targetType(targetType)
+  , m_targetName(targetName)
+{
+}
+
+GetParam::GetParam(const Block& wire)
+{
+  wireDecode(wire);
+}
+
+const Name&
+GetParam::getTargetName() const
+{
+  if (m_targetType == TYPE_ID || m_targetType == TYPE_KEY || m_targetType == TYPE_CERT)
+    return m_targetName;
+  else
+    throw Error("GetParam::getTargetName: target name does not exist");
+}
+
+template<bool T>
+size_t
+GetParam::wireEncode(EncodingImpl<T>& block) const
+{
+  size_t totalLength = 0;
+
+  switch (m_targetType) {
+  case TYPE_ID:
+  case TYPE_KEY:
+  case TYPE_CERT:
+    {
+      totalLength += m_targetName.wireEncode(block);
+      break;
+    }
+  case TYPE_USER:
+    break;
+  default:
+    throw Error("GetParam::wireEncode: unsupported PibType: " +
+                boost::lexical_cast<std::string>(m_targetType));
+  }
+
+  // Encode Type
+  totalLength += prependNonNegativeIntegerBlock(block, tlv::pib::Type, m_targetType);
+  totalLength += block.prependVarNumber(totalLength);
+  totalLength += block.prependVarNumber(tlv::pib::GetParam);
+
+  return totalLength;
+}
+
+template size_t
+GetParam::wireEncode<true>(EncodingImpl<true>& block) const;
+
+template size_t
+GetParam::wireEncode<false>(EncodingImpl<false>& block) const;
+
+const Block&
+GetParam::wireEncode() const
+{
+  if (m_wire.hasWire())
+    return m_wire;
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  return m_wire;
+}
+
+void
+GetParam::wireDecode(const Block& wire)
+{
+  if (!wire.hasWire()) {
+    throw Error("The supplied block does not contain wire format");
+  }
+
+  m_wire = wire;
+  m_wire.parse();
+
+  if (m_wire.type() != tlv::pib::GetParam)
+    throw Error("Unexpected TLV type when decoding GetParam");
+
+  Block::element_const_iterator it = m_wire.elements_begin();
+
+  // the first block must be Type
+  if (it != m_wire.elements_end() && it->type() == tlv::pib::Type) {
+    m_targetType = static_cast<pib::Type>(readNonNegativeInteger(*it));
+    it++;
+  }
+  else
+    throw Error("GetParam requires the first sub-TLV to be PibType");
+
+  switch (m_targetType) {
+  case TYPE_ID:
+  case TYPE_KEY:
+  case TYPE_CERT:
+    {
+      if (it != m_wire.elements_end()) {
+        // the second block, if exists, must be Name
+        m_targetName.wireDecode(*it);
+        return;
+      }
+      else {
+        throw Error("GetParam requires the second sub-TLV to be Name");
+      }
+    }
+  case TYPE_USER:
+    return;
+  default:
+    throw Error("GetParam::wireDecode: unsupported PibType: " +
+                boost::lexical_cast<std::string>(m_targetType));
+  }
+}
+
+} // namespace pib
+} // namespace ndn
diff --git a/tools/pib/encoding/get-param.hpp b/tools/pib/encoding/get-param.hpp
new file mode 100644
index 0000000..39a6c0e
--- /dev/null
+++ b/tools/pib/encoding/get-param.hpp
@@ -0,0 +1,115 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * 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 NDN_PIB_GET_PARAM_HPP
+#define NDN_PIB_GET_PARAM_HPP
+
+#include "pib-common.hpp"
+#include <ndn-cxx/name.hpp>
+
+namespace ndn {
+namespace pib {
+
+/**
+ * @brief GetParam is the abstraction of PIB Get parameter.
+ *
+ * PibGetParam := PIB-GET-PARAM-TYPE TLV-LENGTH
+ *                PibType
+ *                Name?
+ *
+ * @sa http://redmine.named-data.net/projects/ndn-cxx/wiki/PublicKey_Info_Base#Get-Parameters
+ */
+
+class GetParam : noncopyable
+{
+public:
+  class Error : public tlv::Error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : tlv::Error(what)
+    {
+    }
+  };
+
+  GetParam();
+
+  GetParam(const pib::Type targetType, const Name& targetName);
+
+  explicit
+  GetParam(const Block& wire);
+
+  tlv::pib::ParamType
+  getParamType() const
+  {
+    return tlv::pib::GetParam;
+  }
+
+  pib::Type
+  getTargetType() const
+  {
+    return m_targetType;
+  }
+
+  /**
+   * @brief Get target name
+   *
+   * @throws Error if target name does not exist
+   */
+  const Name&
+  getTargetName() const;
+
+  /// @brief Encode to a wire format or estimate wire format
+  template<bool T>
+  size_t
+  wireEncode(EncodingImpl<T>& block) const;
+
+  /**
+   * @brief Encode to a wire format
+   *
+   * @throws Error if encoding fails
+   */
+  const Block&
+  wireEncode() const;
+
+  /**
+   * @brief Decode GetParam from a wire encoded block
+   *
+   * @throws Error if decoding fails
+   */
+  void
+  wireDecode(const Block& wire);
+
+public:
+  static const std::string VERB;
+
+private:
+  pib::Type m_targetType;
+  Name m_targetName;
+
+  mutable Block m_wire;
+};
+
+} // namespace pib
+} // namespace ndn
+
+#endif // NDN_PIB_GET_PARAM_HPP
diff --git a/tools/pib/encoding/list-param.cpp b/tools/pib/encoding/list-param.cpp
new file mode 100644
index 0000000..cb604e6
--- /dev/null
+++ b/tools/pib/encoding/list-param.cpp
@@ -0,0 +1,155 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * 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.
+ */
+
+#include "list-param.hpp"
+#include <ndn-cxx/encoding/block-helpers.hpp>
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace pib {
+
+static_assert(std::is_base_of<tlv::Error, ListParam::Error>::value,
+              "ListParam::Error must inherit from tlv::Error");
+
+const std::string ListParam::VERB("list");
+
+ListParam::ListParam()
+  : m_originType(TYPE_USER)
+{
+}
+
+ListParam::ListParam(const pib::Type originType, const Name& originName)
+  : m_originType(originType)
+  , m_originName(originName)
+{
+}
+
+ListParam::ListParam(const Block& wire)
+{
+  wireDecode(wire);
+}
+
+const Name&
+ListParam::getOriginName() const
+{
+  if (m_originType == TYPE_ID || m_originType == TYPE_KEY || m_originType == TYPE_CERT)
+    return m_originName;
+  else
+    throw Error("ListParam::getOriginName: origin name does not exist");
+}
+
+template<bool T>
+size_t
+ListParam::wireEncode(EncodingImpl<T>& block) const
+{
+  size_t totalLength = 0;
+
+  switch (m_originType) {
+  case TYPE_ID:
+  case TYPE_KEY:
+  case TYPE_CERT:
+    {
+      totalLength += m_originName.wireEncode(block);
+      break;
+    }
+  case TYPE_USER:
+    break;
+  default:
+    throw Error("ListParam::wireEncode: unsupported PibType: " +
+                boost::lexical_cast<std::string>(m_originType));
+  }
+
+  totalLength += prependNonNegativeIntegerBlock(block, tlv::pib::Type, m_originType);
+  totalLength += block.prependVarNumber(totalLength);
+  totalLength += block.prependVarNumber(tlv::pib::ListParam);
+
+  return totalLength;
+}
+
+template size_t
+ListParam::wireEncode<true>(EncodingImpl<true>& block) const;
+
+template size_t
+ListParam::wireEncode<false>(EncodingImpl<false>& block) const;
+
+const Block&
+ListParam::wireEncode() const
+{
+  if (m_wire.hasWire())
+    return m_wire;
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  return m_wire;
+}
+
+void
+ListParam::wireDecode(const Block& wire)
+{
+  if (!wire.hasWire()) {
+    throw Error("The supplied block does not contain wire format");
+  }
+
+  m_wire = wire;
+  m_wire.parse();
+
+  if (m_wire.type() != tlv::pib::ListParam)
+    throw Error("Unexpected TLV type when decoding ListParam");
+
+  Block::element_const_iterator it = m_wire.elements_begin();
+
+  // the first block must be PibType
+  if (it != m_wire.elements_end() && it->type() == tlv::pib::Type) {
+    m_originType = static_cast<pib::Type>(readNonNegativeInteger(*it));
+    it++;
+  }
+  else
+    throw Error("ListParam requires the first sub-TLV to be PibType");
+
+  switch (m_originType) {
+  case TYPE_ID:
+  case TYPE_KEY:
+  case TYPE_CERT:
+    {
+      if (it != m_wire.elements_end()) {
+        // the second block, if exists, must be Name
+        m_originName.wireDecode(*it);
+        return;
+      }
+      else {
+        throw Error("ListParam requires the second sub-TLV to be Name");
+      }
+    }
+  case TYPE_USER:
+    return;
+  default:
+    throw Error("ListParam::wireDecode: unsupported PibType: " +
+                boost::lexical_cast<std::string>(m_originType));
+  }
+}
+
+} // namespace pib
+} // namespace ndn
diff --git a/tools/pib/encoding/list-param.hpp b/tools/pib/encoding/list-param.hpp
new file mode 100644
index 0000000..f94732e
--- /dev/null
+++ b/tools/pib/encoding/list-param.hpp
@@ -0,0 +1,118 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * 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 NDN_PIB_LIST_PARAM_HPP
+#define NDN_PIB_LIST_PARAM_HPP
+
+#include "pib-common.hpp"
+#include <ndn-cxx/name.hpp>
+
+namespace ndn {
+namespace pib {
+
+/**
+ * @brief ListParam is the abstraction of PIB List parameter.
+ *
+ *  PibListParam := PIB-LIST-PARAM-TYPE TLV-LENGTH
+ *                  PibType    // origin type
+ *                  Name?      // origin name
+ *
+ * @sa http://redmine.named-data.net/projects/ndn-cxx/wiki/PublicKey_Info_Base#List-Parameters
+ */
+
+class ListParam : noncopyable
+{
+public:
+  class Error : public tlv::Error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : tlv::Error(what)
+    {
+    }
+  };
+
+  ListParam();
+
+  ListParam(const pib::Type originType, const Name& originName);
+
+  explicit
+  ListParam(const Block& wire);
+
+  tlv::pib::ParamType
+  getParamType() const
+  {
+    return tlv::pib::ListParam;
+  }
+
+  pib::Type
+  getOriginType() const
+  {
+    return m_originType;
+  }
+
+  /**
+   * @brief Get target name
+   *
+   * @throws Error if origin name does not exist
+   */
+  const Name&
+  getOriginName() const;
+
+  /// @brief Encode to a wire format or estimate wire format
+  template<bool T>
+  size_t
+  wireEncode(EncodingImpl<T>& block) const;
+
+  /**
+   * @brief Encode to a wire format
+   *
+   * @throws Error if encoding fails
+   */
+  const Block&
+  wireEncode() const;
+
+  /**
+   * @brief Decode GetParam from a wire encoded block
+   *
+   * @throws Error if decoding fails
+   */
+  void
+  wireDecode(const Block& wire);
+
+public:
+  static const std::string VERB;
+
+private:
+  pib::Type m_originType;
+  Name m_originName;
+
+  mutable Block m_wire;
+};
+
+} // namespace pib
+} // namespace ndn
+
+
+
+#endif // NDN_PIB_LIST_PARAM_HPP
diff --git a/tools/pib/encoding/pib-common.hpp b/tools/pib/encoding/pib-common.hpp
new file mode 100644
index 0000000..74c5daf
--- /dev/null
+++ b/tools/pib/encoding/pib-common.hpp
@@ -0,0 +1,128 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * 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 NDN_PIB_PIB_COMMON_HPP
+#define NDN_PIB_PIB_COMMON_HPP
+
+namespace ndn {
+namespace pib {
+
+/// @sa http://redmine.named-data.net/projects/ndn-cxx/wiki/PublicKey_Info_Base
+
+enum {
+  OFFSET_USER              = 2,
+  OFFSET_VERB              = 3,
+  OFFSET_PARAM             = 4,
+  OFFSET_TIMESTAMP         = 5,
+  OFFSET_NONCE             = 6,
+  OFFSET_SIG_INFO          = 7,
+  OFFSET_SIG_VALUE         = 8,
+
+  MIN_PIB_INTEREST_SIZE    = 5,
+  SIGNED_PIB_INTEREST_SIZE = 9
+};
+
+enum Type {
+  TYPE_USER    = 0,
+  TYPE_ID      = 1,
+  TYPE_KEY     = 2,
+  TYPE_CERT    = 3,
+  TYPE_DEFAULT = 255
+};
+
+enum DefaultOpt {
+  DEFAULT_OPT_NO   = 0,
+  DEFAULT_OPT_KEY  = 1, // 0x01
+  DEFAULT_OPT_ID   = 3, // 0x03
+  DEFAULT_OPT_USER = 7 // 0x07
+};
+
+enum DefaultOptMask {
+  DEFAULT_OPT_KEY_MASK  = 0x1,
+  DEFAULT_OPT_ID_MASK   = 0x2,
+  DEFAULT_OPT_USER_MASK = 0x4
+};
+
+enum ErrCode {
+  ERR_SUCCESS = 0,
+
+  // Invalid query
+  ERR_INCOMPLETE_COMMAND = 1,
+  ERR_WRONG_VERB         = 2,
+  ERR_WRONG_PARAM        = 3,
+  ERR_WRONG_SIGNER       = 4,
+  ERR_INTERNAL_ERROR     = 5,
+
+  // Non existing entity
+  ERR_NON_EXISTING_USER = 128,
+  ERR_NON_EXISTING_ID   = 129,
+  ERR_NON_EXISTING_KEY  = 130,
+  ERR_NON_EXISTING_CERT = 131,
+
+  // No default setting
+  ERR_NO_DEFAULT_ID     = 256,
+  ERR_NO_DEFAULT_KEY    = 257,
+  ERR_NO_DEFAULT_CERT   = 258,
+
+  // Delete default setting
+  ERR_DELETE_DEFAULT_SETTING = 384
+};
+
+} // namespace pib
+
+
+namespace tlv {
+namespace pib {
+
+enum ParamType {
+  GetParam          = 128, // 0x80
+  DefaultParam      = 129, // 0x81
+  ListParam         = 130, // 0x82
+  UpdateParam       = 131, // 0x83
+  DeleteParam       = 132  // 0x84
+};
+
+enum EntityType {
+  User              = 144, // 0x90
+  Identity          = 145, // 0x91
+  PublicKey         = 146, // 0x92
+  Certificate       = 147  // 0x93
+};
+
+// Other
+enum {
+  Bytes             = 148, // 0x94
+  DefaultOpt        = 149, // 0x95
+  NameList          = 150, // 0x96
+  Type              = 151, // 0x97
+  Error             = 152, // 0x98
+  TpmLocator        = 153, // 0x99
+
+  // ErrorCode
+  ErrorCode         = 252  // 0xfc
+};
+
+} // namespace pib
+} // namespace tlv
+} // namespace ndn
+
+
+#endif // NDN_PIB_PIB_COMMON_HPP
diff --git a/tools/pib/encoding/pib-encoding.cpp b/tools/pib/encoding/pib-encoding.cpp
new file mode 100644
index 0000000..e7c5ca4
--- /dev/null
+++ b/tools/pib/encoding/pib-encoding.cpp
@@ -0,0 +1,525 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * 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.
+ */
+
+#include "pib-encoding.hpp"
+#include <ndn-cxx/encoding/block-helpers.hpp>
+#include <ndn-cxx/encoding/encoding-buffer.hpp>
+#include <vector>
+#include <string>
+
+namespace ndn {
+namespace pib {
+
+using std::vector;
+using std::string;
+
+PibIdentity::PibIdentity()
+{
+}
+
+PibIdentity::PibIdentity(const Name& identity)
+  : m_identity(identity)
+{
+}
+
+PibIdentity::PibIdentity(const Block& wire)
+{
+  wireDecode(wire);
+}
+
+template<bool T>
+size_t
+PibIdentity::wireEncode(EncodingImpl<T>& block) const
+{
+  size_t totalLength = m_identity.wireEncode(block);
+  totalLength += block.prependVarNumber(totalLength);
+  totalLength += block.prependVarNumber(tlv::pib::Identity);
+
+  return totalLength;
+}
+
+template size_t
+PibIdentity::wireEncode<true>(EncodingImpl<true>& block) const;
+
+template size_t
+PibIdentity::wireEncode<false>(EncodingImpl<false>& block) const;
+
+const Block&
+PibIdentity::wireEncode() const
+{
+  if (m_wire.hasWire())
+    return m_wire;
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  return m_wire;
+}
+
+void
+PibIdentity::wireDecode(const Block& wire)
+{
+  if (!wire.hasWire()) {
+    throw tlv::Error("The supplied block does not contain wire format");
+  }
+
+  if (wire.type() != tlv::pib::Identity)
+    throw tlv::Error("Unexpected TLV type when decoding PibIdentity");
+
+  m_wire = wire;
+  m_identity.wireDecode(m_wire.blockFromValue());
+}
+
+
+
+PibPublicKey::PibPublicKey()
+  : m_isValueSet(false)
+{
+}
+
+PibPublicKey::PibPublicKey(const Name& keyName, const PublicKey& key)
+  : m_isValueSet(true)
+  , m_keyName(keyName)
+  , m_key(key)
+{
+}
+
+PibPublicKey::PibPublicKey(const Block& wire)
+{
+  wireDecode(wire);
+}
+
+const Name&
+PibPublicKey::getKeyName() const
+{
+  if (m_isValueSet)
+    return m_keyName;
+  else
+    throw tlv::Error("PibPublicKey::getKeyName: keyName is not set");
+}
+
+const PublicKey&
+PibPublicKey::getPublicKey() const
+{
+  if (m_isValueSet)
+    return m_key;
+  else
+    throw tlv::Error("PibPublicKey::getPublicKey: key is not set");
+}
+
+template<bool T>
+size_t
+PibPublicKey::wireEncode(EncodingImpl<T>& block) const
+{
+  size_t totalLength = prependByteArrayBlock(block, tlv::pib::Bytes,
+                                             m_key.get().buf(), m_key.get().size());
+  totalLength += m_keyName.wireEncode(block);
+  totalLength += block.prependVarNumber(totalLength);
+  totalLength += block.prependVarNumber(tlv::pib::PublicKey);
+
+  return totalLength;
+}
+
+template size_t
+PibPublicKey::wireEncode<true>(EncodingImpl<true>& block) const;
+
+template size_t
+PibPublicKey::wireEncode<false>(EncodingImpl<false>& block) const;
+
+const Block&
+PibPublicKey::wireEncode() const
+{
+  if (m_wire.hasWire())
+    return m_wire;
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  return m_wire;
+}
+
+void
+PibPublicKey::wireDecode(const Block& wire)
+{
+  if (!wire.hasWire()) {
+    throw tlv::Error("The supplied block does not contain wire format");
+  }
+
+  if (wire.type() != tlv::pib::PublicKey)
+    throw tlv::Error("Unexpected TLV type when decoding PibPublicKey");
+
+  m_wire = wire;
+  m_wire.parse();
+
+  Block::element_const_iterator it = m_wire.elements_begin();
+  if (it != m_wire.elements_end() && it->type() == tlv::Name) {
+    m_keyName.wireDecode(*it);
+    it++;
+  }
+  else
+    throw tlv::Error("PibPublicKey requires the first sub-TLV to be Name");
+
+  if (it != m_wire.elements_end() && it->type() == tlv::pib::Bytes) {
+    m_key = PublicKey(it->value(), it->value_size());
+    it++;
+  }
+  else
+    throw tlv::Error("PibPublicKey requires the second sub-TLV to be Bytes");
+
+  m_isValueSet = true;
+  if (it != m_wire.elements_end())
+    throw tlv::Error("PibPublicKey must contain only two sub-TLVs");
+}
+
+
+PibCertificate::PibCertificate()
+  : m_isValueSet(false)
+{
+}
+
+PibCertificate::PibCertificate(const IdentityCertificate& certificate)
+  : m_isValueSet(true)
+  , m_certificate(certificate)
+{
+}
+
+PibCertificate::PibCertificate(const Block& wire)
+{
+  wireDecode(wire);
+}
+
+const IdentityCertificate&
+PibCertificate::getCertificate() const
+{
+  if (m_isValueSet)
+    return m_certificate;
+  else
+    throw tlv::Error("PibCertificate::getCertificate: certificate is not set");
+}
+
+template<bool T>
+size_t
+PibCertificate::wireEncode(EncodingImpl<T>& block) const
+{
+  size_t totalLength = prependBlock(block, m_certificate.wireEncode());
+  totalLength += block.prependVarNumber(totalLength);
+  totalLength += block.prependVarNumber(tlv::pib::Certificate);
+
+  return totalLength;
+}
+
+template size_t
+PibCertificate::wireEncode<true>(EncodingImpl<true>& block) const;
+
+template size_t
+PibCertificate::wireEncode<false>(EncodingImpl<false>& block) const;
+
+const Block&
+PibCertificate::wireEncode() const
+{
+  if (m_wire.hasWire())
+    return m_wire;
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  return m_wire;
+}
+
+void
+PibCertificate::wireDecode(const Block& wire)
+{
+  if (!wire.hasWire()) {
+    throw tlv::Error("The supplied block does not contain wire format");
+  }
+
+  if (wire.type() != tlv::pib::Certificate)
+    throw tlv::Error("Unexpected TLV type when decoding PibCertificate");
+
+  m_wire = wire;
+  m_certificate.wireDecode(m_wire.blockFromValue());
+
+  m_isValueSet = true;
+}
+
+
+PibNameList::PibNameList()
+{
+}
+
+PibNameList::PibNameList(const std::vector<Name>& names)
+  : m_names(names)
+{
+}
+
+PibNameList::PibNameList(const Block& wire)
+{
+  wireDecode(wire);
+}
+
+template<bool T>
+size_t
+PibNameList::wireEncode(EncodingImpl<T>& block) const
+{
+  size_t totalLength = 0;
+
+  for (vector<Name>::const_reverse_iterator it = m_names.rbegin();
+       it != m_names.rend(); it++) {
+    totalLength += it->wireEncode(block);
+  }
+
+  totalLength += block.prependVarNumber(totalLength);
+  totalLength += block.prependVarNumber(tlv::pib::NameList);
+  return totalLength;
+}
+
+template size_t
+PibNameList::wireEncode<true>(EncodingImpl<true>& block) const;
+
+template size_t
+PibNameList::wireEncode<false>(EncodingImpl<false>& block) const;
+
+
+const Block&
+PibNameList::wireEncode() const
+{
+  if (m_wire.hasWire())
+    return m_wire;
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  return m_wire;
+}
+
+void
+PibNameList::wireDecode(const Block& wire)
+{
+  if (!wire.hasWire()) {
+    throw tlv::Error("The supplied block does not contain wire format");
+  }
+
+  if (wire.type() != tlv::pib::NameList)
+    throw tlv::Error("Unexpected TLV type when decoding PibNameList");
+
+  m_wire = wire;
+  m_wire.parse();
+  for (Block::element_const_iterator it = m_wire.elements_begin();
+       it != m_wire.elements_end(); it++) {
+    if (it->type() == tlv::Name) {
+      Name name;
+      name.wireDecode(*it);
+      m_names.push_back(name);
+    }
+  }
+}
+
+PibError::PibError()
+  : m_errCode(ERR_SUCCESS)
+{
+}
+
+PibError::PibError(const pib::ErrCode errCode, const std::string& msg)
+  : m_errCode(errCode)
+  , m_msg(msg)
+{
+}
+
+PibError::PibError(const Block& wire)
+{
+  wireDecode(wire);
+}
+
+template<bool T>
+size_t
+PibError::wireEncode(EncodingImpl<T>& block) const
+{
+  size_t totalLength = 0;
+  totalLength += prependByteArrayBlock(block, tlv::pib::Bytes,
+                                       reinterpret_cast<const uint8_t*>(m_msg.c_str()),
+                                       m_msg.size());
+  totalLength += prependNonNegativeIntegerBlock(block, tlv::pib::ErrorCode, m_errCode);
+  totalLength += block.prependVarNumber(totalLength);
+  totalLength += block.prependVarNumber(tlv::pib::Error);
+  return totalLength;
+}
+
+template size_t
+PibError::wireEncode<true>(EncodingImpl<true>& block) const;
+
+template size_t
+PibError::wireEncode<false>(EncodingImpl<false>& block) const;
+
+
+const Block&
+PibError::wireEncode() const
+{
+  if (m_wire.hasWire())
+    return m_wire;
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  return m_wire;
+}
+
+void
+PibError::wireDecode(const Block& wire)
+{
+  if (!wire.hasWire()) {
+    throw tlv::Error("The supplied block does not contain wire format");
+  }
+
+  if (wire.type() != tlv::pib::Error)
+    throw tlv::Error("Unexpected TLV type when decoding Error");
+
+  m_wire = wire;
+  m_wire.parse();
+  Block::element_const_iterator it = m_wire.elements_begin();
+
+  if (it != m_wire.elements_end() && it->type() == tlv::pib::ErrorCode) {
+    m_errCode = static_cast<pib::ErrCode>(readNonNegativeInteger(*it));
+    it++;
+  }
+  else
+    throw tlv::Error("PibError requires the first sub-TLV to be ErrorCode");
+
+  if (it != m_wire.elements_end() && it->type() == tlv::pib::Bytes) {
+    m_msg = string(reinterpret_cast<const char*>(it->value()), it->value_size());
+    it++;
+  }
+  else
+    throw tlv::Error("PibError requires the second sub-TLV to be Bytes");
+}
+
+PibUser::PibUser()
+{
+}
+
+PibUser::PibUser(const Block& wire)
+{
+  wireDecode(wire);
+}
+
+void
+PibUser::setMgmtCert(const IdentityCertificate& mgmtCert)
+{
+  m_wire.reset();
+  m_mgmtCert = mgmtCert;
+}
+
+void
+PibUser::setTpmLocator(const std::string& tpmLocator)
+{
+  m_wire.reset();
+  m_tpmLocator = tpmLocator;
+}
+
+template<bool T>
+size_t
+PibUser::wireEncode(EncodingImpl<T>& block) const
+{
+  size_t totalLength = 0;
+
+  if (!m_tpmLocator.empty())
+    totalLength = prependByteArrayBlock(block, tlv::pib::TpmLocator,
+                                        reinterpret_cast<const uint8_t*>(m_tpmLocator.c_str()),
+                                        m_tpmLocator.size());
+
+  totalLength += prependBlock(block, m_mgmtCert.wireEncode());
+
+  totalLength += block.prependVarNumber(totalLength);
+  totalLength += block.prependVarNumber(tlv::pib::User);
+
+  return totalLength;
+}
+
+template size_t
+PibUser::wireEncode<true>(EncodingImpl<true>& block) const;
+
+template size_t
+PibUser::wireEncode<false>(EncodingImpl<false>& block) const;
+
+
+const Block&
+PibUser::wireEncode() const
+{
+  if (m_wire.hasWire())
+    return m_wire;
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  return m_wire;
+}
+
+void
+PibUser::wireDecode(const Block& wire)
+{
+  if (!wire.hasWire()) {
+    throw tlv::Error("The supplied block does not contain wire format");
+  }
+
+  if (wire.type() != tlv::pib::User)
+    throw tlv::Error("Unexpected TLV type when decoding Content");
+
+  m_wire = wire;
+  m_wire.parse();
+
+  Block::element_const_iterator it = m_wire.elements_begin();
+  if (it != m_wire.elements_end() && it->type() == tlv::Data) {
+    m_mgmtCert.wireDecode(*it);
+    it++;
+  }
+  else
+    throw tlv::Error("PibError requires the first sub-TLV to be Data");
+
+  if (it != m_wire.elements_end() && it->type() == tlv::pib::TpmLocator) {
+    m_tpmLocator = string(reinterpret_cast<const char*>(it->value()), it->value_size());
+  }
+}
+
+} // namespace pib
+} // namespace ndn
diff --git a/tools/pib/encoding/pib-encoding.hpp b/tools/pib/encoding/pib-encoding.hpp
new file mode 100644
index 0000000..9ebb4c4
--- /dev/null
+++ b/tools/pib/encoding/pib-encoding.hpp
@@ -0,0 +1,316 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * 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 NDN_PIB_PIB_ENCODING_HPP
+#define NDN_PIB_PIB_ENCODING_HPP
+
+#include "pib-common.hpp"
+#include <ndn-cxx/security/identity-certificate.hpp>
+
+namespace ndn {
+namespace pib {
+
+/**
+ * @brief Abstraction of pib::Identity TLV.
+ *
+ * This class is copyable since it is used by a variety of pib parameters
+ *
+ * @sa http://redmine.named-data.net/projects/ndn-cxx/wiki/PublicKey_Info_Base#Query-Responses
+ */
+class PibIdentity
+{
+public:
+  PibIdentity();
+
+  explicit
+  PibIdentity(const Name& identity);
+
+  explicit
+  PibIdentity(const Block& wire);
+
+  const Name&
+  getIdentity() const
+  {
+    return m_identity;
+  }
+
+  template<bool T>
+  size_t
+  wireEncode(EncodingImpl<T>& block) const;
+
+  const Block&
+  wireEncode() const;
+
+  /**
+   * @brief Decode PibIdentity from a wire encoded block
+   *
+   * @throws tlv::Error if decoding fails
+   */
+  void
+  wireDecode(const Block& wire);
+
+private:
+  Name m_identity;
+
+  mutable Block m_wire;
+};
+
+/**
+ * @brief Abstraction of pib::PublicKey TLV.
+ *
+ * This class is copyable since it is used by a variety of pib parameters
+ *
+ * @sa http://redmine.named-data.net/projects/ndn-cxx/wiki/PublicKey_Info_Base#Query-Responses
+ */
+class PibPublicKey
+{
+public:
+  PibPublicKey();
+
+  PibPublicKey(const Name& keyName, const PublicKey& key);
+
+  explicit
+  PibPublicKey(const Block& wire);
+
+  const Name&
+  getKeyName() const;
+
+  const PublicKey&
+  getPublicKey() const;
+
+  template<bool T>
+  size_t
+  wireEncode(EncodingImpl<T>& block) const;
+
+  const Block&
+  wireEncode() const;
+
+  /**
+   * @brief Decode PibPublicKey from a wire encoded block
+   *
+   * @throws tlv::Error if decoding fails
+   */
+  void
+  wireDecode(const Block& wire);
+
+private:
+  bool m_isValueSet;
+  Name m_keyName;
+  PublicKey m_key;
+
+  mutable Block m_wire;
+};
+
+/**
+ * @brief Abstraction of pib::Certificate TLV.
+ *
+ * This class is copyable since it is used by a variety of pib parameters
+ *
+ * @sa http://redmine.named-data.net/projects/ndn-cxx/wiki/PublicKey_Info_Base#Query-Responses
+ */
+class PibCertificate
+{
+public:
+  PibCertificate();
+
+  explicit
+  PibCertificate(const IdentityCertificate& certificate);
+
+  explicit
+  PibCertificate(const Block& wire);
+
+  const IdentityCertificate&
+  getCertificate() const;
+
+  template<bool T>
+  size_t
+  wireEncode(EncodingImpl<T>& block) const;
+
+  const Block&
+  wireEncode() const;
+
+  /**
+   * @brief Decode PibCertificate from a wire encoded block
+   *
+   * @throws tlv::Error if decoding fails
+   */
+  void
+  wireDecode(const Block& wire);
+
+private:
+  bool m_isValueSet;
+  IdentityCertificate m_certificate;
+
+  mutable Block m_wire;
+};
+
+/**
+ * @brief Abstraction of pib::NameList TLV.
+ *
+ * @sa http://redmine.named-data.net/projects/ndn-cxx/wiki/PublicKey_Info_Base#List-Parameters
+ */
+class PibNameList : noncopyable
+{
+public:
+  PibNameList();
+
+  explicit
+  PibNameList(const std::vector<Name>& names);
+
+  explicit
+  PibNameList(const Block& wire);
+
+  const std::vector<Name>&
+  getNameList() const
+  {
+    return m_names;
+  }
+
+  template<bool T>
+  size_t
+  wireEncode(EncodingImpl<T>& block) const;
+
+  const Block&
+  wireEncode() const;
+
+  /**
+   * @brief Decode PibCertificate from a wire encoded block
+   *
+   * @throws tlv::Error if decoding fails
+   */
+  void
+  wireDecode(const Block& wire);
+
+private:
+  std::vector<Name> m_names;
+
+  mutable Block m_wire;
+};
+
+/**
+ * @brief Abstraction of pib::Error TLV.
+ *
+ * @sa http://redmine.named-data.net/projects/ndn-cxx/wiki/PublicKey_Info_Base#Query-Responses
+ */
+class PibError : noncopyable
+{
+public:
+  PibError();
+
+  explicit
+  PibError(const pib::ErrCode errCode, const std::string& msg = "");
+
+  explicit
+  PibError(const Block& wire);
+
+  pib::ErrCode
+  getErrorCode() const
+  {
+    return m_errCode;
+  }
+
+  const std::string&
+  getErrorMsg() const
+  {
+    return m_msg;
+  }
+
+  template<bool T>
+  size_t
+  wireEncode(EncodingImpl<T>& block) const;
+
+  const Block&
+  wireEncode() const;
+
+  /**
+   * @brief Decode PibCertificate from a wire encoded block
+   *
+   * @throws tlv::Error if decoding fails
+   */
+  void
+  wireDecode(const Block& wire);
+
+private:
+  pib::ErrCode m_errCode;
+  std::string m_msg;
+
+  mutable Block m_wire;
+};
+
+/**
+ * @brief Abstraction of pib::User TLV.
+ *
+ * This class is copyable since it is used by a variety of pib parameters
+ *
+ * @sa http://redmine.named-data.net/projects/ndn-cxx/wiki/PublicKey_Info_Base#Query-Responses
+ */
+class PibUser
+{
+public:
+  PibUser();
+
+  explicit
+  PibUser(const Block& wire);
+
+  void
+  setMgmtCert(const IdentityCertificate& mgmtCert);
+
+  const IdentityCertificate&
+  getMgmtCert() const
+  {
+    return m_mgmtCert;
+  }
+
+  void
+  setTpmLocator(const std::string& tpmLocator);
+
+  const std::string&
+  getTpmLocator() const
+  {
+    return m_tpmLocator;
+  }
+
+  template<bool T>
+  size_t
+  wireEncode(EncodingImpl<T>& block) const;
+
+  const Block&
+  wireEncode() const;
+
+  /**
+   * @brief Decode PibCertificate from a wire encoded block
+   *
+   * @throws tlv::Error if decoding fails
+   */
+  void
+  wireDecode(const Block& wire);
+
+private:
+  IdentityCertificate m_mgmtCert;
+  std::string m_tpmLocator;
+
+  mutable Block m_wire;
+};
+
+} // namespace pib
+} // namespace ndn
+
+#endif // NDN_PIB_PIB_ENCODING_HPP
diff --git a/tools/pib/encoding/update-param.cpp b/tools/pib/encoding/update-param.cpp
new file mode 100644
index 0000000..71a6602
--- /dev/null
+++ b/tools/pib/encoding/update-param.cpp
@@ -0,0 +1,235 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * 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.
+ */
+
+#include "update-param.hpp"
+#include <ndn-cxx/encoding/block-helpers.hpp>
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace pib {
+
+static_assert(std::is_base_of<tlv::Error, UpdateParam::Error>::value,
+              "UpdateParam::Error must inherit from tlv::Error");
+
+const std::string UpdateParam::VERB("update");
+
+UpdateParam::UpdateParam()
+  : m_defaultOpt(DEFAULT_OPT_NO)
+{
+}
+
+UpdateParam::UpdateParam(const PibUser& user)
+  : m_entityType(tlv::pib::User)
+  , m_user(user)
+  , m_defaultOpt(DEFAULT_OPT_NO)
+{
+}
+
+UpdateParam::UpdateParam(const Name& identity, DefaultOpt defaultOpt)
+  : m_entityType(tlv::pib::Identity)
+  , m_identity(identity)
+  , m_defaultOpt(defaultOpt)
+{
+}
+
+UpdateParam::UpdateParam(const Name& keyName, const PublicKey& key, DefaultOpt defaultOpt)
+  : m_entityType(tlv::pib::PublicKey)
+  , m_key(keyName, key)
+  , m_defaultOpt(defaultOpt)
+{
+}
+
+UpdateParam::UpdateParam(const IdentityCertificate& certificate, DefaultOpt defaultOpt)
+  : m_entityType(tlv::pib::Certificate)
+  , m_certificate(certificate)
+  , m_defaultOpt(defaultOpt)
+{
+}
+
+UpdateParam::UpdateParam(const Block& wire)
+{
+  wireDecode(wire);
+}
+
+const PibUser&
+UpdateParam::getUser() const
+{
+  if (m_entityType == tlv::pib::User)
+      return m_user;
+  else
+    throw Error("UpdateParam::getUser: entityType must be User");
+}
+
+const PibIdentity&
+UpdateParam::getIdentity() const
+{
+  if (m_entityType == tlv::pib::Identity)
+    return m_identity;
+  else
+    throw Error("UpdateParam::getIdentity: entityType must be Identity");
+}
+
+const PibPublicKey&
+UpdateParam::getPublicKey() const
+{
+  if (m_entityType == tlv::pib::PublicKey)
+    return m_key;
+  else
+    throw Error("UpdateParam::getPublicKey: entityType must be PublicKey");
+}
+
+const PibCertificate&
+UpdateParam::getCertificate() const
+{
+  if (m_entityType == tlv::pib::Certificate)
+    return m_certificate;
+  else
+    throw Error("UpdateParam::getCertificate: entityType must be Certificate");
+}
+
+template<bool T>
+size_t
+UpdateParam::wireEncode(EncodingImpl<T>& block) const
+{
+  size_t totalLength = 0;
+
+  totalLength += prependNonNegativeIntegerBlock(block, tlv::pib::DefaultOpt, m_defaultOpt);
+
+  // Encode Entity
+  switch (m_entityType) {
+  case tlv::pib::Identity:
+    {
+      totalLength += m_identity.wireEncode(block);
+      break;
+    }
+  case tlv::pib::PublicKey:
+    {
+      totalLength += m_key.wireEncode(block);
+      break;
+    }
+  case tlv::pib::Certificate:
+    {
+      totalLength += m_certificate.wireEncode(block);
+      break;
+    }
+  case tlv::pib::User:
+    {
+      totalLength += m_user.wireEncode(block);
+      break;
+    }
+  default:
+    throw Error("UpdateParam::wireEncode: unsupported entity type: " +
+                boost::lexical_cast<std::string>(m_entityType));
+  }
+
+  totalLength += block.prependVarNumber(totalLength);
+  totalLength += block.prependVarNumber(tlv::pib::UpdateParam);
+
+  return totalLength;
+}
+
+template size_t
+UpdateParam::wireEncode<true>(EncodingImpl<true>& block) const;
+
+template size_t
+UpdateParam::wireEncode<false>(EncodingImpl<false>& block) const;
+
+const Block&
+UpdateParam::wireEncode() const
+{
+  if (m_wire.hasWire())
+    return m_wire;
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  return m_wire;
+}
+
+void
+UpdateParam::wireDecode(const Block& wire)
+{
+  if (!wire.hasWire()) {
+    throw Error("The supplied block does not contain wire format");
+  }
+
+  m_wire = wire;
+  m_wire.parse();
+
+  if (m_wire.type() != tlv::pib::UpdateParam)
+    throw Error("Unexpected TLV type when decoding UpdateParam");
+
+  Block::element_const_iterator it = m_wire.elements_begin();
+
+  // the first block must be Entity
+  if (it != m_wire.elements_end()) {
+    switch (it->type()) {
+    case tlv::pib::Identity:
+      {
+        m_entityType = tlv::pib::Identity;
+        m_identity.wireDecode(*it);
+        break;
+      }
+    case tlv::pib::PublicKey:
+      {
+        m_entityType = tlv::pib::PublicKey;
+        m_key.wireDecode(*it);
+        break;
+      }
+    case tlv::pib::Certificate:
+      {
+        m_entityType = tlv::pib::Certificate;
+        m_certificate.wireDecode(*it);
+        break;
+      }
+    case tlv::pib::User:
+      {
+        m_entityType = tlv::pib::User;
+        m_user.wireDecode(*it);
+        break;
+      }
+    default:
+      throw Error("The first sub-TLV of UpdateParam is not an entity type");
+    }
+
+    it++;
+  }
+  else
+    throw Error("UpdateParam requires the first sub-TLV to be an entity type");
+
+  // the second block must be DefaultOpt
+  if (it != m_wire.elements_end() && it->type() == tlv::pib::DefaultOpt) {
+    m_defaultOpt = static_cast<pib::DefaultOpt>(readNonNegativeInteger(*it));
+    it++;
+  }
+  else
+    throw Error("UpdateParam requires the second sub-TLV to be DefaultOpt");
+
+  if (it != m_wire.elements_end())
+    throw Error("UpdateParam must not contain more than two sub-TLVs");
+}
+
+} // namespace pib
+} // namespace ndn
diff --git a/tools/pib/encoding/update-param.hpp b/tools/pib/encoding/update-param.hpp
new file mode 100644
index 0000000..6a6e5ba
--- /dev/null
+++ b/tools/pib/encoding/update-param.hpp
@@ -0,0 +1,136 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * 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 NDN_PIB_UPDATE_PARAM_HPP
+#define NDN_PIB_UPDATE_PARAM_HPP
+
+#include "pib-common.hpp"
+#include "pib-encoding.hpp"
+#include <ndn-cxx/name.hpp>
+#include <ndn-cxx/security/identity-certificate.hpp>
+
+namespace ndn {
+namespace pib {
+
+/**
+ * @brief UpdateParam is the abstraction of PIB Update parameter.
+ *
+ * PibUpdateParam := PIB-UPDATE-PARAM-TYPE TLV-LENGTH
+ *                   (PibIdentity | PibPublicKey | PibCertificate)
+ *                   PibDefaultOpt
+ *
+ * @sa http://redmine.named-data.net/projects/ndn-cxx/wiki/PublicKey_Info_Base#Update-Parameters
+ */
+
+class UpdateParam : noncopyable
+{
+public:
+  class Error : public tlv::Error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : tlv::Error(what)
+    {
+    }
+  };
+
+  UpdateParam();
+
+  explicit
+  UpdateParam(const PibUser& user);
+
+  explicit
+  UpdateParam(const Name& identity, DefaultOpt defaultOpt = DEFAULT_OPT_NO);
+
+  UpdateParam(const Name& keyName, const PublicKey& key, DefaultOpt defaultOpt = DEFAULT_OPT_NO);
+
+  explicit
+  UpdateParam(const IdentityCertificate& certificate, DefaultOpt defaultOpt = DEFAULT_OPT_NO);
+
+  explicit
+  UpdateParam(const Block& wire);
+
+  tlv::pib::ParamType
+  getParamType() const
+  {
+    return tlv::pib::UpdateParam;
+  }
+
+  tlv::pib::EntityType
+  getEntityType() const
+  {
+    return m_entityType;
+  }
+
+  const PibUser&
+  getUser() const;
+
+  const PibIdentity&
+  getIdentity() const;
+
+  const PibPublicKey&
+  getPublicKey() const;
+
+  const PibCertificate&
+  getCertificate() const;
+
+  pib::DefaultOpt
+  getDefaultOpt() const
+  {
+    return m_defaultOpt;
+  }
+
+  /// @brief Encode to a wire format or estimate wire format
+  template<bool T>
+  size_t
+  wireEncode(EncodingImpl<T>& block) const;
+
+  /// @brief Encode to a wire format
+  const Block&
+  wireEncode() const;
+
+  /**
+   * @brief Decode GetParam from a wire encoded block
+   *
+   * @throws Error if decoding fails
+   */
+  void
+  wireDecode(const Block& wire);
+
+public:
+  static const std::string VERB;
+
+private:
+  tlv::pib::EntityType m_entityType;
+  PibUser m_user;
+  PibIdentity m_identity;
+  PibPublicKey m_key;
+  PibCertificate m_certificate;
+  pib::DefaultOpt m_defaultOpt;
+
+  mutable Block m_wire;
+};
+
+} // namespace pib
+} // namespace ndn
+
+#endif // NDN_PIB_UPDATE_PARAM_HPP
diff --git a/tools/pib/get-query-processor.cpp b/tools/pib/get-query-processor.cpp
new file mode 100644
index 0000000..bc22747
--- /dev/null
+++ b/tools/pib/get-query-processor.cpp
@@ -0,0 +1,132 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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 "get-query-processor.hpp"
+#include "encoding/pib-encoding.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace pib {
+
+const Name GetQueryProcessor::PIB_PREFIX("/localhost/pib");
+const size_t GetQueryProcessor::GET_QUERY_LENGTH = 5;
+
+GetQueryProcessor::GetQueryProcessor(PibDb& db)
+  : m_db(db)
+{
+}
+
+std::pair<bool, Block>
+GetQueryProcessor::operator()(const Interest& interest)
+{
+  const Name& interestName = interest.getName();
+
+  // handle pib query: /localhost/pib/[UserName]/get/param
+  if (interestName.size() != GET_QUERY_LENGTH) {
+    // malformed interest, discard
+    return std::make_pair(false, Block());
+  }
+
+  GetParam param;
+
+  try {
+    param.wireDecode(interestName.get(OFFSET_PARAM).blockFromValue());
+  }
+  catch (const tlv::Error& e) {
+    PibError error(ERR_WRONG_PARAM, "error in parsing param: " + std::string(e.what()));
+    return std::make_pair(true, error.wireEncode());
+  }
+
+  switch (param.getTargetType()) {
+  case TYPE_ID:
+    return processGetIdQuery(param);
+  case TYPE_KEY:
+    return processGetKeyQuery(param);
+  case TYPE_CERT:
+    return processGetCertQuery(param);
+  case TYPE_USER:
+    if (interest.getName().get(2).toUri() != m_db.getOwnerName())
+      return std::make_pair(false, Block());
+    else
+      return processGetUserQuery(param);
+  default:
+    {
+      PibError error(ERR_WRONG_PARAM, "requested type is not supported:" +
+                     boost::lexical_cast<std::string>(param.getTargetType()));
+      return std::make_pair(true, error.wireEncode());
+    }
+  }
+}
+
+std::pair<bool, Block>
+GetQueryProcessor::processGetIdQuery(const GetParam& param)
+{
+  if (!m_db.hasIdentity(param.getTargetName())) {
+    PibError error(ERR_NON_EXISTING_ID, "requested id does not exist");
+    return std::make_pair(true, error.wireEncode());
+  }
+
+  PibIdentity result(param.getTargetName());
+  return std::make_pair(true, result.wireEncode());
+}
+
+std::pair<bool, Block>
+GetQueryProcessor::processGetKeyQuery(const GetParam& param)
+{
+  if (param.getTargetName().empty()) {
+    PibError error(ERR_WRONG_PARAM, "key name does not have id-component");
+    return std::make_pair(true, error.wireEncode());
+  }
+
+  shared_ptr<PublicKey> key = m_db.getKey(param.getTargetName());
+  if (key == nullptr) {
+    PibError error(ERR_NON_EXISTING_KEY, "requested key does not exist");
+    return std::make_pair(true, error.wireEncode());
+  }
+
+  PibPublicKey result(param.getTargetName(), *key);
+  return std::make_pair(true, result.wireEncode());
+}
+
+std::pair<bool, Block>
+GetQueryProcessor::processGetCertQuery(const GetParam& param)
+{
+  shared_ptr<IdentityCertificate> certificate = m_db.getCertificate(param.getTargetName());
+  if (certificate == nullptr) {
+    PibError error(ERR_NON_EXISTING_CERT, "requested certificate does not exist");
+    return std::make_pair(true, error.wireEncode());
+  }
+
+  PibCertificate result(*certificate);
+  return std::make_pair(true, result.wireEncode());
+}
+
+std::pair<bool, Block>
+GetQueryProcessor::processGetUserQuery(const GetParam& param)
+{
+  PibUser result;
+  result.setMgmtCert(*m_db.getMgmtCertificate());
+  result.setTpmLocator(m_db.getTpmLocator());
+  return std::make_pair(true, result.wireEncode());
+}
+
+} // namespace pib
+} // namespace ndn
diff --git a/tools/pib/get-query-processor.hpp b/tools/pib/get-query-processor.hpp
new file mode 100644
index 0000000..f4eca40
--- /dev/null
+++ b/tools/pib/get-query-processor.hpp
@@ -0,0 +1,82 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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_PIB_GET_QUERY_PROCESSOR_HPP
+#define NDN_PIB_GET_QUERY_PROCESSOR_HPP
+
+#include "pib-db.hpp"
+#include "encoding/get-param.hpp"
+#include <ndn-cxx/interest.hpp>
+#include <utility>
+
+namespace ndn {
+namespace pib {
+
+/// @brief implements the PIB service
+class GetQueryProcessor : noncopyable
+{
+public:
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+  /**
+   * @brief Constructor
+   *
+   * @param db The pib database.
+   * @param owner Owner of the pib database.
+   */
+  explicit
+  GetQueryProcessor(PibDb& db);
+
+  std::pair<bool, Block>
+  operator()(const Interest& interest);
+
+private:
+  std::pair<bool, Block>
+  processGetUserQuery(const GetParam& param);
+
+  std::pair<bool, Block>
+  processGetIdQuery(const GetParam& param);
+
+  std::pair<bool, Block>
+  processGetKeyQuery(const GetParam& param);
+
+  std::pair<bool, Block>
+  processGetCertQuery(const GetParam& param);
+
+private:
+  static const Name PIB_PREFIX;
+  static const size_t GET_QUERY_LENGTH;
+
+  const PibDb&  m_db;
+};
+
+} // namespace pib
+} // namespace ndn
+
+#endif // NDN_PIB_GET_QUERY_PROCESSOR_HPP
diff --git a/tools/pib/key-cache.cpp b/tools/pib/key-cache.cpp
new file mode 100644
index 0000000..431e635
--- /dev/null
+++ b/tools/pib/key-cache.cpp
@@ -0,0 +1,106 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * 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.
+ */
+
+#include "key-cache.hpp"
+
+namespace ndn {
+namespace pib {
+
+KeyCacheEntry::KeyCacheEntry(const Name& name, shared_ptr<PublicKey> key)
+  : name(name)
+  , key(key)
+{
+  BOOST_ASSERT(static_cast<bool>(key));
+}
+
+KeyCache::KeyCache(size_t capacity)
+  : m_capacity(capacity)
+{
+}
+
+void
+KeyCache::insert(const Name& name, shared_ptr<PublicKey> key)
+{
+  // check if key exist
+  KeyContainer::index<byName>::type::iterator it = m_keys.get<byName>().find(name);
+  if (it != m_keys.get<byName>().end()) {
+    adjustLru(it);
+    return;
+  }
+
+  // evict key when capacity is reached
+  if (size() >= m_capacity)
+    evictKey();
+
+  // insert entry
+  m_keys.insert(KeyCacheEntry(name, key));
+}
+
+shared_ptr<PublicKey>
+KeyCache::find(const Name& name) const
+{
+  // check if key exist
+  KeyContainer::index<byName>::type::iterator it = m_keys.get<byName>().find(name);
+  if (it == m_keys.get<byName>().end())
+    return shared_ptr<PublicKey>();
+  else {
+    // adjust lru
+    shared_ptr<PublicKey> key = it->key;
+    adjustLru(it);
+    return key;
+  }
+}
+
+void
+KeyCache::erase(const Name& name)
+{
+  // check if key exist
+  KeyContainer::index<byName>::type::iterator it = m_keys.get<byName>().find(name);
+  if (it != m_keys.get<byName>().end()) {
+    m_keys.erase(it);
+  }
+}
+
+size_t
+KeyCache::size() const
+{
+  return m_keys.size();
+}
+
+void
+KeyCache::evictKey()
+{
+  if (!m_keys.get<byUsedTime>().empty()) {
+    KeyContainer::index<byUsedTime>::type::iterator it = m_keys.get<byUsedTime>().begin();
+    m_keys.erase(m_keys.project<0>(it));
+  }
+}
+
+void
+KeyCache::adjustLru(KeyContainer::iterator it) const
+{
+  KeyCacheEntry entry = std::move(*it);
+  m_keys.erase(it);
+  m_keys.insert(entry);
+}
+
+} // namespace pib
+} // namespace ndn
diff --git a/tools/pib/key-cache.hpp b/tools/pib/key-cache.hpp
new file mode 100644
index 0000000..5df077a
--- /dev/null
+++ b/tools/pib/key-cache.hpp
@@ -0,0 +1,108 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * 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 NDN_PIB_KEY_CACHE_HPP
+#define NDN_PIB_KEY_CACHE_HPP
+
+#include <ndn-cxx/name.hpp>
+#include <ndn-cxx/util/time.hpp>
+#include <ndn-cxx/security/public-key.hpp>
+
+#include <stack>
+
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/member.hpp>
+#include <boost/multi_index/hashed_index.hpp>
+#include <boost/multi_index/sequenced_index.hpp>
+#include <boost/multi_index/key_extractors.hpp>
+
+namespace ndn {
+namespace pib {
+
+struct KeyCacheEntry
+{
+  KeyCacheEntry(const Name& name, shared_ptr<PublicKey> key);
+
+  Name name;
+  shared_ptr<PublicKey> key;
+};
+
+class byName;
+class byUsedTime;
+
+typedef boost::multi_index::multi_index_container<
+  KeyCacheEntry,
+  boost::multi_index::indexed_by<
+    boost::multi_index::hashed_unique<
+      boost::multi_index::tag<byName>,
+      boost::multi_index::member<KeyCacheEntry, Name, &KeyCacheEntry::name>,
+      std::hash<Name>
+    >,
+
+    boost::multi_index::sequenced<
+      boost::multi_index::tag<byUsedTime>
+    >
+  >
+> KeyContainer;
+
+class KeyCache : noncopyable
+{
+public:
+  explicit
+  KeyCache(size_t capacity = getDefaultCapacity());
+
+  void
+  insert(const Name& name, shared_ptr<PublicKey> key);
+
+  shared_ptr<PublicKey>
+  find(const Name& name) const;
+
+  void
+  erase(const Name& name);
+
+  size_t
+  size() const;
+
+private:
+  static size_t
+  getDefaultCapacity()
+  {
+    return 100;
+  }
+
+  void
+  evictKey();
+
+  void
+  adjustLru(KeyContainer::iterator it) const;
+
+  void
+  freeEntry(KeyContainer::iterator it);
+
+private:
+  size_t m_capacity;
+  mutable KeyContainer m_keys;
+};
+
+} // namespace pib
+} // namespace ndn
+
+#endif // NDN_PIB_KEY_CACHE_HPP
diff --git a/tools/pib/list-query-processor.cpp b/tools/pib/list-query-processor.cpp
new file mode 100644
index 0000000..ad4e2d5
--- /dev/null
+++ b/tools/pib/list-query-processor.cpp
@@ -0,0 +1,98 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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 "list-query-processor.hpp"
+#include "encoding/pib-encoding.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace pib {
+
+using std::string;
+using std::vector;
+
+const size_t ListQueryProcessor::LIST_QUERY_LENGTH = 5;
+
+ListQueryProcessor::ListQueryProcessor(PibDb& db)
+  : m_db(db)
+{
+}
+
+std::pair<bool, Block>
+ListQueryProcessor::operator()(const Interest& interest)
+{
+  const Name& interestName = interest.getName();
+
+  // handle pib query: /localhost/pib/[UserName]/list/param
+  if (interestName.size() != MIN_PIB_INTEREST_SIZE) {
+    // malformed interest, discard
+    return std::make_pair(false, Block());
+  }
+
+  ListParam param;
+
+  try {
+    param.wireDecode(interestName.get(OFFSET_PARAM).blockFromValue());
+  }
+  catch (const tlv::Error& e) {
+    PibError error(ERR_WRONG_PARAM, "error in parsing param: " + string(e.what()));
+    return std::make_pair(true, error.wireEncode());
+  }
+
+  vector<Name> nameList;
+  switch (param.getOriginType()) {
+  case TYPE_USER:
+    {
+      nameList = m_db.listIdentities();
+      break;
+    }
+  case TYPE_ID:
+    {
+      nameList = m_db.listKeyNamesOfIdentity(param.getOriginName());
+      break;
+    }
+  case TYPE_KEY:
+    {
+      const Name& keyName = param.getOriginName();
+      if (keyName.empty()) {
+        PibError error(ERR_WRONG_PARAM,
+                       "key name must contain key id component");
+        return std::make_pair(true, error.wireEncode());
+      }
+
+      nameList = m_db.listCertNamesOfKey(keyName);
+      break;
+    }
+  default:
+    {
+      PibError error(ERR_WRONG_PARAM,
+                     "origin type is not supported: " +
+                     boost::lexical_cast<string>(param.getOriginType()));
+      return std::make_pair(true, error.wireEncode());
+    }
+  }
+
+  PibNameList result(nameList);
+  return std::make_pair(true, result.wireEncode());
+}
+
+} // namespace pib
+} // namespace ndn
diff --git a/tools/pib/list-query-processor.hpp b/tools/pib/list-query-processor.hpp
new file mode 100644
index 0000000..4b814f7
--- /dev/null
+++ b/tools/pib/list-query-processor.hpp
@@ -0,0 +1,66 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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_PIB_LIST_QUERY_PROCESSOR_HPP
+#define NDN_PIB_LIST_QUERY_PROCESSOR_HPP
+
+#include "pib-db.hpp"
+#include "encoding/list-param.hpp"
+#include <ndn-cxx/interest.hpp>
+#include <utility>
+
+namespace ndn {
+namespace pib {
+
+class ListQueryProcessor : noncopyable
+{
+public:
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+  /**
+   * @brief Constructor
+   *
+   * @param db The pib database.
+   */
+  explicit
+  ListQueryProcessor(PibDb& db);
+
+  std::pair<bool, Block>
+  operator()(const Interest& interest);
+
+private:
+  static const size_t LIST_QUERY_LENGTH;
+
+  const PibDb&  m_db;
+};
+
+} // namespace pib
+} // namespace ndn
+
+#endif // NDN_PIB_LIST_QUERY_PROCESSOR_HPP
diff --git a/tools/pib/ndn-pib.cpp b/tools/pib/ndn-pib.cpp
new file mode 100644
index 0000000..4166bd8
--- /dev/null
+++ b/tools/pib/ndn-pib.cpp
@@ -0,0 +1,127 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * 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.
+ */
+
+#include "pib.hpp"
+
+#include <ndn-cxx/util/io.hpp>
+#include <ndn-cxx/util/config-file.hpp>
+#include <ndn-cxx/security/key-chain.hpp>
+
+#include <boost/program_options/options_description.hpp>
+#include <boost/program_options/variables_map.hpp>
+#include <boost/program_options/parsers.hpp>
+#include <boost/filesystem.hpp>
+
+namespace ndn {
+namespace pib {
+
+int
+main(int argc, char** argv)
+{
+  namespace po = boost::program_options;
+  namespace fs = boost::filesystem;
+
+  std::string owner;
+  std::string dbDir;
+  std::string tpmLocator;
+  Face face;
+
+  std::cerr << "hello" << std::endl;
+
+  po::options_description description(
+    "General Usage\n"
+    "  ndn-pib [-h] -o owner -d database_dir -t tpm_locator\n"
+    "General options");
+
+  description.add_options()
+    ("help,h",     "produce help message")
+    ("owner,o",    po::value<std::string>(&owner),
+                   "Name of the owner, PIB will listen to /localhost/pib/[owner].")
+    ("database,d", po::value<std::string>(&dbDir),
+                   "Absolute path to the directory of PIB database, /<database_dir>/pib.db")
+    ("tpm,t",      po::value<std::string>(&tpmLocator),
+                   "URI of the tpm. e.g., tpm-file:/var")
+    ;
+
+  po::variables_map vm;
+  try {
+    po::store(po::parse_command_line(argc, argv, description), vm);
+    po::notify(vm);
+  }
+  catch (const std::exception& e) {
+    std::cerr << "ERROR: " << e.what() << std::endl;
+    std::cerr << description << std::endl;
+    return 1;
+  }
+
+  if (vm.count("help") != 0) {
+    std::cerr << description << std::endl;
+    return 0;
+  }
+
+  try {
+    if (vm.count("owner") == 0 && vm.count("database") == 0 && vm.count("tpm") == 0) {
+      if (std::getenv("HOME")) {
+        fs::path pibDir(std::getenv("HOME"));
+        pibDir /= ".ndn/pib";
+        dbDir = pibDir.string();
+      }
+      else {
+        std::cerr << "ERROR: HOME variable is not set" << std::endl;
+        return 1;
+      }
+
+      tpmLocator = KeyChain::getDefaultTpmLocator();
+
+      if (std::getenv("USER")) {
+        owner = std::string(std::getenv("USER"));
+      }
+      else {
+        std::cerr << "ERROR: HOME variable is not set" << std::endl;
+        return 1;
+      }
+    }
+    else if (vm.count("owner") == 0 || vm.count("database") == 0 ||
+             vm.count("tpm") == 0) {
+      std::cerr << description << std::endl;
+      return 1;
+    }
+
+    Pib pib(face, dbDir, tpmLocator, owner);
+    face.processEvents();
+  }
+  catch (std::runtime_error& e) {
+    std::cerr << "ERROR: " << e.what() << std::endl;
+    return 1;
+  }
+
+
+  return 0;
+}
+
+} // namespace pib
+} // namespace ndn
+
+int
+main(int argc, char** argv)
+{
+  ndn::pib::main(argc, argv);
+}
diff --git a/tools/pib/pib-db.cpp b/tools/pib/pib-db.cpp
new file mode 100644
index 0000000..0e2ce87
--- /dev/null
+++ b/tools/pib/pib-db.cpp
@@ -0,0 +1,825 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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 "pib-db.hpp"
+#include <sqlite3.h>
+#include <boost/filesystem.hpp>
+#include <boost/algorithm/string.hpp>
+
+namespace ndn {
+namespace pib {
+
+using std::string;
+using std::vector;
+using std::set;
+
+const Name PibDb::NON_EXISTING_IDENTITY("/localhost/reserved/non-existing-identity");
+const Name PibDb::NON_EXISTING_KEY("/localhost/reserved/non-existing-key");
+const Name PibDb::NON_EXISTING_CERTIFICATE("/localhost/reserved/non-existing-certificate");
+
+const Name PibDb::LOCALHOST_PIB("/localhost/pib");
+const name::Component PibDb::MGMT_LABEL("mgmt");
+
+
+static const string INITIALIZATION =
+  "CREATE TABLE IF NOT EXISTS                    \n"
+  "  mgmt(                                       \n"
+  "    id                    INTEGER PRIMARY KEY,\n"
+  "    owner                 BLOB NOT NULL,      \n"
+  "    tpm_locator           BLOB,               \n"
+  "    local_management_cert BLOB NOT NULL       \n"
+  "  );                                          \n"
+  "CREATE TRIGGER IF NOT EXISTS                  \n"
+  "  mgmt_insert_trigger                         \n"
+  "  BEFORE INSERT ON mgmt                       \n"
+  "  FOR EACH ROW                                \n"
+  "  BEGIN                                       \n"
+  "    DELETE FROM mgmt;                         \n"
+  "  END;                                        \n"
+  "                                              \n"
+  "CREATE TABLE IF NOT EXISTS                    \n"
+  "  identities(                                 \n"
+  "    id                    INTEGER PRIMARY KEY,\n"
+  "    identity              BLOB NOT NULL,      \n"
+  "    is_default            INTEGER DEFAULT 0   \n"
+  "  );                                          \n"
+  "CREATE UNIQUE INDEX IF NOT EXISTS             \n"
+  "  identityIndex ON identities(identity);      \n"
+  "CREATE TRIGGER IF NOT EXISTS                  \n"
+  "  identity_default_before_insert_trigger      \n"
+  "  BEFORE INSERT ON identities                 \n"
+  "  FOR EACH ROW                                \n"
+  "  WHEN NEW.is_default=1                       \n"
+  "  BEGIN                                       \n"
+  "    UPDATE identities SET is_default=0;       \n"
+  "  END;                                        \n"
+  "CREATE TRIGGER IF NOT EXISTS                  \n"
+  "  identity_default_after_insert_trigger       \n"
+  "  AFTER INSERT ON identities                  \n"
+  "  FOR EACH ROW                                \n"
+  "  WHEN NOT EXISTS                             \n"
+  "    (SELECT id                                \n"
+  "       FROM identities                        \n"
+  "       WHERE is_default=1)                    \n"
+  "  BEGIN                                       \n"
+  "    UPDATE identities                         \n"
+  "      SET is_default=1                        \n"
+  "      WHERE identity=NEW.identity;            \n"
+  "  END;                                        \n"
+  "CREATE TRIGGER IF NOT EXISTS                  \n"
+  "  identity_default_update_trigger             \n"
+  "  BEFORE UPDATE ON identities                 \n"
+  "  FOR EACH ROW                                \n"
+  "  WHEN NEW.is_default=1 AND OLD.is_default=0  \n"
+  "  BEGIN                                       \n"
+  "    UPDATE identities SET is_default=0;       \n"
+  "  END;                                        \n"
+  "CREATE TRIGGER IF NOT EXISTS                  \n"
+  "  identity_delete_trigger                     \n"
+  "  AFTER DELETE ON identities                  \n"
+  "  FOR EACH ROW                                \n"
+  "  BEGIN                                       \n"
+  "    SELECT identityDeleted (OLD.identity);    \n"
+  "  END;                                        \n"
+  "                                              \n"
+  "CREATE TABLE IF NOT EXISTS                    \n"
+  "  keys(                                       \n"
+  "    id                    INTEGER PRIMARY KEY,\n"
+  "    identity_id           INTEGER NOT NULL,   \n"
+  "    key_name              BLOB NOT NULL,      \n"
+  "    key_type              INTEGER NOT NULL,   \n"
+  "    key_bits              BLOB NOT NULL,      \n"
+  "    is_default            INTEGER DEFAULT 0,  \n"
+  "    FOREIGN KEY(identity_id)                  \n"
+  "      REFERENCES identities(id)               \n"
+  "      ON DELETE CASCADE                       \n"
+  "      ON UPDATE CASCADE                       \n"
+  "  );                                          \n"
+  "CREATE UNIQUE INDEX IF NOT EXISTS             \n"
+  "  keyIndex ON keys(key_name);                 \n"
+  "CREATE TRIGGER IF NOT EXISTS                  \n"
+  "  key_default_before_insert_trigger           \n"
+  "  BEFORE INSERT ON keys                       \n"
+  "  FOR EACH ROW                                \n"
+  "  WHEN NEW.is_default=1                       \n"
+  "  BEGIN                                       \n"
+  "    UPDATE keys                               \n"
+  "      SET is_default=0                        \n"
+  "      WHERE identity_id=NEW.identity_id;      \n"
+  "  END;                                        \n"
+  "CREATE TRIGGER IF NOT EXISTS                  \n"
+  "  key_default_after_insert_trigger            \n"
+  "  AFTER INSERT ON keys                        \n"
+  "  FOR EACH ROW                                \n"
+  "  WHEN NOT EXISTS                             \n"
+  "    (SELECT id                                \n"
+  "       FROM keys                              \n"
+  "       WHERE is_default=1                     \n"
+  "         AND identity_id=NEW.identity_id)     \n"
+  "  BEGIN                                       \n"
+  "    UPDATE keys                               \n"
+  "      SET is_default=1                        \n"
+  "      WHERE key_name=NEW.key_name;            \n"
+  "  END;                                        \n"
+  "CREATE TRIGGER IF NOT EXISTS                  \n"
+  "  key_default_update_trigger                  \n"
+  "  BEFORE UPDATE ON keys                       \n"
+  "  FOR EACH ROW                                \n"
+  "  WHEN NEW.is_default=1 AND OLD.is_default=0  \n"
+  "  BEGIN                                       \n"
+  "    UPDATE keys                               \n"
+  "      SET is_default=0                        \n"
+  "      WHERE identity_id=NEW.identity_id;      \n"
+  "  END;                                        \n"
+  "CREATE TRIGGER IF NOT EXISTS                  \n"
+  "  key_delete_trigger                          \n"
+  "  AFTER DELETE ON keys                        \n"
+  "  FOR EACH ROW                                \n"
+  "  BEGIN                                       \n"
+  "    SELECT keyDeleted (OLD.key_name);         \n"
+  "  END;                                        \n"
+  "                                              \n"
+  "CREATE TABLE IF NOT EXISTS                    \n"
+  "  certificates(                               \n"
+  "    id                    INTEGER PRIMARY KEY,\n"
+  "    key_id                INTEGER NOT NULL,   \n"
+  "    certificate_name      BLOB NOT NULL,      \n"
+  "    certificate_data      BLOB NOT NULL,      \n"
+  "    is_default            INTEGER DEFAULT 0,  \n"
+  "    FOREIGN KEY(key_id)                       \n"
+  "      REFERENCES keys(id)                     \n"
+  "      ON DELETE CASCADE                       \n"
+  "      ON UPDATE CASCADE                       \n"
+  "  );                                          \n"
+  "CREATE UNIQUE INDEX IF NOT EXISTS             \n"
+  "  certIndex ON certificates(certificate_name);\n"
+  "CREATE TRIGGER IF NOT EXISTS                  \n"
+  "  cert_default_before_insert_trigger          \n"
+  "  BEFORE INSERT ON certificates               \n"
+  "  FOR EACH ROW                                \n"
+  "  WHEN NEW.is_default=1                       \n"
+  "  BEGIN                                       \n"
+  "    UPDATE certificates                       \n"
+  "      SET is_default=0                        \n"
+  "      WHERE key_id=NEW.key_id;                \n"
+  "  END;                                        \n"
+  "CREATE TRIGGER IF NOT EXISTS                  \n"
+  "  cert_default_after_insert_trigger           \n"
+  "  AFTER INSERT ON certificates                \n"
+  "  FOR EACH ROW                                \n"
+  "  WHEN NOT EXISTS                             \n"
+  "    (SELECT id                                \n"
+  "       FROM certificates                      \n"
+  "       WHERE is_default=1                     \n"
+  "         AND key_id=NEW.key_id)               \n"
+  "  BEGIN                                       \n"
+  "    UPDATE certificates                       \n"
+  "      SET is_default=1                        \n"
+  "      WHERE certificate_name=NEW.certificate_name;\n"
+  "  END;                                        \n"
+  "CREATE TRIGGER IF NOT EXISTS                  \n"
+  "  cert_default_update_trigger                 \n"
+  "  BEFORE UPDATE ON certificates               \n"
+  "  FOR EACH ROW                                \n"
+  "  WHEN NEW.is_default=1 AND OLD.is_default=0  \n"
+  "  BEGIN                                       \n"
+  "    UPDATE certificates                       \n"
+  "      SET is_default=0                        \n"
+  "      WHERE key_id=NEW.key_id;                \n"
+  "  END;                                        \n"
+  "CREATE TRIGGER IF NOT EXISTS                  \n"
+  "  cert_delete_trigger                         \n"
+  "  AFTER DELETE ON certificates                \n"
+  "  FOR EACH ROW                                \n"
+  "  BEGIN                                       \n"
+  "    SELECT certDeleted (OLD.certificate_name);\n"
+  "  END;                                        \n"
+  "CREATE TRIGGER IF NOT EXISTS                  \n"
+  "  cert_insert_trigger                         \n"
+  "  AFTER INSERT ON certificates                \n"
+  "  FOR EACH ROW                                \n"
+  "  BEGIN                                       \n"
+  "    SELECT certInserted (NEW.certificate_name);\n"
+  "  END;                                        \n";
+
+
+/**
+ * A utility function to call the normal sqlite3_bind_text where the value and length are
+ * value.c_str() and value.size().
+ */
+static int
+sqlite3_bind_string(sqlite3_stmt* statement,
+                    int index,
+                    const string& value,
+                    void(*destructor)(void*))
+{
+  return sqlite3_bind_text(statement, index, value.c_str(), value.size(), destructor);
+}
+
+/**
+ * A utility function to call the normal sqlite3_bind_blob where the value and length are
+ * block.wire() and block.size().
+ */
+static int
+sqlite3_bind_block(sqlite3_stmt* statement,
+                   int index,
+                   const Block& block,
+                   void(*destructor)(void*))
+{
+  return sqlite3_bind_blob(statement, index, block.wire(), block.size(), destructor);
+}
+
+/**
+ * A utility function to generate string by calling the normal sqlite3_column_text.
+ */
+static string
+sqlite3_column_string(sqlite3_stmt* statement, int column)
+{
+  return string(reinterpret_cast<const char*>(sqlite3_column_text(statement, column)),
+                sqlite3_column_bytes(statement, column));
+}
+
+/**
+ * A utility function to generate block by calling the normal sqlite3_column_text.
+ */
+static Block
+sqlite3_column_block(sqlite3_stmt* statement, int column)
+{
+  return Block(sqlite3_column_blob(statement, column), sqlite3_column_bytes(statement, column));
+}
+
+PibDb::PibDb(const string& dbDir)
+{
+  // Determine the path of PIB DB
+  boost::filesystem::path dir;
+  if (dbDir == "") {
+    dir = boost::filesystem::path(getenv("HOME")) / ".ndn";
+    boost::filesystem::create_directories(dir);
+  }
+  else {
+    dir = boost::filesystem::path(dbDir);
+    boost::filesystem::create_directories(dir);
+  }
+  // Open PIB
+  int result = sqlite3_open_v2((dir / "pib.db").c_str(), &m_database,
+                               SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
+#ifdef NDN_CXX_DISABLE_SQLITE3_FS_LOCKING
+                               "unix-dotfile"
+#else
+                               nullptr
+#endif
+                               );
+
+  if (result != SQLITE_OK)
+    throw Error("PIB DB cannot be opened/created: " + dbDir);
+
+
+  // enable foreign key
+  sqlite3_exec(m_database, "PRAGMA foreign_keys = ON", nullptr, nullptr, nullptr);
+
+  // initialize PIB specific tables
+  char* errorMessage = nullptr;
+  result = sqlite3_exec(m_database, INITIALIZATION.c_str(), nullptr, nullptr, &errorMessage);
+  if (result != SQLITE_OK && errorMessage != nullptr) {
+    sqlite3_free(errorMessage);
+    throw Error("PIB DB cannot be initialized");
+  }
+
+  // create delete trigger functions
+  createDbDeleteTrigger();
+
+  getOwnerName();
+}
+
+void
+PibDb::createDbDeleteTrigger()
+{
+  int res = 0;
+
+  res = sqlite3_create_function(m_database, "identityDeleted", -1, SQLITE_UTF8,
+                                reinterpret_cast<void*>(this),
+                                PibDb::identityDeletedFun, nullptr, nullptr);
+  if (res != SQLITE_OK)
+    throw Error("Cannot create function ``identityDeleted''");
+
+  res = sqlite3_create_function(m_database, "keyDeleted", -1, SQLITE_UTF8,
+                                reinterpret_cast<void*>(this),
+                                PibDb::keyDeletedFun, nullptr, nullptr);
+  if (res != SQLITE_OK)
+    throw Error("Cannot create function ``keyDeleted''");
+
+  res = sqlite3_create_function(m_database, "certDeleted", -1, SQLITE_UTF8,
+                                reinterpret_cast<void*>(this),
+                                PibDb::certDeletedFun, nullptr, nullptr);
+  if (res != SQLITE_OK)
+    throw Error("Cannot create function ``certDeleted''");
+
+  res = sqlite3_create_function(m_database, "certInserted", -1, SQLITE_UTF8,
+                                reinterpret_cast<void*>(this),
+                                PibDb::certInsertedFun, nullptr, nullptr);
+  if (res != SQLITE_OK)
+    throw Error("Cannot create function ``certInserted''");
+}
+
+void
+PibDb::identityDeletedFun(sqlite3_context* context, int argc, sqlite3_value** argv)
+{
+  BOOST_ASSERT(argc == 1);
+
+  PibDb* pibDb = reinterpret_cast<PibDb*>(sqlite3_user_data(context));
+  Name identity(Block(sqlite3_value_blob(argv[0]), sqlite3_value_bytes(argv[0])));
+
+  pibDb->identityDeleted(identity);
+}
+
+void
+PibDb::keyDeletedFun(sqlite3_context* context, int argc, sqlite3_value** argv)
+{
+  BOOST_ASSERT(argc == 1);
+
+  PibDb* pibDb = reinterpret_cast<PibDb*>(sqlite3_user_data(context));
+  Name keyName(Block(sqlite3_value_blob(argv[0]), sqlite3_value_bytes(argv[0])));
+
+  pibDb->keyDeleted(keyName);
+}
+
+void
+PibDb::certDeletedFun(sqlite3_context* context, int argc, sqlite3_value** argv)
+{
+  BOOST_ASSERT(argc == 1);
+
+  PibDb* pibDb = reinterpret_cast<PibDb*>(sqlite3_user_data(context));
+  Name certName(Block(sqlite3_value_blob(argv[0]), sqlite3_value_bytes(argv[0])));
+
+  pibDb->certificateDeleted(certName);
+}
+
+void
+PibDb::certInsertedFun(sqlite3_context* context, int argc, sqlite3_value** argv)
+{
+  BOOST_ASSERT(argc == 1);
+
+  PibDb* pibDb = reinterpret_cast<PibDb*>(sqlite3_user_data(context));
+  Name certName(Block(sqlite3_value_blob(argv[0]), sqlite3_value_bytes(argv[0])));
+
+  pibDb->certificateInserted(certName);
+}
+
+void
+PibDb::updateMgmtCertificate(const IdentityCertificate& certificate)
+{
+  const Name& keyName = certificate.getPublicKeyName();
+
+  // Name of mgmt key should be "/localhost/pib/[UserName]/mgmt/[KeyID]"
+  if (keyName.size() != 5 ||
+      keyName.compare(0, 2, LOCALHOST_PIB) ||
+      keyName.get(3) != MGMT_LABEL)
+    throw Error("PibDb::updateMgmtCertificate: certificate does not follow the naming convention");
+
+  string owner = keyName.get(2).toUri();
+  sqlite3_stmt* statement;
+  if (!m_owner.empty()) {
+    if (m_owner != owner)
+      throw Error("PibDb::updateMgmtCertificate: owner name does not match");
+    else {
+      sqlite3_prepare_v2(m_database,
+                         "UPDATE mgmt SET local_management_cert=? WHERE owner=?",
+                         -1, &statement, nullptr);
+    }
+  }
+  else {
+    sqlite3_prepare_v2(m_database,
+                       "INSERT INTO mgmt (local_management_cert, owner) VALUES (?, ?)",
+                       -1, &statement, nullptr);
+  }
+
+  sqlite3_bind_block(statement, 1, certificate.wireEncode(), SQLITE_TRANSIENT);
+  sqlite3_bind_string(statement, 2, owner, SQLITE_TRANSIENT);
+  sqlite3_step(statement);
+  sqlite3_finalize(statement);
+
+  m_owner = owner;
+
+  mgmtCertificateChanged();
+}
+
+string
+PibDb::getOwnerName() const
+{
+  sqlite3_stmt* statement;
+  sqlite3_prepare_v2(m_database, "SELECT owner FROM mgmt", -1, &statement, nullptr);
+
+  if (sqlite3_step(statement) == SQLITE_ROW) {
+    m_owner = sqlite3_column_string(statement, 0);
+  }
+
+  sqlite3_finalize(statement);
+  return m_owner;
+}
+
+shared_ptr<IdentityCertificate>
+PibDb::getMgmtCertificate() const
+{
+  sqlite3_stmt* statement;
+  sqlite3_prepare_v2(m_database, "SELECT local_management_cert FROM mgmt", -1, &statement, nullptr);
+
+  shared_ptr<IdentityCertificate> certificate;
+  if (sqlite3_step(statement) == SQLITE_ROW) {
+    certificate = make_shared<IdentityCertificate>();
+    certificate->wireDecode(sqlite3_column_block(statement, 0));
+  }
+
+  sqlite3_finalize(statement);
+  return certificate;
+}
+
+void
+PibDb::setTpmLocator(const std::string& tpmLocator)
+{
+  sqlite3_stmt* statement;
+  sqlite3_prepare_v2(m_database,
+                     "UPDATE mgmt SET tpm_locator=? WHERE owner=?",
+                     -1, &statement, nullptr);
+  sqlite3_bind_string(statement, 1, tpmLocator, SQLITE_TRANSIENT);
+  sqlite3_bind_string(statement, 2, m_owner, SQLITE_TRANSIENT);
+  sqlite3_step(statement);
+  sqlite3_finalize(statement);
+}
+
+std::string
+PibDb::getTpmLocator() const
+{
+  sqlite3_stmt* statement;
+  sqlite3_prepare_v2(m_database, "SELECT tpm_locator FROM mgmt", -1, &statement, nullptr);
+
+  string tpmLocator;
+  if (sqlite3_step(statement) == SQLITE_ROW) {
+    tpmLocator = sqlite3_column_string(statement, 0);
+  }
+
+  sqlite3_finalize(statement);
+  return tpmLocator;
+}
+
+int64_t
+PibDb::addIdentity(const Name& identity)
+{
+  sqlite3_stmt* statement;
+  sqlite3_prepare_v2(m_database,
+                     "INSERT INTO identities (identity) values (?)",
+                     -1, &statement, nullptr);
+  sqlite3_bind_block(statement, 1, identity.wireEncode(), SQLITE_TRANSIENT);
+  sqlite3_step(statement);
+  sqlite3_finalize(statement);
+
+  return sqlite3_last_insert_rowid(m_database);
+}
+
+void
+PibDb::deleteIdentity(const Name& identity)
+{
+  sqlite3_stmt* statement;
+  sqlite3_prepare_v2(m_database,
+                     "DELETE FROM identities WHERE identity=?",
+                     -1, &statement, nullptr);
+  sqlite3_bind_block(statement, 1, identity.wireEncode(), SQLITE_TRANSIENT);
+  sqlite3_step(statement);
+  sqlite3_finalize(statement);
+}
+
+bool
+PibDb::hasIdentity(const Name& identity) const
+{
+  sqlite3_stmt* statement;
+  sqlite3_prepare_v2(m_database,
+                     "SELECT id FROM identities WHERE identity=?",
+                     -1, &statement, nullptr);
+  sqlite3_bind_block(statement, 1, identity.wireEncode(), SQLITE_TRANSIENT);
+  int result = sqlite3_step(statement);
+  sqlite3_finalize(statement);
+
+  if (result == SQLITE_ROW)
+    return true;
+  else
+    return false;
+}
+
+void
+PibDb::setDefaultIdentity(const Name& identity)
+{
+  sqlite3_stmt* statement;
+  sqlite3_prepare_v2(m_database,
+                     "UPDATE identities SET is_default=1 WHERE identity=?",
+                     -1, &statement, nullptr);
+  sqlite3_bind_block(statement, 1, identity.wireEncode(), SQLITE_TRANSIENT);
+  sqlite3_step(statement);
+  sqlite3_finalize(statement);
+}
+
+Name
+PibDb::getDefaultIdentity() const
+{
+  sqlite3_stmt* statement;
+  sqlite3_prepare_v2(m_database,
+                     "SELECT identity FROM identities WHERE is_default=1",
+                     -1, &statement, nullptr);
+
+  Name identity = NON_EXISTING_IDENTITY;
+  if (sqlite3_step(statement) == SQLITE_ROW && sqlite3_column_bytes(statement, 0) != 0) {
+    identity = Name(sqlite3_column_block(statement, 0));
+  }
+  sqlite3_finalize(statement);
+  return identity;
+}
+
+vector<Name>
+PibDb::listIdentities() const
+{
+  vector<Name> identities;
+
+  sqlite3_stmt* statement;
+  sqlite3_prepare_v2(m_database, "SELECT identity FROM identities", -1, &statement, nullptr);
+
+  identities.clear();
+  while (sqlite3_step(statement) == SQLITE_ROW) {
+    Name name(sqlite3_column_block(statement, 0));
+    identities.push_back(name);
+  }
+  sqlite3_finalize(statement);
+
+  return identities;
+}
+
+int64_t
+PibDb::addKey(const Name& keyName, const PublicKey& key)
+{
+  if (keyName.empty())
+    return 0;
+
+  Name&& identity = keyName.getPrefix(-1);
+  if (!hasIdentity(identity))
+    addIdentity(identity);
+
+  sqlite3_stmt* statement;
+  sqlite3_prepare_v2(m_database,
+                     "INSERT INTO keys (identity_id, key_name, key_type, key_bits) \
+                      values ((SELECT id FROM identities WHERE identity=?), ?, ?, ?)",
+                     -1, &statement, nullptr);
+  sqlite3_bind_block(statement, 1, identity.wireEncode(), SQLITE_TRANSIENT);
+  sqlite3_bind_block(statement, 2, keyName.wireEncode(), SQLITE_TRANSIENT);
+  sqlite3_bind_int(statement, 3, key.getKeyType());
+  sqlite3_bind_blob(statement, 4, key.get().buf(), key.get().size(), SQLITE_STATIC);
+  sqlite3_step(statement);
+  sqlite3_finalize(statement);
+
+  return sqlite3_last_insert_rowid(m_database);
+}
+
+shared_ptr<PublicKey>
+PibDb::getKey(const Name& keyName) const
+{
+  sqlite3_stmt* statement;
+  sqlite3_prepare_v2(m_database,
+                     "SELECT key_bits FROM keys WHERE key_name=?"
+                     , -1, &statement, nullptr);
+  sqlite3_bind_block(statement, 1, keyName.wireEncode(), SQLITE_TRANSIENT);
+
+  shared_ptr<PublicKey> key;
+  if (sqlite3_step(statement) == SQLITE_ROW) {
+      key = make_shared<PublicKey>(static_cast<const uint8_t*>(sqlite3_column_blob(statement, 0)),
+                                   sqlite3_column_bytes(statement, 0));
+  }
+  sqlite3_finalize(statement);
+  return key;
+}
+
+void
+PibDb::deleteKey(const Name& keyName)
+{
+  sqlite3_stmt* statement;
+  sqlite3_prepare_v2(m_database,
+                     "DELETE FROM keys WHERE key_name=?",
+                     -1, &statement, nullptr);
+  sqlite3_bind_block(statement, 1, keyName.wireEncode(), SQLITE_TRANSIENT);
+  sqlite3_step(statement);
+  sqlite3_finalize(statement);
+}
+
+bool
+PibDb::hasKey(const Name& keyName) const
+{
+  sqlite3_stmt* statement;
+  sqlite3_prepare_v2(m_database,
+                     "SELECT id FROM keys WHERE key_name=?",
+                     -1, &statement, nullptr);
+  sqlite3_bind_block(statement, 1, keyName.wireEncode(), SQLITE_TRANSIENT);
+
+  int result = sqlite3_step(statement);
+  sqlite3_finalize(statement);
+
+  if (result == SQLITE_ROW)
+    return true;
+  else
+    return false;
+}
+
+void
+PibDb::setDefaultKeyNameOfIdentity(const Name& keyName)
+{
+  sqlite3_stmt* statement;
+  sqlite3_prepare_v2(m_database,
+                     "UPDATE keys SET is_default=1 WHERE key_name=?",
+                     -1, &statement, nullptr);
+  sqlite3_bind_block(statement, 1, keyName.wireEncode(), SQLITE_TRANSIENT);
+  sqlite3_step(statement);
+  sqlite3_finalize(statement);
+}
+
+Name
+PibDb::getDefaultKeyNameOfIdentity(const Name& identity) const
+{
+  sqlite3_stmt* statement;
+  sqlite3_prepare_v2(m_database,
+                     "SELECT key_name FROM keys JOIN identities ON keys.identity_id=identities.id\
+                      WHERE identities.identity=? AND keys.is_default=1",
+                     -1, &statement, nullptr);
+  sqlite3_bind_block(statement, 1, identity.wireEncode(), SQLITE_TRANSIENT);
+
+  Name keyName = NON_EXISTING_KEY;
+  if (sqlite3_step(statement) == SQLITE_ROW && sqlite3_column_bytes(statement, 0) != 0) {
+    keyName = Name(sqlite3_column_block(statement, 0));
+  }
+
+  sqlite3_finalize(statement);
+  return keyName;
+}
+
+vector<Name>
+PibDb::listKeyNamesOfIdentity(const Name& identity) const
+{
+  vector<Name> keyNames;
+
+  sqlite3_stmt* statement;
+  sqlite3_prepare_v2(m_database,
+                     "SELECT key_name FROM keys JOIN identities ON keys.identity_id=identities.id\
+                      WHERE identities.identity=?",
+                     -1, &statement, nullptr);
+  sqlite3_bind_block(statement, 1, identity.wireEncode(), SQLITE_TRANSIENT);
+
+  keyNames.clear();
+  while (sqlite3_step(statement) == SQLITE_ROW) {
+    Name keyName(sqlite3_column_block(statement, 0));
+    keyNames.push_back(keyName);
+  }
+
+  sqlite3_finalize(statement);
+  return keyNames;
+}
+
+
+int64_t
+PibDb::addCertificate(const IdentityCertificate& certificate)
+{
+  const Name& certName = certificate.getName();
+  const Name& keyName = certificate.getPublicKeyName();
+
+  if (!hasKey(keyName))
+    addKey(keyName, certificate.getPublicKeyInfo());
+
+  sqlite3_stmt* statement;
+  sqlite3_prepare_v2(m_database,
+                     "INSERT INTO certificates \
+                      (key_id, certificate_name, certificate_data) \
+                      values ((SELECT id FROM keys WHERE key_name=?), ?, ?)",
+                     -1, &statement, nullptr);
+  sqlite3_bind_block(statement, 1, keyName.wireEncode(), SQLITE_TRANSIENT);
+  sqlite3_bind_block(statement, 2, certName.wireEncode(), SQLITE_TRANSIENT);
+  sqlite3_bind_block(statement, 3, certificate.wireEncode(), SQLITE_STATIC);
+  sqlite3_step(statement);
+  sqlite3_finalize(statement);
+
+  return sqlite3_last_insert_rowid(m_database);
+}
+
+shared_ptr<IdentityCertificate>
+PibDb::getCertificate(const Name& certificateName) const
+{
+  sqlite3_stmt* statement;
+  sqlite3_prepare_v2(m_database,
+                     "SELECT certificate_data FROM certificates WHERE certificate_name=?",
+                     -1, &statement, nullptr);
+  sqlite3_bind_block(statement, 1, certificateName.wireEncode(), SQLITE_TRANSIENT);
+
+  shared_ptr<IdentityCertificate> certificate;
+  if (sqlite3_step(statement) == SQLITE_ROW) {
+    certificate = make_shared<IdentityCertificate>();
+    certificate->wireDecode(sqlite3_column_block(statement, 0));
+  }
+
+  sqlite3_finalize(statement);
+  return certificate;
+}
+
+void
+PibDb::deleteCertificate(const Name& certificateName)
+{
+  sqlite3_stmt* statement;
+  sqlite3_prepare_v2(m_database,
+                     "DELETE FROM certificates WHERE certificate_name=?",
+                     -1, &statement, nullptr);
+  sqlite3_bind_block(statement, 1, certificateName.wireEncode(), SQLITE_TRANSIENT);
+  sqlite3_step(statement);
+  sqlite3_finalize(statement);
+}
+
+bool
+PibDb::hasCertificate(const Name& certificateName) const
+{
+  sqlite3_stmt* statement;
+  sqlite3_prepare_v2(m_database,
+                     "SELECT id FROM certificates WHERE certificate_name=?",
+                     -1, &statement, nullptr);
+  sqlite3_bind_block(statement, 1, certificateName.wireEncode(), SQLITE_TRANSIENT);
+  int result = sqlite3_step(statement);
+  sqlite3_finalize(statement);
+
+  if (result == SQLITE_ROW)
+    return true;
+  else
+    return false;
+}
+
+void
+PibDb::setDefaultCertNameOfKey(const Name& certificateName)
+{
+  sqlite3_stmt* statement;
+  sqlite3_prepare_v2(m_database,
+                     "UPDATE certificates SET is_default=1 WHERE certificate_name=?",
+                     -1, &statement, nullptr);
+  sqlite3_bind_block(statement, 1, certificateName.wireEncode(), SQLITE_TRANSIENT);
+  sqlite3_step(statement);
+  sqlite3_finalize(statement);
+}
+
+Name
+PibDb::getDefaultCertNameOfKey(const Name& keyName) const
+{
+  sqlite3_stmt* statement;
+  sqlite3_prepare_v2(m_database,
+                     "SELECT certificate_name\
+                      FROM certificates JOIN keys ON certificates.key_id=keys.id\
+                      WHERE keys.key_name=? AND certificates.is_default=1",
+                     -1, &statement, nullptr);
+  sqlite3_bind_block(statement, 1, keyName.wireEncode(), SQLITE_TRANSIENT);
+
+  Name certName = NON_EXISTING_CERTIFICATE;
+  if (sqlite3_step(statement) == SQLITE_ROW && sqlite3_column_bytes(statement, 0) != 0) {
+    certName = Name(sqlite3_column_block(statement, 0));
+  }
+  sqlite3_finalize(statement);
+  return certName;
+}
+
+vector<Name>
+PibDb::listCertNamesOfKey(const Name& keyName) const
+{
+  vector<Name> certNames;
+
+  sqlite3_stmt* statement;
+  sqlite3_prepare_v2(m_database,
+                     "SELECT certificate_name\
+                      FROM certificates JOIN keys ON certificates.key_id=keys.id\
+                      WHERE keys.key_name=?",
+                     -1, &statement, nullptr);
+  sqlite3_bind_block(statement, 1, keyName.wireEncode(), SQLITE_TRANSIENT);
+
+  certNames.clear();
+  while (sqlite3_step(statement) == SQLITE_ROW) {
+    Name name(sqlite3_column_block(statement, 0));
+    certNames.push_back(name);
+  }
+  sqlite3_finalize(statement);
+
+  return certNames;
+}
+
+} // namespace pib
+} // namespace ndn
diff --git a/tools/pib/pib-db.hpp b/tools/pib/pib-db.hpp
new file mode 100644
index 0000000..5101e25
--- /dev/null
+++ b/tools/pib/pib-db.hpp
@@ -0,0 +1,265 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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_PIB_PIB_DB_HPP
+#define NDN_PIB_PIB_DB_HPP
+
+#include "core/common.hpp"
+#include <ndn-cxx/security/identity-certificate.hpp>
+#include <ndn-cxx/util/signal.hpp>
+
+#include <set>
+#include <vector>
+
+struct sqlite3;
+struct sqlite3_context;
+struct Mem;
+typedef Mem sqlite3_value;
+
+namespace ndn {
+namespace pib {
+
+/// @brief Callback to report changes on user info.
+typedef function<void(const std::string&)> UserChangedEventHandler;
+
+/// @brief Callback to report that a key is deleted.
+typedef function<void(const std::string&, const Name&,
+                      const name::Component&)> KeyDeletedEventHandler;
+
+/**
+ * @brief PibDb is a class to manage the database of PIB service.
+ *
+ * only public key related information is stored in this database.
+ * Detail information can be found at:
+ * http://redmine.named-data.net/projects/ndn-cxx/wiki/PublicKey_Info_Base
+ */
+class PibDb : noncopyable
+{
+public:
+  util::signal::Signal<PibDb> mgmtCertificateChanged;
+  util::signal::Signal<PibDb, Name> certificateDeleted;
+  util::signal::Signal<PibDb, Name> keyDeleted;
+  util::signal::Signal<PibDb, Name> identityDeleted;
+  util::signal::Signal<PibDb, Name> certificateInserted;
+
+public:
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+  explicit
+  PibDb(const std::string& dbDir = "");
+
+public: // Owner management
+  /**
+   * @brief Update owner's management certificate
+   *
+   * Since owner name is encoded in the management certificate,
+   * this method can also set the owner name if it is not set.
+   * If the owner name is set but does not match the one in the
+   * supplied certificate, it throws @p Error.
+   *
+   * @throws Error if supplied certificate is wrong
+   */
+  void
+  updateMgmtCertificate(const IdentityCertificate& certificate);
+
+  /**
+   * @brief Get owner name
+   *
+   * return empty string when owner name is not set.
+   */
+  std::string
+  getOwnerName() const;
+
+  /** @brief Get the management cert
+   *
+   * return nullptr when the management cert is not set
+   */
+  shared_ptr<IdentityCertificate>
+  getMgmtCertificate() const;
+
+  /// @brief Set TPM locator
+  void
+  setTpmLocator(const std::string& tpmLocator);
+
+  /**
+   * @brief Get TPM locator
+   *
+   * return empty string when tpmLocator is not set.
+   */
+  std::string
+  getTpmLocator() const;
+
+public: // Identity management
+
+  /**
+   * @brief Add an identity
+   *
+   * @return row id of the added identity, 0 if insert fails.
+   */
+  int64_t
+  addIdentity(const Name& identity);
+
+  /// @brief Delete an identity
+  void
+  deleteIdentity(const Name& identity);
+
+  /// @brief Check if an identity exists
+  bool
+  hasIdentity(const Name& identity) const;
+
+  /// @brief Get all identities
+  std::vector<Name>
+  listIdentities() const;
+
+  /// @brief Set the default identity
+  void
+  setDefaultIdentity(const Name& identity);
+
+  /**
+   * @brief Get the default identity
+   *
+   * @return default identity or /localhost/reserved/non-existing-identity if no default identity
+   */
+  Name
+  getDefaultIdentity() const;
+
+public: // Key management
+
+  /// @brief Add key
+  int64_t
+  addKey(const Name& keyName, const PublicKey& key);
+
+  /// @brief Delete key
+  void
+  deleteKey(const Name& keyName);
+
+  /// @brief Check if a key exists
+  bool
+  hasKey(const Name& keyName) const;
+
+  /**
+   * @brief Get key
+   *
+   * @return shared pointer to the key, nullptr if the key does not exit
+   */
+  shared_ptr<PublicKey>
+  getKey(const Name& keyName) const;
+
+  /// @brief Get all the key names of an identity
+  std::vector<Name>
+  listKeyNamesOfIdentity(const Name& identity) const;
+
+  /// @brief Set an identity's default key name
+  void
+  setDefaultKeyNameOfIdentity(const Name& keyName);
+
+  /**
+   * @brief Get the default key name of an identity
+   *
+   * @return default key name or /localhost/reserved/non-existing-key if no default key
+   */
+  Name
+  getDefaultKeyNameOfIdentity(const Name& identity) const;
+
+public: // Certificate management
+
+  /// @brief Add a certificate
+  int64_t
+  addCertificate(const IdentityCertificate& certificate);
+
+  /// @brief Delete a certificate
+  void
+  deleteCertificate(const Name& certificateName);
+
+  /// @brief Check if the certificate exist
+  bool
+  hasCertificate(const Name& certificateName) const;
+
+  /**
+   * @brief Get a certificate
+   *
+   * @return shared pointer to the certificate, nullptr if the certificate does not exist
+   */
+  shared_ptr<IdentityCertificate>
+  getCertificate(const Name& certificateName) const;
+
+  /// @brief Get all the cert names of a key
+  std::vector<Name>
+  listCertNamesOfKey(const Name& keyName) const;
+
+  /// @brief Set a key's default certificate name
+  void
+  setDefaultCertNameOfKey(const Name& certificateName);
+
+  /**
+   * @brief Get a key's default certificate name
+   *
+   * @return default certificate name or /localhost/reserved/non-existing-certificate if no default
+   *         certificate.
+   */
+  Name
+  getDefaultCertNameOfKey(const Name& keyName) const;
+
+private:
+  void
+  createDbDeleteTrigger();
+
+private:
+  static void
+  identityDeletedFun(sqlite3_context* context, int argc, sqlite3_value** argv);
+
+  static void
+  keyDeletedFun(sqlite3_context* context, int argc, sqlite3_value** argv);
+
+  static void
+  certDeletedFun(sqlite3_context* context, int argc, sqlite3_value** argv);
+
+  static void
+  certInsertedFun(sqlite3_context* context, int argc, sqlite3_value** argv);
+
+public:
+  static const Name NON_EXISTING_IDENTITY;
+  static const Name NON_EXISTING_KEY;
+  static const Name NON_EXISTING_CERTIFICATE;
+
+private:
+  static const Name LOCALHOST_PIB;
+  static const name::Component MGMT_LABEL;
+
+private:
+  sqlite3* m_database;
+
+  mutable std::string m_owner;
+};
+
+} // namespace pib
+} // namespace ndn
+
+
+#endif // NDN_PIB_PIB_DB_HPP
diff --git a/tools/pib/pib-validator.cpp b/tools/pib/pib-validator.cpp
new file mode 100644
index 0000000..cc34645
--- /dev/null
+++ b/tools/pib/pib-validator.cpp
@@ -0,0 +1,163 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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 "pib-validator.hpp"
+#include "encoding/pib-common.hpp"
+#include "encoding/update-param.hpp"
+#include <set>
+#include <string>
+
+namespace ndn {
+namespace pib {
+
+using std::set;
+using std::string;
+
+PibValidator::PibValidator(const PibDb& db, size_t maxCacheSize)
+  : m_db(db)
+  , m_isMgmtReady(false)
+{
+  m_owner = m_db.getOwnerName();
+  m_mgmtCert = m_db.getMgmtCertificate();
+
+  if (!m_owner.empty() && m_mgmtCert != nullptr)
+    m_isMgmtReady = true;
+
+  m_mgmtChangeConnection =
+    const_cast<PibDb&>(m_db).mgmtCertificateChanged.connect([this] () {
+        m_owner = m_db.getOwnerName();
+        m_mgmtCert = m_db.getMgmtCertificate();
+        if (!m_owner.empty() && m_mgmtCert != nullptr)
+          m_isMgmtReady = true;
+      });
+
+  m_keyDeletedConnection =
+    const_cast<PibDb&>(m_db).keyDeleted.connect([this] (Name keyName) {
+        m_keyCache.erase(keyName);
+      });
+}
+
+PibValidator::~PibValidator()
+{
+}
+
+void
+PibValidator::checkPolicy(const Interest& interest,
+                          int nSteps,
+                          const OnInterestValidated& onValidated,
+                          const OnInterestValidationFailed& onValidationFailed,
+                          std::vector<shared_ptr<ValidationRequest>>& nextSteps)
+{
+  if (!m_isMgmtReady)
+    return onValidationFailed(interest.shared_from_this(), "PibDb is not initialized");
+
+  const Name& interestName = interest.getName();
+
+  if (interestName.size() != SIGNED_PIB_INTEREST_SIZE) {
+    return onValidationFailed(interest.shared_from_this(),
+                              "Interest is not signed: " + interest.getName().toUri());
+  }
+
+  // Check if the user exists in PIB
+  string user = interestName.get(OFFSET_USER).toUri();
+  if (user != m_owner)
+    return onValidationFailed(interest.shared_from_this(), "Wrong user: " + user);
+
+  // Verify signature
+  try {
+    Signature signature(interestName[OFFSET_SIG_INFO].blockFromValue(),
+                        interestName[OFFSET_SIG_VALUE].blockFromValue());
+    // KeyLocator is required to contain the name of signing certificate (w/o version)
+    if (!signature.hasKeyLocator())
+      return onValidationFailed(interest.shared_from_this(), "No valid KeyLocator");
+
+    const KeyLocator& keyLocator = signature.getKeyLocator();
+    if (keyLocator.getType() != KeyLocator::KeyLocator_Name)
+      return onValidationFailed(interest.shared_from_this(), "Key Locator is not a name");
+
+    // Check if PIB has the corresponding public key
+    shared_ptr<PublicKey> publicKey;
+
+    if (keyLocator.getName() == m_mgmtCert->getName().getPrefix(-1)) {
+      // the signing key is mgmt key.
+      publicKey = make_shared<PublicKey>(m_mgmtCert->getPublicKeyInfo());
+    }
+    else {
+      // the signing key is normal key.
+      Name keyName = IdentityCertificate::certificateNameToPublicKeyName(keyLocator.getName());
+
+      shared_ptr<PublicKey> key = m_keyCache.find(keyName);
+      if (key != nullptr) {
+        // the signing key is cached.
+        publicKey = key;
+      }
+      else {
+        // the signing key is not cached.
+        publicKey = m_db.getKey(keyName);
+        if (publicKey == nullptr) {
+          // the signing key does not exist in PIB.
+          return onValidationFailed(interest.shared_from_this(), "Public key is not trusted");
+        }
+        else {
+          // the signing key is retrieved from PIB.
+          m_keyCache.insert(keyName, publicKey);
+        }
+      }
+    }
+
+    if (verifySignature(interest, signature, *publicKey))
+      onValidated(interest.shared_from_this());
+    else
+      onValidationFailed(interest.shared_from_this(), "Cannot verify signature");
+
+  }
+  catch (KeyLocator::Error&) {
+    return onValidationFailed(interest.shared_from_this(),
+                              "No valid KeyLocator");
+  }
+  catch (Signature::Error&) {
+    return onValidationFailed(interest.shared_from_this(),
+                              "No valid signature");
+  }
+  catch (IdentityCertificate::Error&) {
+    return onValidationFailed(interest.shared_from_this(),
+                              "Cannot determine the signing key");
+  }
+  catch (tlv::Error&) {
+    return onValidationFailed(interest.shared_from_this(),
+                              "Cannot decode signature");
+  }
+}
+
+void
+PibValidator::checkPolicy(const Data& data,
+                          int nSteps,
+                          const OnDataValidated& onValidated,
+                          const OnDataValidationFailed& onValidationFailed,
+                          std::vector<shared_ptr<ValidationRequest>>& nextSteps)
+{
+  // Pib does not express any interest, therefor should not validate any data.
+  onValidationFailed(data.shared_from_this(),
+                     "PibValidator should not receive data packet");
+}
+
+} // namespace pib
+} // namespace ndn
diff --git a/tools/pib/pib-validator.hpp b/tools/pib/pib-validator.hpp
new file mode 100644
index 0000000..9d7a3e1
--- /dev/null
+++ b/tools/pib/pib-validator.hpp
@@ -0,0 +1,81 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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_PIB_PIB_VALIDATOR_HPP
+#define NDN_PIB_PIB_VALIDATOR_HPP
+
+#include "pib-db.hpp"
+#include "key-cache.hpp"
+#include <ndn-cxx/security/validator.hpp>
+#include <unordered_map>
+
+namespace ndn {
+namespace pib {
+
+
+/*
+ * @brief The validator to verify the command interests to PIB service
+ *
+ * @sa http://redmine.named-data.net/projects/ndn-cxx/wiki/PublicKey_Info_Base
+ */
+class PibValidator : public Validator
+{
+  struct UserKeyCache;
+public:
+  explicit
+  PibValidator(const PibDb& pibDb,
+               size_t maxCacheSize = 1000);
+
+  ~PibValidator();
+
+protected:
+  virtual void
+  checkPolicy(const Interest& interest,
+              int nSteps,
+              const OnInterestValidated& onValidated,
+              const OnInterestValidationFailed& onValidationFailed,
+              std::vector<shared_ptr<ValidationRequest>>& nextSteps);
+
+  virtual void
+  checkPolicy(const Data& data,
+              int nSteps,
+              const OnDataValidated& onValidated,
+              const OnDataValidationFailed& onValidationFailed,
+              std::vector<shared_ptr<ValidationRequest>>& nextSteps);
+
+private:
+
+  const PibDb& m_db;
+
+  bool m_isMgmtReady;
+  std::string m_owner;
+  shared_ptr<IdentityCertificate> m_mgmtCert;
+
+  KeyCache m_keyCache;
+
+  util::signal::ScopedConnection m_mgmtChangeConnection;
+  util::signal::ScopedConnection m_keyDeletedConnection;
+};
+
+} // namespace pib
+} // namespace ndn
+
+#endif // NDN_PIB_PIB_VALIDATOR_HPP
diff --git a/tools/pib/pib.cpp b/tools/pib/pib.cpp
new file mode 100644
index 0000000..d5a4e5c
--- /dev/null
+++ b/tools/pib/pib.cpp
@@ -0,0 +1,282 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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 "pib.hpp"
+
+#include "encoding/pib-encoding.hpp"
+#include <ndn-cxx/security/key-chain.hpp>
+#include <ndn-cxx/util/io.hpp>
+#include <ndn-cxx/util/crypto.hpp>
+#include <ndn-cxx/util/concepts.hpp>
+
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace pib {
+
+using std::string;
+using std::vector;
+using std::set;
+
+const Name Pib::PIB_PREFIX("/localhost/pib");
+const Name Pib::EMPTY_SIGNER_NAME;
+const name::Component Pib::MGMT_LABEL("mgmt");
+
+// \todo make this a static method in KeyChain
+static inline void
+signWithDigestSha256(Data& data)
+{
+  DigestSha256 sig;
+  data.setSignature(sig);
+  Block sigValue(tlv::SignatureValue,
+                 crypto::sha256(data.wireEncode().value(),
+                                data.wireEncode().value_size() -
+                                data.getSignature().getValue().size()));
+  data.setSignatureValue(sigValue);
+  data.wireEncode();
+}
+
+Pib::Pib(Face& face,
+         const std::string& dbDir,
+         const std::string& tpmLocator,
+         const std::string& owner)
+  : m_db(dbDir)
+  , m_tpm(nullptr)
+  , m_owner(owner)
+  , m_validator(m_db)
+  , m_face(face)
+  , m_certPublisher(m_face, m_db)
+  , m_getProcessor(m_db)
+  , m_defaultProcessor(m_db)
+  , m_listProcessor(m_db)
+  , m_updateProcessor(m_db, *this)
+  , m_deleteProcessor(m_db)
+{
+  if (!m_db.getOwnerName().empty() && m_db.getOwnerName() != owner)
+    throw Error("owner argument differs from OwnerName in database");
+
+  if (!m_db.getTpmLocator().empty() && m_db.getTpmLocator() != tpmLocator)
+    throw Error("tpmLocator argument differs from TpmLocator in database");
+
+  initializeTpm(tpmLocator);
+  initializeMgmtCert();
+  m_db.setTpmLocator(tpmLocator);
+
+  registerPrefix();
+}
+
+Pib::~Pib()
+{
+  m_face.unsetInterestFilter(m_pibMgmtFilterId);
+  m_face.unsetInterestFilter(m_pibGetFilterId);
+  m_face.unsetInterestFilter(m_pibDefaultFilterId);
+  m_face.unsetInterestFilter(m_pibListFilterId);
+  m_face.unsetInterestFilter(m_pibUpdateFilterId);
+  m_face.unsetInterestFilter(m_pibDeleteFilterId);
+
+  m_face.unsetInterestFilter(m_pibPrefixId);
+}
+
+void
+Pib::setMgmtCert(std::shared_ptr<IdentityCertificate> mgmtCert)
+{
+  if (mgmtCert != nullptr)
+    m_mgmtCert = mgmtCert;
+}
+
+void
+Pib::initializeTpm(const string& tpmLocator)
+{
+  m_tpm = KeyChain::createTpm(tpmLocator);
+}
+
+void
+Pib::initializeMgmtCert()
+{
+  shared_ptr<IdentityCertificate> mgmtCert = m_db.getMgmtCertificate();
+
+  if (mgmtCert == nullptr ||
+      !m_tpm->doesKeyExistInTpm(mgmtCert->getPublicKeyName(), KEY_CLASS_PRIVATE)) {
+    // If mgmt cert is set, or corresponding private key of the current mgmt cert is missing,
+    // generate new mgmt cert
+
+    // key name: /localhost/pib/[UserName]/mgmt/dsk-...
+    Name mgmtKeyName = PIB_PREFIX;
+    mgmtKeyName.append(m_owner).append(MGMT_LABEL);
+    std::ostringstream oss;
+    oss << "dsk-" << time::toUnixTimestamp(time::system_clock::now()).count();
+    mgmtKeyName.append(oss.str());
+
+    // self-sign pib root key
+    m_mgmtCert = prepareCertificate(mgmtKeyName, RsaKeyParams(),
+                                    time::system_clock::now(),
+                                    time::system_clock::now() + time::days(7300));
+
+    // update management certificate in database
+    m_db.updateMgmtCertificate(*m_mgmtCert);
+  }
+  else
+    m_mgmtCert = mgmtCert;
+}
+
+shared_ptr<IdentityCertificate>
+Pib::prepareCertificate(const Name& keyName, const KeyParams& keyParams,
+                        const time::system_clock::TimePoint& notBefore,
+                        const time::system_clock::TimePoint& notAfter,
+                        const Name& signerName)
+{
+  // Generate mgmt key
+  m_tpm->generateKeyPairInTpm(keyName, keyParams);
+  shared_ptr<PublicKey> publicKey = m_tpm->getPublicKeyFromTpm(keyName);
+
+  // Set mgmt cert
+  auto certificate = make_shared<IdentityCertificate>();
+  Name certName = keyName.getPrefix(-1);
+  certName.append("KEY").append(keyName.get(-1)).append("ID-CERT").appendVersion();
+  certificate->setName(certName);
+  certificate->setNotBefore(notBefore);
+  certificate->setNotAfter(notAfter);
+  certificate->setPublicKeyInfo(*publicKey);
+  CertificateSubjectDescription subjectName(oid::ATTRIBUTE_NAME, keyName.getPrefix(-1).toUri());
+  certificate->addSubjectDescription(subjectName);
+  certificate->encode();
+
+
+  Name signingKeyName;
+  KeyLocator keyLocator;
+  if (signerName == EMPTY_SIGNER_NAME) {
+    // Self-sign mgmt cert
+    keyLocator = KeyLocator(certificate->getName().getPrefix(-1));
+    signingKeyName = keyName;
+  }
+  else {
+    keyLocator = KeyLocator(signerName.getPrefix(-1));
+    signingKeyName = IdentityCertificate::certificateNameToPublicKeyName(signerName);
+  }
+
+  SignatureSha256WithRsa signature(keyLocator);
+  certificate->setSignature(signature);
+  EncodingBuffer encoder;
+  certificate->wireEncode(encoder, true);
+  Block signatureValue = m_tpm->signInTpm(encoder.buf(), encoder.size(),
+                                          signingKeyName, DIGEST_ALGORITHM_SHA256);
+  certificate->wireEncode(encoder, signatureValue);
+
+  return certificate;
+}
+
+void
+Pib::registerPrefix()
+{
+  // register pib prefix
+  Name pibPrefix = PIB_PREFIX;
+  pibPrefix.append(m_owner);
+  m_pibPrefixId =
+    m_face.registerPrefix(pibPrefix,
+                          [] (const Name& name) {},
+                          [] (const Name& name, const string& msg) {
+                            throw Error("cannot register pib prefix");
+                          });
+
+  // set interest filter for management certificate
+  m_pibMgmtFilterId =
+    m_face.setInterestFilter(Name(pibPrefix).append(MGMT_LABEL),
+                             [this] (const InterestFilter&, const Interest& interest) {
+                               if (m_mgmtCert != nullptr) {
+                                 m_face.put(*m_mgmtCert);
+                               }
+                             });
+
+  // set interest filter for get command
+  m_pibGetFilterId = registerProcessor(Name(pibPrefix).append(GetParam::VERB), m_getProcessor);
+
+  // set interest filter for default command
+  m_pibDefaultFilterId = registerProcessor(Name(pibPrefix).append(DefaultParam::VERB),
+                                           m_defaultProcessor);
+
+  // set interest filter for list command
+  m_pibListFilterId = registerProcessor(Name(pibPrefix).append(ListParam::VERB),
+                                        m_listProcessor);
+
+  // set interest filter for update command
+  m_pibUpdateFilterId = registerSignedCommandProcessor(Name(pibPrefix).append(UpdateParam::VERB),
+                                                       m_updateProcessor);
+
+  // set interest filter for delete command
+  m_pibDeleteFilterId = registerSignedCommandProcessor(Name(pibPrefix).append(DeleteParam::VERB),
+                                                       m_deleteProcessor);
+}
+
+template<class Processor>
+const InterestFilterId*
+Pib::registerProcessor(const Name& prefix, Processor& process)
+{
+  return m_face.setInterestFilter(prefix,
+                                  [&] (const InterestFilter&, const Interest& interest) {
+                                    processCommand(process, interest);
+                                  });
+}
+
+template<class Processor>
+const InterestFilterId*
+Pib::registerSignedCommandProcessor(const Name& prefix, Processor& process)
+{
+  const InterestFilterId* filterId =
+    m_face.setInterestFilter(prefix,
+      [&] (const InterestFilter&, const Interest& interest) {
+        m_validator.validate(interest,
+                             [&] (const shared_ptr<const Interest>& interest) {
+                               processCommand(process, *interest);
+                             },
+                             [] (const shared_ptr<const Interest>&, const string&) {});
+      });
+
+  return filterId;
+}
+
+template<class Processor>
+void
+Pib::processCommand(Processor& process, const Interest& interest)
+{
+  std::pair<bool, Block> result = process(interest);
+  if (result.first)
+    returnResult(Name(interest.getName()).appendVersion(),
+                 result.second);
+}
+
+void
+Pib::returnResult(const Name& dataName, const Block& content)
+{
+  shared_ptr<Data> data = make_shared<Data>(dataName);
+
+  data->setFreshnessPeriod(time::milliseconds::zero());
+  data->setContent(content);
+  signWithDigestSha256(*data);
+
+  // Put data into response cache
+  m_responseCache.insert(*data);
+
+  // Put data to face.
+  m_face.put(*data);
+}
+
+} // namespace pib
+} // namespace ndn
diff --git a/tools/pib/pib.hpp b/tools/pib/pib.hpp
new file mode 100644
index 0000000..3128191
--- /dev/null
+++ b/tools/pib/pib.hpp
@@ -0,0 +1,178 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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_PIB_PIB_HPP
+#define NDN_PIB_PIB_HPP
+
+#include "pib-db.hpp"
+#include "pib-validator.hpp"
+#include "cert-publisher.hpp"
+
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/util/in-memory-storage-persistent.hpp>
+
+#include "get-query-processor.hpp"
+#include "default-query-processor.hpp"
+#include "list-query-processor.hpp"
+#include "update-query-processor.hpp"
+#include "delete-query-processor.hpp"
+
+#include <ndn-cxx/security/sec-tpm.hpp>
+
+namespace ndn {
+namespace pib {
+
+/// @brief implements the PIB service
+class Pib : noncopyable
+{
+public:
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+  /**
+   * @brief Constructor
+   *
+   * @param face The face pib used to receive queries and serve certificates.
+   * @param dbDir Absolute path to the directory of the pib database.
+   * @param tpmLocator URI to locate the TPM for pib service.
+   * @param owner Owner of the pib database.
+   */
+  Pib(Face& face,
+      const std::string& dbDir,
+      const std::string& tpmLocator,
+      const std::string& owner);
+
+  ~Pib();
+
+  void
+  setMgmtCert(std::shared_ptr<IdentityCertificate> mgmtCert);
+
+PUBLIC_WITH_TESTS_ELSE_PROTECTED:
+  PibDb&
+  getDb()
+  {
+    return m_db;
+  }
+
+  SecTpm&
+  getTpm()
+  {
+    return *m_tpm;
+  }
+
+  util::InMemoryStoragePersistent&
+  getResponseCache()
+  {
+    return m_responseCache;
+  }
+
+  const std::string&
+  getOwner() const
+  {
+    return m_owner;
+  }
+
+  const IdentityCertificate&
+  getMgmtCert() const
+  {
+    BOOST_ASSERT(m_mgmtCert != nullptr);
+    return *m_mgmtCert;
+  }
+
+private: // initialization
+  /// @brief initialize the PIB's own TPM.
+  void
+  initializeTpm(const std::string& tpmLocator);
+
+  /// @brief initialize management certificate
+  void
+  initializeMgmtCert();
+
+  std::shared_ptr<IdentityCertificate>
+  prepareCertificate(const Name& keyName, const KeyParams& keyParams,
+                     const time::system_clock::TimePoint& notBefore,
+                     const time::system_clock::TimePoint& notAfter,
+                     const Name& signerName = EMPTY_SIGNER_NAME);
+
+  /// @brief register prefix for PIB query and management certificate
+  void
+  registerPrefix();
+
+  template<class Processor>
+  const InterestFilterId*
+  registerProcessor(const Name& prefix, Processor& process);
+
+  template<class Processor>
+  const InterestFilterId*
+  registerSignedCommandProcessor(const Name& prefix, Processor& process);
+
+  template<class Processor>
+  void
+  processCommand(Processor& process, const Interest& interest);
+
+  void
+  returnResult(const Name& dataName, const Block& content);
+
+private:
+
+  static const Name EMPTY_SIGNER_NAME;
+  static const Name PIB_PREFIX;
+  static const name::Component MGMT_LABEL;
+
+  PibDb m_db;
+  std::unique_ptr<SecTpm> m_tpm;
+  std::string m_owner;
+  std::shared_ptr<IdentityCertificate> m_mgmtCert;
+
+  PibValidator m_validator;
+
+  Face& m_face;
+  CertPublisher m_certPublisher;
+  util::InMemoryStoragePersistent m_responseCache;
+
+  const RegisteredPrefixId* m_pibPrefixId;
+  const InterestFilterId* m_pibMgmtFilterId;
+  const InterestFilterId* m_pibGetFilterId;
+  const InterestFilterId* m_pibDefaultFilterId;
+  const InterestFilterId* m_pibListFilterId;
+  const InterestFilterId* m_pibUpdateFilterId;
+  const InterestFilterId* m_pibDeleteFilterId;
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+
+  GetQueryProcessor m_getProcessor;
+  DefaultQueryProcessor m_defaultProcessor;
+  ListQueryProcessor m_listProcessor;
+  UpdateQueryProcessor m_updateProcessor;
+  DeleteQueryProcessor m_deleteProcessor;
+};
+
+} // namespace pib
+} // namespace ndn
+
+#endif // NDN_PIB_PIB_HPP
diff --git a/tools/pib/response-cache.cpp b/tools/pib/response-cache.cpp
new file mode 100644
index 0000000..57c211e
--- /dev/null
+++ b/tools/pib/response-cache.cpp
@@ -0,0 +1,73 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * 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.
+ */
+
+#include "response-cache.hpp"
+
+namespace ndn {
+namespace pib {
+
+using std::map;
+
+ResponseCache::ResponseCache()
+{
+}
+
+
+shared_ptr<const Data>
+ResponseCache::find(const Name& dataName, bool hasVersion) const
+{
+  if (!hasVersion) {
+    Storage::const_iterator it = m_storage.find(dataName);
+    if (it != m_storage.end())
+      return it->second;
+    else
+      return shared_ptr<const Data>();
+  }
+  else {
+    Storage::const_iterator it = m_storage.find(dataName.getPrefix(-1));
+    if (it != m_storage.end() && it->second->getName() == dataName)
+      return it->second;
+    else
+      return shared_ptr<const Data>();
+  }
+}
+
+void
+ResponseCache::insert(const Data& data)
+{
+  data.getName().at(-1).toVersion(); // ensures last component is version
+  m_storage[data.getName().getPrefix(-1)] = data.shared_from_this();
+}
+
+void
+ResponseCache::erase(const Name& dataNameWithoutVersion)
+{
+  m_storage.erase(dataNameWithoutVersion);
+}
+
+void
+ResponseCache::clear()
+{
+  m_storage.clear();
+}
+
+} // namespace pib
+} // namespace ndn
diff --git a/tools/pib/response-cache.hpp b/tools/pib/response-cache.hpp
new file mode 100644
index 0000000..40edd04
--- /dev/null
+++ b/tools/pib/response-cache.hpp
@@ -0,0 +1,74 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * 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 NDN_PIB_RESPONSE_CACHE_HPP
+#define NDN_PIB_RESPONSE_CACHE_HPP
+
+#include <ndn-cxx/data.hpp>
+#include <map>
+
+
+namespace ndn {
+namespace pib {
+
+/**
+ * @brief ResponseCache is an abstraction of a cache of response made before
+ *
+ * ResponseCache is used to reduce the number of PibDb lookup and Data signing
+ * operations.
+ *
+ * Eventually, it should be replaced by a formal application level cache. This
+ * one is only a temporary module and is used for test.
+ */
+class ResponseCache : noncopyable
+{
+public:
+  ResponseCache();
+
+  shared_ptr<const Data>
+  find(const Name& dataName, bool hasVersion = false) const;
+
+  /**
+   * @brief Insert a data packet into cache
+   *
+   * Name of the inserted data must end with a version component
+   *
+   * @param data Data to insert. It MUST have been created with make_shared.
+   */
+  void
+  insert(const Data& data);
+
+  void
+  erase(const Name& dataNameWithoutVersion);
+
+  void
+  clear();
+
+private:
+  typedef std::map<Name, shared_ptr<const Data> > Storage;
+
+  Storage m_storage;
+};
+
+} // namespace ndn
+} // namespace pib
+
+#endif // NDN_PIB_RESPONSE_CACHE_HPP
diff --git a/tools/pib/update-query-processor.cpp b/tools/pib/update-query-processor.cpp
new file mode 100644
index 0000000..4ab853d
--- /dev/null
+++ b/tools/pib/update-query-processor.cpp
@@ -0,0 +1,267 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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 "update-query-processor.hpp"
+#include "encoding/pib-encoding.hpp"
+#include "pib.hpp"
+
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace pib {
+
+using std::string;
+
+const size_t UpdateQueryProcessor::UPDATE_QUERY_LENGTH = 9;
+const Name UpdateQueryProcessor::PIB_PREFIX("/localhost/pib");
+
+UpdateQueryProcessor::UpdateQueryProcessor(PibDb& db, Pib& pib)
+  : m_db(db)
+  , m_pib(pib)
+{
+}
+
+std::pair<bool, Block>
+UpdateQueryProcessor::operator()(const Interest& interest)
+{
+  const Name& interestName = interest.getName();
+
+  // handle pib query: /localhost/pib/[UserName]/update/param/<signed_interest_related_components>
+  if (interestName.size() != UPDATE_QUERY_LENGTH) {
+    // malformed interest, discard
+    return std::make_pair(false, Block());
+  }
+
+  try {
+    UpdateParam param;
+    param.wireDecode(interestName.get(OFFSET_PARAM).blockFromValue());
+
+    SignatureInfo sigInfo;
+    sigInfo.wireDecode(interestName.get(OFFSET_SIG_INFO).blockFromValue());
+
+    // sigInfo must have KeyLocator.Name if the interest passed validation.
+    Name signerName;
+    signerName = sigInfo.getKeyLocator().getName();
+
+    switch (param.getEntityType()) {
+    case tlv::pib::User:
+      return processUpdateUserQuery(param, signerName);
+    case tlv::pib::Identity:
+      return processUpdateIdQuery(param, signerName);
+    case tlv::pib::PublicKey:
+      return processUpdateKeyQuery(param, signerName);
+    case tlv::pib::Certificate:
+      return processUpdateCertQuery(param, signerName);
+    default:
+      {
+        PibError error(ERR_WRONG_PARAM,
+                       "entity type is not supported: " +
+                       boost::lexical_cast<string>(param.getEntityType()));
+        return std::make_pair(true, error.wireEncode());
+      }
+    }
+  }
+  catch (const PibDb::Error& e) {
+    PibError error(ERR_INTERNAL_ERROR, e.what());
+    return std::make_pair(true, error.wireEncode());
+  }
+  catch (const tlv::Error& e) {
+    PibError error(ERR_WRONG_PARAM, "error in parsing param: " + string(e.what()));
+    return std::make_pair(true, error.wireEncode());
+  }
+}
+
+std::pair<bool, Block>
+UpdateQueryProcessor::processUpdateUserQuery(const UpdateParam& param, const Name& signerName)
+{
+  Name expectedId = m_db.getMgmtCertificate()->getPublicKeyName().getPrefix(-1);
+  Name targetId = param.getUser().getMgmtCert().getPublicKeyName().getPrefix(-1);
+  Name signerId = IdentityCertificate::certificateNameToPublicKeyName(signerName).getPrefix(-1);
+
+  if (expectedId == targetId && expectedId == signerId) {
+    m_db.updateMgmtCertificate(param.getUser().getMgmtCert());
+    m_pib.setMgmtCert(make_shared<IdentityCertificate>(param.getUser().getMgmtCert()));
+    return std::make_pair(true, PibError(ERR_SUCCESS).wireEncode());
+  }
+
+  PibError error(ERR_WRONG_PARAM, "not allowed to update user");
+  return std::make_pair(true, error.wireEncode());
+}
+
+std::pair<bool, Block>
+UpdateQueryProcessor::processUpdateIdQuery(const UpdateParam& param, const Name& signerName)
+{
+  const Name& identity = param.getIdentity().getIdentity();
+  if (!isUpdateAllowed(TYPE_ID, identity, signerName, param.getDefaultOpt())) {
+    PibError error(ERR_WRONG_SIGNER, "signer is not trusted for this command");
+    return std::make_pair(true, error.wireEncode());
+  }
+
+  // add the identity
+  m_db.addIdentity(identity);
+
+  // set the identity as user default if it is requested.
+  const pib::DefaultOpt& defaultOpt = param.getDefaultOpt();
+  if (DEFAULT_OPT_USER_MASK & defaultOpt) {
+    m_db.setDefaultIdentity(identity);
+  }
+
+  return std::make_pair(true, PibError(ERR_SUCCESS).wireEncode());
+}
+
+std::pair<bool, Block>
+UpdateQueryProcessor::processUpdateKeyQuery(const UpdateParam& param, const Name& signerName)
+{
+  const Name& keyName = param.getPublicKey().getKeyName();
+  if (!isUpdateAllowed(TYPE_KEY, keyName, signerName, param.getDefaultOpt())) {
+    PibError error(ERR_WRONG_SIGNER, "signer is not trusted for this command");
+    return std::make_pair(true, error.wireEncode());
+  }
+
+  Name identity = keyName.getPrefix(-1);
+
+  // add the key
+  m_db.addKey(keyName, param.getPublicKey().getPublicKey());
+
+  const pib::DefaultOpt& defaultOpt = param.getDefaultOpt();
+  if (DEFAULT_OPT_ID_MASK & defaultOpt) // set the key as identity default if requested.
+    m_db.setDefaultKeyNameOfIdentity(keyName);
+  if (DEFAULT_OPT_USER_MASK & defaultOpt) // set the identity as user default if requested.
+    m_db.setDefaultIdentity(identity);
+
+  return std::make_pair(true, PibError(ERR_SUCCESS).wireEncode());
+}
+
+std::pair<bool, Block>
+UpdateQueryProcessor::processUpdateCertQuery(const UpdateParam& param, const Name& signerName)
+{
+  const IdentityCertificate& cert = param.getCertificate().getCertificate();
+  const Name& certName = cert.getName();
+  if (!isUpdateAllowed(TYPE_CERT, certName, signerName, param.getDefaultOpt())) {
+    PibError error(ERR_WRONG_SIGNER, "signer is not trusted for this command");
+    return std::make_pair(true, error.wireEncode());
+  }
+
+  const Name& keyName = cert.getPublicKeyName();
+  Name identity = keyName.getPrefix(-1);
+
+  // add certificate
+  m_db.addCertificate(cert);
+
+  const pib::DefaultOpt& defaultOpt = param.getDefaultOpt();
+  if (DEFAULT_OPT_KEY_MASK & defaultOpt) // set the cert as key default if requested.
+    m_db.setDefaultCertNameOfKey(certName);
+  if (DEFAULT_OPT_ID_MASK & defaultOpt) // set the key as identity default if requested.
+    m_db.setDefaultKeyNameOfIdentity(keyName);
+  if (DEFAULT_OPT_USER_MASK & defaultOpt) // set the identity as user default if requested.
+    m_db.setDefaultIdentity(identity);
+
+  return std::make_pair(true, PibError(ERR_SUCCESS).wireEncode());
+}
+
+bool
+UpdateQueryProcessor::isUpdateAllowed(const pib::Type targetType,
+                                      const Name& targetName,
+                                      const Name& signer,
+                                      const pib::DefaultOpt defaultOpt) const
+{
+  // Any identity with prefix /localhost/pib is reserved. Any operation with a targetName under
+  // that prefix will be rejected.
+  if (PIB_PREFIX.isPrefixOf(targetName))
+    return false;
+
+  // A request is wrong if
+  // 1) it wants to change the default setting of an identity, but the target is not key nor cert
+  // 2) it wants to change the default setting of a key, but the target is not cert
+  if ((defaultOpt == DEFAULT_OPT_ID && (targetType != TYPE_KEY && targetType != TYPE_CERT)) ||
+      (defaultOpt == DEFAULT_OPT_KEY && targetType != TYPE_CERT))
+    return false;
+
+
+  // Rules for other items:
+  //
+  //    signer          | adding privilege         | default setting privilege
+  //    ================+==========================+============================================
+  //    local mgmt key  | any id, key, and cert    | the default id,
+  //                    |                          | the default key of any id,
+  //                    |                          | the default cert of any key of any id
+  //    ----------------+--------------------------+--------------------------------------------
+  //    default key of  | any id, key, and cert    | the default key of the id and its sub ids,
+  //    an id           | under the id's namespace | the default cert of any key of the id
+  //                    |                          | and its sub ids
+  //    ----------------+--------------------------+--------------------------------------------
+  //    non-default key | any cert of the key      | the default cert of the key
+  //    of an id
+
+  const Name& signerKeyName = IdentityCertificate::certificateNameToPublicKeyName(signer);
+  const Name& signerId = signerKeyName.getPrefix(-1);
+
+  bool hasSignerDefaultKey = true;
+  Name signerDefaultKeyName;
+  try {
+    signerDefaultKeyName = m_db.getDefaultKeyNameOfIdentity(signerId);
+  }
+  catch (PibDb::Error&) {
+    hasSignerDefaultKey = false;
+  }
+
+  Name mgmtCertName;
+  try {
+    mgmtCertName = m_db.getMgmtCertificate()->getName().getPrefix(-1);
+  }
+  catch (PibDb::Error&) {
+    return false;
+  }
+
+  if (signer == mgmtCertName) {
+    // signer is current management key, anything is allowed.
+    return true;
+  }
+  else if (hasSignerDefaultKey && signerDefaultKeyName == signerKeyName) {
+    // signer is an identity's default key
+    if (!signerId.isPrefixOf(targetName))
+      return false;
+
+    // check default setting
+    // user default setting is not allowed
+    if (defaultOpt == DEFAULT_OPT_USER)
+      return false;
+    else
+      return true;
+  }
+  else {
+    // non-default key
+    if (targetType != TYPE_CERT)
+      return false;
+
+    // check if it is for the key's cert
+    if (IdentityCertificate::certificateNameToPublicKeyName(targetName) != signerKeyName)
+      return false;
+
+    if (defaultOpt == DEFAULT_OPT_USER || defaultOpt == DEFAULT_OPT_ID)
+      return false;
+    else
+      return true;
+  }
+}
+
+} // namespace pib
+} // namespace ndn
diff --git a/tools/pib/update-query-processor.hpp b/tools/pib/update-query-processor.hpp
new file mode 100644
index 0000000..6c0655b
--- /dev/null
+++ b/tools/pib/update-query-processor.hpp
@@ -0,0 +1,98 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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_PIB_UPDATE_QUERY_PROCESSOR_HPP
+#define NDN_PIB_UPDATE_QUERY_PROCESSOR_HPP
+
+#include "pib-db.hpp"
+#include "encoding/update-param.hpp"
+#include <ndn-cxx/interest.hpp>
+#include <utility>
+
+namespace ndn {
+namespace pib {
+
+class Pib;
+
+/// @brief Processing unit for PIB update query
+class UpdateQueryProcessor : noncopyable
+{
+public:
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+  /**
+   * @brief Constructor
+   *
+   * @param db The pib database.
+   */
+  UpdateQueryProcessor(PibDb& db, Pib& pib);
+
+  std::pair<bool, Block>
+  operator()(const Interest& interest);
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  /**
+   * @brief Determine if an update command is allowed.
+   *
+   * @param targetType The type of the target that will be updated.
+   * @param targetName The name of the target that will be updated.
+   * @param signer     The name of the command signer.
+   * @param defaultOpt The default settings that is requested to update.
+   */
+  bool
+  isUpdateAllowed(const pib::Type targetType,
+                  const Name& targetName,
+                  const Name& signer,
+                  const pib::DefaultOpt defaultOpt) const;
+
+private:
+  std::pair<bool, Block>
+  processUpdateUserQuery(const UpdateParam& param, const Name& signerName);
+
+  std::pair<bool, Block>
+  processUpdateIdQuery(const UpdateParam& param, const Name& signerName);
+
+  std::pair<bool, Block>
+  processUpdateKeyQuery(const UpdateParam& param, const Name& signerName);
+
+  std::pair<bool, Block>
+  processUpdateCertQuery(const UpdateParam& param, const Name& signerName);
+
+private:
+  static const size_t UPDATE_QUERY_LENGTH;
+  static const Name PIB_PREFIX;
+
+  PibDb&  m_db;
+  Pib& m_pib;
+};
+
+} // namespace pib
+} // namespace ndn
+
+#endif // NDN_PIB_UPDATE_QUERY_PROCESSOR_HPP
diff --git a/tools/pib/wscript b/tools/pib/wscript
new file mode 100644
index 0000000..7a40d5a
--- /dev/null
+++ b/tools/pib/wscript
@@ -0,0 +1,19 @@
+# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+top = '../..'
+
+def build(bld):
+
+    bld(features=['cxx'],
+        name="pib-objects",
+        target="pib-objects",
+        source=bld.path.ant_glob('**/*.cpp', excl=['ndn-pib.cpp']),
+        use='core-objects',
+        install_path=None,
+        )
+
+
+    bld(features=['cxx', 'cxxprogram'],
+        target='ndn-pib',
+        source='ndn-pib.cpp',
+        use='pib-objects',
+        )