security: implement PibSqlite3 based on PibImpl

Change-Id: I47a61cf8ebe5f76d5ca492db52c2f4f2f5ac3c9b
Refs: #2807
diff --git a/src/security/pib-sqlite3.cpp b/src/security/pib-sqlite3.cpp
new file mode 100644
index 0000000..3a95b5d
--- /dev/null
+++ b/src/security/pib-sqlite3.cpp
@@ -0,0 +1,557 @@
+/* -*- 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-sqlite3.hpp"
+
+#include "common.hpp"
+#include "pib.hpp"
+#include "util/sqlite3-statement.hpp"
+
+#include <sqlite3.h>
+#include <boost/filesystem.hpp>
+#include <boost/algorithm/string.hpp>
+
+namespace ndn {
+namespace security {
+
+using std::string;
+using util::Sqlite3Statement;
+
+static const string INITIALIZATION =
+  "CREATE TABLE IF NOT EXISTS                    \n"
+  "  tpmInfo(                                    \n"
+  "    tpm_locator           BLOB                \n"
+  "  );                                          \n"
+  "                                              \n"
+  "CREATE TRIGGER IF NOT EXISTS                  \n"
+  "  tpm_update_trigger                          \n"
+  "  BEFORE UPDATE ON tpmInfo                    \n"
+  "  WHEN NEW.tpm_locator!=OLD.tpm_locator       \n"
+  "  BEGIN                                       \n"
+  "    DELETE FROM certificates;                 \n"
+  "    DELETE FROM keys;                         \n"
+  "    DELETE FROM identities;                   \n"
+  "  END;                                        \n"
+  "                                              \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"
+  "                                              \n"
+  "CREATE UNIQUE INDEX IF NOT EXISTS             \n"
+  "  identityIndex ON identities(identity);      \n"
+  "                                              \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"
+  "                                              \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"
+  "                                              \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"
+  "                                              \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"
+  "                                              \n"
+  "CREATE UNIQUE INDEX IF NOT EXISTS             \n"
+  "  keyIndex ON keys(key_name);                 \n"
+  "                                              \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"
+  "                                              \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"
+  "                                              \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"
+  "                                              \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"
+  "                                              \n"
+  "CREATE UNIQUE INDEX IF NOT EXISTS             \n"
+  "  certIndex ON certificates(certificate_name);\n"
+  "                                              \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"
+  "                                              \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"
+  "                                              \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";
+
+static Name
+getKeyName(const Name& identity, const name::Component& keyId)
+{
+  Name keyName = identity;
+  keyName.append(keyId);
+  return keyName;
+}
+
+PibSqlite3::PibSqlite3(const string& dir)
+{
+  // Determine the path of PIB DB
+  boost::filesystem::path actualDir;
+  if (dir == "") {
+    if (getenv("HOME") == nullptr)
+      throw PibImpl::Error("Environment variable HOME is not set");
+
+    actualDir = boost::filesystem::path(getenv("HOME")) / ".ndn";
+    boost::filesystem::create_directories(actualDir);
+  }
+  else {
+    actualDir = boost::filesystem::path(dir);
+    boost::filesystem::create_directories(actualDir);
+  }
+  // Open PIB
+  int result = sqlite3_open_v2((actualDir / "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 PibImpl::Error("PIB DB cannot be opened/created: " + dir);
+
+
+  // enable foreign key
+  sqlite3_exec(m_database, "PRAGMA foreign_keys=ON", nullptr, nullptr, nullptr);
+
+  // initialize PIB 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 PibImpl::Error("PIB DB cannot be initialized");
+  }
+}
+
+PibSqlite3::~PibSqlite3()
+{
+  sqlite3_close(m_database);
+}
+
+void
+PibSqlite3::setTpmLocator(const std::string& tpmLocator)
+{
+  Sqlite3Statement statement(m_database, "UPDATE tpmInfo SET tpm_locator=?");
+  statement.bind(1, tpmLocator, SQLITE_TRANSIENT);
+  statement.step();
+
+  // no row is updated, tpm_locator does not exist, insert it directly
+  if (0 == sqlite3_changes(m_database)) {
+    Sqlite3Statement insertStatement(m_database, "INSERT INTO tpmInfo (tpm_locator) values (?)");
+    insertStatement.bind(1, tpmLocator, SQLITE_TRANSIENT);
+    insertStatement.step();
+  }
+}
+
+std::string
+PibSqlite3::getTpmLocator() const
+{
+  Sqlite3Statement statement(m_database, "SELECT tpm_locator FROM tpmInfo");
+  int res = statement.step();
+
+  string tpmLocator;
+  if (res == SQLITE_ROW)
+    return statement.getString(0);
+  else
+    throw Pib::Error("TPM info does not exist");
+}
+
+bool
+PibSqlite3::hasIdentity(const Name& identity) const
+{
+  Sqlite3Statement statement(m_database, "SELECT id FROM identities WHERE identity=?");
+  statement.bind(1, identity.wireEncode(), SQLITE_TRANSIENT);
+  return (statement.step() == SQLITE_ROW);
+}
+
+void
+PibSqlite3::addIdentity(const Name& identity)
+{
+  Sqlite3Statement statement(m_database, "INSERT INTO identities (identity) values (?)");
+  statement.bind(1, identity.wireEncode(), SQLITE_TRANSIENT);
+  statement.step();
+}
+
+void
+PibSqlite3::removeIdentity(const Name& identity)
+{
+  Sqlite3Statement statement(m_database, "DELETE FROM identities WHERE identity=?");
+  statement.bind(1, identity.wireEncode(), SQLITE_TRANSIENT);
+  statement.step();
+}
+
+std::set<Name>
+PibSqlite3::getIdentities() const
+{
+  std::set<Name> identities;
+  Sqlite3Statement statement(m_database, "SELECT identity FROM identities");
+
+  while (statement.step() == SQLITE_ROW)
+    identities.insert(Name(statement.getBlock(0)));
+
+  return identities;
+}
+
+void
+PibSqlite3::setDefaultIdentity(const Name& identityName)
+{
+  Sqlite3Statement statement(m_database, "UPDATE identities SET is_default=1 WHERE identity=?");
+  statement.bind(1, identityName.wireEncode(), SQLITE_TRANSIENT);
+  statement.step();
+}
+
+Name
+PibSqlite3::getDefaultIdentity() const
+{
+  Sqlite3Statement statement(m_database, "SELECT identity FROM identities WHERE is_default=1");
+
+  if (statement.step() == SQLITE_ROW)
+    return Name(statement.getBlock(0));
+  else
+    throw Pib::Error("No default identity");
+}
+
+bool
+PibSqlite3::hasKey(const Name& identity, const name::Component& keyId) const
+{
+  Name keyName = getKeyName(identity, keyId);
+
+  Sqlite3Statement statement(m_database, "SELECT id FROM keys WHERE key_name=?");
+  statement.bind(1, keyName.wireEncode(), SQLITE_TRANSIENT);
+
+  return (statement.step() == SQLITE_ROW);
+}
+
+void
+PibSqlite3::addKey(const Name& identity, const name::Component& keyId, const PublicKey& publicKey)
+{
+  if (hasKey(identity, keyId)) {
+    return;
+  }
+
+  // ensure identity exists
+  addIdentity(identity);
+
+  // add key
+  Name keyName = getKeyName(identity, keyId);
+
+  Sqlite3Statement statement(m_database,
+                             "INSERT INTO keys (identity_id, key_name, key_type, key_bits) "
+                             "VALUES ((SELECT id FROM identities WHERE identity=?), ?, ?, ?)");
+  statement.bind(1, identity.wireEncode(), SQLITE_TRANSIENT);
+  statement.bind(2, keyName.wireEncode(), SQLITE_TRANSIENT);
+  statement.bind(3, publicKey.getKeyType());
+  statement.bind(4, publicKey.get().buf(), publicKey.get().size(), SQLITE_STATIC);
+  statement.step();
+}
+
+void
+PibSqlite3::removeKey(const Name& identity, const name::Component& keyId)
+{
+  Name keyName = getKeyName(identity, keyId);
+
+  Sqlite3Statement statement(m_database, "DELETE FROM keys WHERE key_name=?");
+  statement.bind(1, keyName.wireEncode(), SQLITE_TRANSIENT);
+  statement.step();
+}
+
+PublicKey
+PibSqlite3::getKeyBits(const Name& identity, const name::Component& keyId) const
+{
+  Name keyName = getKeyName(identity, keyId);
+
+  Sqlite3Statement statement(m_database, "SELECT key_bits FROM keys WHERE key_name=?");
+  statement.bind(1, keyName.wireEncode(), SQLITE_TRANSIENT);
+
+  if (statement.step() == SQLITE_ROW)
+    return PublicKey(statement.getBlob(0), statement.getSize(0));
+  else
+    throw Pib::Error("Key does not exist");
+}
+
+std::set<name::Component>
+PibSqlite3::getKeysOfIdentity(const Name& identity) const
+{
+  std::set<name::Component> keyNames;
+
+  Sqlite3Statement statement(m_database,
+                             "SELECT key_name "
+                             "FROM keys JOIN identities ON keys.identity_id=identities.id "
+                             "WHERE identities.identity=?");
+  statement.bind(1, identity.wireEncode(), SQLITE_TRANSIENT);
+
+  while (statement.step() == SQLITE_ROW) {
+    Name keyName(statement.getBlock(0));
+    keyNames.insert(keyName.get(-1));
+  }
+
+  return keyNames;
+}
+
+void
+PibSqlite3::setDefaultKeyOfIdentity(const Name& identity, const name::Component& keyId)
+{
+  Name keyName = getKeyName(identity, keyId);
+
+  if (!hasKey(identity, keyId)) {
+    throw Pib::Error("No such key");
+  }
+
+  Sqlite3Statement statement(m_database, "UPDATE keys SET is_default=1 WHERE key_name=?");
+  statement.bind(1, keyName.wireEncode(), SQLITE_TRANSIENT);
+  statement.step();
+}
+
+name::Component
+PibSqlite3::getDefaultKeyOfIdentity(const Name& identity) const
+{
+  if (!hasIdentity(identity)) {
+    throw Pib::Error("Identity does not exist");
+  }
+
+  Sqlite3Statement statement(m_database,
+                             "SELECT key_name "
+                             "FROM keys JOIN identities ON keys.identity_id=identities.id "
+                             "WHERE identities.identity=? AND keys.is_default=1");
+  statement.bind(1, identity.wireEncode(), SQLITE_TRANSIENT);
+
+  if (statement.step() == SQLITE_ROW) {
+    Name keyName(statement.getBlock(0));
+    return keyName.get(-1);
+  }
+  else
+    throw Pib::Error("No default key");
+}
+
+bool
+PibSqlite3::hasCertificate(const Name& certName) const
+{
+  Sqlite3Statement statement(m_database, "SELECT id FROM certificates WHERE certificate_name=?");
+  statement.bind(1, certName.wireEncode(), SQLITE_TRANSIENT);
+  return (statement.step() == SQLITE_ROW);
+}
+
+void
+PibSqlite3::addCertificate(const IdentityCertificate& certificate)
+{
+  const Name& certName = certificate.getName();
+  const Name& keyName = certificate.getPublicKeyName();
+
+  name::Component keyId = keyName.get(-1);
+  Name identityName = keyName.getPrefix(-1);
+
+  // ensure key exists
+  addKey(identityName, keyId, certificate.getPublicKeyInfo());
+
+  Sqlite3Statement statement(m_database,
+                             "INSERT INTO certificates "
+                             "(key_id, certificate_name, certificate_data) "
+                             "VALUES ((SELECT id FROM keys WHERE key_name=?), ?, ?)");
+  statement.bind(1, keyName.wireEncode(), SQLITE_TRANSIENT);
+  statement.bind(2, certName.wireEncode(), SQLITE_TRANSIENT);
+  statement.bind(3, certificate.wireEncode(), SQLITE_STATIC);
+  statement.step();
+}
+
+void
+PibSqlite3::removeCertificate(const Name& certName)
+{
+  Sqlite3Statement statement(m_database, "DELETE FROM certificates WHERE certificate_name=?");
+  statement.bind(1, certName.wireEncode(), SQLITE_TRANSIENT);
+  statement.step();
+}
+
+IdentityCertificate
+PibSqlite3::getCertificate(const Name& certName) const
+{
+  Sqlite3Statement statement(m_database,
+                             "SELECT certificate_data FROM certificates WHERE certificate_name=?");
+  statement.bind(1, certName.wireEncode(), SQLITE_TRANSIENT);
+
+  if (statement.step() == SQLITE_ROW)
+    return IdentityCertificate(statement.getBlock(0));
+  else
+    throw Pib::Error("Certificate does not exit");
+}
+
+std::set<Name>
+PibSqlite3::getCertificatesOfKey(const Name& identity, const name::Component& keyId) const
+{
+  std::set<Name> certNames;
+
+  Name keyName = getKeyName(identity, keyId);
+
+  Sqlite3Statement statement(m_database,
+                             "SELECT certificate_name "
+                             "FROM certificates JOIN keys ON certificates.key_id=keys.id "
+                             "WHERE keys.key_name=?");
+  statement.bind(1, keyName.wireEncode(), SQLITE_TRANSIENT);
+
+  while (statement.step() == SQLITE_ROW)
+    certNames.insert(Name(statement.getBlock(0)));
+
+  return certNames;
+}
+
+void
+PibSqlite3::setDefaultCertificateOfKey(const Name& identity, const name::Component& keyId,
+                                       const Name& certName)
+{
+  if (!hasCertificate(certName)) {
+    throw Pib::Error("Certificate does not exist");
+  }
+
+  Sqlite3Statement statement(m_database,
+                             "UPDATE certificates SET is_default=1 WHERE certificate_name=?");
+  statement.bind(1, certName.wireEncode(), SQLITE_TRANSIENT);
+  statement.step();
+}
+
+IdentityCertificate
+PibSqlite3::getDefaultCertificateOfKey(const Name& identity, const name::Component& keyId) const
+{
+  Name keyName = getKeyName(identity, keyId);
+
+  Sqlite3Statement statement(m_database,
+                             "SELECT certificate_data "
+                             "FROM certificates JOIN keys ON certificates.key_id=keys.id "
+                             "WHERE certificates.is_default=1 AND keys.key_name=?");
+  statement.bind(1, keyName.wireEncode(), SQLITE_TRANSIENT);
+
+  if (statement.step() == SQLITE_ROW)
+    return IdentityCertificate(statement.getBlock(0));
+  else
+    throw Pib::Error("Certificate does not exit");
+}
+
+} // namespace security
+} // namespace ndn
diff --git a/src/security/pib-sqlite3.hpp b/src/security/pib-sqlite3.hpp
new file mode 100644
index 0000000..4e521eb
--- /dev/null
+++ b/src/security/pib-sqlite3.hpp
@@ -0,0 +1,306 @@
+/* -*- 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_SECURITTY_PIB_SQLITE3_HPP
+#define NDN_SECURITTY_PIB_SQLITE3_HPP
+
+#include "pib-impl.hpp"
+
+struct sqlite3;
+
+namespace ndn {
+namespace security {
+
+/**
+ * @brief Pib backend implementation based on SQLite3 database
+ *
+ * All the contents in Pib are stored in a SQLite3 database file.
+ * This backend provides more persistent storage than PibMemory.
+ */
+class PibSqlite3 : public PibImpl
+{
+public:
+  /**
+   * @brief Constructor of PibSqlite3
+   *
+   * This method will create a SQLite3 database file under the directory @p dir.
+   * If the directory does not exist, it will be created automatically.
+   * It assumes that the directory does not contain a PIB database of an older version,
+   * It is user's responsibility to update the older version database or remove the database.
+   *
+   * @param dir The directory where the database file is located. By default, it points to the
+   *        $HOME/.ndn directory.
+   * @throws PibImpl::Error when initialization fails.
+   */
+  explicit
+  PibSqlite3(const std::string& dir = "");
+
+  /**
+   * @brief Destruct and cleanup internal state
+   */
+  ~PibSqlite3();
+
+public: // TpmLocator management
+
+  /**
+   * @brief Set the corresponding TPM information to @p tpmLocator.
+   *
+   * If the provided @p tpmLocator is different from the existing one, the
+   * content in PIB will be cleaned up, otherwise nothing will be changed.
+   *
+   * @param tmpLocator The name for the new tmpLocator
+   */
+  virtual void
+  setTpmLocator(const std::string& tpmLocator) NDN_CXX_DECL_FINAL;
+
+  /**
+   * @brief Get TPM Locator
+   */
+  virtual std::string
+  getTpmLocator() const NDN_CXX_DECL_FINAL;
+
+public: // Identity management
+
+  /**
+   * @brief Check the existence of an identity.
+   *
+   * @param identity The name of the identity.
+   * @return true if the identity exists, otherwise false.
+   */
+  virtual bool
+  hasIdentity(const Name& identity) const NDN_CXX_DECL_FINAL;
+
+  /**
+   * @brief Add an identity.
+   *
+   * If the identity already exists, do nothing.
+   * If no default identity has been set, set the added one as default identity.
+   *
+   * @param identity The name of the identity to add.
+   */
+  virtual void
+  addIdentity(const Name& identity) NDN_CXX_DECL_FINAL;
+
+  /**
+   * @brief Remove an identity
+   *
+   * If the identity does not exist, do nothing.
+   * Remove related keys and certificates as well.
+   *
+   * @param identity The name of the identity to remove.
+   */
+  virtual void
+  removeIdentity(const Name& identity) NDN_CXX_DECL_FINAL;
+
+  /**
+   * @brief Get the name of all the identities
+   */
+  virtual std::set<Name>
+  getIdentities() const NDN_CXX_DECL_FINAL;
+
+  /**
+   * @brief Set an identity with name @p identityName as the default identity.
+   *
+   * Since adding an identity only requires the identity name, create the
+   * identity if it does not exist.
+   *
+   * @param identityName The name for the default identity.
+   */
+  virtual void
+  setDefaultIdentity(const Name& identityName) NDN_CXX_DECL_FINAL;
+
+  /**
+   * @brief Get the default identity.
+   *
+   * @return The name for the default identity.
+   * @throws Pib::Error if no default identity.
+   */
+  virtual Name
+  getDefaultIdentity() const NDN_CXX_DECL_FINAL;
+
+public: // Key management
+
+  /**
+   * @brief Check the existence of a key.
+   *
+   * @param identity The name of the belonged identity.
+   * @param keyId The key id component.
+   * @return true if the key exists, otherwise false. Return false if the identity does not exist
+   */
+  virtual bool
+  hasKey(const Name& identity, const name::Component& keyId) const NDN_CXX_DECL_FINAL;
+
+  /**
+   * @brief Add a key.
+   *
+   * If the key already exists, do nothing.
+   * If the identity does not exist, add the identity as well.
+   * If no default key of the identity has been set, set the added one as default
+   * key of the identity.
+   *
+   * @param identity The name of the belonged identity.
+   * @param keyId The key id component.
+   * @param publicKey The public key bits.
+   */
+  virtual void
+  addKey(const Name& identity, const name::Component& keyId, const PublicKey& publicKey) NDN_CXX_DECL_FINAL;
+
+  /**
+   * @brief Remove a key.
+   *
+   * If the key does not exist, do nothing.
+   * Remove related certificates as well.
+   *
+   * @param identity The name of the belonged identity.
+   * @param keyId The key id component.
+   */
+  virtual void
+  removeKey(const Name& identity, const name::Component& keyId) NDN_CXX_DECL_FINAL;
+
+  /**
+   * @brief Get the key bits of a key.
+   *
+   * @param identity The name of the belonged identity.
+   * @param keyId The key id component.
+   * @return key bits
+   * @throws Pib::Error if the key does not exist.
+   */
+  virtual PublicKey
+  getKeyBits(const Name& identity, const name::Component& keyId) const NDN_CXX_DECL_FINAL;
+
+  /**
+   * @brief Get all the key ids of an identity with name @p identity
+   *
+   * The returned key ids can be used to create a KeyContainer.
+   * With key id, identity name, backend implementation, one can create a Key frontend instance.
+   *
+   * @return the key id name component set. If the identity does not exist, return an empty set.
+   */
+  virtual std::set<name::Component>
+  getKeysOfIdentity(const Name& identity) const NDN_CXX_DECL_FINAL;
+
+  /**
+   * @brief Set an key with id @p keyId as the default key of an identity with name @p identity.
+   *
+   * @param identity The name of the belonged identity.
+   * @param keyId The key id component.
+   * @throws Pib::Error if the key does not exist.
+   */
+  virtual void
+  setDefaultKeyOfIdentity(const Name& identity, const name::Component& keyId) NDN_CXX_DECL_FINAL;
+
+  /**
+   * @brief Get the id of the default key of an identity with name @p identity.
+   *
+   * @param identity The name of the belonged identity.
+   * @throws Pib::Error if no default key or the identity does not exist.
+   */
+  virtual name::Component
+  getDefaultKeyOfIdentity(const Name& identity) const NDN_CXX_DECL_FINAL;
+
+public: // Certificate Management
+
+  /**
+   * @brief Check the existence of a certificate with name @p certName.
+   *
+   * @param certName The name of the certificate.
+   * @return true if the certificate exists, otherwise false.
+   */
+  virtual bool
+  hasCertificate(const Name& certName) const NDN_CXX_DECL_FINAL;
+
+  /**
+   * @brief Add a certificate.
+   *
+   * If the certificate already exists, do nothing.
+   * If the key or identity do not exist, add them as well.
+   * If no default certificate of the key has been set, set the added one as
+   * default certificate of the key.
+   *
+   * @param certificate The certificate to add.
+   */
+  virtual void
+  addCertificate(const IdentityCertificate& certificate) NDN_CXX_DECL_FINAL;
+
+  /**
+   * @brief Remove a certificate with name @p certName.
+   *
+   * If the certificate does not exist, do nothing.
+   *
+   * @param certName The name of the certificate.
+   */
+  virtual void
+  removeCertificate(const Name& certName) NDN_CXX_DECL_FINAL;
+
+  /**
+   * @brief Get a certificate with name @p certName.
+   *
+   * @param certName The name of the certificate.
+   * @return the certificate.
+   * @throws Pib::Error if the certificate does not exist.
+   */
+  virtual IdentityCertificate
+  getCertificate(const Name& certName) const NDN_CXX_DECL_FINAL;
+
+  /**
+   * @brief Get a list of certificate names of a key with id @p keyId of @p identity.
+   *
+   * The returned certificate names can be used to create a CertificateContainer.
+   * With certificate name and backend implementation, one can obtain the certificate directly.
+   *
+   * @param identity The name of the belonging identity.
+   * @param keyId The key id.
+   * @return The certificate name set. If the key does not exist, return an empty set.
+   */
+  virtual std::set<Name>
+  getCertificatesOfKey(const Name& identity, const name::Component& keyId) const NDN_CXX_DECL_FINAL;
+
+  /**
+   * @brief Set a cert with name @p certName as the default of a key with id @keyId of @identity.
+   *
+   * @param identity The name of the belonging identity.
+   * @param keyId The key id.
+   * @param certName The name of the certificate.
+   * @throws Pib::Error if the certificate with name @p certName does not exist.
+   */
+  virtual void
+  setDefaultCertificateOfKey(const Name& identity, const name::Component& keyId,
+                             const Name& certName) NDN_CXX_DECL_FINAL;
+
+  /**
+   * @brief Get the default certificate of a key with id @keyId of @identity.
+   *
+   * @param identity The name of the belonging identity.
+   * @param keyId The key id.
+   * @return a pointer to the certificate, null if no default certificate for the key.
+   * @throws Pib::Error if the default certificate does not exist.
+   */
+  virtual IdentityCertificate
+  getDefaultCertificateOfKey(const Name& identity, const name::Component& keyId) const NDN_CXX_DECL_FINAL;
+
+private:
+  sqlite3* m_database;
+};
+
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_SECURITTY_PIB_SQLITE3_HPP
diff --git a/tests/unit-tests/security/pib-impl.t.cpp b/tests/unit-tests/security/pib-impl.t.cpp
index 1f6158b..4c01033 100644
--- a/tests/unit-tests/security/pib-impl.t.cpp
+++ b/tests/unit-tests/security/pib-impl.t.cpp
@@ -20,10 +20,11 @@
  */
 
 #include "security/pib-memory.hpp"
