security: add HMAC support in KeyChain and SigningInfo

Refs: #3075
Change-Id: Ia1f557ca7c83f4df7f9f87bbe1b4fc214940dcd8
diff --git a/ndn-cxx/security/signing-info.cpp b/ndn-cxx/security/signing-info.cpp
index f4ef2c5..14d9312 100644
--- a/ndn-cxx/security/signing-info.cpp
+++ b/ndn-cxx/security/signing-info.cpp
@@ -21,6 +21,12 @@
 
 #include "ndn-cxx/security/signing-info.hpp"
 
+#include "ndn-cxx/encoding/buffer-stream.hpp"
+#include "ndn-cxx/security/transform/base64-decode.hpp"
+#include "ndn-cxx/security/transform/buffer-source.hpp"
+#include "ndn-cxx/security/transform/digest-filter.hpp"
+#include "ndn-cxx/security/transform/stream-sink.hpp"
+
 namespace ndn {
 namespace security {
 
@@ -45,6 +51,13 @@
   return digestSha256Identity;
 }
 
+const Name&
+SigningInfo::getHmacIdentity()
+{
+  static Name hmacIdentity("/localhost/identity/hmac");
+  return hmacIdentity;
+}
+
 SigningInfo::SigningInfo(SignerType signerType,
                          const Name& signerName,
                          const SignatureInfo& signatureInfo)
@@ -53,11 +66,7 @@
   , m_digestAlgorithm(DigestAlgorithm::SHA256)
   , m_info(signatureInfo)
 {
-  BOOST_ASSERT(signerType == SIGNER_TYPE_NULL ||
-               signerType == SIGNER_TYPE_ID ||
-               signerType == SIGNER_TYPE_KEY ||
-               signerType == SIGNER_TYPE_CERT ||
-               signerType == SIGNER_TYPE_SHA256);
+  BOOST_ASSERT(signerType >= SIGNER_TYPE_NULL && signerType <= SIGNER_TYPE_HMAC);
 }
 
 SigningInfo::SigningInfo(const Identity& identity)
@@ -101,6 +110,10 @@
   else if (scheme == "cert") {
     setSigningCertName(nameArg);
   }
+  else if (scheme == "hmac-sha256") {
+    setSigningHmacKey(nameArg);
+    setDigestAlgorithm(DigestAlgorithm::SHA256);
+  }
   else {
     NDN_THROW(std::invalid_argument("Invalid signing string scheme"));
   }
@@ -133,6 +146,25 @@
 }
 
 SigningInfo&
+SigningInfo::setSigningHmacKey(const std::string& hmacKey)
+{
+  m_type = SIGNER_TYPE_HMAC;
+
+  OBufferStream os;
+  transform::bufferSource(hmacKey) >>
+    transform::base64Decode(false) >>
+    transform::streamSink(os);
+  m_hmacKey = make_shared<transform::PrivateKey>();
+  m_hmacKey->loadRaw(KeyType::HMAC, os.buf()->data(), os.buf()->size());
+
+  // generate key name
+  m_name = getHmacIdentity();
+  m_name.append(name::Component(m_hmacKey->getKeyDigest(DigestAlgorithm::SHA256)));
+
+  return *this;
+}
+
+SigningInfo&
 SigningInfo::setSha256Signing()
 {
   m_type = SIGNER_TYPE_SHA256;
@@ -179,8 +211,9 @@
       return os << "cert:" << si.getSignerName();
     case SigningInfo::SIGNER_TYPE_SHA256:
       return os << "id:" << SigningInfo::getDigestSha256Identity();
+    case SigningInfo::SIGNER_TYPE_HMAC:
+      return os << "id:" << si.getSignerName();
   }
-
   NDN_THROW(std::invalid_argument("Unknown signer type"));
   return os;
 }
diff --git a/ndn-cxx/security/signing-info.hpp b/ndn-cxx/security/signing-info.hpp
index ab60b19..9089429 100644
--- a/ndn-cxx/security/signing-info.hpp
+++ b/ndn-cxx/security/signing-info.hpp
@@ -27,6 +27,7 @@
 #include "ndn-cxx/security/pib/identity.hpp"
 #include "ndn-cxx/security/pib/key.hpp"
 #include "ndn-cxx/security/security-common.hpp"
+#include "ndn-cxx/security/transform/private-key.hpp"
 
 namespace ndn {
 namespace security {
@@ -47,21 +48,23 @@
   };
 
   enum SignerType {
-    /// @brief no signer is specified, use default setting or follow the trust schema
+    /// No signer is specified, use default setting or follow the trust schema.
     SIGNER_TYPE_NULL = 0,
-    /// @brief signer is an identity, use its default key and default certificate
+    /// Signer is an identity, use its default key and default certificate.
     SIGNER_TYPE_ID = 1,
-    /// @brief signer is a key, use its default certificate
+    /// Signer is a key, use its default certificate.
     SIGNER_TYPE_KEY = 2,
-    /// @brief signer is a certificate, use it directly
+    /// Signer is a certificate, use it directly.
     SIGNER_TYPE_CERT = 3,
-    /// @brief use sha256 digest, no signer needs to be specified
+    /// Use a SHA-256 digest only, no signer needs to be specified.
     SIGNER_TYPE_SHA256 = 4,
+    /// Signer is a HMAC key.
+    SIGNER_TYPE_HMAC = 5,
   };
 
 public:
   /**
-   * @brief Constructor
+   * @brief Constructor.
    *
    * @param signerType The type of signer
    * @param signerName The name of signer; interpretation differs per signerType
@@ -75,28 +78,29 @@
               const SignatureInfo& signatureInfo = getEmptySignatureInfo());
 
   /**
-   * @brief Create a signingInfo using pib identity;
+   * @brief Construct from a PIB identity.
    */
   explicit
   SigningInfo(const Identity& identity);
 
   /**
-   * @brief Create a signingInfo using pib key;
+   * @brief Construct from a PIB key.
    */
   explicit
   SigningInfo(const Key& key);
 
   /**
-   * @brief Construct SigningInfo from its string representation
+   * @brief Construct SigningInfo from its string representation.
    *
    * @param signingStr The representative signing string for SigningInfo signing method
    *
-   * Structure of the representative string is as follows:
+   * Syntax of the representative string is as follows:
    * - default signing: "" (empty string)
-   * - signing with a default certificate of a default key for the identity: `id:/my-identity`
-   * - signing with a default certificate of the key: `key:/my-identity/ksk-1`
-   * - signing with the certificate: `cert:/my-identity/KEY/ksk-1/ID-CERT/%FD%01`
-   * - signing with sha256 digest: `id:/localhost/identity/digest-sha256`
+   * - sign with the default certificate of the default key of an identity: `id:/<my-identity>`
+   * - sign with the default certificate of a specific key: `key:/<my-identity>/ksk-1`
+   * - sign with a specific certificate: `cert:/<my-identity>/KEY/ksk-1/ID-CERT/%FD%01`
+   * - sign with HMAC-SHA-256: `hmac-sha256:<base64-encoded-key>`
+   * - sign with SHA-256 (digest only): `id:/localhost/identity/digest-sha256`
    */
   explicit
   SigningInfo(const std::string& signingStr);
@@ -123,7 +127,14 @@
   setSigningCertName(const Name& certificateName);
 
   /**
-   * @brief Set Sha256 as the signing method
+   * @brief Set signer to a base64-encoded HMAC key
+   * @post Change the signerType to SIGNER_TYPE_HMAC
+   */
+  SigningInfo&
+  setSigningHmacKey(const std::string& hmacKey);
+
+  /**
+   * @brief Set SHA-256 as the signing method
    * @post Reset signerName, also change the signerType to SIGNER_TYPE_SHA256
    */
   SigningInfo&
@@ -184,8 +195,15 @@
     return m_key;
   }
 
