security: use EVP_DigestVerify* routines in VerifierFilter

Change-Id: I2f5323fee86dd08c16552c2310b3181a620013b5
diff --git a/src/security/transform/verifier-filter.cpp b/src/security/transform/verifier-filter.cpp
index 6e54c34..e5977a1 100644
--- a/src/security/transform/verifier-filter.cpp
+++ b/src/security/transform/verifier-filter.cpp
@@ -32,44 +32,48 @@
 class VerifierFilter::Impl
 {
 public:
-  Impl(const PublicKey& key, const uint8_t* sig, size_t sigLen)
-    : m_key(key)
-    , m_md(BIO_new(BIO_f_md()))
-    , m_sink(BIO_new(BIO_s_null()))
-    , m_sig(sig)
-    , m_sigLen(sigLen)
+  Impl(const uint8_t* sig, size_t siglen) noexcept
+    : sig(sig)
+    , siglen(siglen)
   {
-    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 PublicKey& m_key;
+  EVP_MD_CTX* ctx;
 
-  BIO* m_md;
-  BIO* m_sink;
-
-  const uint8_t* m_sig;
-  size_t m_sigLen;
+  const uint8_t* sig;
+  size_t siglen;
 };
 
 
 VerifierFilter::VerifierFilter(DigestAlgorithm algo, const PublicKey& key,
                                const uint8_t* sig, size_t sigLen)
-  : m_impl(make_unique<Impl>(key, sig, sigLen))
+  : m_impl(make_unique<Impl>(sig, sigLen))
 {
   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_DigestVerifyInit(m_impl->ctx, nullptr, md, nullptr,
+                           reinterpret_cast<EVP_PKEY*>(key.getEvpPkey())) != 1)
+    BOOST_THROW_EXCEPTION(Error(getIndex(), "Failed to initialize verification context with " +
+                                boost::lexical_cast<std::string>(algo) + " digest and " +
+                                boost::lexical_cast<std::string>(key.getKeyType()) + " key"));
 }
 
 VerifierFilter::~VerifierFilter() = default;
@@ -77,34 +81,25 @@
 size_t
 VerifierFilter::convert(const uint8_t* buf, size_t size)
 {
-  int wLen = BIO_write(m_impl->m_md, buf, size);
+  if (EVP_DigestVerifyUpdate(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
 VerifierFilter::finalize()
 {
-  EVP_PKEY* key = reinterpret_cast<EVP_PKEY*>(m_impl->m_key.getEvpPkey());
+  int res = EVP_DigestVerifyFinal(m_impl->ctx,
+#if OPENSSL_VERSION_NUMBER < 0x1000200fL
+                                  const_cast<uint8_t*>(m_impl->sig),
+#else
+                                  m_impl->sig,
+#endif
+                                  m_impl->siglen);
+
   auto buffer = make_unique<OBuffer>(1);
-
-  EVP_MD_CTX* ctx = nullptr;
-  BIO_get_md_ctx(m_impl->m_md, &ctx);
-  int res = EVP_VerifyFinal(ctx, m_impl->m_sig, m_impl->m_sigLen, key);
-
-  if (res < 0)
-    BOOST_THROW_EXCEPTION(Error(getIndex(), "Verification error"));
-
-  (*buffer)[0] = (res != 0) ? 1 : 0;
+  (*buffer)[0] = (res == 1) ? 1 : 0;
   setOutputBuffer(std::move(buffer));
 
   flushAllOutput();
diff --git a/src/security/transform/verifier-filter.hpp b/src/security/transform/verifier-filter.hpp
index e08cc7d..34e3c1d 100644
--- a/src/security/transform/verifier-filter.hpp
+++ b/src/security/transform/verifier-filter.hpp
@@ -35,6 +35,8 @@
  * @brief The module to verify signatures.
  *
  * The next module in the chain is usually BoolSink.
+ *
+ * @note This module cannot be used to verify HMACs.
  */
 class VerifierFilter : public Transform
 {
diff --git a/tests/unit-tests/security/transform/verifier-filter.t.cpp b/tests/unit-tests/security/transform/verifier-filter.t.cpp
index 4464c4e..217cf87 100644
--- a/tests/unit-tests/security/transform/verifier-filter.t.cpp
+++ b/tests/unit-tests/security/transform/verifier-filter.t.cpp
@@ -82,10 +82,10 @@
 
   OBufferStream os1;
   bufferSource(publicKeyPkcs8) >> base64Decode() >> streamSink(os1);
-  auto publicKeyBuffer = os1.buf();
+  auto pubKey = os1.buf();
 
   PublicKey pKey;
-  pKey.loadPkcs8(publicKeyBuffer->buf(), publicKeyBuffer->size());
+  pKey.loadPkcs8(pubKey->buf(), pubKey->size());
 
   PrivateKey sKey;
   sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(privateKeyPkcs1.data()), privateKeyPkcs1.size());
@@ -94,6 +94,8 @@
   bufferSource(data, sizeof(data)) >> signerFilter(DigestAlgorithm::SHA256, sKey) >> streamSink(os2);
   auto sig = os2.buf();
 
+  BOOST_CHECK_THROW(VerifierFilter(DigestAlgorithm::NONE, pKey, sig->buf(), sig->size()), Error);
+
   bool result = false;
   bufferSource(data, sizeof(data)) >>
     verifierFilter(DigestAlgorithm::SHA256, pKey, sig->buf(), sig->size()) >>
@@ -125,10 +127,10 @@
 
   OBufferStream os1;
   bufferSource(publicKeyPkcs8) >> base64Decode() >> streamSink(os1);
-  auto publicKeyBuffer = os1.buf();
+  auto pubKey = os1.buf();
 
   PublicKey pKey;
-  pKey.loadPkcs8(publicKeyBuffer->buf(), publicKeyBuffer->size());
+  pKey.loadPkcs8(pubKey->buf(), pubKey->size());
 
   PrivateKey sKey;
   sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(privateKeyPkcs1.data()), privateKeyPkcs1.size());
@@ -137,6 +139,8 @@
   bufferSource(data, sizeof(data)) >> signerFilter(DigestAlgorithm::SHA256, sKey) >> streamSink(os2);
   auto sig = os2.buf();
 
+  BOOST_CHECK_THROW(VerifierFilter(DigestAlgorithm::NONE, pKey, sig->buf(), sig->size()), Error);
+
   bool result = false;
   bufferSource(data, sizeof(data)) >>
     verifierFilter(DigestAlgorithm::SHA256, pKey, sig->buf(), sig->size()) >>
@@ -145,6 +149,12 @@
   BOOST_CHECK_EQUAL(result, true);
 }
 
+BOOST_AUTO_TEST_CASE(InvalidKey)
+{
+  PublicKey pKey;
+  BOOST_CHECK_THROW(VerifierFilter(DigestAlgorithm::SHA256, pKey, nullptr, 0), Error);
+}
+
 BOOST_AUTO_TEST_SUITE_END() // TestVerifierFilter
 BOOST_AUTO_TEST_SUITE_END() // Transform
 BOOST_AUTO_TEST_SUITE_END() // Security