+#include "security/pib-sqlite3.hpp"
 #include "security/pib.hpp"
 #include "pib-data-fixture.hpp"
 
-#include <boost/test/test_case_template.hpp>
+#include <boost/filesystem.hpp>
 #include <boost/mpl/list.hpp>
 #include "boost-test.hpp"
 
@@ -33,11 +34,37 @@
 
 BOOST_AUTO_TEST_SUITE(SecurityPibImpl)
 
-typedef boost::mpl::list<PibMemory> PibImpls;
+class PibMemoryWrapper
+{
+public:
+  PibMemory impl;
+};
+
+class PibSqlite3Wrapper
+{
+public:
+  PibSqlite3Wrapper()
+    : tmpPath(boost::filesystem::path(UNIT_TEST_CONFIG_PATH) / "DbTest")
+    , impl(tmpPath.c_str())
+  {
+  }
+
+  ~PibSqlite3Wrapper()
+  {
+    boost::filesystem::remove_all(tmpPath);
+  }
+
+  boost::filesystem::path tmpPath;
+  PibSqlite3 impl;
+};
+
+typedef boost::mpl::list<PibMemoryWrapper,
+                         PibSqlite3Wrapper> PibImpls;
 
 BOOST_FIXTURE_TEST_CASE_TEMPLATE(IdentityManagement, T, PibImpls, PibDataFixture)
 {
-  T pibImpl;
+  T wrapper;
+  PibImpl& pibImpl = wrapper.impl;
 
   // no default setting, throw Error
   BOOST_CHECK_THROW(pibImpl.getDefaultIdentity(), Pib::Error);
@@ -78,7 +105,8 @@
 
 BOOST_FIXTURE_TEST_CASE_TEMPLATE(KeyManagement, T, PibImpls, PibDataFixture)
 {
-  T pibImpl;
+  T wrapper;
+  PibImpl& pibImpl = wrapper.impl;
 
   // no default setting, throw Error
   BOOST_CHECK_THROW(pibImpl.getDefaultKeyOfIdentity(id1), Pib::Error);
@@ -135,7 +163,8 @@
 
 BOOST_FIXTURE_TEST_CASE_TEMPLATE(CertificateManagement, T, PibImpls, PibDataFixture)
 {
-  T pibImpl;
+  T wrapper;
+  PibImpl& pibImpl = wrapper.impl;
 
   // no default setting, throw Error
   BOOST_CHECK_THROW(pibImpl.getDefaultCertificateOfKey(id1, id1Key1Name.get(-1)), Pib::Error);
diff --git a/tests/unit-tests/security/pib-sqlite3.t.cpp b/tests/unit-tests/security/pib-sqlite3.t.cpp
new file mode 100644
index 0000000..52d5ff1
--- /dev/null
+++ b/tests/unit-tests/security/pib-sqlite3.t.cpp
@@ -0,0 +1,124 @@
+/* -*- 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 "security/pib-sqlite3.hpp"
+#include "security/pib.hpp"
+
+#include <boost/filesystem.hpp>
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+class PibSqlite3TestFixture
+{
+public:
+  PibSqlite3TestFixture()
+    : m_path(boost::filesystem::path(UNIT_TEST_CONFIG_PATH) / "DbTest")
+    , impl(m_path.c_str())
+  {
+  }
+
+  ~PibSqlite3TestFixture()
+  {
+    boost::filesystem::remove_all(m_path);
+  }
+
+private:
+  boost::filesystem::path m_path;
+
+public:
+  PibSqlite3 impl;
+};
+
+
+BOOST_FIXTURE_TEST_SUITE(SecurityPibSqlite3, PibSqlite3TestFixture)
+
+// most functionalities are tested in pib-impl.t.cpp
+
+const uint8_t SELF_SIGNED_ECDSA_CERT[] = {
+0x06, 0xfd, 0x01, 0x5b, 0x07, 0x33, 0x08, 0x05, 0x65, 0x63, 0x64, 0x73, 0x61, 0x08, 0x03,
+0x4b, 0x45, 0x59, 0x08, 0x11, 0x6b, 0x73, 0x6b, 0x2d, 0x31, 0x34, 0x31, 0x36, 0x35, 0x39,
+0x34, 0x35, 0x35, 0x32, 0x38, 0x32, 0x37, 0x08, 0x07, 0x49, 0x44, 0x2d, 0x43, 0x45, 0x52,
+0x54, 0x08, 0x09, 0xfd, 0x00, 0x00, 0x01, 0x49, 0xd3, 0x9d, 0x78, 0x00, 0x14, 0x03, 0x18,
+0x01, 0x02, 0x15, 0xa5, 0x30, 0x81, 0xa2, 0x30, 0x22, 0x18, 0x0f, 0x32, 0x30, 0x31, 0x34,
+0x31, 0x31, 0x32, 0x31, 0x31, 0x38, 0x32, 0x39, 0x31, 0x32, 0x5a, 0x18, 0x0f, 0x32, 0x30,
+0x33, 0x34, 0x31, 0x31, 0x31, 0x36, 0x31, 0x38, 0x32, 0x39, 0x31, 0x32, 0x5a, 0x30, 0x21,
+0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x29, 0x13, 0x18, 0x2f, 0x65, 0x63, 0x64, 0x73, 0x61,
+0x2f, 0x6b, 0x73, 0x6b, 0x2d, 0x31, 0x34, 0x31, 0x36, 0x35, 0x39, 0x34, 0x35, 0x35, 0x32,
+0x38, 0x32, 0x37, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
+0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04,
+0x83, 0xe5, 0x81, 0x19, 0xd9, 0xfa, 0x64, 0x40, 0xad, 0x7c, 0x93, 0xfc, 0x15, 0x90, 0x6b,
+0x38, 0x1e, 0xc5, 0xca, 0xb1, 0x6b, 0x0b, 0x1f, 0x64, 0xbf, 0x48, 0xaa, 0xd0, 0x91, 0x5c,
+0x24, 0xd6, 0x78, 0x40, 0xfd, 0x95, 0x5d, 0x54, 0x64, 0xe1, 0x2d, 0x0e, 0x98, 0x66, 0x1d,
+0x7a, 0xb0, 0x61, 0x17, 0x05, 0x26, 0x13, 0x63, 0x25, 0x7c, 0xda, 0x87, 0x11, 0xc9, 0x67,
+0xcd, 0x12, 0x05, 0xf0, 0x16, 0x2f, 0x1b, 0x01, 0x03, 0x1c, 0x2a, 0x07, 0x28, 0x08, 0x05,
+0x65, 0x63, 0x64, 0x73, 0x61, 0x08, 0x03, 0x4b, 0x45, 0x59, 0x08, 0x11, 0x6b, 0x73, 0x6b,
+0x2d, 0x31, 0x34, 0x31, 0x36, 0x35, 0x39, 0x34, 0x35, 0x35, 0x32, 0x38, 0x32, 0x37, 0x08,
+0x07, 0x49, 0x44, 0x2d, 0x43, 0x45, 0x52, 0x54, 0x17, 0x47, 0x30, 0x45, 0x02, 0x21, 0x00,
+0x9b, 0xae, 0xf4, 0x87, 0x55, 0xaa, 0x78, 0xbf, 0x00, 0xff, 0x1a, 0xbe, 0x90, 0x46, 0x6e,
+0xdd, 0xe6, 0x3b, 0x44, 0xfd, 0x41, 0x04, 0x86, 0xcc, 0x6a, 0x8b, 0x5a, 0x25, 0xbb, 0xf1,
+0x55, 0xcd, 0x02, 0x20, 0x0e, 0x67, 0xd8, 0x86, 0xe8, 0x7c, 0x90, 0x3c, 0x13, 0xfd, 0x36,
+0x9c, 0xbc, 0xa1, 0xc3, 0x7c, 0xe0, 0x0c, 0x6d, 0x64, 0xac, 0xdb, 0x69, 0x99, 0xde, 0x80,
+0x35, 0x3f, 0xf4, 0x6a, 0xcd, 0x6f
+};
+
+BOOST_AUTO_TEST_CASE(TpmTest)
+{
+  ndn::Block selfSignedCertBlock(SELF_SIGNED_ECDSA_CERT, sizeof(SELF_SIGNED_ECDSA_CERT));
+  ndn::IdentityCertificate cert;
+  cert.wireDecode(selfSignedCertBlock);
+  Name identity = cert.getPublicKeyName().getPrefix(-1);
+  name::Component keyId = cert.getPublicKeyName().get(-1);
+  Name certName = cert.getName();
+
+  // Basic getting and setting
+  BOOST_REQUIRE_THROW(impl.getTpmLocator(), Pib::Error);
+  impl.setTpmLocator("tpmLocator");
+  BOOST_CHECK_EQUAL(impl.getTpmLocator(), "tpmLocator");
+
+  // Add cert, and do not change tpmLocator
+  impl.addCertificate(cert);
+  BOOST_CHECK(impl.hasIdentity(identity));
+  BOOST_CHECK(impl.hasKey(identity, keyId));
+  BOOST_CHECK(impl.hasCertificate(certName));
+
+  // Set tpmLocator with the existing value, nothing should change.
+  impl.setTpmLocator("tpmLocator");
+  BOOST_CHECK(impl.hasIdentity(identity));
+  BOOST_CHECK(impl.hasKey(identity, keyId));
+  BOOST_CHECK(impl.hasCertificate(certName));
+
+  // Change tpmLocator and ensure the pib is reset
+  impl.setTpmLocator("newTpmLocator");
+  BOOST_CHECK_EQUAL(impl.getTpmLocator(), "newTpmLocator");
+
+  BOOST_CHECK_EQUAL(impl.getIdentities().size(), 0);
+  BOOST_CHECK_EQUAL(impl.getKeysOfIdentity(identity).size(), 0);
+  BOOST_CHECK_EQUAL(impl.getCertificatesOfKey(identity, keyId).size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace security
+} // namespace ndn