+  shared_ptr<transform::PrivateKey>
+  getHmacKey() const
+  {
+    BOOST_ASSERT(m_type == SIGNER_TYPE_HMAC);
+    return m_hmacKey;
+  }
+
   /**
-   * @brief Set the digest algorithm for public key operations
+   * @brief Set the digest algorithm for signing operations
    */
   SigningInfo&
   setDigestAlgorithm(const DigestAlgorithm& algorithm)
@@ -195,7 +213,7 @@
   }
 
   /**
-   * @return The digest algorithm for public key operations
+   * @return The digest algorithm for signing operations
    */
   DigestAlgorithm
   getDigestAlgorithm() const
@@ -231,6 +249,12 @@
   static const Name&
   getDigestSha256Identity();
 
+  /**
+   * @brief A localhost identity to indicate that the signature is generated using an HMAC key.
+   */
+  static const Name&
+  getHmacIdentity();
+
 private: // non-member operators
   // NOTE: the following "hidden friend" operators are available via
   //       argument-dependent lookup only and must be defined inline.
@@ -255,6 +279,7 @@
   Name m_name;
   Identity m_identity;
   Key m_key;
+  shared_ptr<transform::PrivateKey> m_hmacKey;
   DigestAlgorithm m_digestAlgorithm;
   SignatureInfo m_info;
 };
diff --git a/ndn-cxx/security/tpm/back-end-file.cpp b/ndn-cxx/security/tpm/back-end-file.cpp
index 22783ae..8aa98c6 100644
--- a/ndn-cxx/security/tpm/back-end-file.cpp
+++ b/ndn-cxx/security/tpm/back-end-file.cpp
@@ -30,11 +30,13 @@
 #include <sys/stat.h>
 
 #include <boost/filesystem.hpp>
