security: Add new v2::KeyChain
Change-Id: I5fdf51ecd96b50db2a7cbf730c6e8b1d9fbe09e9
Refs: #2926
diff --git a/src/security/key-chain.cpp b/src/security/key-chain.cpp
index 827f9df..1896c7a 100644
--- a/src/security/key-chain.cpp
+++ b/src/security/key-chain.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -40,7 +40,6 @@
// Use a GUID as a magic number of KeyChain::DEFAULT_PREFIX identifier
const Name KeyChain::DEFAULT_PREFIX("/723821fd-f534-44b3-80d9-44bf5f58bbbb");
-const Name KeyChain::DIGEST_SHA256_IDENTITY("/localhost/identity/digest-sha256");
// Note: cannot use default constructor, as it depends on static variables which may or may not be
// initialized at this point
@@ -500,7 +499,7 @@
}
case SigningInfo::SIGNER_TYPE_SHA256: {
sigInfo.setSignatureType(tlv::DigestSha256);
- return std::make_tuple(DIGEST_SHA256_IDENTITY, sigInfo);
+ return std::make_tuple(SigningInfo::getDigestSha256Identity(), sigInfo);
}
default:
BOOST_THROW_EXCEPTION(Error("Unrecognized signer type"));
@@ -758,7 +757,7 @@
KeyChain::pureSign(const uint8_t* buf, size_t size,
const Name& keyName, DigestAlgorithm digestAlgorithm) const
{
- if (keyName == DIGEST_SHA256_IDENTITY)
+ if (keyName == SigningInfo::getDigestSha256Identity())
return Block(tlv::SignatureValue, crypto::computeSha256Digest(buf, size));
return m_tpm->signInTpm(buf, size, keyName, digestAlgorithm);
diff --git a/src/security/key-chain.hpp b/src/security/key-chain.hpp
index 796aa33..4f43139 100644
--- a/src/security/key-chain.hpp
+++ b/src/security/key-chain.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -871,12 +871,6 @@
static const Name DEFAULT_PREFIX;
static const SigningInfo DEFAULT_SIGNING_INFO;
- /**
- * @brief A localhost identity which indicates that signature is generated using SHA-256.
- * @todo Passing this as identity is not implemented.
- */
- static const Name DIGEST_SHA256_IDENTITY;
-
// RsaKeyParams is set to be default for backward compatibility.
static const RsaKeyParams DEFAULT_KEY_PARAMS;
diff --git a/src/security/pib/identity-container.hpp b/src/security/pib/identity-container.hpp
index 94cd370..9e45cf9 100644
--- a/src/security/pib/identity-container.hpp
+++ b/src/security/pib/identity-container.hpp
@@ -153,6 +153,7 @@
mutable std::unordered_map<Name, shared_ptr<detail::IdentityImpl>> m_identities;
shared_ptr<PibImpl> m_pibImpl;
+
friend class Pib;
};
diff --git a/src/security/pib/identity.cpp b/src/security/pib/identity.cpp
index 4797cb6..79cb090 100644
--- a/src/security/pib/identity.cpp
+++ b/src/security/pib/identity.cpp
@@ -40,13 +40,13 @@
}
Key
-Identity::addKey(const uint8_t* key, size_t keyLen, const Name& keyName)
+Identity::addKey(const uint8_t* key, size_t keyLen, const Name& keyName) const
{
return lock()->addKey(key, keyLen, keyName);
}
void
-Identity::removeKey(const Name& keyName)
+Identity::removeKey(const Name& keyName) const
{
return lock()->removeKey(keyName);
}
@@ -64,13 +64,13 @@
}
const Key&
-Identity::setDefaultKey(const Name& keyName)
+Identity::setDefaultKey(const Name& keyName) const
{
return lock()->setDefaultKey(keyName);
}
const Key&
-Identity::setDefaultKey(const uint8_t* key, size_t keyLen, const Name& keyName)
+Identity::setDefaultKey(const uint8_t* key, size_t keyLen, const Name& keyName) const
{
return lock()->setDefaultKey(key, keyLen, keyName);
}
diff --git a/src/security/pib/identity.hpp b/src/security/pib/identity.hpp
index 3d0117d..2bb1706 100644
--- a/src/security/pib/identity.hpp
+++ b/src/security/pib/identity.hpp
@@ -116,14 +116,14 @@
* @throw Pib::Error a key with the same name already exists
*/
Key
- addKey(const uint8_t* key, size_t keyLen, const Name& keyName);
+ addKey(const uint8_t* key, size_t keyLen, const Name& keyName) const;
/**
* @brief Remove a key with @p keyName
* @throw std::invalid_argument @p keyName does not match identity
*/
void
- removeKey(const Name& keyName);
+ removeKey(const Name& keyName) const;
/**
* @brief Set an existing key with @p keyName as the default key.
@@ -132,7 +132,7 @@
* @return The default key
*/
const Key&
- setDefaultKey(const Name& keyName);
+ setDefaultKey(const Name& keyName) const;
/**
* @brief Add a @p key of @p keyLen bytes with @p keyName and set it as the default key
@@ -141,7 +141,7 @@
* @return the default key
*/
const Key&
- setDefaultKey(const uint8_t* key, size_t keyLen, const Name& keyName);
+ setDefaultKey(const uint8_t* key, size_t keyLen, const Name& keyName) const;
private:
/**
@@ -154,6 +154,8 @@
private:
weak_ptr<detail::IdentityImpl> m_impl;
+
+ friend class v2::KeyChain;
};
} // namespace pib
diff --git a/src/security/pib/key.cpp b/src/security/pib/key.cpp
index b4e6daf..0d45140 100644
--- a/src/security/pib/key.cpp
+++ b/src/security/pib/key.cpp
@@ -59,13 +59,13 @@
}
void
-Key::addCertificate(const v2::Certificate& certificate)
+Key::addCertificate(const v2::Certificate& certificate) const
{
return lock()->addCertificate(certificate);
}
void
-Key::removeCertificate(const Name& certName)
+Key::removeCertificate(const Name& certName) const
{
return lock()->removeCertificate(certName);
}
@@ -83,13 +83,13 @@
}
const v2::Certificate&
-Key::setDefaultCertificate(const Name& certName)
+Key::setDefaultCertificate(const Name& certName) const
{
return lock()->setDefaultCertificate(certName);
}
const v2::Certificate&
-Key::setDefaultCertificate(const v2::Certificate& certificate)
+Key::setDefaultCertificate(const v2::Certificate& certificate) const
{
return lock()->setDefaultCertificate(certificate);
}
diff --git a/src/security/pib/key.hpp b/src/security/pib/key.hpp
index 69ceec8..4acc54a 100644
--- a/src/security/pib/key.hpp
+++ b/src/security/pib/key.hpp
@@ -28,7 +28,9 @@
namespace ndn {
namespace security {
+namespace v2 {
class KeyChain;
+} // namespace v2
namespace pib {
@@ -138,14 +140,14 @@
* @throw Pib::Error a certificate with the same name already exists
*/
void
- addCertificate(const v2::Certificate& certificate);
+ addCertificate(const v2::Certificate& certificate) const;
/**
* @brief Remove a certificate with @p certName
* @throw std::invalid_argument @p certName does not match key name
*/
void
- removeCertificate(const Name& certName);
+ removeCertificate(const Name& certName) const;
/**
* @brief Set an existing certificate with @p certName as the default certificate
@@ -154,7 +156,7 @@
* @return the default certificate
*/
const v2::Certificate&
- setDefaultCertificate(const Name& certName);
+ setDefaultCertificate(const Name& certName) const;
/**
* @brief Add @p certificate and set it as the default certificate of the key
@@ -163,7 +165,7 @@
* @return the default certificate
*/
const v2::Certificate&
- setDefaultCertificate(const v2::Certificate& certificate);
+ setDefaultCertificate(const v2::Certificate& certificate) const;
private:
/**
@@ -176,6 +178,8 @@
private:
weak_ptr<detail::KeyImpl> m_impl;
+
+ friend class v2::KeyChain;
};
} // namespace pib
diff --git a/src/security/pib/pib-memory.cpp b/src/security/pib/pib-memory.cpp
index aa93ba7..2d6aa7d 100644
--- a/src/security/pib/pib-memory.cpp
+++ b/src/security/pib/pib-memory.cpp
@@ -27,11 +27,18 @@
namespace security {
namespace pib {
-PibMemory::PibMemory()
+PibMemory::PibMemory(const std::string&)
: m_hasDefaultIdentity(false)
{
}
+const std::string&
+PibMemory::getScheme()
+{
+ static std::string scheme = "pib-memory";
+ return scheme;
+}
+
void
PibMemory::setTpmLocator(const std::string& tpmLocator)
{
diff --git a/src/security/pib/pib-memory.hpp b/src/security/pib/pib-memory.hpp
index 192f000..1252909 100644
--- a/src/security/pib/pib-memory.hpp
+++ b/src/security/pib/pib-memory.hpp
@@ -48,7 +48,15 @@
};
public:
- PibMemory();
+ /**
+ * @brief Create memory based PIB backend
+ * @param location Not used (required by the PIB-registration interface)
+ */
+ explicit
+ PibMemory(const std::string& location = "");
+
+ static const std::string&
+ getScheme();
public: // TpmLocator management
void
diff --git a/src/security/pib/pib-sqlite3.cpp b/src/security/pib/pib-sqlite3.cpp
index b19f32c..238a0f8 100644
--- a/src/security/pib/pib-sqlite3.cpp
+++ b/src/security/pib/pib-sqlite3.cpp
@@ -32,171 +32,171 @@
namespace security {
namespace pib {
-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 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_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 const std::string INITIALIZATION = R"SQL(
+CREATE TABLE IF NOT EXISTS
+ tpmInfo(
+ tpm_locator BLOB
+ );
-PibSqlite3::PibSqlite3(const string& dir)
+CREATE TABLE IF NOT EXISTS
+ identities(
+ id INTEGER PRIMARY KEY,
+ identity BLOB NOT NULL,
+ is_default INTEGER DEFAULT 0
+ );
+
+CREATE UNIQUE INDEX IF NOT EXISTS
+ identityIndex ON identities(identity);
+
+CREATE TRIGGER IF NOT EXISTS
+ identity_default_before_insert_trigger
+ BEFORE INSERT ON identities
+ FOR EACH ROW
+ WHEN NEW.is_default=1
+ BEGIN
+ UPDATE identities SET is_default=0;
+ END;
+
+CREATE TRIGGER IF NOT EXISTS
+ identity_default_after_insert_trigger
+ AFTER INSERT ON identities
+ FOR EACH ROW
+ WHEN NOT EXISTS
+ (SELECT id
+ FROM identities
+ WHERE is_default=1)
+ BEGIN
+ UPDATE identities
+ SET is_default=1
+ WHERE identity=NEW.identity;
+ END;
+
+CREATE TRIGGER IF NOT EXISTS
+ identity_default_update_trigger
+ BEFORE UPDATE ON identities
+ FOR EACH ROW
+ WHEN NEW.is_default=1 AND OLD.is_default=0
+ BEGIN
+ UPDATE identities SET is_default=0;
+ END;
+
+
+CREATE TABLE IF NOT EXISTS
+ keys(
+ id INTEGER PRIMARY KEY,
+ identity_id INTEGER NOT NULL,
+ key_name BLOB NOT NULL,
+ key_bits BLOB NOT NULL,
+ is_default INTEGER DEFAULT 0,
+ FOREIGN KEY(identity_id)
+ REFERENCES identities(id)
+ ON DELETE CASCADE
+ ON UPDATE CASCADE
+ );
+
+CREATE UNIQUE INDEX IF NOT EXISTS
+ keyIndex ON keys(key_name);
+
+CREATE TRIGGER IF NOT EXISTS
+ key_default_before_insert_trigger
+ BEFORE INSERT ON keys
+ FOR EACH ROW
+ WHEN NEW.is_default=1
+ BEGIN
+ UPDATE keys
+ SET is_default=0
+ WHERE identity_id=NEW.identity_id;
+ END;
+
+CREATE TRIGGER IF NOT EXISTS
+ key_default_after_insert_trigger
+ AFTER INSERT ON keys
+ FOR EACH ROW
+ WHEN NOT EXISTS
+ (SELECT id
+ FROM keys
+ WHERE is_default=1
+ AND identity_id=NEW.identity_id)
+ BEGIN
+ UPDATE keys
+ SET is_default=1
+ WHERE key_name=NEW.key_name;
+ END;
+
+CREATE TRIGGER IF NOT EXISTS
+ key_default_update_trigger
+ BEFORE UPDATE ON keys
+ FOR EACH ROW
+ WHEN NEW.is_default=1 AND OLD.is_default=0
+ BEGIN
+ UPDATE keys
+ SET is_default=0
+ WHERE identity_id=NEW.identity_id;
+ END;
+
+
+CREATE TABLE IF NOT EXISTS
+ certificates(
+ id INTEGER PRIMARY KEY,
+ key_id INTEGER NOT NULL,
+ certificate_name BLOB NOT NULL,
+ certificate_data BLOB NOT NULL,
+ is_default INTEGER DEFAULT 0,
+ FOREIGN KEY(key_id)
+ REFERENCES keys(id)
+ ON DELETE CASCADE
+ ON UPDATE CASCADE
+ );
+
+CREATE UNIQUE INDEX IF NOT EXISTS
+ certIndex ON certificates(certificate_name);
+
+CREATE TRIGGER IF NOT EXISTS
+ cert_default_before_insert_trigger
+ BEFORE INSERT ON certificates
+ FOR EACH ROW
+ WHEN NEW.is_default=1
+ BEGIN
+ UPDATE certificates
+ SET is_default=0
+ WHERE key_id=NEW.key_id;
+ END;
+
+CREATE TRIGGER IF NOT EXISTS
+ cert_default_after_insert_trigger
+ AFTER INSERT ON certificates
+ FOR EACH ROW
+ WHEN NOT EXISTS
+ (SELECT id
+ FROM certificates
+ WHERE is_default=1
+ AND key_id=NEW.key_id)
+ BEGIN
+ UPDATE certificates
+ SET is_default=1
+ WHERE certificate_name=NEW.certificate_name;
+ END;
+
+CREATE TRIGGER IF NOT EXISTS
+ cert_default_update_trigger
+ BEFORE UPDATE ON certificates
+ FOR EACH ROW
+ WHEN NEW.is_default=1 AND OLD.is_default=0
+ BEGIN
+ UPDATE certificates
+ SET is_default=0
+ WHERE key_id=NEW.key_id;
+ END;
+)SQL";
+
+PibSqlite3::PibSqlite3(const std::string& location)
{
// Determine the path of PIB DB
boost::filesystem::path dbDir;
- if (!dir.empty()) {
- dbDir = boost::filesystem::path(dir);
+ if (!location.empty()) {
+ dbDir = boost::filesystem::path(location);
}
#ifdef NDN_CXX_HAVE_TESTS
else if (getenv("TEST_HOME") != nullptr) {
@@ -222,7 +222,7 @@
);
if (result != SQLITE_OK) {
- BOOST_THROW_EXCEPTION(PibImpl::Error("PIB database cannot be opened/created in " + dir));
+ BOOST_THROW_EXCEPTION(PibImpl::Error("PIB database cannot be opened/created in " + location));
}
// enable foreign key
@@ -242,6 +242,13 @@
sqlite3_close(m_database);
}
+const std::string&
+PibSqlite3::getScheme()
+{
+ static std::string scheme = "pib-sqlite3";
+ return scheme;
+}
+
void
PibSqlite3::setTpmLocator(const std::string& tpmLocator)
{
diff --git a/src/security/pib/pib-sqlite3.hpp b/src/security/pib/pib-sqlite3.hpp
index 5571657..d27dab6 100644
--- a/src/security/pib/pib-sqlite3.hpp
+++ b/src/security/pib/pib-sqlite3.hpp
@@ -40,25 +40,28 @@
{
public:
/**
- * @brief Constructor of PibSqlite3
+ * @brief Create sqlite3-based PIB backed
*
- * This method will create a SQLite3 database file under the directory @p dir.
+ * This method will create a SQLite3 database file under the directory @p location.
* 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.
+ * @param location The directory where the database file is located. By default, it points to the
+ * $HOME/.ndn directory.
* @throw PibImpl::Error when initialization fails.
*/
explicit
- PibSqlite3(const std::string& dir = "");
+ PibSqlite3(const std::string& location = "");
/**
* @brief Destruct and cleanup internal state
*/
~PibSqlite3();
+ static const std::string&
+ getScheme();
+
public: // TpmLocator management
void
setTpmLocator(const std::string& tpmLocator) final;
diff --git a/src/security/pib/pib.hpp b/src/security/pib/pib.hpp
index 47dbd5c..947e5a5 100644
--- a/src/security/pib/pib.hpp
+++ b/src/security/pib/pib.hpp
@@ -26,9 +26,6 @@
namespace ndn {
namespace security {
-
-class KeyChain;
-
namespace pib {
class PibImpl;
@@ -46,6 +43,10 @@
* the Pib class provides access to identities, and allows setting a default identity. Properties of
* an identity can be accessed after obtaining an Identity object.
*
+ * @note Pib instance is created and managed only by v2::KeyChain. v2::KeyChain::getPib()
+ * returns a const reference to the managed Pib instance, through which it is possible to
+ * retrieve information about identities, keys, and certificates.
+ *
* @throw PibImpl::Error when underlying implementation has non-semantic error.
*/
class Pib : noncopyable
@@ -184,7 +185,7 @@
shared_ptr<PibImpl> m_impl;
- friend class KeyChain;
+ friend class v2::KeyChain;
};
} // namespace pib
diff --git a/src/security/signing-info.cpp b/src/security/signing-info.cpp
index f8a7892..48b278a 100644
--- a/src/security/signing-info.cpp
+++ b/src/security/signing-info.cpp
@@ -20,13 +20,30 @@
*/
#include "signing-info.hpp"
-#include "key-chain.hpp"
namespace ndn {
namespace security {
-const Name SigningInfo::EMPTY_NAME;
-const SignatureInfo SigningInfo::EMPTY_SIGNATURE_INFO;
+const Name&
+SigningInfo::getEmptyName()
+{
+ static Name emptyName;
+ return emptyName;
+}
+
+const SignatureInfo&
+SigningInfo::getEmptySignatureInfo()
+{
+ static SignatureInfo emptySignatureInfo;
+ return emptySignatureInfo;
+}
+
+const Name&
+SigningInfo::getDigestSha256Identity()
+{
+ static Name digestSha256Identity("/localhost/identity/digest-sha256");
+ return digestSha256Identity;
+}
SigningInfo::SigningInfo(SignerType signerType,
const Name& signerName,
@@ -56,7 +73,7 @@
std::string nameArg = signingStr.substr(pos + 1);
if (scheme == "id") {
- if (nameArg == KeyChain::DIGEST_SHA256_IDENTITY.toUri()) {
+ if (nameArg == getDigestSha256Identity().toUri()) {
setSha256Signing();
}
else {
@@ -129,7 +146,7 @@
os << "cert:";
break;
case SigningInfo::SIGNER_TYPE_SHA256:
- os << "id:" << KeyChain::DIGEST_SHA256_IDENTITY;
+ os << "id:" << SigningInfo::getDigestSha256Identity();
return os;
default:
BOOST_THROW_EXCEPTION(std::invalid_argument("Unknown signer type"));
diff --git a/src/security/signing-info.hpp b/src/security/signing-info.hpp
index 375d9e8..499f25b 100644
--- a/src/security/signing-info.hpp
+++ b/src/security/signing-info.hpp
@@ -71,8 +71,8 @@
*/
explicit
SigningInfo(SignerType signerType = SIGNER_TYPE_NULL,
- const Name& signerName = EMPTY_NAME,
- const SignatureInfo& signatureInfo = EMPTY_SIGNATURE_INFO);
+ const Name& signerName = getEmptyName(),
+ const SignatureInfo& signatureInfo = getEmptySignatureInfo());
/**
* @brief Construct SigningInfo from its string representation
@@ -170,15 +170,22 @@
}
public:
- static const Name EMPTY_NAME;
- static const SignatureInfo EMPTY_SIGNATURE_INFO;
+ static const Name&
+ getEmptyName();
+
+ static const SignatureInfo&
+ getEmptySignatureInfo();
+
+ /**
+ * @brief A localhost identity to indicate that the signature is generated using SHA-256.
+ */
+ static const Name&
+ getDigestSha256Identity();
private:
SignerType m_type;
Name m_name;
-
DigestAlgorithm m_digestAlgorithm;
-
SignatureInfo m_info;
};
diff --git a/src/security/tpm/back-end-file.cpp b/src/security/tpm/back-end-file.cpp
index f16d7cf..1adcf1c 100644
--- a/src/security/tpm/back-end-file.cpp
+++ b/src/security/tpm/back-end-file.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -83,6 +83,13 @@
BackEndFile::~BackEndFile() = default;
+const std::string&
+BackEndFile::getScheme()
+{
+ static std::string scheme = "tpm-file";
+ return scheme;
+}
+
bool
BackEndFile::doHasKey(const Name& keyName) const
{
diff --git a/src/security/tpm/back-end-file.hpp b/src/security/tpm/back-end-file.hpp
index 02b575f..db58907 100644
--- a/src/security/tpm/back-end-file.hpp
+++ b/src/security/tpm/back-end-file.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -52,11 +52,18 @@
};
public:
+ /**
+ * @brief Create file-based TPM backend
+ * @param location Directory to store private keys
+ */
explicit
BackEndFile(const std::string& location = "");
~BackEndFile() override;
+ static const std::string&
+ getScheme();
+
private: // inherited from tpm::BackEnd
/**
* @return True if a key with name @p keyName exists in TPM.
@@ -87,14 +94,14 @@
/**
* @brief Delete a key with name @p keyName.
*
- * @throws Error if the deletion fails.
+ * @throw Error the deletion failed
*/
void
doDeleteKey(const Name& keyName) final;
/**
* @return A private key with name @p keyName in encrypted PKCS #8 format using password @p pw
- * @throws Error if the key cannot be exported, e.g., not enough privilege
+ * @throw Error the key cannot be exported, e.g., not enough privilege
*/
ConstBufferPtr
doExportKey(const Name& keyName, const char* pw, size_t pwLen) final;
@@ -107,7 +114,7 @@
* @param size The size of the key in encrypted PKCS #8 format
* @param pw The password to decrypt the key
* @param pwLen The length of the password
- * @throws Error if import fails.
+ * @throw Error import failed
*/
void
doImportKey(const Name& keyName, const uint8_t* buf, size_t size, const char* pw, size_t pwLen) final;
diff --git a/src/security/tpm/back-end-mem.cpp b/src/security/tpm/back-end-mem.cpp
index beac9db..9d051c6 100644
--- a/src/security/tpm/back-end-mem.cpp
+++ b/src/security/tpm/back-end-mem.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -37,13 +37,20 @@
std::unordered_map<Name, shared_ptr<PrivateKey>> keys;
};
-BackEndMem::BackEndMem()
+BackEndMem::BackEndMem(const std::string&)
: m_impl(new Impl)
{
}
BackEndMem::~BackEndMem() = default;
+const std::string&
+BackEndMem::getScheme()
+{
+ static std::string scheme = "tpm-memory";
+ return scheme;
+}
+
bool
BackEndMem::doHasKey(const Name& keyName) const
{
diff --git a/src/security/tpm/back-end-mem.hpp b/src/security/tpm/back-end-mem.hpp
index 4253b5e..4bfa212 100644
--- a/src/security/tpm/back-end-mem.hpp
+++ b/src/security/tpm/back-end-mem.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -45,11 +45,18 @@
};
public:
+ /**
+ * @brief Create memory-based TPM backend
+ * @param location Not used (required by the TPM-registration interface)
+ */
explicit
- BackEndMem();
+ BackEndMem(const std::string& location = "");
~BackEndMem() override;
+ static const std::string&
+ getScheme();
+
private: // inherited from tpm::BackEnd
/**
@@ -80,14 +87,14 @@
/**
* @brief Delete a key with name @p keyName.
*
- * @throws Error if the deletion fails.
+ * @throw Error the deletion failed
*/
void
doDeleteKey(const Name& keyName) final;
/**
* @return A private key with name @p keyName in encrypted PKCS #8 format using password @p pw
- * @throws Error if the key cannot be exported, e.g., not enough privilege
+ * @throw Error the key cannot be exported, e.g., not enough privilege
*/
ConstBufferPtr
doExportKey(const Name& keyName, const char* pw, size_t pwLen) final;
@@ -100,7 +107,7 @@
* @param size The size of the key in encrypted PKCS #8 format
* @param pw The password to decrypt the key
* @param pwLen The length of password
- * @throws Error if import fails.
+ * @throw Error import failed
*/
void
doImportKey(const Name& keyName, const uint8_t* buf, size_t size, const char* pw, size_t pwLen) final;
diff --git a/src/security/tpm/back-end-osx.cpp b/src/security/tpm/back-end-osx.cpp
index d423b10..178ae55 100644
--- a/src/security/tpm/back-end-osx.cpp
+++ b/src/security/tpm/back-end-osx.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -117,7 +117,7 @@
}
}
-BackEndOsx::BackEndOsx()
+BackEndOsx::BackEndOsx(const std::string&)
: m_impl(new Impl)
{
SecKeychainSetUserInteractionAllowed(!m_impl->isTerminalMode);
@@ -131,11 +131,11 @@
BackEndOsx::~BackEndOsx() = default;
-void
-BackEndOsx::setTerminalMode(bool isTerminal)
+const std::string&
+BackEndOsx::getScheme()
{
- m_impl->isTerminalMode = isTerminal;
- SecKeychainSetUserInteractionAllowed(!isTerminal);
+ static std::string scheme = "tpm-osxkeychain";
+ return scheme;
}
bool
@@ -144,8 +144,15 @@
return m_impl->isTerminalMode;
}
+void
+BackEndOsx::setTerminalMode(bool isTerminal) const
+{
+ m_impl->isTerminalMode = isTerminal;
+ SecKeychainSetUserInteractionAllowed(!isTerminal);
+}
+
bool
-BackEndOsx::isLocked() const
+BackEndOsx::isTpmLocked() const
{
SecKeychainStatus keychainStatus;
@@ -157,22 +164,22 @@
}
bool
-BackEndOsx::unlockTpm(const char* password, size_t passwordLength)
+BackEndOsx::unlockTpm(const char* pw, size_t pwLen) const
{
// If the default key chain is already unlocked, return immediately.
- if (!isLocked())
+ if (!isTpmLocked())
return true;
if (m_impl->isTerminalMode) {
// Use the supplied password.
- SecKeychainUnlock(m_impl->keyChainRef, passwordLength, password, true);
+ SecKeychainUnlock(m_impl->keyChainRef, pwLen, pw, true);
}
else {
// If inTerminal is not set, get the password from GUI.
SecKeychainUnlock(m_impl->keyChainRef, 0, nullptr, false);
}
- return !isLocked();
+ return !isTpmLocked();
}
ConstBufferPtr
diff --git a/src/security/tpm/back-end-osx.hpp b/src/security/tpm/back-end-osx.hpp
index 0fcd49f..29465c8 100644
--- a/src/security/tpm/back-end-osx.hpp
+++ b/src/security/tpm/back-end-osx.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -34,7 +34,7 @@
namespace tpm {
/**
- * @brief The back-end implementation of TPM based on OS X KeyChain service.
+ * @brief The back-end implementation of TPM based on macOS Keychain Services.
*/
class BackEndOsx : public BackEnd
{
@@ -50,39 +50,30 @@
};
public:
- BackEndOsx();
+ /**
+ * @brief Create TPM backed based on macOS KeyChain service
+ * @param location Not used (required by the TPM-registration interface)
+ */
+ explicit
+ BackEndOsx(const std::string& location = "");
~BackEndOsx() override;
+ static const std::string&
+ getScheme();
+
public: // management
- /**
- * @brief Set the terminal mode of TPM.
- *
- * In terminal mode, TPM will not ask user permission from GUI.
- */
+ bool
+ isTerminalMode() const final;
+
void
- setTerminalMode(bool isTerminal);
+ setTerminalMode(bool isTerminal) const final;
- /**
- * @brief Check if TPM is in terminal mode
- */
bool
- isTerminalMode() const;
+ isTpmLocked() const final;
- /**
- * @return True if TPM is locked, otherwise false
- */
bool
- isLocked() const;
-
- /**
- * @brief Unlock TPM
- *
- * @param password The password to unlock TPM
- * @param passwordLength The password size.
- */
- bool
- unlockTpm(const char* password = nullptr, size_t passwordLength = 0);
+ unlockTpm(const char* pw, size_t pwLen) const final;
public: // crypto transformation
/**
@@ -125,14 +116,14 @@
/**
* @brief Delete a key with name @p keyName.
*
- * @throws Error if the deletion fails.
+ * @throw Error the deletion failed
*/
void
doDeleteKey(const Name& keyName) final;
/**
* @return A private key with name @p keyName in encrypted PKCS #8 format using password @p pw
- * @throws Error if the key cannot be exported, e.g., not enough privilege
+ * @throw Error the key cannot be exported, e.g., not enough privilege
*/
ConstBufferPtr
doExportKey(const Name& keyName, const char* pw, size_t pwLen) final;
@@ -145,7 +136,7 @@
* @param size The size of the key in encrypted PKCS #8 format
* @param pw The password to decrypt the private key
* @param pwLen The length of the password
- * @throws Error if import fails
+ * @throw Error import fails
*/
void
doImportKey(const Name& keyName, const uint8_t* buf, size_t size, const char* pw, size_t pwLen) final;
diff --git a/src/security/tpm/back-end.cpp b/src/security/tpm/back-end.cpp
index 18df659..72ef5fd 100644
--- a/src/security/tpm/back-end.cpp
+++ b/src/security/tpm/back-end.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -66,7 +66,7 @@
name::Component keyId;
do {
keyId = name::Component::fromNumber(random::generateSecureWord64());
- keyName = v2::constructKeyName(identity, params.getKeyId());
+ keyName = v2::constructKeyName(identity, keyId);
} while (hasKey(keyName));
const_cast<KeyParams&>(params).setKeyId(keyId);
@@ -133,6 +133,29 @@
keyHandle.setKeyName(v2::constructKeyName(identity, keyId));
}
+bool
+BackEnd::isTerminalMode() const
+{
+ return true;
+}
+
+void
+BackEnd::setTerminalMode(bool isTerminal) const
+{
+}
+
+bool
+BackEnd::isTpmLocked() const
+{
+ return false;
+}
+
+bool
+BackEnd::unlockTpm(const char* pw, size_t pwLen) const
+{
+ return !isTpmLocked();
+}
+
} // namespace tpm
} // namespace security
} // namespace ndn
diff --git a/src/security/tpm/back-end.hpp b/src/security/tpm/back-end.hpp
index 749b53d..4c55ab2 100644
--- a/src/security/tpm/back-end.hpp
+++ b/src/security/tpm/back-end.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -77,8 +77,8 @@
* The key name is set in the returned KeyHandle.
*
* @return The handle of the created key.
- * @throws Tpm::Error if @p params is invalid
- * @throws Error if the key cannot be created.
+ * @throw Tpm::Error @p params are invalid
+ * @throw Error the key cannot be created
*/
unique_ptr<KeyHandle>
createKey(const Name& identity, const KeyParams& params);
@@ -88,15 +88,15 @@
*
* Continuing to use existing KeyHandles on a deleted key results in undefined behavior.
*
- * @throws Error if the deletion fails.
+ * @throw Error if the deletion fails.
*/
void
deleteKey(const Name& keyName);
/**
* @return A private key with name @p keyName in encrypted PKCS #8 format using password @p pw
- * @throws Error if the key does not exist
- * @throws Error if the key cannot be exported, e.g., insufficient privilege
+ * @throw Error the key does not exist
+ * @throw Error the key cannot be exported, e.g., insufficient privilege
*/
ConstBufferPtr
exportKey(const Name& keyName, const char* pw, size_t pwLen);
@@ -109,11 +109,48 @@
* @param pkcs8Len The size of the key in encrypted PKCS #8 format
* @param pw The password to decrypt the private key
* @param pwLen The length of the password
- * @throws Error if import fails.
+ * @throw Error import failed
*/
void
importKey(const Name& keyName, const uint8_t* pkcs8, size_t pkcs8Len, const char* pw, size_t pwLen);
+ /**
+ * @brief Check if TPM is in terminal mode
+ *
+ * Default implementation always returns true.
+ */
+ virtual bool
+ isTerminalMode() const;
+
+ /**
+ * @brief Set the terminal mode of TPM.
+ *
+ * In terminal mode, TPM will not ask user permission from GUI.
+ *
+ * Default implementation does nothing.
+ */
+ virtual void
+ setTerminalMode(bool isTerminal) const;
+
+ /**
+ * @return True if TPM is locked, otherwise false
+ *
+ * Default implementation always returns false.
+ */
+ virtual bool
+ isTpmLocked() const;
+
+ /**
+ * @brief Unlock TPM
+ *
+ * @param pw The password to unlock TPM
+ * @param pwLen The password size.
+ *
+ * Default implementation always returns !isTpmLocked()
+ */
+ virtual bool
+ unlockTpm(const char* pw, size_t pwLen) const;
+
protected: // static helper method
/**
* @brief Set the key name in @p keyHandle according to @p identity and @p params
@@ -141,7 +178,7 @@
* The key name is set in the returned KeyHandle.
*
* @return The handle of the created key.
- * @throws Error when key cannot be created.
+ * @throw Error key cannot be created
*/
virtual unique_ptr<KeyHandle>
doCreateKey(const Name& identity, const KeyParams& params) = 0;
@@ -149,14 +186,14 @@
/**
* @brief Delete a key with name @p keyName.
*
- * @throws Error if the deletion fails.
+ * @throw Error the deletion failed
*/
virtual void
doDeleteKey(const Name& keyName) = 0;
/**
* @return A private key with name @p keyName in encrypted PKCS #8 format using password @p pw
- * @throws Error if the key cannot be exported, e.g., insufficient privilege
+ * @throw Error the key cannot be exported, e.g., insufficient privilege
*/
virtual ConstBufferPtr
doExportKey(const Name& keyName, const char* pw, size_t pwLen) = 0;
@@ -169,7 +206,7 @@
* @param pkcs8Len The size of the key in PKCS #8 format
* @param pw The password to decrypt the private key
* @param pwLen The length of the password
- * @throws Error if import fails.
+ * @throw Error import failed
*/
virtual void
doImportKey(const Name& keyName, const uint8_t* pkcs8, size_t pkcs8Len, const char* pw, size_t pwLen) = 0;
diff --git a/src/security/tpm/tpm.cpp b/src/security/tpm/tpm.cpp
index 234c48d..fad73b9 100644
--- a/src/security/tpm/tpm.cpp
+++ b/src/security/tpm/tpm.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -108,6 +108,30 @@
return key->decrypt(buf, size);
}
+bool
+Tpm::isTerminalMode() const
+{
+ return m_backEnd->isTerminalMode();
+}
+
+void
+Tpm::setTerminalMode(bool isTerminal) const
+{
+ m_backEnd->setTerminalMode(isTerminal);
+}
+
+bool
+Tpm::isTpmLocked() const
+{
+ return m_backEnd->isTpmLocked();
+}
+
+bool
+Tpm::unlockTpm(const char* password, size_t passwordLength) const
+{
+ return m_backEnd->unlockTpm(password, passwordLength);
+}
+
ConstBufferPtr
Tpm::exportPrivateKey(const Name& keyName, const char* pw, size_t pwLen)
{
diff --git a/src/security/tpm/tpm.hpp b/src/security/tpm/tpm.hpp
index 01b22ae..5c985f1 100644
--- a/src/security/tpm/tpm.hpp
+++ b/src/security/tpm/tpm.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -31,6 +31,11 @@
namespace ndn {
namespace security {
+
+namespace v2 {
+class KeyChain;
+} // namespace v2
+
namespace tpm {
class BackEnd;
@@ -48,14 +53,17 @@
* A TPM consists of a unified front-end interface and a back-end implementation. The front-end
* cache the handles of private keys which is provided by the back-end implementation.
*
- * @throw tpm::BackEnd::Error when underlying implementation has non-semantic error.
- * @throw Tpm::Error when there is an semantic error.
+ * @note Tpm instance is created and managed only by v2::KeyChain. v2::KeyChain::getTpm()
+ * returns a const reference to the managed Tpm instance, through which it is possible to
+ * check existence of private keys, get public keys for the private keys, sign, and decrypt
+ * the supplied buffers using managed private keys.
+ *
+ * @throw BackEnd::Error Failure with the underlying implementation having non-semantic errors
+ * @throw Tpm::Error Failure with semantic error in the underlying implementation
*/
class Tpm : noncopyable
{
public:
- friend class KeyChain;
-
class Error : public std::runtime_error
{
public:
@@ -106,6 +114,36 @@
ConstBufferPtr
decrypt(const uint8_t* buf, size_t size, const Name& keyName) const;
+public: // Management
+ /**
+ * @brief Check if TPM is in terminal mode
+ */
+ bool
+ isTerminalMode() const;
+
+ /**
+ * @brief Set the terminal mode of TPM.
+ *
+ * In terminal mode, TPM will not ask user permission from GUI.
+ */
+ void
+ setTerminalMode(bool isTerminal) const;
+
+ /**
+ * @return True if TPM is locked, otherwise false
+ */
+ bool
+ isTpmLocked() const;
+
+ /**
+ * @brief Unlock TPM
+ *
+ * @param password The password to unlock TPM
+ * @param passwordLength The password size.
+ */
+ bool
+ unlockTpm(const char* password, size_t passwordLength) const;
+
NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
/*
* @brief Create a new TPM instance with the specified @p location
@@ -128,7 +166,7 @@
* The created key is named as: /<identityName>/[keyId]/KEY
*
* @return the key name
- * @throws Tpm::Error if the key has already existed or the params is invalid
+ * @throw Tpm::Error the key has already existed or the params is invalid
*/
Name
createKey(const Name& identityName, const KeyParams& params);
@@ -179,7 +217,6 @@
}
private:
-
/**
* @brief Internal KeyHandle lookup
*
@@ -195,6 +232,8 @@
mutable std::unordered_map<Name, unique_ptr<KeyHandle>> m_keys;
unique_ptr<BackEnd> m_backEnd;
+
+ friend class v2::KeyChain;
};
} // namespace tpm
diff --git a/src/security/v2/key-chain.cpp b/src/security/v2/key-chain.cpp
new file mode 100644
index 0000000..0f3909e
--- /dev/null
+++ b/src/security/v2/key-chain.cpp
@@ -0,0 +1,698 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "key-chain.hpp"
+
+#include "../../util/config-file.hpp"
+
+#include "../pib/pib-sqlite3.hpp"
+#include "../pib/pib-memory.hpp"
+
+#ifdef NDN_CXX_HAVE_OSX_SECURITY
+#include "../tpm/back-end-osx.hpp"
+#endif // NDN_CXX_HAVE_OSX_SECURITY
+
+#include "../tpm/back-end-file.hpp"
+#include "../tpm/back-end-mem.hpp"
+
+#include "../transform/bool-sink.hpp"
+#include "../transform/buffer-source.hpp"
+#include "../transform/private-key.hpp"
+#include "../transform/verifier-filter.hpp"
+#include "../../encoding/buffer-stream.hpp"
+#include "../../util/crypto.hpp"
+
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace security {
+
+// When static library is used, not everything is compiled into the resulting binary.
+// Therefore, the following standard PIB and TPMs need to be registered here.
+// http://stackoverflow.com/q/9459980/2150331
+
+/////////
+// PIB //
+/////////
+namespace pib {
+NDN_CXX_V2_KEYCHAIN_REGISTER_PIB_BACKEND(PibSqlite3);
+NDN_CXX_V2_KEYCHAIN_REGISTER_PIB_BACKEND(PibMemory);
+} // namespace pib
+
+/////////
+// TPM //
+/////////
+namespace tpm {
+#if defined(NDN_CXX_HAVE_OSX_SECURITY) && defined(NDN_CXX_WITH_OSX_KEYCHAIN)
+NDN_CXX_V2_KEYCHAIN_REGISTER_TPM_BACKEND(BackEndOsx);
+#endif // defined(NDN_CXX_HAVE_OSX_SECURITY) && defined(NDN_CXX_WITH_OSX_KEYCHAIN)
+
+NDN_CXX_V2_KEYCHAIN_REGISTER_TPM_BACKEND(BackEndFile);
+NDN_CXX_V2_KEYCHAIN_REGISTER_TPM_BACKEND(BackEndMem);
+} // namespace tpm
+
+namespace v2 {
+
+std::string KeyChain::s_defaultPibLocator;
+std::string KeyChain::s_defaultTpmLocator;
+
+KeyChain::PibFactories&
+KeyChain::getPibFactories()
+{
+ static PibFactories pibFactories;
+ return pibFactories;
+}
+
+KeyChain::TpmFactories&
+KeyChain::getTpmFactories()
+{
+ static TpmFactories tpmFactories;
+ return tpmFactories;
+}
+
+const std::string&
+KeyChain::getDefaultPibScheme()
+{
+ return pib::PibSqlite3::getScheme();;
+}
+
+const std::string&
+KeyChain::getDefaultTpmScheme()
+{
+#if defined(NDN_CXX_HAVE_OSX_SECURITY) && defined(NDN_CXX_WITH_OSX_KEYCHAIN)
+ return tpm::BackEndOsx::getScheme();;
+#else
+ return tpm::BackEndFile::getScheme();
+#endif // defined(NDN_CXX_HAVE_OSX_SECURITY) && defined(NDN_CXX_WITH_OSX_KEYCHAIN)
+}
+
+const std::string&
+KeyChain::getDefaultPibLocator()
+{
+ if (!s_defaultPibLocator.empty())
+ return s_defaultPibLocator;
+
+ if (getenv("NDN_CLIENT_PIB") != nullptr) {
+ s_defaultPibLocator = getenv("NDN_CLIENT_PIB");
+ }
+ else {
+ ConfigFile config;
+ s_defaultPibLocator = config.getParsedConfiguration().get<std::string>("pib", getDefaultPibScheme() + ":");
+ }
+
+ return s_defaultPibLocator;
+}
+
+const std::string&
+KeyChain::getDefaultTpmLocator()
+{
+ if (!s_defaultTpmLocator.empty())
+ return s_defaultTpmLocator;
+
+ if (getenv("NDN_CLIENT_TPM") != nullptr) {
+ s_defaultTpmLocator = getenv("NDN_CLIENT_TPM");
+ }
+ else {
+ ConfigFile config;
+ s_defaultTpmLocator = config.getParsedConfiguration().get<std::string>("tpm", getDefaultTpmScheme() + ":");
+ }
+
+ return s_defaultTpmLocator;
+}
+
+
+// Other defaults
+
+const SigningInfo&
+KeyChain::getDefaultSigningInfo()
+{
+ static SigningInfo signingInfo;
+ return signingInfo;
+}
+
+const KeyParams&
+KeyChain::getDefaultKeyParams()
+{
+ static EcdsaKeyParams keyParams;
+ return keyParams;
+}
+
+//
+
+KeyChain::KeyChain()
+ : KeyChain(getDefaultPibLocator(), getDefaultTpmLocator(), true)
+{
+}
+
+KeyChain::KeyChain(const std::string& pibLocator, const std::string& tpmLocator, bool allowReset)
+{
+ // PIB Locator
+ std::string pibScheme, pibLocation;
+ std::tie(pibScheme, pibLocation) = parseAndCheckPibLocator(pibLocator);
+ std::string canonicalPibLocator = pibScheme + ":" + pibLocation;
+
+ // Create PIB
+ m_pib = createPib(canonicalPibLocator);
+ std::string oldTpmLocator;
+ try {
+ oldTpmLocator = m_pib->getTpmLocator();
+ }
+ catch (const Pib::Error&) {
+ // TPM locator is not set in PIB yet.
+ }
+
+ // TPM Locator
+ std::string tpmScheme, tpmLocation;
+ std::tie(tpmScheme, tpmLocation) = parseAndCheckTpmLocator(tpmLocator);
+ std::string canonicalTpmLocator = tpmScheme + ":" + tpmLocation;
+
+ if (canonicalPibLocator == getDefaultPibLocator()) {
+ // Default PIB must use default TPM
+ if (!oldTpmLocator.empty() && oldTpmLocator != getDefaultTpmLocator()) {
+ m_pib->reset();
+ canonicalTpmLocator = getDefaultTpmLocator();
+ }
+ }
+ else {
+ // non-default PIB check consistency
+ if (!oldTpmLocator.empty() && oldTpmLocator != canonicalTpmLocator) {
+ if (allowReset)
+ m_pib->reset();
+ else
+ BOOST_THROW_EXCEPTION(LocatorMismatchError("TPM locator supplied does not match TPM locator in PIB: " +
+ oldTpmLocator + " != " + canonicalTpmLocator));
+ }
+ }
+
+ // note that key mismatch may still happen if the TPM locator is initially set to a
+ // wrong one or if the PIB was shared by more than one TPMs before. This is due to the
+ // old PIB does not have TPM info, new pib should not have this problem.
+ m_tpm = createTpm(canonicalTpmLocator);
+ m_pib->setTpmLocator(canonicalTpmLocator);
+}
+
+KeyChain::~KeyChain() = default;
+
+// public: management
+
+Identity
+KeyChain::createIdentity(const Name& identityName, const KeyParams& params)
+{
+ Identity id = m_pib->addIdentity(identityName);
+
+ Key key;
+ try {
+ key = id.getDefaultKey();
+ }
+ catch (const Pib::Error&) {
+ key = createKey(id, params);
+ }
+
+ try {
+ key.getDefaultCertificate();
+ }
+ catch (const Pib::Error&) {
+ selfSign(key);
+ }
+
+ return id;
+}
+
+void
+KeyChain::deleteIdentity(const Identity& identity)
+{
+ BOOST_ASSERT(static_cast<bool>(identity));
+
+ Name identityName = identity.getName();
+
+ for (const auto& key : identity.getKeys()) {
+ m_tpm->deleteKey(key.getName());
+ }
+
+ m_pib->removeIdentity(identityName);
+}
+
+void
+KeyChain::setDefaultIdentity(const Identity& identity)
+{
+ BOOST_ASSERT(static_cast<bool>(identity));
+
+ m_pib->setDefaultIdentity(identity.getName());
+}
+
+Key
+KeyChain::createKey(const Identity& identity, const KeyParams& params)
+{
+ BOOST_ASSERT(static_cast<bool>(identity));
+
+ // create key in TPM
+ Name keyName = m_tpm->createKey(identity.getName(), params);
+
+ // set up key info in PIB
+ ConstBufferPtr pubKey = m_tpm->getPublicKey(keyName);
+ Key key = identity.addKey(pubKey->buf(), pubKey->size(), keyName);
+ selfSign(key);
+
+ return key;
+}
+
+void
+KeyChain::deleteKey(const Identity& identity, const Key& key)
+{
+ BOOST_ASSERT(static_cast<bool>(identity));
+ BOOST_ASSERT(static_cast<bool>(key));
+
+ Name keyName = key.getName();
+ if (identity.getName() != key.getIdentity()) {
+ BOOST_THROW_EXCEPTION(std::invalid_argument("Identity `" + identity.getName().toUri() + "` "
+ "does match key `" + keyName.toUri() + "`"));
+ }
+
+ identity.removeKey(keyName);
+ m_tpm->deleteKey(keyName);
+}
+
+void
+KeyChain::setDefaultKey(const Identity& identity, const Key& key)
+{
+ BOOST_ASSERT(static_cast<bool>(identity));
+ BOOST_ASSERT(static_cast<bool>(key));
+
+ if (identity.getName() != key.getIdentity())
+ BOOST_THROW_EXCEPTION(std::invalid_argument("Identity `" + identity.getName().toUri() + "` "
+ "does match key `" + key.getName().toUri() + "`"));
+
+ identity.setDefaultKey(key.getName());
+}
+
+void
+KeyChain::addCertificate(const Key& key, const Certificate& certificate)
+{
+ BOOST_ASSERT(static_cast<bool>(key));
+
+ if (key.getName() != certificate.getKeyName() ||
+ !std::equal(certificate.getContent().value_begin(), certificate.getContent().value_end(),
+ key.getPublicKey().begin()))
+ BOOST_THROW_EXCEPTION(std::invalid_argument("Key `" + key.getName().toUri() + "` "
+ "does match certificate `" + certificate.getName().toUri() + "`"));
+
+ key.addCertificate(certificate);
+}
+
+void
+KeyChain::deleteCertificate(const Key& key, const Name& certificateName)
+{
+ BOOST_ASSERT(static_cast<bool>(key));
+
+ if (!Certificate::isValidName(certificateName)) {
+ BOOST_THROW_EXCEPTION(std::invalid_argument("Wrong certificate name `" + certificateName.toUri() + "`"));
+ }
+
+ key.removeCertificate(certificateName);
+}
+
+void
+KeyChain::setDefaultCertificate(const Key& key, const Certificate& cert)
+{
+ BOOST_ASSERT(static_cast<bool>(key));
+
+ try {
+ addCertificate(key, cert);
+ }
+ catch (const Pib::Error&) { // force to overwrite the existing certificates
+ key.removeCertificate(cert.getName());
+ addCertificate(key, cert);
+ }
+ key.setDefaultCertificate(cert.getName());
+}
+
+shared_ptr<SafeBag>
+KeyChain::exportSafeBag(const Certificate& certificate, const char* pw, size_t pwLen)
+{
+ Name identity = certificate.getIdentity();
+ Name keyName = certificate.getKeyName();
+
+ ConstBufferPtr encryptedKey;
+ try {
+ encryptedKey = m_tpm->exportPrivateKey(keyName, pw, pwLen);
+ }
+ catch (const tpm::BackEnd::Error&) {
+ BOOST_THROW_EXCEPTION(Error("Private `" + keyName.toUri() + "` key does not exist"));
+ }
+
+ return make_shared<SafeBag>(certificate, *encryptedKey);
+}
+
+void
+KeyChain::importSafeBag(const SafeBag& safeBag, const char* pw, size_t pwLen)
+{
+ Data certData = safeBag.getCertificate();
+ Certificate cert(std::move(certData));
+ Name identity = cert.getIdentity();
+ Name keyName = cert.getKeyName();
+ const Buffer publicKeyBits = cert.getPublicKey();
+
+ if (m_tpm->hasKey(keyName)) {
+ BOOST_THROW_EXCEPTION(Error("Private key `" + keyName.toUri() + "` already exists"));
+ }
+
+ try {
+ Identity existingId = m_pib->getIdentity(identity);
+ existingId.getKey(keyName);
+ BOOST_THROW_EXCEPTION(Error("Public key `" + keyName.toUri() + "` already exists"));
+ }
+ catch (const Pib::Error&) {
+ // Either identity or key doesn't exist. OK to import.
+ }
+
+ try {
+ m_tpm->importPrivateKey(keyName,
+ safeBag.getEncryptedKeyBag().buf(), safeBag.getEncryptedKeyBag().size(),
+ pw, pwLen);
+ }
+ catch (const std::runtime_error&) {
+ BOOST_THROW_EXCEPTION(Error("Fail to import private key `" + keyName.toUri() + "`"));
+ }
+
+ // check the consistency of private key and certificate
+ const uint8_t content[] = {0x01, 0x02, 0x03, 0x04};
+ ConstBufferPtr sigBits;
+ try {
+ sigBits = m_tpm->sign(content, 4, keyName, DigestAlgorithm::SHA256);
+ }
+ catch (const std::runtime_error&) {
+ m_tpm->deleteKey(keyName);
+ BOOST_THROW_EXCEPTION(Error("Invalid private key `" + keyName.toUri() + "`"));
+ }
+ bool isVerified = false;
+ {
+ using namespace transform;
+ PublicKey publicKey;
+ publicKey.loadPkcs8(publicKeyBits.buf(), publicKeyBits.size());
+ bufferSource(content, sizeof(content)) >> verifierFilter(DigestAlgorithm::SHA256, publicKey,
+ sigBits->buf(), sigBits->size())
+ >> boolSink(isVerified);
+ }
+ if (!isVerified) {
+ m_tpm->deleteKey(keyName);
+ BOOST_THROW_EXCEPTION(Error("Certificate `" + cert.getName().toUri() + "` "
+ "and private key `" + keyName.toUri() + "` do not match"));
+ }
+
+ Identity id = m_pib->addIdentity(identity);
+ Key key = id.addKey(cert.getPublicKey().buf(), cert.getPublicKey().size(), keyName);
+ key.addCertificate(cert);
+}
+
+
+// public: signing
+
+void
+KeyChain::sign(Data& data, const SigningInfo& params)
+{
+ Name keyName;
+ SignatureInfo sigInfo;
+ std::tie(keyName, sigInfo) = prepareSignatureInfo(params);
+
+ data.setSignature(Signature(sigInfo));
+
+ EncodingBuffer encoder;
+ data.wireEncode(encoder, true);
+
+ Block sigValue = sign(encoder.buf(), encoder.size(), keyName, params.getDigestAlgorithm());
+
+ data.wireEncode(encoder, sigValue);
+}
+
+void
+KeyChain::sign(Interest& interest, const SigningInfo& params)
+{
+ Name keyName;
+ SignatureInfo sigInfo;
+ std::tie(keyName, sigInfo) = prepareSignatureInfo(params);
+
+ Name signedName = interest.getName();
+ signedName.append(sigInfo.wireEncode()); // signatureInfo
+
+ Block sigValue = sign(signedName.wireEncode().value(), signedName.wireEncode().value_size(),
+ keyName, params.getDigestAlgorithm());
+
+ sigValue.encode();
+ signedName.append(sigValue); // signatureValue
+ interest.setName(signedName);
+}
+
+Block
+KeyChain::sign(const uint8_t* buffer, size_t bufferLength, const SigningInfo& params)
+{
+ Name keyName;
+ SignatureInfo sigInfo;
+ std::tie(keyName, sigInfo) = prepareSignatureInfo(params);
+
+ return sign(buffer, bufferLength, keyName, params.getDigestAlgorithm());
+}
+
+// public: PIB/TPM creation helpers
+
+static inline std::tuple<std::string/*type*/, std::string/*location*/>
+parseLocatorUri(const std::string& uri)
+{
+ size_t pos = uri.find(':');
+ if (pos != std::string::npos) {
+ return std::make_tuple(uri.substr(0, pos), uri.substr(pos + 1));
+ }
+ else {
+ return std::make_tuple(uri, "");
+ }
+}
+
+std::tuple<std::string/*type*/, std::string/*location*/>
+KeyChain::parseAndCheckPibLocator(const std::string& pibLocator)
+{
+ std::string pibScheme, pibLocation;
+ std::tie(pibScheme, pibLocation) = parseLocatorUri(pibLocator);
+
+ if (pibScheme.empty()) {
+ pibScheme = getDefaultPibScheme();
+ }
+
+ auto pibFactory = getPibFactories().find(pibScheme);
+ if (pibFactory == getPibFactories().end()) {
+ BOOST_THROW_EXCEPTION(KeyChain::Error("PIB scheme `" + pibScheme + "` is not supported"));
+ }
+
+ return std::make_tuple(pibScheme, pibLocation);
+}
+
+unique_ptr<Pib>
+KeyChain::createPib(const std::string& pibLocator)
+{
+ std::string pibScheme, pibLocation;
+ std::tie(pibScheme, pibLocation) = parseAndCheckPibLocator(pibLocator);
+ auto pibFactory = getPibFactories().find(pibScheme);
+ BOOST_ASSERT(pibFactory != getPibFactories().end());
+ return unique_ptr<Pib>(new Pib(pibScheme, pibLocation, pibFactory->second(pibLocation)));
+}
+
+std::tuple<std::string/*type*/, std::string/*location*/>
+KeyChain::parseAndCheckTpmLocator(const std::string& tpmLocator)
+{
+ std::string tpmScheme, tpmLocation;
+ std::tie(tpmScheme, tpmLocation) = parseLocatorUri(tpmLocator);
+
+ if (tpmScheme.empty()) {
+ tpmScheme = getDefaultTpmScheme();
+ }
+ auto tpmFactory = getTpmFactories().find(tpmScheme);
+ if (tpmFactory == getTpmFactories().end()) {
+ BOOST_THROW_EXCEPTION(KeyChain::Error("TPM scheme `" + tpmScheme + "` is not supported"));
+ }
+
+ return std::make_tuple(tpmScheme, tpmLocation);
+}
+
+unique_ptr<Tpm>
+KeyChain::createTpm(const std::string& tpmLocator)
+{
+ std::string tpmScheme, tpmLocation;
+ std::tie(tpmScheme, tpmLocation) = parseAndCheckTpmLocator(tpmLocator);
+ auto tpmFactory = getTpmFactories().find(tpmScheme);
+ BOOST_ASSERT(tpmFactory != getTpmFactories().end());
+ return unique_ptr<Tpm>(new Tpm(tpmScheme, tpmLocation, tpmFactory->second(tpmLocation)));
+}
+
+// private: signing
+
+Certificate
+KeyChain::selfSign(Key& key)
+{
+ Certificate certificate;
+
+ // set name
+ Name certificateName = key.getName();
+ certificateName
+ .append("self")
+ .appendVersion();
+ certificate.setName(certificateName);
+
+ // set metainfo
+ certificate.setContentType(tlv::ContentType_Key);
+ certificate.setFreshnessPeriod(time::hours(1));
+
+ // set content
+ certificate.setContent(key.getPublicKey().buf(), key.getPublicKey().size());
+
+ // set signature-info
+ SignatureInfo sigInfo;
+ sigInfo.setKeyLocator(key.getName());
+ sigInfo.setSignatureType(getSignatureType(key.getKeyType(), DigestAlgorithm::SHA256));
+ sigInfo.setValidityPeriod(ValidityPeriod(time::system_clock::now(),
+ time::system_clock::now() + time::days(1000 * 3365)));
+ certificate.setSignature(Signature(sigInfo));
+
+ EncodingBuffer encoder;
+ certificate.wireEncode(encoder, true);
+ Block sigValue = sign(encoder.buf(), encoder.size(), key.getName(), DigestAlgorithm::SHA256);
+ certificate.wireEncode(encoder, sigValue);
+
+ key.addCertificate(certificate);
+ return certificate;
+}
+
+std::tuple<Name, SignatureInfo>
+KeyChain::prepareSignatureInfo(const SigningInfo& params)
+{
+ SignatureInfo sigInfo = params.getSignatureInfo();
+
+ Name identityName;
+ name::Component keyId;
+ Name certificateName;
+
+ pib::Identity identity;
+ pib::Key key;
+
+ switch (params.getSignerType()) {
+ case SigningInfo::SIGNER_TYPE_NULL: {
+ try {
+ identity = m_pib->getDefaultIdentity();
+ }
+ catch (const Pib::Error&) { // no default identity, use sha256 for signing.
+ sigInfo.setSignatureType(tlv::DigestSha256);
+ return std::make_tuple(SigningInfo::getDigestSha256Identity(), sigInfo);
+ }
+ break;
+ }
+ case SigningInfo::SIGNER_TYPE_ID: {
+ try {
+ identity = m_pib->getIdentity(params.getSignerName());
+ }
+ catch (const Pib::Error&) {
+ BOOST_THROW_EXCEPTION(InvalidSigningInfoError("Signing identity `" +
+ params.getSignerName().toUri() + "` does not exist"));
+ }
+ break;
+ }
+ case SigningInfo::SIGNER_TYPE_KEY: {
+ Name identityName = extractIdentityFromKeyName(params.getSignerName());
+
+ try {
+ identity = m_pib->getIdentity(identityName);
+ key = identity.getKey(params.getSignerName());
+ identity = Identity(); // we will use the PIB key instance, so reset identity;
+ }
+ catch (const Pib::Error&) {
+ BOOST_THROW_EXCEPTION(InvalidSigningInfoError("Signing key `" +
+ params.getSignerName().toUri() + "` does not exist"));
+ }
+ break;
+ }
+ case SigningInfo::SIGNER_TYPE_CERT: {
+ Name identityName = extractIdentityFromCertName(params.getSignerName());
+ Name keyName = extractKeyNameFromCertName(params.getSignerName());
+
+ try {
+ identity = m_pib->getIdentity(identityName);
+ key = identity.getKey(keyName);
+ }
+ catch (const Pib::Error&) {
+ BOOST_THROW_EXCEPTION(InvalidSigningInfoError("Signing certificate `" +
+ params.getSignerName().toUri() + "` does not exist"));
+ }
+
+ break;
+ }
+ case SigningInfo::SIGNER_TYPE_SHA256: {
+ sigInfo.setSignatureType(tlv::DigestSha256);
+ return std::make_tuple(SigningInfo::getDigestSha256Identity(), sigInfo);
+ }
+ default: {
+ BOOST_THROW_EXCEPTION(InvalidSigningInfoError("Unrecognized signer type " +
+ boost::lexical_cast<std::string>(params.getSignerType())));
+ }
+ }
+
+ if (!identity && !key) {
+ BOOST_THROW_EXCEPTION(InvalidSigningInfoError("Cannot determine signing parameters"));
+ }
+
+ if (identity && !key) {
+ try {
+ key = identity.getDefaultKey();
+ }
+ catch (const Pib::Error&) {
+ BOOST_THROW_EXCEPTION(InvalidSigningInfoError("Signing identity `" + identity.getName().toUri() +
+ "` does not have default certificate"));
+ }
+ }
+
+ BOOST_ASSERT(key);
+
+ sigInfo.setSignatureType(getSignatureType(key.getKeyType(), params.getDigestAlgorithm()));
+ sigInfo.setKeyLocator(KeyLocator(key.getName()));
+ return std::make_tuple(key.getName(), sigInfo);
+}
+
+Block
+KeyChain::sign(const uint8_t* buf, size_t size,
+ const Name& keyName, DigestAlgorithm digestAlgorithm) const
+{
+ if (keyName == SigningInfo::getDigestSha256Identity())
+ return Block(tlv::SignatureValue, crypto::sha256(buf, size));
+
+ return Block(tlv::SignatureValue, m_tpm->sign(buf, size, keyName, digestAlgorithm));
+}
+
+tlv::SignatureTypeValue
+KeyChain::getSignatureType(KeyType keyType, DigestAlgorithm digestAlgorithm)
+{
+ switch (keyType) {
+ case KeyType::RSA:
+ return tlv::SignatureSha256WithRsa;
+ case KeyType::EC:
+ return tlv::SignatureSha256WithEcdsa;
+ default:
+ BOOST_THROW_EXCEPTION(Error("Unsupported key types"));
+ }
+}
+
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/src/security/v2/key-chain.hpp b/src/security/v2/key-chain.hpp
new file mode 100644
index 0000000..ca394a5
--- /dev/null
+++ b/src/security/v2/key-chain.hpp
@@ -0,0 +1,508 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 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_SECURITY_V2_KEY_CHAIN_HPP
+#define NDN_SECURITY_V2_KEY_CHAIN_HPP
+
+#include "certificate.hpp"
+#include "../key-params.hpp"
+#include "../pib/pib.hpp"
+#include "../safe-bag.hpp"
+#include "../signing-info.hpp"
+#include "../tpm/tpm.hpp"
+#include "../../interest.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+
+/**
+ * @brief The interface of signing key management.
+ *
+ * The KeyChain class provides an interface to manage entities related to packet signing,
+ * such as Identity, Key, and Certificates. It consists of two parts: a private key module
+ * (TPM) and a public key information base (PIB). Managing signing key and its related
+ * entities through KeyChain interface guarantees the consistency between TPM and PIB.
+ */
+class KeyChain : noncopyable
+{
+public:
+ class Error : public std::runtime_error
+ {
+ public:
+ explicit
+ Error(const std::string& what)
+ : std::runtime_error(what)
+ {
+ }
+ };
+
+ /**
+ * @brief Error indicating that the supplied TPM locator does not match the locator stored in PIB.
+ */
+ class LocatorMismatchError : public Error
+ {
+ public:
+ explicit
+ LocatorMismatchError(const std::string& what)
+ : Error(what)
+ {
+ }
+ };
+
+ /**
+ * @brief Error indicating that the supplied SigningInfo is invalid.
+ */
+ class InvalidSigningInfoError : public Error
+ {
+ public:
+ explicit
+ InvalidSigningInfoError(const std::string& what)
+ : Error(what)
+ {
+ }
+ };
+
+ /**
+ * @brief Constructor to create KeyChain with default PIB and TPM.
+ *
+ * Default PIB and TPM are platform-dependent and can be overriden system-wide or
+ * individually for the user.
+ *
+ * @sa manpage ndn-client.conf
+ *
+ * @todo Add detailed description about config file behavior here
+ */
+ KeyChain();
+
+ /**
+ * @brief KeyChain constructor
+ *
+ * @sa manpage ndn-client.conf
+ *
+ * @param pibLocator PIB locator, e.g., pib-sqlite3:/example/dir
+ * @param tpmLocator TPM locator, e.g., tpm-memory:
+ * @param allowReset if true, the PIB will be reset when the supplied tpmLocator
+ * mismatches the one in PIB
+ */
+ KeyChain(const std::string& pibLocator, const std::string& tpmLocator, bool allowReset = false);
+
+ ~KeyChain();
+
+ const Pib&
+ getPib() const
+ {
+ return *m_pib;
+ }
+
+ const Tpm&
+ getTpm() const
+ {
+ return *m_tpm;
+ }
+
+public: // Identity management
+ /**
+ * @brief Create an identity @p identityName.
+ *
+ * This method will check if the identity exists in PIB and whether the identity has a
+ * default key and default certificate. If the identity does not exist, this method will
+ * create the identity in PIB. If the identity's default key does not exist, this method
+ * will create a key pair and set it as the identity's default key. If the key's default
+ * certificate is missing, this method will create a self-signed certificate for the key.
+ *
+ * If @p identityName did not exist and no default identity was selected before, the created
+ * identity will be set as the default identity
+ *
+ * @param identityName The name of the identity.
+ * @param params The key parameters if a key needs to be created for the identity (default:
+ * ECDSA key with random key id)
+ * @return The created Identity instance.
+ */
+ Identity
+ createIdentity(const Name& identityName, const KeyParams& params = getDefaultKeyParams());
+
+ /**
+ * @brief delete @p identity.
+ *
+ * @pre @p identity must be valid.
+ * @post @p identity becomes invalid.
+ */
+ void
+ deleteIdentity(const Identity& identity);
+
+ /**
+ * @brief Set @p identity as the default identity.
+ * @pre @p identity must be valid.
+ */
+ void
+ setDefaultIdentity(const Identity& identity);
+
+public: // Key management
+ /**
+ * @brief Create a key for @p identity according to @p params.
+ *
+ * @param identity reference to a valid Identity object
+ * @param params The key parameters if a key needs to be created for the identity (default:
+ * ECDSA key with random key id)
+ *
+ * If @p identity had no default key selected, the created key will be set as the default for
+ * this identity.
+ *
+ * This method will also create a self-signed certificate for the created key.
+ * @pre @p identity must be valid.
+ */
+ Key
+ createKey(const Identity& identity, const KeyParams& params = getDefaultKeyParams());
+
+ /**
+ * @brief Delete a key @p key of @p identity.
+ *
+ * @pre @p identity must be valid.
+ * @pre @p key must be valid.
+ * @post @p key becomes invalid.
+ * @throw std::invalid_argument @p key does not belong to @p identity
+ */
+ void
+ deleteKey(const Identity& identity, const Key& key);
+
+ /**
+ * @brief Set @p key as the default key of @p identity.
+ *
+ * @pre @p identity must be valid.
+ * @pre @p key must be valid.
+ * @throw std::invalid_argument @p key does not belong to @p identity
+ */
+ void
+ setDefaultKey(const Identity& identity, const Key& key);
+
+public: // Certificate management
+ /**
+ * @brief Add a certificate @p certificate for @p key
+ *
+ * If @p key had no default certificate selected, the added certificate will be set as the
+ * default certificate for this key.
+ *
+ * @note This method overwrites certificate with the same name, without considering the
+ * implicit digest.
+ *
+ * @pre @p key must be valid.
+ * @throw std::invalid_argument @p key does not match @p certificate
+ * @throw Pib::Error a certificate with the same name already exists
+ */
+ void
+ addCertificate(const Key& key, const Certificate& certificate);
+
+ /**
+ * @brief delete a certificate with name @p certificateName of @p key.
+ *
+ * If the certificate @p certificateName does not exist, this method has no effect.
+ *
+ * @pre @p key must be valid.
+ * @throw std::invalid_argument @p certificateName does not follow certificate naming convention.
+ */
+ void
+ deleteCertificate(const Key& key, const Name& certificateName);
+
+ /**
+ * @brief Set @p cert as the default certificate of @p key.
+ *
+ * The certificate @p cert will be added to the @p key, potentially overriding existing
+ * certificate if it has the same name (without considering implicit digest).
+ *
+ * @pre @p key must be valid.
+ */
+ void
+ setDefaultCertificate(const Key& key, const Certificate& cert);
+
+public: // signing
+ /**
+ * @brief Sign data according to the supplied signing information.
+ *
+ * This method uses the supplied signing information @p params to create the SignatureInfo block:
+ * - it selects a private key and its certificate to sign the packet
+ * - sets the KeyLocator field with the certificate name, and
+ * - adds other requested information to the SignatureInfo block.
+ *
+ * After that, the method assigns the created SignatureInfo to the data packets, generate a
+ * signature and sets as part of the SignatureValue block.
+ *
+ * @note The exception throwing semantics has changed from v1::KeyChain.
+ * If the requested identity/key/certificate does not exist, it will **not** be created
+ * and exception will be thrown.
+ *
+ * @param data The data to sign
+ * @param params The signing parameters.
+ * @throw Error signing fails
+ * @throw InvalidSigningInfoError invalid @p params is specified or specified identity, key,
+ * or certificate does not exist
+ * @see SigningInfo
+ */
+ void
+ sign(Data& data, const SigningInfo& params = getDefaultSigningInfo());
+
+ /**
+ * @brief Sign interest according to the supplied signing information
+ *
+ * This method uses the supplied signing information @p params to create the SignatureInfo block:
+ * - it selects a private key and its certificate to sign the packet
+ * - sets the KeyLocator field with the certificate name, and
+ * - adds other requested information to the SignatureInfo block.
+ *
+ * After that, the method appends the created SignatureInfo to the interest name, generate a
+ * signature and appends it as part of the SignatureValue block to the interest name.
+ *
+ * @note The exception throwing semantics has changed from v1::KeyChain. If the requested
+ * identity/key/certificate does not exist, it will **not** be created and exception
+ * will be thrown.
+ *
+ * @param interest The interest to sign
+ * @param params The signing parameters.
+ * @throw Error signing fails
+ * @throw InvalidSigningInfoError invalid @p params is specified or specified identity, key,
+ * or certificate does not exist
+ * @see SigningInfo
+ * @see docs/specs/signed-interest.rst
+ */
+ void
+ sign(Interest& interest, const SigningInfo& params = getDefaultSigningInfo());
+
+ /**
+ * @brief Sign buffer according to the supplied signing information @p params
+ *
+ * If @p params refers to an identity, the method selects the default key of the identity.
+ * If @p params refers to a key or certificate, the method select the corresponding key.
+ *
+ * @param buffer The buffer to sign
+ * @param bufferLength The buffer size
+ * @param params The signing parameters.
+ * @return a SignatureValue TLV block
+ * @throw Error signing fails
+ * @see SigningInfo
+ */
+ Block
+ sign(const uint8_t* buffer, size_t bufferLength, const SigningInfo& params = getDefaultSigningInfo());
+
+public: // export & import
+ /**
+ * @brief export a certificate of name @p certificateName and its corresponding private key.
+ *
+ * @param certificate The certificate to export.
+ * @param pw The password to secure the private key.
+ * @param pwLen The length of password.
+ * @return A SafeBag carrying the certificate and encrypted private key.
+ * @throw Error the certificate or private key does not exist
+ */
+ shared_ptr<SafeBag>
+ exportSafeBag(const Certificate& certificate, const char* pw, size_t pwLen);
+
+ /**
+ * @brief Import a pair of certificate and its corresponding private key encapsulated in a SafeBag.
+ *
+ * If the certificate and key are imported properly, the default setting will be updated as if
+ * a new key and certificate is added into KeyChain.
+ *
+ * @param safeBag The encoded data to import.
+ * @param pw The password to secure the private key.
+ * @param pwLen The length of password.
+ * @throw Error any of following conditions:
+ * - the safebag cannot be decoded or its content does not match;
+ * - private key cannot be imported;
+ * - a private/public key of the same name already exists;
+ * - a certificate of the same name already exists.
+ */
+ void
+ importSafeBag(const SafeBag& safeBag, const char* pw, size_t pwLen);
+
+NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+ /**
+ * @brief Derive SignatureTypeValue according to key type and digest algorithm.
+ */
+ static tlv::SignatureTypeValue
+ getSignatureType(KeyType keyType, DigestAlgorithm digestAlgorithm);
+
+public: // PIB & TPM backend registry
+ /**
+ * @brief Register a new PIB backend
+ * @param scheme Name for the registered PIB backend scheme
+ *
+ * @note This interface is implementation detail and may change without notice.
+ */
+ template<class PibBackendType>
+ static void
+ registerPibBackend(const std::string& scheme);
+
+ /**
+ * @brief Register a new TPM backend
+ * @param scheme Name for the registered TPM backend scheme
+ *
+ * @note This interface is implementation detail and may change without notice.
+ */
+ template<class TpmBackendType>
+ static void
+ registerTpmBackend(const std::string& scheme);
+
+private:
+ typedef std::map<std::string, function<unique_ptr<pib::PibImpl>(const std::string& location)>> PibFactories;
+ typedef std::map<std::string, function<unique_ptr<tpm::BackEnd>(const std::string& location)>> TpmFactories;
+
+ static PibFactories&
+ getPibFactories();
+
+ static TpmFactories&
+ getTpmFactories();
+
+ static std::tuple<std::string/*type*/, std::string/*location*/>
+ parseAndCheckPibLocator(const std::string& pibLocator);
+
+ static std::tuple<std::string/*type*/, std::string/*location*/>
+ parseAndCheckTpmLocator(const std::string& tpmLocator);
+
+ static const std::string&
+ getDefaultPibScheme();
+
+ static const std::string&
+ getDefaultTpmScheme();
+
+ /**
+ * @brief Create a PIB according to @p pibLocator
+ */
+ static unique_ptr<Pib>
+ createPib(const std::string& pibLocator);
+
+ /**
+ * @brief Create a TPM according to @p tpmLocator
+ */
+ static unique_ptr<Tpm>
+ createTpm(const std::string& tpmLocator);
+
+NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+ static const std::string&
+ getDefaultPibLocator();
+
+ static const std::string&
+ getDefaultTpmLocator();
+
+private: // signing
+ /**
+ * @brief Generate a self-signed certificate for a public key.
+ *
+ * The self-signed certificate will also be added into PIB
+ *
+ * @param keyName The name of the public key
+ * @return The generated certificate
+ */
+ Certificate
+ selfSign(Key& key);
+
+ /**
+ * @brief Prepare a SignatureInfo TLV according to signing information and return the signing
+ * key name
+ *
+ * @param sigInfo The SignatureInfo to prepare.
+ * @param params The signing parameters.
+ * @return The signing key name and prepared SignatureInfo.
+ * @throw InvalidSigningInfoError when the requested signing method cannot be satisfied.
+ */
+ std::tuple<Name, SignatureInfo>
+ prepareSignatureInfo(const SigningInfo& params);
+
+ /**
+ * @brief Generate a SignatureValue block for a buffer @p buf with size @p size using
+ * a key with name @p keyName and digest algorithm @p digestAlgorithm.
+ */
+ Block
+ sign(const uint8_t* buf, size_t size, const Name& keyName, DigestAlgorithm digestAlgorithm) const;
+
+public:
+ static const SigningInfo&
+ getDefaultSigningInfo();
+
+ static const KeyParams&
+ getDefaultKeyParams();
+
+private:
+ std::unique_ptr<Pib> m_pib;
+ std::unique_ptr<Tpm> m_tpm;
+
+ static std::string s_defaultPibLocator;
+ static std::string s_defaultTpmLocator;
+};
+
+template<class PibType>
+inline void
+KeyChain::registerPibBackend(const std::string& scheme)
+{
+ getPibFactories().emplace(scheme, [] (const std::string& locator) {
+ return unique_ptr<pib::PibImpl>(new PibType(locator));
+ });
+}
+
+template<class TpmType>
+inline void
+KeyChain::registerTpmBackend(const std::string& scheme)
+{
+ getTpmFactories().emplace(scheme, [] (const std::string& locator) {
+ return unique_ptr<tpm::BackEnd>(new TpmType(locator));
+ });
+}
+
+/**
+ * @brief Register Pib backend class in KeyChain
+ *
+ * This macro should be placed once in the implementation file of the
+ * Pib backend class within the namespace where the type is declared.
+ *
+ * @note This interface is implementation detail and may change without notice.
+ */
+#define NDN_CXX_V2_KEYCHAIN_REGISTER_PIB_BACKEND(PibType) \
+static class NdnCxxAuto ## PibType ## PibRegistrationClass \
+{ \
+public: \
+ NdnCxxAuto ## PibType ## PibRegistrationClass() \
+ { \
+ ::ndn::security::v2::KeyChain::registerPibBackend<PibType>(PibType::getScheme()); \
+ } \
+} ndnCxxAuto ## PibType ## PibRegistrationVariable
+
+/**
+ * @brief Register Tpm backend class in KeyChain
+ *
+ * This macro should be placed once in the implementation file of the
+ * Tpm backend class within the namespace where the type is declared.
+ *
+ * @note This interface is implementation detail and may change without notice.
+ */
+#define NDN_CXX_V2_KEYCHAIN_REGISTER_TPM_BACKEND(TpmType) \
+static class NdnCxxAuto ## TpmType ## TpmRegistrationClass \
+{ \
+public: \
+ NdnCxxAuto ## TpmType ## TpmRegistrationClass() \
+ { \
+ ::ndn::security::v2::KeyChain::registerTpmBackend<TpmType>(TpmType::getScheme()); \
+ } \
+} ndnCxxAuto ## TpmType ## TpmRegistrationVariable
+
+} // namespace v2
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_SECURITY_V2_KEY_CHAIN_HPP
diff --git a/tests/key-chain-fixture.hpp b/tests/key-chain-fixture.hpp
index e9218a6..9301b23 100644
--- a/tests/key-chain-fixture.hpp
+++ b/tests/key-chain-fixture.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -23,6 +23,7 @@
#define NDN_TESTS_KEY_CHAIN_FIXTURE_HPP
#include "security/key-chain.hpp"
+#include "security/v2/key-chain.hpp"
#include "boost-test.hpp"
#include "identity-management-fixture.hpp"
@@ -76,6 +77,8 @@
}
boost::filesystem::remove_all(m_pibDir);
+ const_cast<std::string&>(security::v2::KeyChain::getDefaultPibLocator()).clear();
+ const_cast<std::string&>(security::v2::KeyChain::getDefaultTpmLocator()).clear();
}
protected:
diff --git a/tests/unit-tests/security/signing-helpers.t.cpp b/tests/unit-tests/security/signing-helpers.t.cpp
index b29954c..eeefcbc 100644
--- a/tests/unit-tests/security/signing-helpers.t.cpp
+++ b/tests/unit-tests/security/signing-helpers.t.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -58,7 +58,7 @@
{
SigningInfo info = signingWithSha256();
BOOST_CHECK_EQUAL(info.getSignerType(), SigningInfo::SIGNER_TYPE_SHA256);
- BOOST_CHECK_EQUAL(info.getSignerName(), SigningInfo::EMPTY_NAME);
+ BOOST_CHECK_EQUAL(info.getSignerName(), SigningInfo::getEmptyName());
}
BOOST_AUTO_TEST_SUITE_END() // TestSigningHelpers
diff --git a/tests/unit-tests/security/signing-info.t.cpp b/tests/unit-tests/security/signing-info.t.cpp
index 6613fea..5b63ae7 100644
--- a/tests/unit-tests/security/signing-info.t.cpp
+++ b/tests/unit-tests/security/signing-info.t.cpp
@@ -43,7 +43,7 @@
SigningInfo info;
BOOST_CHECK_EQUAL(info.getSignerType(), SigningInfo::SIGNER_TYPE_NULL);
- BOOST_CHECK_EQUAL(info.getSignerName(), SigningInfo::EMPTY_NAME);
+ BOOST_CHECK_EQUAL(info.getSignerName(), SigningInfo::getEmptyName());
BOOST_CHECK_EQUAL(info.getDigestAlgorithm(), DigestAlgorithm::SHA256);
const SignatureInfo& sigInfo = info.getSignatureInfo();
@@ -82,12 +82,12 @@
info.setSha256Signing();
BOOST_CHECK_EQUAL(info.getSignerType(), SigningInfo::SIGNER_TYPE_SHA256);
- BOOST_CHECK_EQUAL(info.getSignerName(), SigningInfo::EMPTY_NAME);
+ BOOST_CHECK_EQUAL(info.getSignerName(), SigningInfo::getEmptyName());
BOOST_CHECK_EQUAL(info.getDigestAlgorithm(), DigestAlgorithm::SHA256);
SigningInfo infoSha(SigningInfo::SIGNER_TYPE_SHA256);
BOOST_CHECK_EQUAL(infoSha.getSignerType(), SigningInfo::SIGNER_TYPE_SHA256);
- BOOST_CHECK_EQUAL(infoSha.getSignerName(), SigningInfo::EMPTY_NAME);
+ BOOST_CHECK_EQUAL(infoSha.getSignerName(), SigningInfo::getEmptyName());
BOOST_CHECK_EQUAL(infoSha.getDigestAlgorithm(), DigestAlgorithm::SHA256);
}
@@ -102,7 +102,7 @@
BOOST_CHECK(info1.getSignatureInfo() == si);
- SigningInfo info2(SigningInfo::SIGNER_TYPE_NULL, SigningInfo::EMPTY_NAME, si);
+ SigningInfo info2(SigningInfo::SIGNER_TYPE_NULL, SigningInfo::getEmptyName(), si);
BOOST_CHECK(info2.getSignatureInfo() == si);
}
@@ -110,7 +110,7 @@
{
SigningInfo infoDefault("");
BOOST_CHECK_EQUAL(infoDefault.getSignerType(), SigningInfo::SIGNER_TYPE_NULL);
- BOOST_CHECK_EQUAL(infoDefault.getSignerName(), SigningInfo::EMPTY_NAME);
+ BOOST_CHECK_EQUAL(infoDefault.getSignerName(), SigningInfo::getEmptyName());
BOOST_CHECK_EQUAL(infoDefault.getDigestAlgorithm(), DigestAlgorithm::SHA256);
SigningInfo infoId("id:/my-identity");
@@ -130,7 +130,7 @@
SigningInfo infoSha("id:/localhost/identity/digest-sha256");
BOOST_CHECK_EQUAL(infoSha.getSignerType(), SigningInfo::SIGNER_TYPE_SHA256);
- BOOST_CHECK_EQUAL(infoSha.getSignerName(), SigningInfo::EMPTY_NAME);
+ BOOST_CHECK_EQUAL(infoSha.getSignerName(), SigningInfo::getEmptyName());
BOOST_CHECK_EQUAL(infoSha.getDigestAlgorithm(), DigestAlgorithm::SHA256);
}
diff --git a/tests/unit-tests/security/v2/key-chain.t.cpp b/tests/unit-tests/security/v2/key-chain.t.cpp
new file mode 100644
index 0000000..ddc020b
--- /dev/null
+++ b/tests/unit-tests/security/v2/key-chain.t.cpp
@@ -0,0 +1,410 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 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/v2/key-chain.hpp"
+#include "security/signing-helpers.hpp"
+
+#include "boost-test.hpp"
+#include "unit-tests/test-home-env-saver.hpp"
+#include "key-chain-fixture.hpp"
+#include "validator.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_FIXTURE_TEST_SUITE(TestKeyChain, TestHomeEnvSaver)
+
+template<class Path>
+class TestHomeAndPibFixture : public TestHomeFixture<Path>
+{
+public:
+ TestHomeAndPibFixture()
+ {
+ unsetenv("NDN_CLIENT_PIB");
+ unsetenv("NDN_CLIENT_TPM");
+ }
+};
+
+struct PibPathConfigFileHome
+{
+ const std::string PATH = "build/config-file-home/";
+};
+
+BOOST_FIXTURE_TEST_CASE(ConstructorNormalConfig, TestHomeAndPibFixture<PibPathConfigFileHome>)
+{
+ createClientConf({"pib=pib-memory:", "tpm=tpm-memory:"});
+
+ BOOST_REQUIRE_NO_THROW(KeyChain());
+
+ KeyChain keyChain;
+ BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-memory:");
+ BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-memory:");
+ BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-memory:");
+}
+
+struct PibPathConfigFileEmptyHome
+{
+ const std::string PATH = "build/config-file-empty-home/";
+};
+
+BOOST_FIXTURE_TEST_CASE(ConstructorEmptyConfig, TestHomeAndPibFixture<PibPathConfigFileEmptyHome>)
+{
+ createClientConf({"pib=pib-memory:"});
+
+#if defined(NDN_CXX_HAVE_OSX_SECURITY)
+ std::string oldHOME;
+ if (std::getenv("OLD_HOME"))
+ oldHOME = std::getenv("OLD_HOME");
+
+ std::string HOME;
+ if (std::getenv("HOME"))
+ HOME = std::getenv("HOME");
+
+ if (!oldHOME.empty())
+ setenv("HOME", oldHOME.c_str(), 1);
+ else
+ unsetenv("HOME");
+#endif
+
+ BOOST_REQUIRE_NO_THROW(KeyChain());
+ KeyChain keyChain;
+ BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-memory:");
+
+#if defined(NDN_CXX_HAVE_OSX_SECURITY)
+ BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-osxkeychain:");
+ BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-osxkeychain:");
+#else
+ BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-file:");
+ BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-file:");
+#endif
+
+#if defined(NDN_CXX_HAVE_OSX_SECURITY)
+ if (!HOME.empty())
+ setenv("HOME", HOME.c_str(), 1);
+ else
+ unsetenv("HOME");
+
+ if (!oldHOME.empty())
+ setenv("OLD_HOME", oldHOME.c_str(), 1);
+ else
+ unsetenv("OLD_HOME");
+#endif
+}
+
+struct PibPathConfigFileEmpty2Home
+{
+ const std::string PATH = "build/config-file-empty2-home/";
+};
+
+BOOST_FIXTURE_TEST_CASE(ConstructorEmpty2Config, TestHomeAndPibFixture<PibPathConfigFileEmpty2Home>)
+{
+ createClientConf({"tpm=tpm-memory:"});
+
+ BOOST_REQUIRE_NO_THROW(KeyChain());
+
+ KeyChain keyChain;
+ BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-sqlite3:");
+ BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-memory:");
+ BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-memory:");
+}
+
+struct PibPathConfigFileMalformedHome
+{
+ const std::string PATH = "build/config-file-malformed-home/";
+};
+
+BOOST_FIXTURE_TEST_CASE(ConstructorMalConfig, TestHomeAndPibFixture<PibPathConfigFileMalformedHome>)
+{
+ createClientConf({"pib=lord", "tpm=ring"});
+
+ BOOST_REQUIRE_THROW(KeyChain(), KeyChain::Error); // Wrong configuration. Error expected.
+}
+
+struct PibPathConfigFileMalformed2Home
+{
+ const std::string PATH = "build/config-file-malformed2-home/";
+};
+
+BOOST_FIXTURE_TEST_CASE(ConstructorMal2Config, TestHomeAndPibFixture<PibPathConfigFileMalformed2Home>)
+{
+ createClientConf({"pib=pib-sqlite3:%PATH%", "tpm=just-wrong"});
+
+ BOOST_REQUIRE_THROW(KeyChain(), KeyChain::Error); // Wrong configuration. Error expected.
+}
+
+BOOST_AUTO_TEST_CASE(KeyChainWithCustomTpmAndPib)
+{
+ BOOST_REQUIRE_NO_THROW((KeyChain("pib-memory", "tpm-memory")));
+ BOOST_REQUIRE_NO_THROW((KeyChain("pib-memory:", "tpm-memory:")));
+ BOOST_REQUIRE_NO_THROW((KeyChain("pib-memory:/something", "tpm-memory:/something")));
+
+ KeyChain keyChain("pib-memory", "tpm-memory");
+ BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-memory:");
+ BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-memory:");
+ BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-memory:");
+}
+
+// @TODO Delete after upgrade of the existing management fixture
+class IdentityManagementFixture
+{
+public:
+ IdentityManagementFixture()
+ : m_keyChain("pib-memory:", "tpm-memory:")
+ {
+ }
+
+ Identity
+ addIdentity(const Name& identityName, const KeyParams& params = KeyChain::getDefaultKeyParams())
+ {
+ Identity identity = m_keyChain.createIdentity(identityName, params);
+ m_identities.push_back(identity);
+ return identity;
+ }
+
+protected:
+ KeyChain m_keyChain;
+
+private:
+ std::vector<Identity> m_identities;
+};
+
+BOOST_FIXTURE_TEST_CASE(Management, IdentityManagementFixture)
+{
+ Name identityName("/test/id");
+ Name identity2Name("/test/id2");
+
+ BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 0);
+ BOOST_REQUIRE_THROW(m_keyChain.getPib().getDefaultIdentity(), Pib::Error);
+
+ // Create identity
+ Identity id = m_keyChain.createIdentity(identityName);
+ BOOST_CHECK(id);
+ BOOST_CHECK(m_keyChain.getPib().getIdentities().find(identityName) != m_keyChain.getPib().getIdentities().end());
+ // The first added identity becomes the default identity
+ BOOST_REQUIRE_NO_THROW(m_keyChain.getPib().getDefaultIdentity());
+ // The default key of the added identity must exist
+ Key key;
+ BOOST_REQUIRE_NO_THROW(key = id.getDefaultKey());
+ // The default certificate of the default key must exist
+ BOOST_REQUIRE_NO_THROW(key.getDefaultCertificate());
+
+ // Delete key
+ Name key1Name = key.getName();
+ BOOST_CHECK_NO_THROW(id.getKey(key1Name));
+ BOOST_CHECK_EQUAL(id.getKeys().size(), 1);
+ m_keyChain.deleteKey(id, key);
+ // The key instance should not be valid any more
+ BOOST_CHECK(!key);
+ BOOST_CHECK_THROW(id.getKey(key1Name), Pib::Error);
+ BOOST_CHECK_EQUAL(id.getKeys().size(), 0);
+
+ // Create another key
+ m_keyChain.createKey(id);
+ // The added key becomes the default key.
+ BOOST_REQUIRE_NO_THROW(id.getDefaultKey());
+ Key key2 = id.getDefaultKey();
+ BOOST_REQUIRE(key2);
+ BOOST_CHECK_NE(key2.getName(), key1Name);
+ BOOST_CHECK_EQUAL(id.getKeys().size(), 1);
+ BOOST_REQUIRE_NO_THROW(key2.getDefaultCertificate());
+
+ // Create the third key
+ Key key3 = m_keyChain.createKey(id);
+ BOOST_CHECK(key3.getName() != key2.getName());
+ // The added key will not be the default key, because the default key already exists
+ BOOST_CHECK(id.getDefaultKey().getName() == key2.getName());
+ BOOST_CHECK_EQUAL(id.getKeys().size(), 2);
+ BOOST_REQUIRE_NO_THROW(key3.getDefaultCertificate());
+
+ // Delete cert
+ BOOST_CHECK_EQUAL(key3.getCertificates().size(), 1);
+ Certificate key3Cert1 = *key3.getCertificates().begin();
+ Name key3CertName = key3Cert1.getName();
+ m_keyChain.deleteCertificate(key3, key3CertName);
+ BOOST_CHECK_EQUAL(key3.getCertificates().size(), 0);
+ BOOST_REQUIRE_THROW(key3.getDefaultCertificate(), Pib::Error);
+
+ // Add cert
+ m_keyChain.addCertificate(key3, key3Cert1);
+ BOOST_CHECK_EQUAL(key3.getCertificates().size(), 1);
+ BOOST_REQUIRE_NO_THROW(key3.getDefaultCertificate());
+ // Overwrite the same cert again, should throw Pib::Error.
+ BOOST_REQUIRE_THROW(m_keyChain.addCertificate(key3, key3Cert1), Pib::Error);
+ BOOST_CHECK_EQUAL(key3.getCertificates().size(), 1);
+ // Add another cert
+ Certificate key3Cert2 = key3Cert1;
+ Name key3Cert2Name = key3.getName();
+ key3Cert2Name.append("Self");
+ key3Cert2Name.appendVersion();
+ key3Cert2.setName(key3Cert2Name);
+ m_keyChain.addCertificate(key3, key3Cert2);
+ BOOST_CHECK_EQUAL(key3.getCertificates().size(), 2);
+
+ // Default certificate setting
+ BOOST_CHECK_EQUAL(key3.getDefaultCertificate().getName(), key3CertName);
+ m_keyChain.setDefaultCertificate(key3, key3Cert2);
+ BOOST_CHECK_EQUAL(key3.getDefaultCertificate().getName(), key3Cert2Name);
+
+ // Default key setting
+ BOOST_CHECK_EQUAL(id.getDefaultKey().getName(), key2.getName());
+ m_keyChain.setDefaultKey(id, key3);
+ BOOST_CHECK_EQUAL(id.getDefaultKey().getName(), key3.getName());
+
+ // Default identity setting
+ Identity id2 = m_keyChain.createIdentity(identity2Name);
+ BOOST_CHECK_EQUAL(m_keyChain.getPib().getDefaultIdentity().getName(), id.getName());
+ m_keyChain.setDefaultIdentity(id2);
+ BOOST_CHECK_EQUAL(m_keyChain.getPib().getDefaultIdentity().getName(), id2.getName());
+
+ // Delete identity
+ m_keyChain.deleteIdentity(id);
+ // The identity instance should not be valid any more
+ BOOST_CHECK(!id);
+ BOOST_REQUIRE_THROW(m_keyChain.getPib().getIdentity(identityName), Pib::Error);
+ BOOST_CHECK(m_keyChain.getPib().getIdentities().find(identityName) == m_keyChain.getPib().getIdentities().end());
+}
+
+BOOST_FIXTURE_TEST_CASE(GeneralSigningInterface, IdentityManagementFixture)
+{
+ Identity id = addIdentity("/id");
+ Key key = id.getDefaultKey();
+ Certificate cert = key.getDefaultCertificate();
+
+ std::list<SigningInfo> signingInfos = {
+ SigningInfo(),
+
+ SigningInfo(SigningInfo::SIGNER_TYPE_ID, id.getName()),
+ signingByIdentity(id.getName()),
+
+ SigningInfo(SigningInfo::SIGNER_TYPE_KEY, key.getName()),
+ signingByKey(key.getName()),
+
+ SigningInfo(SigningInfo::SIGNER_TYPE_CERT, cert.getName()),
+ signingByCertificate(cert.getName()),
+
+ SigningInfo(SigningInfo::SIGNER_TYPE_SHA256),
+ signingWithSha256()
+ };
+
+ for (const auto& signingInfo : signingInfos) {
+ BOOST_TEST_MESSAGE("SigningInfo: " << signingInfo);
+ Data data("/data");
+ Interest interest("/interest");
+
+ if (signingInfo.getSignerType() == SigningInfo::SIGNER_TYPE_NULL) {
+ m_keyChain.sign(data);
+ m_keyChain.sign(interest);
+ }
+ else {
+ m_keyChain.sign(data, signingInfo);
+ m_keyChain.sign(interest, signingInfo);
+ }
+
+ Signature interestSignature(interest.getName()[-2].blockFromValue(), interest.getName()[-1].blockFromValue());
+
+ if (signingInfo.getSignerType() == SigningInfo::SIGNER_TYPE_SHA256) {
+ BOOST_CHECK_EQUAL(data.getSignature().getType(), tlv::DigestSha256);
+ BOOST_CHECK_EQUAL(interestSignature.getType(), tlv::DigestSha256);
+
+ BOOST_CHECK(Validator::verifySha256Digest(data));
+ BOOST_CHECK(Validator::verifySha256Digest(interest));
+ }
+ else {
+ BOOST_CHECK_EQUAL(data.getSignature().getType(), tlv::SignatureSha256WithEcdsa);
+ BOOST_CHECK_EQUAL(interestSignature.getType(), tlv::SignatureSha256WithEcdsa);
+
+ BOOST_CHECK_EQUAL(data.getSignature().getKeyLocator().getName(), cert.getName().getPrefix(-2));
+ BOOST_CHECK_EQUAL(interestSignature.getKeyLocator().getName(), cert.getName().getPrefix(-2));
+
+ BOOST_CHECK(Validator::verifySignature(data, key.getPublicKey()));
+ BOOST_CHECK(Validator::verifySignature(interest, key.getPublicKey()));
+ }
+ }
+}
+
+BOOST_FIXTURE_TEST_CASE(PublicKeySigningDefaults, IdentityManagementFixture)
+{
+ Data data("/test/data");
+
+ // Identity will be created with generated key and self-signed cert with default parameters
+ BOOST_CHECK_THROW(m_keyChain.sign(data, signingByIdentity("/non-existing/identity")), KeyChain::InvalidSigningInfoError);
+
+ // Create identity with ECDSA key and the corresponding self-signed certificate
+ Identity id = addIdentity("/ndn/test/ecdsa", EcdsaKeyParams());
+ BOOST_CHECK_NO_THROW(m_keyChain.sign(data, signingByIdentity(id.getName())));
+ BOOST_CHECK_EQUAL(data.getSignature().getType(),
+ KeyChain::getSignatureType(EcdsaKeyParams().getKeyType(), DigestAlgorithm::SHA256));
+ BOOST_CHECK(id.getName().isPrefixOf(data.getSignature().getKeyLocator().getName()));
+
+ // Create identity with RSA key and the corresponding self-signed certificate
+ id = addIdentity("/ndn/test/rsa", RsaKeyParams());
+ BOOST_CHECK_NO_THROW(m_keyChain.sign(data, signingByIdentity(id.getName())));
+ BOOST_CHECK_EQUAL(data.getSignature().getType(),
+ KeyChain::getSignatureType(RsaKeyParams().getKeyType(), DigestAlgorithm::SHA256));
+ BOOST_CHECK(id.getName().isPrefixOf(data.getSignature().getKeyLocator().getName()));
+}
+
+BOOST_FIXTURE_TEST_CASE(ExportImport, IdentityManagementFixture)
+{
+ Identity id = addIdentity("/TestKeyChain/ExportIdentity/");
+ Certificate cert = id.getDefaultKey().getDefaultCertificate();
+
+ shared_ptr<SafeBag> exported = m_keyChain.exportSafeBag(cert, "1234", 4);
+ Block block = exported->wireEncode();
+
+ m_keyChain.deleteIdentity(id);
+
+ BOOST_CHECK_EQUAL(m_keyChain.getTpm().hasKey(cert.getKeyName()), false);
+ BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 0);
+
+ SafeBag imported;
+ imported.wireDecode(block);
+ m_keyChain.importSafeBag(imported, "1234", 4);
+
+ BOOST_CHECK_EQUAL(m_keyChain.getTpm().hasKey(cert.getKeyName()), true);
+ BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 1);
+ BOOST_REQUIRE_NO_THROW(m_keyChain.getPib().getIdentity(cert.getIdentity()));
+ Identity newId = m_keyChain.getPib().getIdentity(cert.getIdentity());
+ BOOST_CHECK_EQUAL(newId.getKeys().size(), 1);
+ BOOST_REQUIRE_NO_THROW(newId.getKey(cert.getKeyName()));
+ Key newKey = newId.getKey(cert.getKeyName());
+ BOOST_CHECK_EQUAL(newKey.getCertificates().size(), 1);
+ BOOST_REQUIRE_NO_THROW(newKey.getCertificate(cert.getName()));
+
+ m_keyChain.deleteIdentity(newId);
+ BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 0);
+ BOOST_CHECK_EQUAL(m_keyChain.getTpm().hasKey(cert.getKeyName()), false);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestKeyChain
+BOOST_AUTO_TEST_SUITE_END() // Tmp
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit-tests/security/v2/validator.hpp b/tests/unit-tests/security/v2/validator.hpp
new file mode 100644
index 0000000..960796c
--- /dev/null
+++ b/tests/unit-tests/security/v2/validator.hpp
@@ -0,0 +1,102 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 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/transform.hpp"
+#include "encoding/buffer-stream.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+
+// TODO: Remove after the new validator is merged
+class Validator
+{
+public:
+ static bool
+ verifySignature(const uint8_t* data, size_t dataLen,
+ const uint8_t* sig, size_t sigLen,
+ const Buffer& key)
+ {
+ using namespace transform;
+
+ PublicKey pKey;
+ bool result = false;
+ pKey.loadPkcs8(key.buf(), key.size());
+ bufferSource(data, dataLen) >> verifierFilter(DigestAlgorithm::SHA256, pKey, sig, sigLen) >> boolSink(result);
+
+ return result;
+ }
+
+ static bool
+ verifySignature(const Data& data, const Buffer& key)
+ {
+ return verifySignature(data.wireEncode().value(), data.wireEncode().value_size() - data.getSignature().getValue().size(),
+ data.getSignature().getValue().value(), data.getSignature().getValue().value_size(),
+ key);
+ }
+
+ static bool
+ verifySignature(const Interest& interest, const Buffer& key)
+ {
+ const Name& interestName = interest.getName();
+ const Block& nameBlock = interestName.wireEncode();
+ const Block& sigValue = interestName[-1].blockFromValue();
+
+ return verifySignature(nameBlock.value(), nameBlock.value_size() - interestName[-1].size(),
+ sigValue.value(), sigValue.value_size(),
+ key);
+ }
+
+ static bool
+ verifySha256Digest(const uint8_t* data, size_t dataLen,
+ const uint8_t* sig, size_t sigLen)
+ {
+ using namespace transform;
+
+ OBufferStream os;
+ bufferSource(data, dataLen) >> digestFilter(DigestAlgorithm::SHA256) >> streamSink(os);
+ ConstBufferPtr digest = os.buf();
+
+ return std::equal(digest->begin(), digest->end(), sig);
+ }
+
+ static bool
+ verifySha256Digest(const Data& data)
+ {
+ return verifySha256Digest(data.wireEncode().value(), data.wireEncode().value_size() - data.getSignature().getValue().size(),
+ data.getSignature().getValue().value(), data.getSignature().getValue().value_size());
+ }
+
+ static bool
+ verifySha256Digest(const Interest& interest)
+ {
+ const Name& interestName = interest.getName();
+ const Block& nameBlock = interestName.wireEncode();
+ const Block& sigValue = interestName[-1].blockFromValue();
+
+ return verifySha256Digest(nameBlock.value(), nameBlock.value_size() - interestName[-1].size(),
+ sigValue.value(), sigValue.value_size());
+ }
+};
+
+} // namespace v2
+} // namespace security
+} // namespace ndn