security: add PrivateKey::loadRaw() for HMAC keys

Refs: #3075
Change-Id: I535a0e1ec2a48fba0e1dccc4b77ea9900a61a464
diff --git a/.mailmap b/.mailmap
index 60170e7..967715e 100644
--- a/.mailmap
+++ b/.mailmap
@@ -5,3 +5,4 @@
 Teng Liang <philoliang2011@gmail.com>
 <lybmath2009@gmail.com> <lybmath@ucla.edu>
 Hila Ben Abraham <hilata@gmail.com>
+Laqin Fan <flq12021@gmail.com>
diff --git a/ndn-cxx/security/transform/private-key.cpp b/ndn-cxx/security/transform/private-key.cpp
index f4029d3..e6fa54c 100644
--- a/ndn-cxx/security/transform/private-key.cpp
+++ b/ndn-cxx/security/transform/private-key.cpp
@@ -99,6 +99,32 @@
 }
 
 void
+PrivateKey::loadRaw(KeyType type, const uint8_t* buf, size_t size)
+{
+  ENSURE_PRIVATE_KEY_NOT_LOADED(m_impl->key);
+
+  int pkeyType;
+  switch (type) {
+  case KeyType::HMAC:
+    pkeyType = EVP_PKEY_HMAC;
+    break;
+  default:
+    NDN_THROW(std::invalid_argument("Unsupported key type " + boost::lexical_cast<std::string>(type)));
+  }
+
+  m_impl->key =
+#if OPENSSL_VERSION_NUMBER >= 0x1010100fL
+      EVP_PKEY_new_raw_private_key(pkeyType, nullptr, buf, size);
+#else
+      EVP_PKEY_new_mac_key(pkeyType, nullptr, buf, static_cast<int>(size));
+#endif
+  if (m_impl->key == nullptr)
+    NDN_THROW(Error("Failed to load private key"));
+
+  m_keySize = size * 8;
+}
+
+void
 PrivateKey::loadPkcs1(const uint8_t* buf, size_t size)
 {
   ENSURE_PRIVATE_KEY_NOT_LOADED(m_impl->key);
@@ -441,16 +467,13 @@
   random::generateSecureBytes(rawKey.data(), rawKey.size());
 
   auto privateKey = make_unique<PrivateKey>();
-  privateKey->m_impl->key =
-#if OPENSSL_VERSION_NUMBER >= 0x1010100fL
-      EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, nullptr, rawKey.data(), rawKey.size());
-#else
-      EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, nullptr, rawKey.data(), static_cast<int>(rawKey.size()));
-#endif
-  if (privateKey->m_impl->key == nullptr)
+  try {
+    privateKey->loadRaw(KeyType::HMAC, rawKey.data(), rawKey.size());
+  }
+  catch (const PrivateKey::Error&) {
     NDN_THROW(PrivateKey::Error("Failed to generate HMAC key"));
+  }
 
-  privateKey->m_keySize = keySize;
   return privateKey;
 }
 
@@ -471,7 +494,7 @@
       return PrivateKey::generateHmacKey(hmacParams.getKeySize());
     }
     default:
-      NDN_THROW(std::invalid_argument("Unsupported asymmetric key type " +
+      NDN_THROW(std::invalid_argument("Unsupported key type " +
                                       boost::lexical_cast<std::string>(keyParams.getKeyType())));
   }
 }
diff --git a/ndn-cxx/security/transform/private-key.hpp b/ndn-cxx/security/transform/private-key.hpp
index d3ae45c..d16a59a 100644
--- a/ndn-cxx/security/transform/private-key.hpp
+++ b/ndn-cxx/security/transform/private-key.hpp
@@ -74,7 +74,8 @@
    * @brief Get the size of the private key in bits
    *
    * @note The return value is meaningful only if the PrivateKey was created via
-   *       generatePrivateKey(), otherwise this function will always return zero.
+   *       generatePrivateKey() or loaded via loadRaw(), otherwise this function
+   *       will always return zero.
    */
   size_t
   getKeySize() const