+#include <boost/lexical_cast.hpp>
 
 namespace ndn {
 namespace security {
 namespace tpm {
 
+namespace fs = boost::filesystem;
 using transform::PrivateKey;
 
 class BackEndFile::Impl
@@ -44,42 +46,44 @@
   Impl(const std::string& dir)
   {
     if (!dir.empty()) {
-      keystorePath = boost::filesystem::path(dir);
+      m_keystorePath = fs::path(dir);
     }
 #ifdef NDN_CXX_HAVE_TESTS
     else if (std::getenv("TEST_HOME") != nullptr) {
-      keystorePath = boost::filesystem::path(std::getenv("TEST_HOME")) / ".ndn";
+      m_keystorePath = fs::path(std::getenv("TEST_HOME")) / ".ndn";
     }
 #endif // NDN_CXX_HAVE_TESTS
     else if (std::getenv("HOME") != nullptr) {
-      keystorePath = boost::filesystem::path(std::getenv("HOME")) / ".ndn";
+      m_keystorePath = fs::path(std::getenv("HOME")) / ".ndn";
     }
     else {
-      keystorePath = boost::filesystem::current_path() / ".ndn";
+      m_keystorePath = fs::current_path() / ".ndn";
     }
 
-    keystorePath /= "ndnsec-key-file";
-    boost::filesystem::create_directories(keystorePath);
+    m_keystorePath /= "ndnsec-key-file";
+    fs::create_directories(m_keystorePath);
   }
 
-  boost::filesystem::path
-  toFileName(const Name& keyName)
+  fs::path
+  toFileName(const Name& keyName) const
   {
-    std::stringstream os;
+    std::ostringstream os;
     {
       using namespace transform;
-      bufferSource(keyName.wireEncode().wire(), keyName.wireEncode().size()) >>
-        digestFilter(DigestAlgorithm::SHA256) >> hexEncode() >> streamSink(os);
+      bufferSource(keyName.wireEncode().wire(), keyName.wireEncode().size())
+        >> digestFilter(DigestAlgorithm::SHA256)
+        >> hexEncode()
+        >> streamSink(os);
     }
-    return keystorePath / (os.str() + ".privkey");
+    return m_keystorePath / (os.str() + ".privkey");
   }
 
-public:
-  boost::filesystem::path keystorePath;
+private:
+  fs::path m_keystorePath;
 };
 
 BackEndFile::BackEndFile(const std::string& location)
-  : m_impl(new Impl(location))
+  : m_impl(make_unique<Impl>(location))
 {
 }
 
@@ -95,7 +99,7 @@
 bool
 BackEndFile::doHasKey(const Name& keyName) const
 {
-  if (!boost::filesystem::exists(m_impl->toFileName(keyName)))
+  if (!fs::exists(m_impl->toFileName(keyName)))
     return false;
 
   try {
@@ -119,13 +123,23 @@
 unique_ptr<KeyHandle>
 BackEndFile::doCreateKey(const Name& identityName, const KeyParams& params)
 {
+  switch (params.getKeyType()) {
+  case KeyType::RSA:
+  case KeyType::EC:
+    break;
+  default:
+    NDN_THROW(Error("File-based TPM does not support creating a key of type " +
+                    boost::lexical_cast<std::string>(params.getKeyType())));
+  }
+
   shared_ptr<PrivateKey> key(transform::generatePrivateKey(params).release());
   unique_ptr<KeyHandle> keyHandle = make_unique<KeyHandleMem>(key);
 
-  setKeyName(*keyHandle, identityName, params);
+  Name keyName = constructAsymmetricKeyName(*keyHandle, identityName, params);
+  keyHandle->setKeyName(keyName);
 
   try {
-    saveKey(keyHandle->getKeyName(), *key);
+    saveKey(keyName, *key);
     return keyHandle;
   }
   catch (const std::runtime_error&) {
@@ -136,14 +150,14 @@
 void
 BackEndFile::doDeleteKey(const Name& keyName)
 {
-  boost::filesystem::path keyPath(m_impl->toFileName(keyName));
-  if (!boost::filesystem::exists(keyPath))
+  auto keyPath = m_impl->toFileName(keyName);
+  if (!fs::exists(keyPath))
     return;
 
   try {
-    boost::filesystem::remove(keyPath);
+    fs::remove(keyPath);
   }
-  catch (const boost::filesystem::filesystem_error&) {
+  catch (const fs::filesystem_error&) {
     NDN_THROW_NESTED(Error("Cannot remove key file"));
   }
 }
@@ -177,6 +191,17 @@
   }
 }
 
+void
+BackEndFile::doImportKey(const Name& keyName, shared_ptr<transform::PrivateKey> key)
+{
+  try {
+    saveKey(keyName, *key);
+  }
+  catch (const PrivateKey::Error&) {
+    NDN_THROW_NESTED(Error("Cannot import private key"));
+  }
+}
+
 unique_ptr<PrivateKey>
 BackEndFile::loadKey(const Name& keyName) const
 {
diff --git a/ndn-cxx/security/tpm/back-end-file.hpp b/ndn-cxx/security/tpm/back-end-file.hpp
index 658d12f..97c1935 100644
--- a/ndn-cxx/security/tpm/back-end-file.hpp
+++ b/ndn-cxx/security/tpm/back-end-file.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2018 Regents of the University of California.
+ * Copyright (c) 2013-2019 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -74,6 +74,9 @@
   void
   doImportKey(const Name& keyName, const uint8_t* buf, size_t size, const char* pw, size_t pwLen) final;
 
+  void
+  doImportKey(const Name& keyName, shared_ptr<transform::PrivateKey> key) final;
+
 private:
   /**
    * @brief Load a private key with name @p keyName from the key directory.
diff --git a/ndn-cxx/security/tpm/back-end-mem.cpp b/ndn-cxx/security/tpm/back-end-mem.cpp
index 68e49d9..5c20f71 100644
--- a/ndn-cxx/security/tpm/back-end-mem.cpp
+++ b/ndn-cxx/security/tpm/back-end-mem.cpp
@@ -26,6 +26,8 @@
 
 #include <unordered_map>
 
+#include <boost/lexical_cast.hpp>
+
 namespace ndn {
 namespace security {
 namespace tpm {
@@ -39,7 +41,7 @@
 };
 
 BackEndMem::BackEndMem(const std::string&)
-  : m_impl(new Impl)
+  : m_impl(make_unique<Impl>())
 {
 }
 
@@ -70,12 +72,29 @@
 unique_ptr<KeyHandle>
 BackEndMem::doCreateKey(const Name& identityName, const KeyParams& params)
 {
+  switch (params.getKeyType()) {
+  case KeyType::RSA:
+  case KeyType::EC:
+  case KeyType::HMAC:
+    break;
+  default:
+    NDN_THROW(Error("Memory-based TPM does not support creating a key of type " +
+                    boost::lexical_cast<std::string>(params.getKeyType())));
+  }
+
   shared_ptr<PrivateKey> key(transform::generatePrivateKey(params).release());
   unique_ptr<KeyHandle> keyHandle = make_unique<KeyHandleMem>(key);
 
-  setKeyName(*keyHandle, identityName, params);
+  Name keyName;
+  if (params.getKeyType() == KeyType::HMAC) {
+    keyName = constructHmacKeyName(*key, identityName, params);
+  }
+  else {
+    keyName = constructAsymmetricKeyName(*keyHandle, identityName, params);
+  }
+  keyHandle->setKeyName(keyName);
 
-  m_impl->keys[keyHandle->getKeyName()] = key;
+  m_impl->keys[keyName] = std::move(key);
   return keyHandle;
 }
 
@@ -96,14 +115,20 @@
 void
 BackEndMem::doImportKey(const Name& keyName, const uint8_t* buf, size_t size, const char* pw, size_t pwLen)
 {
+  auto key = make_shared<PrivateKey>();
   try {
-    auto key = make_shared<PrivateKey>();
     key->loadPkcs8(buf, size, pw, pwLen);
-    m_impl->keys[keyName] = key;
   }
   catch (const PrivateKey::Error&) {
     NDN_THROW_NESTED(Error("Cannot import private key"));
   }
+  doImportKey(keyName, std::move(key));
+}
+
+void
+BackEndMem::doImportKey(const Name& keyName, shared_ptr<transform::PrivateKey> key)
+{
+  m_impl->keys[keyName] = std::move(key);
 }
 
 } // namespace tpm
diff --git a/ndn-cxx/security/tpm/back-end-mem.hpp b/ndn-cxx/security/tpm/back-end-mem.hpp
index 5d1dd3e..eb96090 100644
--- a/ndn-cxx/security/tpm/back-end-mem.hpp
+++ b/ndn-cxx/security/tpm/back-end-mem.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2018 Regents of the University of California.
+ * Copyright (c) 2013-2019 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -66,6 +66,9 @@
   void
   doImportKey(const Name& keyName, const uint8_t* buf, size_t size, const char* pw, size_t pwLen) final;
 
+  void
+  doImportKey(const Name& keyName, shared_ptr<transform::PrivateKey> key) final;
+
 private:
   class Impl;
   const unique_ptr<Impl> m_impl;
diff --git a/ndn-cxx/security/tpm/back-end-osx.cpp b/ndn-cxx/security/tpm/back-end-osx.cpp
index 9e28aab..afa7b27 100644
--- a/ndn-cxx/security/tpm/back-end-osx.cpp
+++ b/ndn-cxx/security/tpm/back-end-osx.cpp
@@ -29,6 +29,8 @@
 #include <Security/Security.h>
 #include <cstring>
 
+#include <boost/lexical_cast.hpp>
+
 namespace ndn {
 namespace security {
 namespace tpm {
@@ -361,7 +363,8 @@
       break;
     }
     default: {
-      NDN_THROW(Tpm::Error("Failed to generate key pair: Unsupported key type"));
+      NDN_THROW(Error("macOS-based TPM does not support creating a key of type " +
+                      boost::lexical_cast<std::string>(keyType)));
     }
   }
   CFReleaser<CFNumberRef> cfKeySize = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &keySize);
@@ -380,11 +383,12 @@
   }
 
   unique_ptr<KeyHandle> keyHandle = make_unique<KeyHandleOsx>(privateKey.get());
-  setKeyName(*keyHandle, identityName, params);
+  Name keyName = constructAsymmetricKeyName(*keyHandle, identityName, params);
+  keyHandle->setKeyName(keyName);
 
   SecKeychainAttribute attrs[1]; // maximum number of attributes
   SecKeychainAttributeList attrList = {0, attrs};
-  std::string keyUri = keyHandle->getKeyName().toUri();
+  std::string keyUri = keyName.toUri();
   {
     attrs[attrList.count].tag = kSecKeyPrintName;
     attrs[attrList.count].length = keyUri.size();
@@ -497,6 +501,12 @@
   SecKeychainItemModifyAttributesAndData(keychainItem, &attrList, 0, nullptr);
 }
 
+void
+BackEndOsx::doImportKey(const Name& keyName, shared_ptr<transform::PrivateKey> key)
+{
+  NDN_THROW(Error("macOS-based TPM does not support importing a transform::PrivateKey"));
+}
+
 } // namespace tpm
 } // namespace security
 } // namespace ndn
diff --git a/ndn-cxx/security/tpm/back-end-osx.hpp b/ndn-cxx/security/tpm/back-end-osx.hpp
index bbbc159..611ae97 100644
--- a/ndn-cxx/security/tpm/back-end-osx.hpp
+++ b/ndn-cxx/security/tpm/back-end-osx.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2018 Regents of the University of California.
+ * Copyright (c) 2013-2019 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -98,6 +98,9 @@
   void
   doImportKey(const Name& keyName, const uint8_t* buf, size_t size, const char* pw, size_t pwLen) final;
 
+  void
+  doImportKey(const Name& keyName, shared_ptr<transform::PrivateKey> key) final;
+
 private:
   class Impl;
   const unique_ptr<Impl> m_impl;
diff --git a/ndn-cxx/security/tpm/back-end.cpp b/ndn-cxx/security/tpm/back-end.cpp
index 8c54a1c..decbc13 100644
--- a/ndn-cxx/security/tpm/back-end.cpp
+++ b/ndn-cxx/security/tpm/back-end.cpp
@@ -25,6 +25,7 @@
 #include "ndn-cxx/security/pib/key.hpp"
 #include "ndn-cxx/security/transform/buffer-source.hpp"
 #include "ndn-cxx/security/transform/digest-filter.hpp"
+#include "ndn-cxx/security/transform/private-key.hpp"
 #include "ndn-cxx/security/transform/stream-sink.hpp"
 #include "ndn-cxx/encoding/buffer-stream.hpp"
 #include "ndn-cxx/util/random.hpp"
@@ -50,6 +51,10 @@
 unique_ptr<KeyHandle>
 BackEnd::createKey(const Name& identity, const KeyParams& params)
 {
+  if (params.getKeyType() == KeyType::HMAC) {
+    return doCreateKey(identity, params);
+  }
+
   // key name checking
   switch (params.getKeyIdType()) {
     case KeyIdType::USER_SPECIFIED: { // keyId is pre-set.
@@ -107,7 +112,17 @@
 }
 
 void
-BackEnd::setKeyName(KeyHandle& keyHandle, const Name& identity, const KeyParams& params)
+BackEnd::importKey(const Name& keyName, shared_ptr<transform::PrivateKey> key)
+{
+  if (hasKey(keyName)) {
+    NDN_THROW(Error("Key `" + keyName.toUri() + "` already exists"));
+  }
+  doImportKey(keyName, key);
+}
+
+Name
+BackEnd::constructAsymmetricKeyName(const KeyHandle& keyHandle, const Name& identity,
+                                    const KeyParams& params)
 {
   name::Component keyId;
 
@@ -135,7 +150,14 @@
     }
   }
 
-  keyHandle.setKeyName(v2::constructKeyName(identity, keyId));
+  return v2::constructKeyName(identity, keyId);
+}
+
+Name
+BackEnd::constructHmacKeyName(const transform::PrivateKey& key, const Name& identity,
+                              const KeyParams& params)
+{
+  return Name(identity).append(name::Component(key.getKeyDigest(DigestAlgorithm::SHA256)));
 }
 
 bool
diff --git a/ndn-cxx/security/tpm/back-end.hpp b/ndn-cxx/security/tpm/back-end.hpp
index 069bfe1..8309b52 100644
--- a/ndn-cxx/security/tpm/back-end.hpp
+++ b/ndn-cxx/security/tpm/back-end.hpp
@@ -28,6 +28,11 @@
 
 namespace ndn {
 namespace security {
+
+namespace transform {
+class PrivateKey;
+} // namespace transform
+
 namespace tpm {
 
 class KeyHandle;
@@ -119,6 +124,14 @@
   importKey(const Name& keyName, const uint8_t* pkcs8, size_t pkcs8Len, const char* pw, size_t pwLen);
 
   /**
+   * @brief Import a private key.
+   *
+   * @throw Error The key could not be imported.
+   */
+  void
+  importKey(const Name& keyName, shared_ptr<transform::PrivateKey> key);
+
+  /**
    * @brief Check if the TPM is in terminal mode.
    *
    * The default implementation always returns true.
@@ -162,10 +175,16 @@
 
 protected: // static helper methods
   /**
-   * @brief Set the key name in @p keyHandle according to @p identity and @p params.
+   * @brief Construct and return the name of a RSA or EC key, based on @p identity and @p params.
    */
-  static void
-  setKeyName(KeyHandle& keyHandle, const Name& identity, const KeyParams& params);
+  static Name
+  constructAsymmetricKeyName(const KeyHandle& key, const Name& identity, const KeyParams& params);
+
+  /**
+   * @brief Construct and return the name of a HMAC key, based on @p identity and @p params.
+   */
+  static Name
+  constructHmacKeyName(const transform::PrivateKey& key, const Name& identity, const KeyParams& params);
 
 private: // pure virtual methods
   virtual bool
@@ -185,6 +204,9 @@
 
   virtual void
   doImportKey(const Name& keyName, const uint8_t* pkcs8, size_t pkcs8Len, const char* pw, size_t pwLen) = 0;
+
+  virtual void
+  doImportKey(const Name& keyName, shared_ptr<transform::PrivateKey> key) = 0;
 };
 
 } // namespace tpm
diff --git a/ndn-cxx/security/tpm/key-handle.cpp b/ndn-cxx/security/tpm/key-handle.cpp
index d5cea11..56057a2 100644
--- a/ndn-cxx/security/tpm/key-handle.cpp
+++ b/ndn-cxx/security/tpm/key-handle.cpp
@@ -52,18 +52,6 @@
   return doDerivePublicKey();
 }
 
-void
-KeyHandle::setKeyName(const Name& keyName)
-{
-  m_keyName = keyName;
-}
-
-Name
-KeyHandle::getKeyName() const
-{
-  return m_keyName;
-}
-
 } // namespace tpm
 } // namespace security
 } // namespace ndn
