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