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