diff --git a/ndn-cxx/security/tpm/key-handle.hpp b/ndn-cxx/security/tpm/key-handle.hpp
index 743dac5..0a4fd8a 100644
--- a/ndn-cxx/security/tpm/key-handle.hpp
+++ b/ndn-cxx/security/tpm/key-handle.hpp
@@ -72,11 +72,17 @@
   ConstBufferPtr
   derivePublicKey() const;
 
-  void
-  setKeyName(const Name& keyName);
-
   Name
-  getKeyName() const;
+  getKeyName() const
+  {
+    return m_keyName;
+  }
+
+  void
+  setKeyName(const Name& keyName)
+  {
+    m_keyName = keyName;
+  }
 
 private:
   virtual ConstBufferPtr
diff --git a/ndn-cxx/security/tpm/tpm.cpp b/ndn-cxx/security/tpm/tpm.cpp
index a4398e5..d325955 100644
--- a/ndn-cxx/security/tpm/tpm.cpp
+++ b/ndn-cxx/security/tpm/tpm.cpp
@@ -53,19 +53,10 @@
 Name
 Tpm::createKey(const Name& identityName, const KeyParams& params)
 {
-  switch (params.getKeyType()) {
-    case KeyType::RSA:
-    case KeyType::EC: {
-      unique_ptr<KeyHandle> keyHandle = m_backEnd->createKey(identityName, params);
-      Name keyName = keyHandle->getKeyName();
-      m_keys[keyName] = std::move(keyHandle);
-      return keyName;
-    }
-    default: {
-      NDN_THROW(Error("Failed to create key pair: Unsupported key type " +
-                      boost::lexical_cast<std::string>(params.getKeyType())));
-    }
-  }
+  auto keyHandle = m_backEnd->createKey(identityName, params);
+  auto keyName = keyHandle->getKeyName();
+  m_keys[keyName] = std::move(keyHandle);
+  return keyName;
 }
 
 void
