security: Construct KeyChain from configuration file.

Change-Id: Iaddac24e2c4e199fdde83fa1d0067a87e18729c4
Refs: #1532
diff --git a/src/security/key-chain.cpp b/src/security/key-chain.cpp
new file mode 100644
index 0000000..58c3570
--- /dev/null
+++ b/src/security/key-chain.cpp
@@ -0,0 +1,323 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/**
+ * Copyright (c) 2013-2014,  Regents of the University of California.
+ * All rights reserved.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ *
+ * This file licensed under New BSD License.  See COPYING for detailed information about
+ * ndn-cxx library copyright, permissions, and redistribution restrictions.
+ *
+ * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>
+ */
+
+#include "key-chain.hpp"
+
+#include "sec-public-info-sqlite3.hpp"
+#include "sec-tpm-file.hpp"
+
+#ifdef NDN_CXX_HAVE_OSX_SECURITY
+#include "sec-tpm-osx.hpp"
+#endif
+
+#include "../util/random.hpp"
+#include "../util/config-file.hpp"
+
+namespace ndn {
+
+KeyChain::KeyChain()
+  : m_pib(0)
+  , m_tpm(0)
+{
+
+  ConfigFile config;
+  const ConfigFile::Parsed& parsed = config.getParsedConfiguration();
+
+  std::string pibName;
+  try
+    {
+      pibName = parsed.get<std::string>("pib");
+    }
+  catch (boost::property_tree::ptree_bad_path& error)
+    {
+      // pib is not specified, take the default
+    }
+  catch (boost::property_tree::ptree_bad_data& error)
+    {
+      throw ConfigFile::Error(error.what());
+    }
+
+  std::string tpmName;
+  try
+    {
+      tpmName = parsed.get<std::string>("tpm");
+    }
+  catch (boost::property_tree::ptree_bad_path& error)
+    {
+      // tpm is not specified, take the default
+    }
+  catch (boost::property_tree::ptree_bad_data& error)
+    {
+      throw ConfigFile::Error(error.what());
+    }
+
+
+  if (pibName.empty() || pibName == "sqlite3")
+    m_pib = new SecPublicInfoSqlite3;
+  else
+    throw Error("PIB type '" + pibName + "' is not supported");
+
+  if (tpmName.empty())
+#if defined(NDN_CXX_HAVE_OSX_SECURITY) and defined(NDN_CXX_WITH_OSX_KEYCHAIN)
+    m_tpm = new SecTpmOsx();
+#else
+    m_tpm = new SecTpmFile();
+#endif // defined(NDN_CXX_HAVE_OSX_SECURITY) and defined(NDN_CXX_WITH_OSX_KEYCHAIN)
+  else if (tpmName == "osx-keychain")
+#if defined(NDN_CXX_HAVE_OSX_SECURITY)
+    m_tpm = new SecTpmOsx();
+#else
+    throw Error("TPM type '" + tpmName + "' is not supported on this platform");
+#endif // NDN_CXX_HAVE_OSX_SECURITY
+  else if (tpmName == "file")
+    m_tpm = new SecTpmFile();
+  else
+    throw Error("TPM type '" + tpmName + "' is not supported");
+}
+
+KeyChain::KeyChain(const std::string& pibName,
+                   const std::string& tpmName)
+  : m_pib(0)
+  , m_tpm(0)
+{
+  if (pibName == "sqlite3")
+    m_pib = new SecPublicInfoSqlite3;
+  else
+    throw Error("PIB type '" + pibName + "' is not supported");
+
+  if (tpmName == "file")
+    m_tpm = new SecTpmFile;
+#if defined(NDN_CXX_HAVE_OSX_SECURITY)
+  else if (tpmName == "osx-keychain")
+    m_tpm = new SecTpmOsx();
+#endif //NDN_CXX_HAVE_OSX_SECURITY
+  else
+    throw Error("TPM type '" + tpmName + "' is not supported");
+}
+
+shared_ptr<IdentityCertificate>
+KeyChain::prepareUnsignedIdentityCertificate(const Name& keyName,
+  const Name& signingIdentity,
+  const time::system_clock::TimePoint& notBefore,
+  const time::system_clock::TimePoint& notAfter,
+  const std::vector<CertificateSubjectDescription>& subjectDescription)
+{
+  if (keyName.size() < 1)
+    return shared_ptr<IdentityCertificate>();
+
+  std::string keyIdPrefix = keyName.get(-1).toEscapedString().substr(0, 4);
+  if (keyIdPrefix != "ksk-" && keyIdPrefix != "dsk-")
+    return shared_ptr<IdentityCertificate>();
+
+  shared_ptr<IdentityCertificate> certificate = make_shared<IdentityCertificate>();
+  Name certName;
+
+  if (signingIdentity.isPrefixOf(keyName))
+    {
+      certName.append(signingIdentity)
+        .append("KEY")
+        .append(keyName.getSubName(signingIdentity.size()))
+        .append("ID-CERT")
+        .appendVersion();
+    }
+  else
+    {
+      certName.append(keyName.getPrefix(-1))
+        .append("KEY")
+        .append(keyName.get(-1))
+        .append("ID-CERT")
+        .appendVersion();
+    }
+
+  certificate->setName(certName);
+  certificate->setNotBefore(notBefore);
+  certificate->setNotAfter(notAfter);
+
+  shared_ptr<PublicKey> publicKey;
+  try
+    {
+      publicKey = m_pib->getPublicKey(keyName);
+    }
+  catch (SecPublicInfo::Error& e)
+    {
+      return shared_ptr<IdentityCertificate>();
+    }
+  certificate->setPublicKeyInfo(*publicKey);
+
+  if (subjectDescription.empty())
+    {
+      CertificateSubjectDescription subjectName("2.5.4.41", keyName.getPrefix(-1).toUri());
+      certificate->addSubjectDescription(subjectName);
+    }
+  else
+    {
+      std::vector<CertificateSubjectDescription>::const_iterator sdIt =
+        subjectDescription.begin();
+      std::vector<CertificateSubjectDescription>::const_iterator sdEnd =
+        subjectDescription.end();
+      for(; sdIt != sdEnd; sdIt++)
+        certificate->addSubjectDescription(*sdIt);
+    }
+
+  certificate->encode();
+
+  return certificate;
+}
+
+Signature
+KeyChain::sign(const uint8_t* buffer, size_t bufferLength, const Name& certificateName)
+{
+  if (!m_pib->doesCertificateExist(certificateName))
+    throw SecPublicInfo::Error("Requested certificate ["
+                               + certificateName.toUri() + "] doesn't exist");
+
+  Name keyName = IdentityCertificate::certificateNameToPublicKeyName(certificateName);
+
+  SignatureSha256WithRsa signature;
+  // implicit conversion should take care
+  signature.setKeyLocator(certificateName.getPrefix(-1));
+
+  // For temporary usage, we support RSA + SHA256 only, but will support more.
+  signature.setValue(m_tpm->signInTpm(buffer, bufferLength, keyName, DIGEST_ALGORITHM_SHA256));
+  return signature;
+}
+
+shared_ptr<IdentityCertificate>
+KeyChain::selfSign(const Name& keyName)
+{
+  shared_ptr<PublicKey> pubKey;
+  try
+    {
+      pubKey = m_pib->getPublicKey(keyName); // may throw an exception.
+    }
+  catch (SecPublicInfo::Error& e)
+    {
+      return shared_ptr<IdentityCertificate>();
+    }
+
+  shared_ptr<IdentityCertificate> certificate = make_shared<IdentityCertificate>();
+
+  Name certificateName = keyName.getPrefix(-1);
+  certificateName.append("KEY").append(keyName.get(-1)).append("ID-CERT").appendVersion();
+
+  certificate->setName(certificateName);
+  certificate->setNotBefore(time::system_clock::now());
+  certificate->setNotAfter(time::system_clock::now() + time::days(7300)/* ~20 years*/);
+  certificate->setPublicKeyInfo(*pubKey);
+  certificate->addSubjectDescription(CertificateSubjectDescription("2.5.4.41", keyName.toUri()));
+  certificate->encode();
+
+  selfSign(*certificate);
+  return certificate;
+}
+
+void
+KeyChain::selfSign(IdentityCertificate& cert)
+{
+  Name keyName = IdentityCertificate::certificateNameToPublicKeyName(cert.getName());
+  if (!m_tpm->doesKeyExistInTpm(keyName, KEY_CLASS_PRIVATE))
+    throw SecTpm::Error("private key does not exist!");
+
+  SignatureSha256WithRsa signature;
+  signature.setKeyLocator(cert.getName().getPrefix(-1)); // implicit conversion should take care
+
+  // For temporary usage, we support RSA + SHA256 only, but will support more.
+  signPacketWrapper(cert, signature, keyName, DIGEST_ALGORITHM_SHA256);
+}
+
+shared_ptr<SecuredBag>
+KeyChain::exportIdentity(const Name& identity, const std::string& passwordStr)
+{
+  if (!m_pib->doesIdentityExist(identity))
+    throw SecPublicInfo::Error("Identity does not exist!");
+
+  Name keyName = m_pib->getDefaultKeyNameForIdentity(identity);
+
+  ConstBufferPtr pkcs5;
+  try
+    {
+      pkcs5 = m_tpm->exportPrivateKeyPkcs5FromTpm(keyName, passwordStr);
+    }
+  catch (SecTpm::Error& e)
+    {
+      throw SecPublicInfo::Error("Fail to export PKCS5 of private key");
+    }
+
+  shared_ptr<IdentityCertificate> cert;
+  try
+    {
+      cert = m_pib->getCertificate(m_pib->getDefaultCertificateNameForKey(keyName));
+    }
+  catch (SecPublicInfo::Error& e)
+    {
+      cert = selfSign(keyName);
+      m_pib->addCertificateAsIdentityDefault(*cert);
+    }
+
+  shared_ptr<SecuredBag> secureBag = make_shared<SecuredBag>(boost::cref(*cert),
+                                                             boost::cref(pkcs5));
+
+  return secureBag;
+}
+
+void
+KeyChain::importIdentity(const SecuredBag& securedBag, const std::string& passwordStr)
+{
+  Name certificateName = securedBag.getCertificate().getName();
+  Name keyName = IdentityCertificate::certificateNameToPublicKeyName(certificateName);
+  Name identity = keyName.getPrefix(-1);
+
+  // Add identity
+  m_pib->addIdentity(identity);
+
+  // Add key
+  m_tpm->importPrivateKeyPkcs5IntoTpm(keyName,
+                                      securedBag.getKey()->buf(),
+                                      securedBag.getKey()->size(),
+                                      passwordStr);
+
+  shared_ptr<PublicKey> pubKey = m_tpm->getPublicKeyFromTpm(keyName.toUri());
+  // HACK! We should set key type according to the pkcs8 info.
+  m_pib->addPublicKey(keyName, KEY_TYPE_RSA, *pubKey);
+  m_pib->setDefaultKeyNameForIdentity(keyName);
+
+  // Add cert
+  m_pib->addCertificateAsIdentityDefault(securedBag.getCertificate());
+}
+
+void
+KeyChain::setDefaultCertificateInternal()
+{
+  m_pib->refreshDefaultCertificate();
+
+  if (!static_cast<bool>(m_pib->defaultCertificate()))
+    {
+      Name defaultIdentity;
+      try
+        {
+          defaultIdentity = m_pib->getDefaultIdentity();
+        }
+      catch (SecPublicInfo::Error& e)
+        {
+          uint32_t random = random::generateWord32();
+          defaultIdentity.append("tmp-identity")
+            .append(reinterpret_cast<uint8_t*>(&random), 4);
+        }
+      createIdentity(defaultIdentity);
+      m_pib->setDefaultIdentity(defaultIdentity);
+      m_pib->refreshDefaultCertificate();
+    }
+}
+
+}