security: Add new v2::KeyChain

Change-Id: I5fdf51ecd96b50db2a7cbf730c6e8b1d9fbe09e9
Refs: #2926
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