@@ -160,6 +151,12 @@
   m_backEnd->importKey(keyName, pkcs8, pkcs8Len, pw, pwLen);
 }
 
+void
+Tpm::importPrivateKey(const Name& keyName, shared_ptr<transform::PrivateKey> key)
+{
+  m_backEnd->importKey(keyName, std::move(key));
+}
+
 const KeyHandle*
 Tpm::findKey(const Name& keyName) const
 {
diff --git a/ndn-cxx/security/tpm/tpm.hpp b/ndn-cxx/security/tpm/tpm.hpp
index 5fc6636..be31a7e 100644
--- a/ndn-cxx/security/tpm/tpm.hpp
+++ b/ndn-cxx/security/tpm/tpm.hpp
@@ -32,6 +32,10 @@
 namespace ndn {
 namespace security {
 
+namespace transform {
+class PrivateKey;
+} // namespace transform
+
 namespace v2 {
 class KeyChain;
 } // namespace v2
@@ -105,8 +109,9 @@
   /**
    * @brief Verify blob using the key with name @p keyName and using the digest @p digestAlgorithm.
    *
-   * @return true if the signature is valid; false if the signature is not valid;
-   *         `boost::logic::indeterminate` if the key does not exist.
+   * @retval true the signature is valid
+   * @retval false the signature is not valid
+   * @retval indeterminate the key does not exist
    */
   boost::logic::tribool
   verify(const uint8_t* buf, size_t bufLen, const uint8_t* sig, size_t sigLen,
@@ -163,7 +168,9 @@
   /**
    * @brief Create key for @p identityName according to @p params.
    *
-   * The created key is named as: /<identityName>/[keyId]/KEY
+   * The created key is named as follows:
+   * - RSA and EC keys: `/<identityName>/KEY/<keyId>`
+   * - HMAC keys: `/<identityName>/<keyDigest>`
    *
    * @return The key name.
    * @throw Tpm::Error the key already exists or @p params is invalid.
@@ -206,6 +213,12 @@
                    const char* pw, size_t pwLen);
 
   /**
+   * @brief Import a private key.
+   */
+  void
+  importPrivateKey(const Name& keyName, shared_ptr<transform::PrivateKey> key);
+
+  /**
    * @brief Clear the key cache.
    *
    * An empty cache can force Tpm to do key lookup in the back-end.
diff --git a/ndn-cxx/security/transform/private-key.cpp b/ndn-cxx/security/transform/private-key.cpp
index 570c18c..659de11 100644
--- a/ndn-cxx/security/transform/private-key.cpp
+++ b/ndn-cxx/security/transform/private-key.cpp
@@ -23,6 +23,7 @@
 #include "ndn-cxx/security/transform/base64-decode.hpp"
 #include "ndn-cxx/security/transform/base64-encode.hpp"
 #include "ndn-cxx/security/transform/buffer-source.hpp"
+#include "ndn-cxx/security/transform/digest-filter.hpp"
 #include "ndn-cxx/security/transform/stream-sink.hpp"
 #include "ndn-cxx/security/transform/stream-source.hpp"
 #include "ndn-cxx/security/impl/openssl-helper.hpp"
@@ -123,6 +124,32 @@
   }
 }
 
+ConstBufferPtr
+PrivateKey::getKeyDigest(DigestAlgorithm algo) const
+{
+  if (getKeyType() != KeyType::HMAC)
+    NDN_THROW(Error("Digest is not supported for key type " +
+                    boost::lexical_cast<std::string>(getKeyType())));
+
+  const uint8_t* buf = nullptr;
+  size_t len = 0;
+#if OPENSSL_VERSION_NUMBER >= 0x1010000fL
+  buf = EVP_PKEY_get0_hmac(m_impl->key, &len);
+#else
+  const auto* octstr = reinterpret_cast<ASN1_OCTET_STRING*>(EVP_PKEY_get0(m_impl->key));
+  buf = octstr->data;
+  len = octstr->length;
+#endif
+  if (buf == nullptr)
+    NDN_THROW(Error("Failed to obtain raw key pointer"));
+  if (len * 8 != getKeySize())
+    NDN_THROW(Error("Key length mismatch"));
+
+  OBufferStream os;
+  bufferSource(buf, len) >> digestFilter(algo) >> streamSink(os);
+  return os.buf();
+}
+
 void
 PrivateKey::loadRaw(KeyType type, const uint8_t* buf, size_t size)
 {
diff --git a/ndn-cxx/security/transform/private-key.hpp b/ndn-cxx/security/transform/private-key.hpp
index 1e31b8d..85051f3 100644
--- a/ndn-cxx/security/transform/private-key.hpp
+++ b/ndn-cxx/security/transform/private-key.hpp
@@ -77,6 +77,14 @@
   getKeySize() const;
 
   /**
+   * @brief Returns a digest of the private key.
+   *
+   * @note Currently supports only HMAC keys.
+   */
+  ConstBufferPtr
+  getKeyDigest(DigestAlgorithm algo) const;
+
+  /**
    * @brief Load a raw private key from a buffer @p buf
    *
    * @note Currently supports only HMAC keys.
diff --git a/ndn-cxx/security/v2/key-chain.cpp b/ndn-cxx/security/v2/key-chain.cpp
index 8043635..05fc2b1 100644
--- a/ndn-cxx/security/v2/key-chain.cpp
+++ b/ndn-cxx/security/v2/key-chain.cpp
@@ -282,6 +282,12 @@
   return key;
 }
 
+Name
+KeyChain::createHmacKey(const Name& prefix, const HmacKeyParams& params)
+{
+  return m_tpm->createKey(prefix, params);
+}
+
 void
 KeyChain::deleteKey(const Identity& identity, const Key& key)
 {
@@ -424,6 +430,21 @@
   key.addCertificate(cert);
 }
 
+void
+KeyChain::importPrivateKey(const Name& keyName, shared_ptr<transform::PrivateKey> key)
+{
+  if (m_tpm->hasKey(keyName)) {
+    NDN_THROW(Error("Private key `" + keyName.toUri() + "` already exists"));
+  }
+
+  try {
+    m_tpm->importPrivateKey(keyName, std::move(key));
+  }
+  catch (const tpm::BackEnd::Error&) {
+    NDN_THROW_NESTED(Error("Failed to import private key `" + keyName.toUri() + "`"));
+  }
+}
+
 // public: signing
 
 void
@@ -578,8 +599,6 @@
 KeyChain::prepareSignatureInfo(const SigningInfo& params)
 {
   SignatureInfo sigInfo = params.getSignatureInfo();
-  name::Component keyId;
-  Name certificateName;
   pib::Identity identity;
   pib::Key key;
 
@@ -590,6 +609,7 @@
       }
       catch (const Pib::Error&) { // no default identity, use sha256 for signing.
         sigInfo.setSignatureType(tlv::DigestSha256);
+        NDN_LOG_TRACE("Prepared signature info: " << sigInfo);
         return std::make_tuple(SigningInfo::getDigestSha256Identity(), sigInfo);
       }
       break;
@@ -612,9 +632,7 @@
       if (!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;
+          key = m_pib->getIdentity(identityName).getKey(params.getSignerName());
         }
         catch (const Pib::Error&) {
           NDN_THROW_NESTED(InvalidSigningInfoError("Signing key `" +
@@ -638,19 +656,29 @@
     }
     case SigningInfo::SIGNER_TYPE_SHA256: {
       sigInfo.setSignatureType(tlv::DigestSha256);
+      NDN_LOG_TRACE("Prepared signature info: " << sigInfo);
       return std::make_tuple(SigningInfo::getDigestSha256Identity(), sigInfo);
     }
+    case SigningInfo::SIGNER_TYPE_HMAC: {
+      const Name& keyName = params.getSignerName();
+      if (!m_tpm->hasKey(keyName)) {
+        m_tpm->importPrivateKey(keyName, params.getHmacKey());
+      }
+      sigInfo.setSignatureType(getSignatureType(KeyType::HMAC, params.getDigestAlgorithm()));
+      sigInfo.setKeyLocator(keyName);
+      NDN_LOG_TRACE("Prepared signature info: " << sigInfo);
+      return std::make_tuple(keyName, sigInfo);
+    }
     default: {
       NDN_THROW(InvalidSigningInfoError("Unrecognized signer type " +
                                         boost::lexical_cast<std::string>(params.getSignerType())));
     }
   }
 
-  if (!identity && !key) {
-    NDN_THROW(InvalidSigningInfoError("Cannot determine signing parameters"));
-  }
-
-  if (identity && !key) {
+  if (!key) {
+    if (!identity) {
+      NDN_THROW(InvalidSigningInfoError("Cannot determine signing parameters"));
+    }
     try {
       key = identity.getDefaultKey();
     }
@@ -663,7 +691,7 @@
   BOOST_ASSERT(key);
 
   sigInfo.setSignatureType(getSignatureType(key.getKeyType(), params.getDigestAlgorithm()));
-  sigInfo.setKeyLocator(KeyLocator(key.getName()));
+  sigInfo.setKeyLocator(key.getName());
 
   NDN_LOG_TRACE("Prepared signature info: " << sigInfo);
   return std::make_tuple(key.getName(), sigInfo);
@@ -687,6 +715,8 @@
     return tlv::SignatureSha256WithRsa;
   case KeyType::EC:
     return tlv::SignatureSha256WithEcdsa;
+  case KeyType::HMAC:
+    return tlv::SignatureHmacWithSha256;
   default:
     NDN_THROW(Error("Unsupported key type " + boost::lexical_cast<std::string>(keyType)));
   }
diff --git a/ndn-cxx/security/v2/key-chain.hpp b/ndn-cxx/security/v2/key-chain.hpp
index 298f14a..1021e8b 100644
--- a/ndn-cxx/security/v2/key-chain.hpp
+++ b/ndn-cxx/security/v2/key-chain.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2018 Regents of the University of California.
+ * Copyright (c) 2013-2019 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -147,21 +147,34 @@
 
 public: // Key management
   /**
-   * @brief Create a key for @p identity according to @p params.
+   * @brief Create a new key for @p identity.
    *
-   * @param identity reference to a valid Identity object
-   * @param params The key parameters if a key needs to be created for the identity (default:
-   *               EC key with random key id)
+   * @param identity Reference to a valid Identity object
+   * @param params Key creation parameters (default: EC key with random key id)
+   * @pre @p identity must be valid.
    *
    * 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 Create a new HMAC key.
+   *
+   * @param prefix Prefix used to construct the key name (default: `/localhost/identity/hmac`);
+   *               the full key name will include additional components according to @p params
+   * @param params Key creation parameters
+   * @return A name that can be subsequently used to reference the created key.
+   *
+   * The newly created key will be inserted in the TPM. HMAC keys don't have any PIB entries.
+   */
+  Name
+  createHmacKey(const Name& prefix = SigningInfo::getHmacIdentity(),
+                const HmacKeyParams& params = HmacKeyParams());
+
   /**
    * @brief Delete a key @p key of @p identity.
    *
@@ -304,7 +317,7 @@
   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.
+   * @brief Import a certificate and its corresponding private key from 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.
@@ -321,6 +334,12 @@
   void
   importSafeBag(const SafeBag& safeBag, const char* pw, size_t pwLen);
 
+  /**
+   * @brief Import a private key into the TPM.
+   */
+  void
+  importPrivateKey(const Name& keyName, shared_ptr<transform::PrivateKey> key);
+
 NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   /**
    * @brief Derive SignatureTypeValue according to key type and digest algorithm.
@@ -394,19 +413,15 @@
   /**
    * @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
+   * The self-signed certificate will also be added to the PIB.
    */
   Certificate
   selfSign(Key& key);
 
   /**
    * @brief Prepare a SignatureInfo TLV according to signing information and return the signing
-   *        key name
+   *        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.
@@ -415,7 +430,7 @@
   prepareSignatureInfo(const SigningInfo& params);
 
   /**
-   * @brief Generate a SignatureValue block for a buffer @p buf with size @p size using
+   * @brief Generate a SignatureValue block for a buffer @p buf of size @p size using
    *        a key with name @p keyName and digest algorithm @p digestAlgorithm.
    */
   Block
diff --git a/tests/unit/security/signing-info.t.cpp b/tests/unit/security/signing-info.t.cpp
index 816cb52..e32d5b1 100644
--- a/tests/unit/security/signing-info.t.cpp
+++ b/tests/unit/security/signing-info.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2018 Regents of the University of California.
+ * Copyright (c) 2013-2019 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -88,6 +88,16 @@
   BOOST_CHECK_EQUAL(infoSha.getSignerType(), SigningInfo::SIGNER_TYPE_SHA256);
   BOOST_CHECK_EQUAL(infoSha.getSignerName(), SigningInfo::getEmptyName());
   BOOST_CHECK_EQUAL(infoSha.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+  std::string encodedKey("QjM3NEEyNkE3MTQ5MDQzN0FBMDI0RTRGQURENUI0OTdGRE"
+                         "ZGMUE4RUE2RkYxMkY2RkI2NUFGMjcyMEI1OUNDRg==");
+  info.setSigningHmacKey(encodedKey);
+  BOOST_CHECK_EQUAL(info.getSignerType(), SigningInfo::SIGNER_TYPE_HMAC);
+  BOOST_CHECK_EQUAL(info.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+  SigningInfo infoHmac(SigningInfo::SIGNER_TYPE_HMAC, info.getSignerName());
+  BOOST_CHECK_EQUAL(infoHmac.getSignerType(), SigningInfo::SIGNER_TYPE_HMAC);
+  BOOST_CHECK_EQUAL(infoHmac.getDigestAlgorithm(), DigestAlgorithm::SHA256);
 }
 
 BOOST_AUTO_TEST_CASE(CustomSignatureInfo)
@@ -127,6 +137,12 @@
   BOOST_CHECK_EQUAL(infoCert.getSignerName(), "/my-cert");
   BOOST_CHECK_EQUAL(infoCert.getDigestAlgorithm(), DigestAlgorithm::SHA256);
 
+  SigningInfo infoHmac("hmac-sha256:QjM3NEEyNkE3MTQ5MDQzN0FBMDI0RTRGQURENU"
+                       "I0OTdGREZGMUE4RUE2RkYxMkY2RkI2NUFGMjcyMEI1OUNDRg==");
+  BOOST_CHECK_EQUAL(infoHmac.getSignerType(), SigningInfo::SIGNER_TYPE_HMAC);
+  BOOST_CHECK_EQUAL(infoHmac.getSignerName().getPrefix(3), SigningInfo::getHmacIdentity());
+  BOOST_CHECK_EQUAL(infoHmac.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
   SigningInfo infoSha("id:/localhost/identity/digest-sha256");
   BOOST_CHECK_EQUAL(infoSha.getSignerType(), SigningInfo::SIGNER_TYPE_SHA256);
   BOOST_CHECK_EQUAL(infoSha.getSignerName(), SigningInfo::getEmptyName());
@@ -149,6 +165,9 @@
   BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(
                     SigningInfo(SigningInfo::SIGNER_TYPE_SHA256)),
                     "id:/localhost/identity/digest-sha256");
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(
+                    SigningInfo(SigningInfo::SIGNER_TYPE_HMAC, "/localhost/identity/hmac/1234")),
+                    "id:/localhost/identity/hmac/1234");
 }
 
 BOOST_AUTO_TEST_CASE(Chaining)
diff --git a/tests/unit/security/tpm/back-end.t.cpp b/tests/unit/security/tpm/back-end.t.cpp
index 1ef374b..aee042f 100644
--- a/tests/unit/security/tpm/back-end.t.cpp
+++ b/tests/unit/security/tpm/back-end.t.cpp
@@ -87,6 +87,28 @@
   BOOST_CHECK(tpm.getKeyHandle(keyName) == nullptr);
 }
 
+BOOST_AUTO_TEST_CASE(CreateHmacKey)
+{
+  Name identity("/Test/Identity/HMAC");
+
+  BackEndWrapperMem mem;
+  BackEnd& memTpm = mem.getTpm();
+  auto key = memTpm.createKey(identity, HmacKeyParams());
+  BOOST_REQUIRE(key != nullptr);
+  BOOST_CHECK(!key->getKeyName().empty());
+  BOOST_CHECK(memTpm.hasKey(key->getKeyName()));
+
+  BackEndWrapperFile file;
+  BackEnd& fileTpm = file.getTpm();
+  BOOST_CHECK_THROW(fileTpm.createKey(identity, HmacKeyParams()), BackEnd::Error);
+
+#ifdef NDN_CXX_HAVE_OSX_FRAMEWORKS
+  BackEndWrapperOsx osx;
+  BackEnd& osxTpm = osx.getTpm();
+  BOOST_CHECK_THROW(osxTpm.createKey(identity, HmacKeyParams()), BackEnd::Error);
+#endif // NDN_CXX_HAVE_OSX_FRAMEWORKS
+}
+
 BOOST_AUTO_TEST_CASE_TEMPLATE(RsaSigning, T, TestBackEnds)
 {
   T wrapper;
diff --git a/tests/unit/security/transform/private-key.t.cpp b/tests/unit/security/transform/private-key.t.cpp
index 79b3599..2317479 100644
--- a/tests/unit/security/transform/private-key.t.cpp
+++ b/tests/unit/security/transform/private-key.t.cpp
@@ -53,6 +53,7 @@
   PrivateKey sKey;
   BOOST_CHECK_EQUAL(sKey.getKeyType(), KeyType::NONE);
   BOOST_CHECK_EQUAL(sKey.getKeySize(), 0);
+  BOOST_CHECK_THROW(sKey.getKeyDigest(DigestAlgorithm::SHA256), PrivateKey::Error);
   BOOST_CHECK_THROW(sKey.derivePublicKey(), PrivateKey::Error);
   const uint8_t theAnswer = 42;
   BOOST_CHECK_THROW(sKey.decrypt(&theAnswer, sizeof(theAnswer)), PrivateKey::Error);
@@ -62,6 +63,21 @@
   BOOST_CHECK_THROW(sKey.savePkcs8(os, passwd.data(), passwd.size()), PrivateKey::Error);
 }
 
+BOOST_AUTO_TEST_CASE(KeyDigest)
+{
+  const Buffer buf(16);
+  PrivateKey sKey;
+  sKey.loadRaw(KeyType::HMAC, buf.data(), buf.size());
+  auto digest = sKey.getKeyDigest(DigestAlgorithm::SHA256);
+
+  const uint8_t expected[] = {
+    0x37, 0x47, 0x08, 0xff, 0xf7, 0x71, 0x9d, 0xd5, 0x97, 0x9e, 0xc8, 0x75, 0xd5, 0x6c, 0xd2, 0x28,
+    0x6f, 0x6d, 0x3c, 0xf7, 0xec, 0x31, 0x7a, 0x3b, 0x25, 0x63, 0x2a, 0xab, 0x28, 0xec, 0x37, 0xbb,
+  };
+  BOOST_CHECK_EQUAL_COLLECTIONS(digest->begin(), digest->end(),
+                                expected, expected + sizeof(expected));
+}
+
 BOOST_AUTO_TEST_CASE(LoadRaw)
 {
   const Buffer buf(32);
diff --git a/tests/unit/security/v2/key-chain.t.cpp b/tests/unit/security/v2/key-chain.t.cpp
index 8f5844b..17f8569 100644
--- a/tests/unit/security/v2/key-chain.t.cpp
+++ b/tests/unit/security/v2/key-chain.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2018 Regents of the University of California.
+ * Copyright (c) 2013-2019 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -22,6 +22,7 @@
 #include "ndn-cxx/security/v2/key-chain.hpp"
 #include "ndn-cxx/security/signing-helpers.hpp"
 #include "ndn-cxx/security/verification-helpers.hpp"
+#include "ndn-cxx/security/transform/private-key.hpp"
 
 #include "tests/boost-test.hpp"
 #include "tests/identity-management-fixture.hpp"
@@ -222,12 +223,11 @@
   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());
+  BOOST_CHECK_NO_THROW(m_keyChain.getPib().getDefaultIdentity());
   // The default key of the added identity must exist
-  Key key;
-  BOOST_REQUIRE_NO_THROW(key = id.getDefaultKey());
+  Key key = id.getDefaultKey();
   // The default certificate of the default key must exist
-  BOOST_REQUIRE_NO_THROW(key.getDefaultCertificate());
+  BOOST_CHECK_NO_THROW(key.getDefaultCertificate());
 
   // Delete key
   Name key1Name = key.getName();
@@ -242,12 +242,11 @@
   // 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());
+  BOOST_CHECK_NO_THROW(key2.getDefaultCertificate());
 
   // Create the third key
   Key key3 = m_keyChain.createKey(id);
@@ -255,7 +254,7 @@
   // The added key will not be the default key, because the default key already exists
   BOOST_CHECK_EQUAL(id.getDefaultKey().getName(), key2.getName());
   BOOST_CHECK_EQUAL(id.getKeys().size(), 2);
-  BOOST_REQUIRE_NO_THROW(key3.getDefaultCertificate());
+  BOOST_CHECK_NO_THROW(key3.getDefaultCertificate());
 
   // Delete cert
   BOOST_CHECK_EQUAL(key3.getCertificates().size(), 1);
@@ -268,7 +267,7 @@
   // Add cert
   m_keyChain.addCertificate(key3, key3Cert1);
   BOOST_CHECK_EQUAL(key3.getCertificates().size(), 1);
-  BOOST_REQUIRE_NO_THROW(key3.getDefaultCertificate());
+  BOOST_CHECK_NO_THROW(key3.getDefaultCertificate());
   m_keyChain.addCertificate(key3, key3Cert1); // overwriting the cert should work
   BOOST_CHECK_EQUAL(key3.getCertificates().size(), 1);
   // Add another cert
@@ -310,7 +309,10 @@
   Key key = id.getDefaultKey();
   Certificate cert = key.getDefaultCertificate();
 
-  std::list<SigningInfo> signingInfos = {
+  Name hmacKeyName = m_keyChain.createHmacKey();
+  const Tpm& tpm = m_keyChain.getTpm();
+
+  std::vector<SigningInfo> signingInfos = {
     SigningInfo(),
 
     SigningInfo(SigningInfo::SIGNER_TYPE_ID, id.getName()),
@@ -329,6 +331,9 @@
     signingByCertificate(cert.getName()),
     signingByCertificate(cert),
 
+    SigningInfo(SigningInfo::SIGNER_TYPE_HMAC, hmacKeyName),
+    SigningInfo("hmac-sha256:QjM3NEEyNkE3MTQ5MDQzN0FBMDI0RTRGQURENUI0OTdGREZGMUE4RUE2RkYxMkY2RkI2NUFGMjcyMEI1OUNDRg=="),
+
     SigningInfo(SigningInfo::SIGNER_TYPE_SHA256),
     signingWithSha256()
   };
@@ -356,6 +361,25 @@
       BOOST_CHECK(verifyDigest(data, DigestAlgorithm::SHA256));
       BOOST_CHECK(verifyDigest(interest, DigestAlgorithm::SHA256));
     }
+    else if (signingInfo.getSignerType() == SigningInfo::SIGNER_TYPE_HMAC) {
+      Name keyName = signingInfo.getSignerName();
+      BOOST_CHECK_EQUAL(data.getSignature().getType(), tlv::SignatureHmacWithSha256);
+      BOOST_CHECK_EQUAL(interestSignature.getType(), tlv::SignatureHmacWithSha256);
+
+      BOOST_CHECK(bool(tpm.verify(data.wireEncode().value(),
+                                  data.wireEncode().value_size() - data.getSignature().getValue().size(),
+                                  data.getSignature().getValue().value(),
+                                  data.getSignature().getValue().value_size(),
+                                  keyName, DigestAlgorithm::SHA256)));
+
+      const Name& interestName = interest.getName();
+      auto nameBlock = interestName.wireEncode();
+      BOOST_CHECK(bool(tpm.verify(nameBlock.value(),
+                                  nameBlock.value_size() - interestName[signed_interest::POS_SIG_VALUE].size(),
+                                  interestName[signed_interest::POS_SIG_VALUE].blockFromValue().value(),
+                                  interestName[signed_interest::POS_SIG_VALUE].blockFromValue().value_size(),
+                                  keyName, DigestAlgorithm::SHA256)));
+    }
     else {
       BOOST_CHECK_EQUAL(data.getSignature().getType(), tlv::SignatureSha256WithEcdsa);
       BOOST_CHECK_EQUAL(interestSignature.getType(), tlv::SignatureSha256WithEcdsa);
@@ -391,9 +415,21 @@
   BOOST_CHECK(id.getName().isPrefixOf(data.getSignature().getKeyLocator().getName()));
 }
 
+BOOST_FIXTURE_TEST_CASE(ImportPrivateKey, IdentityManagementFixture)
+{
+  Name keyName("/test/device2");
+  std::string rawKey("nPSNOHyZKsg2WLqHAs7MXGb0sjQb4zCT");
+  auto key = make_shared<transform::PrivateKey>();
+  key->loadRaw(KeyType::HMAC, reinterpret_cast<const uint8_t*>(rawKey.data()), rawKey.size());
+
+  m_keyChain.importPrivateKey(keyName, key);
+  BOOST_CHECK_EQUAL(m_keyChain.getTpm().hasKey(keyName), true);
+  BOOST_CHECK_THROW(m_keyChain.importPrivateKey(keyName, key), KeyChain::Error);
+}
+
 BOOST_FIXTURE_TEST_CASE(ExportImport, IdentityManagementFixture)
 {
-  Identity id = addIdentity("/TestKeyChain/ExportIdentity/");
+  Identity id = addIdentity("/TestKeyChain/ExportIdentity");
   Certificate cert = id.getDefaultKey().getDefaultCertificate();
 
   shared_ptr<SafeBag> exported = m_keyChain.exportSafeBag(cert, "1234", 4);
@@ -412,13 +448,11 @@
 
   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()));
+  BOOST_CHECK_NO_THROW(newKey.getCertificate(cert.getName()));
 
   m_keyChain.deleteIdentity(newId);
   BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 0);