security: use EVP_DigestSign* routines in SignerFilter

Change-Id: Ib87c4681084bf0389de527450a2248fe2161e6ee
diff --git a/src/security/transform/private-key.cpp b/src/security/transform/private-key.cpp
index 3ec0ff6..8139a0f 100644
--- a/src/security/transform/private-key.cpp
+++ b/src/security/transform/private-key.cpp
@@ -84,6 +84,22 @@
 
 PrivateKey::~PrivateKey() = default;
 
+KeyType
+PrivateKey::getKeyType() const
+{
+  if (!m_impl->key)
+    return KeyType::NONE;
+
+  switch (detail::getEvpPkeyType(m_impl->key)) {
+  case EVP_PKEY_RSA:
+    return KeyType::RSA;
+  case EVP_PKEY_EC:
+    return KeyType::EC;
+  default:
+    return KeyType::NONE;
+  }
+}
+
 void
 PrivateKey::loadPkcs1(const uint8_t* buf, size_t size)
 {
diff --git a/src/security/transform/private-key.hpp b/src/security/transform/private-key.hpp
index 98f5bd6..17d582b 100644
--- a/src/security/transform/private-key.hpp
+++ b/src/security/transform/private-key.hpp
@@ -22,6 +22,7 @@
 #ifndef NDN_CXX_SECURITY_TRANSFORM_PRIVATE_KEY_HPP
 #define NDN_CXX_SECURITY_TRANSFORM_PRIVATE_KEY_HPP
 
+#include "../security-common.hpp"
 #include "../../encoding/buffer.hpp"
 
 namespace ndn {
@@ -68,6 +69,12 @@
   ~PrivateKey();
 
   /**
+   * @brief Get the type of the private key
+   */
+  KeyType
+  getKeyType() const;
+
+  /**
    * @brief Load the private key in PKCS#1 format from a buffer @p buf
    */
   void
diff --git a/src/security/transform/public-key.cpp b/src/security/transform/public-key.cpp
index 13b8f00..16dd3d1 100644
--- a/src/security/transform/public-key.cpp
+++ b/src/security/transform/public-key.cpp
@@ -71,7 +71,8 @@
 KeyType
 PublicKey::getKeyType() const
 {
-  ENSURE_PUBLIC_KEY_LOADED(m_impl->key);
+  if (!m_impl->key)
+    return KeyType::NONE;
 
   switch (detail::getEvpPkeyType(m_impl->key)) {
   case EVP_PKEY_RSA:
diff --git a/src/security/transform/signer-filter.cpp b/src/security/transform/signer-filter.cpp
index ab9fdb3..959723c 100644
--- a/src/security/transform/signer-filter.cpp
+++ b/src/security/transform/signer-filter.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "signer-filter.hpp"
+#include "private-key.hpp"
 #include "../detail/openssl-helper.hpp"
 
 #include <boost/lexical_cast.hpp>
@@ -31,66 +32,65 @@
 class SignerFilter::Impl
 {
 public:
-  Impl(const PrivateKey& key)
-    : m_key(key)
-    , m_md(BIO_new(BIO_f_md()))
-    , m_sink(BIO_new(BIO_s_null()))
+  Impl() noexcept
   {
-    BIO_push(m_md, m_sink);
+#if OPENSSL_VERSION_NUMBER < 0x1010000fL
+    ctx = EVP_MD_CTX_create();
+#else
+    ctx = EVP_MD_CTX_new();
+#endif
   }
 
   ~Impl()
   {
-    BIO_free_all(m_md);
+#if OPENSSL_VERSION_NUMBER < 0x1010000fL
+    EVP_MD_CTX_destroy(ctx);
+#else
+    EVP_MD_CTX_free(ctx);
+#endif
   }
 
 public:
-  const PrivateKey& m_key;
-
-  BIO* m_md;
-  BIO* m_sink;
+  EVP_MD_CTX* ctx;
 };
 
+
 SignerFilter::SignerFilter(DigestAlgorithm algo, const PrivateKey& key)
-  : m_impl(new Impl(key))
+  : m_impl(make_unique<Impl>())
 {
   const EVP_MD* md = detail::digestAlgorithmToEvpMd(algo);
   if (md == nullptr)
     BOOST_THROW_EXCEPTION(Error(getIndex(), "Unsupported digest algorithm " +
                                 boost::lexical_cast<std::string>(algo)));
 
-  if (!BIO_set_md(m_impl->m_md, md))
-    BOOST_THROW_EXCEPTION(Error(getIndex(), "Cannot set digest " +
-                                boost::lexical_cast<std::string>(algo)));
+  if (EVP_DigestSignInit(m_impl->ctx, nullptr, md, nullptr,
+                         reinterpret_cast<EVP_PKEY*>(key.getEvpPkey())) != 1)
+    BOOST_THROW_EXCEPTION(Error(getIndex(), "Failed to initialize signing context with " +
+                                boost::lexical_cast<std::string>(algo) + " digest and " +
+                                boost::lexical_cast<std::string>(key.getKeyType()) + " key"));
 }
 
+SignerFilter::~SignerFilter() = default;
+
 size_t
 SignerFilter::convert(const uint8_t* buf, size_t size)
 {
-  int wLen = BIO_write(m_impl->m_md, buf, size);
+  if (EVP_DigestSignUpdate(m_impl->ctx, buf, size) != 1)
+    BOOST_THROW_EXCEPTION(Error(getIndex(), "Failed to accept more input"));
 
-  if (wLen <= 0) { // fail to write data
-    if (!BIO_should_retry(m_impl->m_md)) {
-      // we haven't written everything but some error happens, and we cannot retry
-      BOOST_THROW_EXCEPTION(Error(getIndex(), "Failed to accept more input"));
-    }
-    return 0;
-  }
-  else { // update number of bytes written
-    return wLen;
-  }
+  return size;
 }
 
 void
 SignerFilter::finalize()
 {
-  EVP_PKEY* key = reinterpret_cast<EVP_PKEY*>(m_impl->m_key.getEvpPkey());
-  auto buffer = make_unique<OBuffer>(EVP_PKEY_size(key));
-  unsigned int sigLen = 0;
+  size_t sigLen = 0;
+  if (EVP_DigestSignFinal(m_impl->ctx, nullptr, &sigLen) != 1)
+    BOOST_THROW_EXCEPTION(Error(getIndex(), "Failed to estimate buffer length"));
 
-  EVP_MD_CTX* ctx = nullptr;
-  BIO_get_md_ctx(m_impl->m_md, &ctx);
-  EVP_SignFinal(ctx, &(*buffer)[0], &sigLen, key); // should be ok, enough space is allocated in buffer
+  auto buffer = make_unique<OBuffer>(sigLen);
+  if (EVP_DigestSignFinal(m_impl->ctx, buffer->data(), &sigLen) != 1)
+    BOOST_THROW_EXCEPTION(Error(getIndex(), "Failed to finalize signature"));
 
   buffer->erase(buffer->begin() + sigLen, buffer->end());
   setOutputBuffer(std::move(buffer));
diff --git a/src/security/transform/signer-filter.hpp b/src/security/transform/signer-filter.hpp
index a6d06c5..fda45f1 100644
--- a/src/security/transform/signer-filter.hpp
+++ b/src/security/transform/signer-filter.hpp
@@ -1,5 +1,5 @@
 /* -*- 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).
@@ -23,13 +23,14 @@
 #define NDN_CXX_SECURITY_TRANSFORM_SIGNER_FILTER_HPP
 
 #include "transform-base.hpp"
-#include "private-key.hpp"
 #include "../security-common.hpp"
 
 namespace ndn {
 namespace security {
 namespace transform {
 
+class PrivateKey;
+
 /**
  * @brief The module to sign data.
  */
@@ -37,10 +38,12 @@
 {
 public:
   /**
-   * @brief Create a signer module to generate signature using algorithm @p algo and @p key
+   * @brief Create a module to sign using digest algorithm @p algo and private key @p key
    */
   SignerFilter(DigestAlgorithm algo, const PrivateKey& key);
 
+  ~SignerFilter();
+
 private:
   /**
    * @brief Write data @p buf into signer
diff --git a/tests/unit-tests/security/transform/private-key.t.cpp b/tests/unit-tests/security/transform/private-key.t.cpp
index 539f3b1..6a6ba93 100644
--- a/tests/unit-tests/security/transform/private-key.t.cpp
+++ b/tests/unit-tests/security/transform/private-key.t.cpp
@@ -295,6 +295,7 @@
   PrivateKey sKey;
   sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(dataSet.privateKeyPkcs1.c_str()),
                        dataSet.privateKeyPkcs1.size());
+  BOOST_CHECK_EQUAL(sKey.getKeyType(), KeyType::RSA);
 
   const uint8_t plainText[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
 
@@ -320,10 +321,12 @@
   PublicKey pKey;
   pKey.loadPkcs8Base64(reinterpret_cast<const uint8_t*>(dataSet.publicKeyPkcs8.c_str()),
                        dataSet.publicKeyPkcs8.size());
+  BOOST_CHECK_EQUAL(pKey.getKeyType(), KeyType::RSA);
 
   PrivateKey sKey;
   sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(dataSet.privateKeyPkcs1.c_str()),
                        dataSet.privateKeyPkcs1.size());
+  BOOST_CHECK_EQUAL(sKey.getKeyType(), KeyType::RSA);
 
   const uint8_t plainText[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
 
@@ -340,6 +343,7 @@
   PrivateKey sKey;
   sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(dataSet.privateKeyPkcs1.c_str()),
                        dataSet.privateKeyPkcs1.size());
+  BOOST_CHECK_EQUAL(sKey.getKeyType(), KeyType::EC);
 
   OBufferStream os;
   bufferSource("Y2lhbyFob2xhIWhlbGxvIQ==") >> base64Decode() >> streamSink(os);
diff --git a/tests/unit-tests/security/transform/public-key.t.cpp b/tests/unit-tests/security/transform/public-key.t.cpp
index 41b0582..fee246f 100644
--- a/tests/unit-tests/security/transform/public-key.t.cpp
+++ b/tests/unit-tests/security/transform/public-key.t.cpp
@@ -114,6 +114,7 @@
   PublicKey pKey;
   pKey.loadPkcs8Base64(reinterpret_cast<const uint8_t*>(dataSet.publicKeyPkcs8.c_str()),
                        dataSet.publicKeyPkcs8.size());
+  BOOST_CHECK_EQUAL(pKey.getKeyType(), KeyType::EC);
 
   OBufferStream os;
   bufferSource("Y2lhbyFob2xhIWhlbGxvIQ==") >> base64Decode() >> streamSink(os);
diff --git a/tests/unit-tests/security/transform/signer-filter.t.cpp b/tests/unit-tests/security/transform/signer-filter.t.cpp
index f041e12..450f9ff 100644
--- a/tests/unit-tests/security/transform/signer-filter.t.cpp
+++ b/tests/unit-tests/security/transform/signer-filter.t.cpp
@@ -24,6 +24,7 @@
 #include "encoding/buffer-stream.hpp"
 #include "security/transform/base64-decode.hpp"
 #include "security/transform/buffer-source.hpp"
+#include "security/transform/private-key.hpp"
 #include "security/transform/stream-sink.hpp"
 #include "security/verification-helpers.hpp"
 
@@ -83,6 +84,8 @@
   PrivateKey sKey;
   sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(privateKeyPkcs1.data()), privateKeyPkcs1.size());
 
+  BOOST_CHECK_THROW(SignerFilter(DigestAlgorithm::NONE, sKey), Error);
+
   OBufferStream os2;
   bufferSource(data, sizeof(data)) >> signerFilter(DigestAlgorithm::SHA256, sKey) >> streamSink(os2);
   auto sig = os2.buf();
@@ -118,6 +121,8 @@
   PrivateKey sKey;
   sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(privateKeyPkcs1.data()), privateKeyPkcs1.size());
 
+  BOOST_CHECK_THROW(SignerFilter(DigestAlgorithm::NONE, sKey), Error);
+
   OBufferStream os2;
   bufferSource(data, sizeof(data)) >> signerFilter(DigestAlgorithm::SHA256, sKey) >> streamSink(os2);
   auto sig = os2.buf();
@@ -125,6 +130,12 @@
   BOOST_CHECK(verifySignature(data, sizeof(data), sig->buf(), sig->size(), pubKey->buf(), pubKey->size()));
 }
 
+BOOST_AUTO_TEST_CASE(InvalidKey)
+{
+  PrivateKey sKey;
+  BOOST_CHECK_THROW(SignerFilter(DigestAlgorithm::SHA256, sKey), Error);
+}
+
 BOOST_AUTO_TEST_SUITE_END() // TestSignerFilter
 BOOST_AUTO_TEST_SUITE_END() // Transform
 BOOST_AUTO_TEST_SUITE_END() // Security
diff --git a/tests/unit-tests/security/transform/verifier-filter.t.cpp b/tests/unit-tests/security/transform/verifier-filter.t.cpp
index 98a7c32..a877889 100644
--- a/tests/unit-tests/security/transform/verifier-filter.t.cpp
+++ b/tests/unit-tests/security/transform/verifier-filter.t.cpp
@@ -25,6 +25,7 @@
 #include "security/transform/base64-decode.hpp"
 #include "security/transform/bool-sink.hpp"
 #include "security/transform/buffer-source.hpp"
+#include "security/transform/private-key.hpp"
 #include "security/transform/signer-filter.hpp"
 #include "security/transform/stream-sink.hpp"