@@ -83,6 +84,14 @@
   }
 
   /**
+   * @brief Load a raw private key from a buffer @p buf
+   *
+   * @note Currently supports only HMAC keys.
+   */
+  void
+  loadRaw(KeyType type, const uint8_t* buf, size_t size);
+
+  /**
    * @brief Load the private key in PKCS#1 format from a buffer @p buf
    */
   void
diff --git a/ndn-cxx/security/transform/signer-filter.cpp b/ndn-cxx/security/transform/signer-filter.cpp
index ddaf4cd..3c0535d 100644
--- a/ndn-cxx/security/transform/signer-filter.cpp
+++ b/ndn-cxx/security/transform/signer-filter.cpp
@@ -48,7 +48,7 @@
     size_t mdSize = static_cast<size_t>(EVP_MD_size(md)) * 8;
     if (key.getKeySize() < mdSize)
       NDN_THROW(Error(getIndex(), "HMAC key is shorter than the digest output (" +
-                      to_string(key.getKeySize()) + " < " + to_string(mdSize) + ")"));
+                      to_string(key.getKeySize()) + " < " + to_string(mdSize) + " bits)"));
   }
 
   if (EVP_DigestSignInit(m_impl->ctx, nullptr, md, nullptr,
diff --git a/ndn-cxx/security/verification-helpers.hpp b/ndn-cxx/security/verification-helpers.hpp
index b864e3d..c3c848a 100644
--- a/ndn-cxx/security/verification-helpers.hpp
+++ b/ndn-cxx/security/verification-helpers.hpp
@@ -52,6 +52,7 @@
 
 /**
  * @brief Verify @p blob using @p key against @p sig.
+ * @note @p key must be a public key in PKCS #8 format.
  */
 bool
 verifySignature(const uint8_t* blob, size_t blobLen, const uint8_t* sig, size_t sigLen,
@@ -59,12 +60,14 @@
 
 /**
  * @brief Verify @p data using @p key.
+ * @note @p key must be a public key in PKCS #8 format.
  */
 bool
 verifySignature(const Data& data, const uint8_t* key, size_t keyLen);
 
 /**
  * @brief Verify @p interest using @p key.
+ * @note @p key must be a public key in PKCS #8 format.
  * @note This method verifies only signature of the signed interest.
  * @sa docs/specs/signed-interest.rst
  */
diff --git a/tests/unit/security/transform/private-key.t.cpp b/tests/unit/security/transform/private-key.t.cpp
index de1d9b2..673f551 100644
--- a/tests/unit/security/transform/private-key.t.cpp
+++ b/tests/unit/security/transform/private-key.t.cpp
@@ -47,6 +47,21 @@
 BOOST_AUTO_TEST_SUITE(Transform)
 BOOST_AUTO_TEST_SUITE(TestPrivateKey)
 
+BOOST_AUTO_TEST_CASE(LoadRaw)
+{
+  const Buffer buf(32);
+  PrivateKey sKey;
+  sKey.loadRaw(KeyType::HMAC, buf.data(), buf.size());
+  BOOST_CHECK_EQUAL(sKey.getKeyType(), KeyType::HMAC);
+  BOOST_CHECK_EQUAL(sKey.getKeySize(), 256);
+
+  PrivateKey sKey2;
+  BOOST_CHECK_THROW(sKey2.loadRaw(KeyType::NONE, buf.data(), buf.size()), std::invalid_argument);
+  BOOST_CHECK_THROW(sKey2.loadRaw(KeyType::RSA, buf.data(), buf.size()), std::invalid_argument);
+  BOOST_CHECK_THROW(sKey2.loadRaw(KeyType::EC, buf.data(), buf.size()), std::invalid_argument);
+  BOOST_CHECK_THROW(sKey2.loadRaw(KeyType::AES, buf.data(), buf.size()), std::invalid_argument);
+}
+
 struct RsaKeyTestData
 {
   const std::string privateKeyPkcs1 =