security: add vectored API for verify and sign

Add vectored input for BufferSource transform

refs #4804

Change-Id: I4949afe5ddb5a49ce6a956da6bc7931cf3719021
diff --git a/ndn-cxx/security/security-common.hpp b/ndn-cxx/security/security-common.hpp
index cf0346b..9e1cb22 100644
--- a/ndn-cxx/security/security-common.hpp
+++ b/ndn-cxx/security/security-common.hpp
@@ -24,6 +24,8 @@
 
 #include "ndn-cxx/detail/common.hpp"
 
+#include <vector>
+
 namespace ndn {
 
 namespace signed_interest {
@@ -52,6 +54,9 @@
 
 } // namespace command_interest
 
+/// Represents a range of distcontiguous buffers as input to a security operation
+typedef std::vector<std::pair<const uint8_t*, size_t>> InputBuffers;
+
 /**
  * @brief The type of KeyId component in a key name.
  */
diff --git a/ndn-cxx/security/tpm/impl/back-end-osx.cpp b/ndn-cxx/security/tpm/impl/back-end-osx.cpp
index 85e0594..da318fd 100644
--- a/ndn-cxx/security/tpm/impl/back-end-osx.cpp
+++ b/ndn-cxx/security/tpm/impl/back-end-osx.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2019 Regents of the University of California.
+ * Copyright (c) 2013-2020 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -21,7 +21,10 @@
 
 #include "ndn-cxx/security/tpm/impl/back-end-osx.hpp"
 #include "ndn-cxx/security/tpm/impl/key-handle-osx.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/detail/cf-string-osx.hpp"
 #include "ndn-cxx/encoding/buffer-stream.hpp"
 
@@ -246,7 +249,7 @@
 }
 
 ConstBufferPtr
-BackEndOsx::sign(const KeyRefOsx& key, DigestAlgorithm digestAlgo, const uint8_t* buf, size_t size)
+BackEndOsx::sign(const KeyRefOsx& key, DigestAlgorithm digestAlgo, const InputBuffers& bufs)
 {
   CFReleaser<CFErrorRef> error;
   CFReleaser<SecTransformRef> signer = SecSignTransformCreate(key.get(), &error.get());
@@ -254,13 +257,26 @@
     NDN_THROW(Error("Failed to create sign transform: " + getFailureReason(error.get())));
   }
 
+  // Generate digest
+  OBufferStream digestSink;
+  using namespace transform;
+  bufferSource(bufs) >> digestFilter(digestAlgo) >> streamSink(digestSink);
+
   // Set input
-  auto data = makeCFDataNoCopy(buf, size);
+  auto buffer = digestSink.buf();
+  BOOST_ASSERT(buffer->size() * 8 == static_cast<size_t>(getDigestSize(digestAlgo)));
+  auto data = makeCFDataNoCopy(buffer->data(), buffer->size());
   SecTransformSetAttribute(signer.get(), kSecTransformInputAttributeName, data.get(), &error.get());
   if (error != nullptr) {
     NDN_THROW(Error("Failed to configure input of sign transform: " + getFailureReason(error.get())));
   }
 
+  // Configure input as digest
+  SecTransformSetAttribute(signer.get(), kSecInputIsAttributeName, kSecInputIsDigest, &error.get());
+  if (error != nullptr) {
+    NDN_THROW(Error("Failed to configure sign transform input as digest: " + getFailureReason(error.get())));
+  }
+
   // Enable use of padding
   SecTransformSetAttribute(signer.get(), kSecPaddingKey, kSecPaddingPKCS1Key, &error.get());
   if (error != nullptr) {
diff --git a/ndn-cxx/security/tpm/impl/back-end-osx.hpp b/ndn-cxx/security/tpm/impl/back-end-osx.hpp
index 18df66a..326268b 100644
--- a/ndn-cxx/security/tpm/impl/back-end-osx.hpp
+++ b/ndn-cxx/security/tpm/impl/back-end-osx.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2019 Regents of the University of California.
+ * Copyright (c) 2013-2020 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -68,11 +68,14 @@
 
 public: // crypto transformation
   /**
-   * @brief Sign @p buf with @p key using @p digestAlgorithm.
+   * @brief Sign @p bufs with @p key using @p digestAlgorithm.
    */
   static ConstBufferPtr
-  sign(const KeyRefOsx& key, DigestAlgorithm digestAlgorithm, const uint8_t* buf, size_t size);
+  sign(const KeyRefOsx& key, DigestAlgorithm digestAlgorithm, const InputBuffers& bufs);
 
+  /**
+   * @brief Decrypt @p cipherText with @p key.
+   */
   static ConstBufferPtr
   decrypt(const KeyRefOsx& key, const uint8_t* cipherText, size_t cipherSize);
 
diff --git a/ndn-cxx/security/tpm/impl/key-handle-mem.cpp b/ndn-cxx/security/tpm/impl/key-handle-mem.cpp
index 99275e8..fcee350 100644
--- a/ndn-cxx/security/tpm/impl/key-handle-mem.cpp
+++ b/ndn-cxx/security/tpm/impl/key-handle-mem.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2019 Regents of the University of California.
+ * Copyright (c) 2013-2020 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -39,24 +39,23 @@
 }
 
 ConstBufferPtr
-KeyHandleMem::doSign(DigestAlgorithm digestAlgorithm, const uint8_t* buf, size_t size) const
+KeyHandleMem::doSign(DigestAlgorithm digestAlgorithm, const InputBuffers& bufs) const
 {
   using namespace transform;
 
   OBufferStream sigOs;
-  bufferSource(buf, size) >> signerFilter(digestAlgorithm, *m_key) >> streamSink(sigOs);
+  bufferSource(bufs) >> signerFilter(digestAlgorithm, *m_key) >> streamSink(sigOs);
   return sigOs.buf();
 }
 
 bool
-KeyHandleMem::doVerify(DigestAlgorithm digestAlgorithm, const uint8_t* buf, size_t size,
+KeyHandleMem::doVerify(DigestAlgorithm digestAlgorithm, const InputBuffers& bufs,
                        const uint8_t* sig, size_t sigLen) const
 {
   using namespace transform;
 
   bool result = false;
-  bufferSource(buf, size) >> verifierFilter(digestAlgorithm, *m_key, sig, sigLen)
-                          >> boolSink(result);
+  bufferSource(bufs) >> verifierFilter(digestAlgorithm, *m_key, sig, sigLen) >> boolSink(result);
   return result;
 }
 
diff --git a/ndn-cxx/security/tpm/impl/key-handle-mem.hpp b/ndn-cxx/security/tpm/impl/key-handle-mem.hpp
index 5ed9dff..63a4785 100644
--- a/ndn-cxx/security/tpm/impl/key-handle-mem.hpp
+++ b/ndn-cxx/security/tpm/impl/key-handle-mem.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2019 Regents of the University of California.
+ * Copyright (c) 2013-2020 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -44,10 +44,10 @@
 
 private:
   ConstBufferPtr
-  doSign(DigestAlgorithm digestAlgorithm, const uint8_t* buf, size_t size) const final;
+  doSign(DigestAlgorithm digestAlgorithm, const InputBuffers& bufs) const final;
 
   bool
-  doVerify(DigestAlgorithm digestAlgorithm, const uint8_t* buf, size_t size,
+  doVerify(DigestAlgorithm digestAlgorithm, const InputBuffers& bufs,
            const uint8_t* sig, size_t sigLen) const final;
 
   ConstBufferPtr
diff --git a/ndn-cxx/security/tpm/impl/key-handle-osx.cpp b/ndn-cxx/security/tpm/impl/key-handle-osx.cpp
index 0ff3d54..63d5095 100644
--- a/ndn-cxx/security/tpm/impl/key-handle-osx.cpp
+++ b/ndn-cxx/security/tpm/impl/key-handle-osx.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2019 Regents of the University of California.
+ * Copyright (c) 2013-2020 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -34,13 +34,13 @@
 }
 
 ConstBufferPtr
-KeyHandleOsx::doSign(DigestAlgorithm digestAlgorithm, const uint8_t* buf, size_t size) const
+KeyHandleOsx::doSign(DigestAlgorithm digestAlgorithm, const InputBuffers& bufs) const
 {
-  return BackEndOsx::sign(m_key, digestAlgorithm, buf, size);
+  return BackEndOsx::sign(m_key, digestAlgorithm, bufs);
 }
 
 bool
-KeyHandleOsx::doVerify(DigestAlgorithm digestAlgorithm, const uint8_t* buf, size_t size,
+KeyHandleOsx::doVerify(DigestAlgorithm digestAlgorithm, const InputBuffers& bufs,
                        const uint8_t* sig, size_t sigLen) const
 {
   NDN_THROW(Error("Signature verification is not supported with macOS Keychain-based TPM"));
diff --git a/ndn-cxx/security/tpm/impl/key-handle-osx.hpp b/ndn-cxx/security/tpm/impl/key-handle-osx.hpp
index bb65944..23415d8 100644
--- a/ndn-cxx/security/tpm/impl/key-handle-osx.hpp
+++ b/ndn-cxx/security/tpm/impl/key-handle-osx.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2019 Regents of the University of California.
+ * Copyright (c) 2013-2020 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -45,10 +45,10 @@
 
 private:
   ConstBufferPtr
-  doSign(DigestAlgorithm digestAlgorithm, const uint8_t* buf, size_t size) const final;
+  doSign(DigestAlgorithm digestAlgorithm, const InputBuffers& bufs) const final;
 
   bool
-  doVerify(DigestAlgorithm digestAlgorithm, const uint8_t* buf, size_t size,
+  doVerify(DigestAlgorithm digestAlgorithm, const InputBuffers& bufs,
            const uint8_t* sig, size_t sigLen) const final;
 
   ConstBufferPtr
diff --git a/ndn-cxx/security/tpm/key-handle.cpp b/ndn-cxx/security/tpm/key-handle.cpp
index 56057a2..dc850bb 100644
--- a/ndn-cxx/security/tpm/key-handle.cpp
+++ b/ndn-cxx/security/tpm/key-handle.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2019 Regents of the University of California.
+ * Copyright (c) 2013-2020 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -28,16 +28,29 @@
 KeyHandle::~KeyHandle() = default;
 
 ConstBufferPtr
+KeyHandle::sign(DigestAlgorithm digestAlgorithm, const InputBuffers& bufs) const
+{
+  return doSign(digestAlgorithm, bufs);
+}
+
+ConstBufferPtr
 KeyHandle::sign(DigestAlgorithm digestAlgorithm, const uint8_t* buf, size_t size) const
 {
-  return doSign(digestAlgorithm, buf, size);
+  return doSign(digestAlgorithm, {{buf, size}});
+}
+
+bool
+KeyHandle::verify(DigestAlgorithm digestAlgorithm, const InputBuffers& bufs,
+                  const uint8_t* sig, size_t sigLen) const
+{
+  return doVerify(digestAlgorithm, bufs, sig, sigLen);
 }
 
 bool
 KeyHandle::verify(DigestAlgorithm digestAlgorithm, const uint8_t* buf, size_t bufLen,
                   const uint8_t* sig, size_t sigLen) const
 {
-  return doVerify(digestAlgorithm, buf, bufLen, sig, sigLen);
+  return doVerify(digestAlgorithm, {{buf, bufLen}}, sig, sigLen);
 }
 
 ConstBufferPtr
diff --git a/ndn-cxx/security/tpm/key-handle.hpp b/ndn-cxx/security/tpm/key-handle.hpp
index 0a4fd8a..c8ae068 100644
--- a/ndn-cxx/security/tpm/key-handle.hpp
+++ b/ndn-cxx/security/tpm/key-handle.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2019 Regents of the University of California.
+ * Copyright (c) 2013-2020 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -48,20 +48,33 @@
   ~KeyHandle();
 
   /**
-   * @return a digital signature created on @p buf using this key with @p digestAlgorithm.
+   * @brief Generate a digital signature for @p bufs using this key with @p digestAlgorithm.
+   */
+  ConstBufferPtr
+  sign(DigestAlgorithm digestAlgorithm, const InputBuffers& bufs) const;
+
+  /**
+   * @brief Generate a digital signature for @p buf using this key with @p digestAlgorithm.
    */
   ConstBufferPtr
   sign(DigestAlgorithm digestAlgorithm, const uint8_t* buf, size_t size) const;
 
   /**
-   * @brief Verify the signature @p sig created on @p buf using this key and @p digestAlgorithm.
+   * @brief Verify the signature @p sig for @p bufs using this key and @p digestAlgorithm.
+   */
+  bool
+  verify(DigestAlgorithm digestAlgorithm, const InputBuffers& bufs,
+         const uint8_t* sig, size_t sigLen) const;
+
+  /**
+   * @brief Verify the signature @p sig for @p buf using this key and @p digestAlgorithm.
    */
   bool
   verify(DigestAlgorithm digestAlgorithm, const uint8_t* buf, size_t bufLen,
          const uint8_t* sig, size_t sigLen) const;
 
   /**
-   * @return plain text content decrypted from @p cipherText using this key.
+   * @brief Return plain text content decrypted from @p cipherText using this key.
    */
   ConstBufferPtr
   decrypt(const uint8_t* cipherText, size_t cipherTextLen) const;
@@ -86,10 +99,10 @@
 
 private:
   virtual ConstBufferPtr
-  doSign(DigestAlgorithm digestAlgorithm, const uint8_t* buf, size_t size) const = 0;
+  doSign(DigestAlgorithm digestAlgorithm, const InputBuffers& bufs) const = 0;
 
   virtual bool
-  doVerify(DigestAlgorithm digestAlgorithm, const uint8_t* buf, size_t bufLen,
+  doVerify(DigestAlgorithm digestAlgorithm, const InputBuffers& bufs,
            const uint8_t* sig, size_t sigLen) const = 0;
 
   virtual ConstBufferPtr
diff --git a/ndn-cxx/security/tpm/tpm.cpp b/ndn-cxx/security/tpm/tpm.cpp
index d325955..27a2f05 100644
--- a/ndn-cxx/security/tpm/tpm.cpp
+++ b/ndn-cxx/security/tpm/tpm.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2019 Regents of the University of California.
+ * Copyright (c) 2013-2020 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -81,26 +81,30 @@
 }
 
 ConstBufferPtr
-Tpm::sign(const uint8_t* buf, size_t size, const Name& keyName, DigestAlgorithm digestAlgorithm) const
+Tpm::sign(const InputBuffers& bufs, const Name& keyName, DigestAlgorithm digestAlgorithm) const
 {
   const KeyHandle* key = findKey(keyName);
 
-  if (key == nullptr)
+  if (key == nullptr) {
     return nullptr;
-  else
-    return key->sign(digestAlgorithm, buf, size);
+  }
+  else {
+    return key->sign(digestAlgorithm, bufs);
+  }
 }
 
 boost::logic::tribool
-Tpm::verify(const uint8_t* buf, size_t bufLen, const uint8_t* sig, size_t sigLen,
-            const Name& keyName, DigestAlgorithm digestAlgorithm) const
+Tpm::verify(const InputBuffers& bufs, const uint8_t* sig, size_t sigLen, const Name& keyName,
+            DigestAlgorithm digestAlgorithm) const
 {
   const KeyHandle* key = findKey(keyName);
 
-  if (key == nullptr)
+  if (key == nullptr) {
     return boost::logic::indeterminate;
-  else
-    return key->verify(digestAlgorithm, buf, bufLen, sig, sigLen);
+  }
+  else {
+    return key->verify(digestAlgorithm, bufs, sig, sigLen);
+  }
 }
 
 ConstBufferPtr
diff --git a/ndn-cxx/security/tpm/tpm.hpp b/ndn-cxx/security/tpm/tpm.hpp
index 4a6a8ac..25754b8 100644
--- a/ndn-cxx/security/tpm/tpm.hpp
+++ b/ndn-cxx/security/tpm/tpm.hpp
@@ -95,12 +95,36 @@
   getPublicKey(const Name& keyName) const;
 
   /**
+   * @brief Sign discontiguous ranges using the key with name @p keyName and using the digest
+   *        @p digestAlgorithm.
+   *
+   * @return The signature, or nullptr if the key does not exist.
+   */
+  ConstBufferPtr
+  sign(const InputBuffers& bufs, const Name& keyName, DigestAlgorithm digestAlgorithm) const;
+
+  /**
    * @brief Sign blob using the key with name @p keyName and using the digest @p digestAlgorithm.
    *
    * @return The signature, or nullptr if the key does not exist.
    */
   ConstBufferPtr
-  sign(const uint8_t* buf, size_t size, const Name& keyName, DigestAlgorithm digestAlgorithm) const;
+  sign(const uint8_t* buf, size_t size, const Name& keyName, DigestAlgorithm digestAlgorithm) const
+  {
+    return sign({{buf, size}}, keyName, digestAlgorithm);
+  }
+
+  /**
+   * @brief Verify discontiguous ranges using the key with name @p keyName and using the digest
+   *        @p digestAlgorithm.
+   *
+   * @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 InputBuffers& bufs, const uint8_t* sig, size_t sigLen, const Name& keyName,
+         DigestAlgorithm digestAlgorithm) const;
 
   /**
    * @brief Verify blob using the key with name @p keyName and using the digest @p digestAlgorithm.
@@ -111,7 +135,10 @@
    */
   boost::logic::tribool
   verify(const uint8_t* buf, size_t bufLen, const uint8_t* sig, size_t sigLen,
-         const Name& keyName, DigestAlgorithm digestAlgorithm) const;
+         const Name& keyName, DigestAlgorithm digestAlgorithm) const
+  {
+    return verify({{buf, bufLen}}, sig, sigLen, keyName, digestAlgorithm);
+  }
 
   /**
    * @brief Decrypt blob using the key with name @p keyName.
diff --git a/ndn-cxx/security/transform/buffer-source.cpp b/ndn-cxx/security/transform/buffer-source.cpp
index 95fd2d7..36f5d2d 100644
--- a/ndn-cxx/security/transform/buffer-source.cpp
+++ b/ndn-cxx/security/transform/buffer-source.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-2020 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -26,20 +26,22 @@
 namespace transform {
 
 BufferSource::BufferSource(const uint8_t* buf, size_t size)
-  : m_buf(buf)
-  , m_size(size)
+  : m_bufs({{buf, size}})
 {
 }
 
 BufferSource::BufferSource(const std::string& string)
-  : m_buf(reinterpret_cast<const uint8_t*>(string.data()))
-  , m_size(string.size())
+  : m_bufs({{reinterpret_cast<const uint8_t*>(string.data()), string.size()}})
 {
 }
 
 BufferSource::BufferSource(const Buffer& buffer)
-  : m_buf(buffer.data())
-  , m_size(buffer.size())
+  : m_bufs({{buffer.data(), buffer.size()}})
+{
+}
+
+BufferSource::BufferSource(InputBuffers buffers)
+  : m_bufs(std::move(buffers))
 {
 }
 
@@ -48,13 +50,15 @@
 {
   BOOST_ASSERT(m_next != nullptr);
 
-  const uint8_t* buf = m_buf;
-  size_t size = m_size;
+  for (const auto& buffer : m_bufs) {
+    const uint8_t* buf = buffer.first;
+    size_t size = buffer.second;
 
-  while (0 < size) {
-    size_t nBytesWritten = m_next->write(buf, size);
-    buf += nBytesWritten;
-    size -= nBytesWritten;
+    while (size > 0) {
+      size_t nBytesWritten = m_next->write(buf, size);
+      buf += nBytesWritten;
+      size -= nBytesWritten;
+    }
   }
 
   m_next->end();
diff --git a/ndn-cxx/security/transform/buffer-source.hpp b/ndn-cxx/security/transform/buffer-source.hpp
index 6119999..4b4ab44 100644
--- a/ndn-cxx/security/transform/buffer-source.hpp
+++ b/ndn-cxx/security/transform/buffer-source.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-2020 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -24,28 +24,29 @@
 
 #include "ndn-cxx/security/transform/transform-base.hpp"
 #include "ndn-cxx/encoding/buffer.hpp"
+#include "ndn-cxx/security/security-common.hpp"
 
 namespace ndn {
 namespace security {
 namespace transform {
 
 /**
- * @brief A source taking a memory buffer as input
+ * @brief A source taking one or more memory buffers as input
  */
 class BufferSource : public Source
 {
 public:
   /**
-   * @brief Take a buffer @p buf with size of @p size as input.
+   * @brief Take a buffer @p buf with size @p size as input.
    *
-   * Caller must not destroy the buffer before transformation is done
+   * Caller must not destroy the buffer before the transformation is completed.
    */
   BufferSource(const uint8_t* buf, size_t size);
 
   /**
    * @brief Take @p string as input.
    *
-   * Caller must not destroy the string before transformation is done
+   * Caller must not destroy the string before the transformation is completed.
    */
   explicit
   BufferSource(const std::string& string);
@@ -53,11 +54,19 @@
   /**
    * @brief Take @p buffer as input.
    *
-   * Caller must not destroy the buffer before transformation is done
+   * Caller must not destroy the buffer before the transformation is completed.
    */
   explicit
   BufferSource(const Buffer& buffer);
 
+  /**
+   * @brief Take @p buffers as input.
+   *
+   * Caller must not destroy any of the input buffers before the transformation is completed.
+   */
+  explicit
+  BufferSource(InputBuffers buffers);
+
 private:
   /**
    * @brief Write the whole buffer into the next module.
@@ -66,8 +75,7 @@
   doPump() final;
 
 private:
-  const uint8_t* m_buf;
-  size_t m_size;
+  InputBuffers m_bufs;
 };
 
 typedef BufferSource bufferSource;
diff --git a/tests/unit/security/tpm/back-end.t.cpp b/tests/unit/security/tpm/back-end.t.cpp
index 8d4b41a..8abc745 100644
--- a/tests/unit/security/tpm/back-end.t.cpp
+++ b/tests/unit/security/tpm/back-end.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2019 Regents of the University of California.
+ * Copyright (c) 2013-2020 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -117,23 +117,40 @@
   unique_ptr<KeyHandle> key = tpm.createKey(identity, RsaKeyParams());
   Name keyName = key->getKeyName();
 
-  const uint8_t content[] = {0x01, 0x02, 0x03, 0x04};
-  auto sigValue = key->sign(DigestAlgorithm::SHA256, content, sizeof(content));
-  BOOST_REQUIRE(sigValue != nullptr);
-  Block sigBlock(tlv::SignatureValue, sigValue);
-
   transform::PublicKey pubKey;
   ConstBufferPtr pubKeyBits = key->derivePublicKey();
   pubKey.loadPkcs8(pubKeyBits->data(), pubKeyBits->size());
 
-  bool result;
+  // Sign using single buffer API
+  const uint8_t content1[] = {0x01, 0x02, 0x03, 0x04};
+  auto sigValueSingle = key->sign(DigestAlgorithm::SHA256, content1, sizeof(content1));
+  BOOST_REQUIRE(sigValueSingle != nullptr);
+
+  bool resultSingle;
   {
     using namespace transform;
-    bufferSource(content, sizeof(content)) >>
-      verifierFilter(DigestAlgorithm::SHA256, pubKey, sigBlock.value(), sigBlock.value_size()) >>
-      boolSink(result);
+    bufferSource(content1, sizeof(content1)) >>
+      verifierFilter(DigestAlgorithm::SHA256, pubKey,
+                     sigValueSingle->data(), sigValueSingle->size()) >>
+      boolSink(resultSingle);
   }
-  BOOST_CHECK_EQUAL(result, true);
+  BOOST_CHECK_EQUAL(resultSingle, true);
+
+  // Sign using vectored API
+  const uint8_t content2[] = {0x05, 0x06, 0x07, 0x08};
+  auto sigValueVector = key->sign(DigestAlgorithm::SHA256, {{content1, sizeof(content1)},
+                                                            {content2, sizeof(content2)}});
+  BOOST_REQUIRE(sigValueVector != nullptr);
+
+  bool resultVector;
+  {
+    using namespace transform;
+    bufferSource({{content1, sizeof(content1)}, {content2, sizeof(content2)}}) >>
+      verifierFilter(DigestAlgorithm::SHA256, pubKey,
+                     sigValueVector->data(), sigValueVector->size()) >>
+      boolSink(resultVector);
+  }
+  BOOST_CHECK_EQUAL(resultVector, true);
 
   tpm.deleteKey(keyName);
   BOOST_CHECK_EQUAL(tpm.hasKey(keyName), false);
@@ -175,23 +192,40 @@
   unique_ptr<KeyHandle> key = tpm.createKey(identity, EcKeyParams());
   Name ecKeyName = key->getKeyName();
 
-  const uint8_t content[] = {0x01, 0x02, 0x03, 0x04};
-  auto sigValue = key->sign(DigestAlgorithm::SHA256, content, sizeof(content));
-  BOOST_REQUIRE(sigValue != nullptr);
-  Block sigBlock(tlv::SignatureValue, sigValue);
-
   transform::PublicKey pubKey;
   ConstBufferPtr pubKeyBits = key->derivePublicKey();
   pubKey.loadPkcs8(pubKeyBits->data(), pubKeyBits->size());
 
-  bool result;
+  // Sign using single buffer API
+  const uint8_t content1[] = {0x01, 0x02, 0x03, 0x04};
+  auto sigValueSingle = key->sign(DigestAlgorithm::SHA256, content1, sizeof(content1));
+  BOOST_REQUIRE(sigValueSingle != nullptr);
+
+  bool resultSingle;
   {
     using namespace transform;
-    bufferSource(content, sizeof(content)) >>
-      verifierFilter(DigestAlgorithm::SHA256, pubKey, sigBlock.value(), sigBlock.value_size()) >>
-      boolSink(result);
+    bufferSource(content1, sizeof(content1)) >>
+      verifierFilter(DigestAlgorithm::SHA256, pubKey,
+                     sigValueSingle->data(), sigValueSingle->size()) >>
+      boolSink(resultSingle);
   }
-  BOOST_CHECK_EQUAL(result, true);
+  BOOST_CHECK_EQUAL(resultSingle, true);
+
+  // Sign using vectored API
+  const uint8_t content2[] = {0x05, 0x06, 0x07, 0x08};
+  auto sigValueVector = key->sign(DigestAlgorithm::SHA256, {{content1, sizeof(content1)},
+                                                            {content2, sizeof(content2)}});
+  BOOST_REQUIRE(sigValueVector != nullptr);
+
+  bool resultVector;
+  {
+    using namespace transform;
+    bufferSource({{content1, sizeof(content1)}, {content2, sizeof(content2)}}) >>
+      verifierFilter(DigestAlgorithm::SHA256, pubKey,
+                     sigValueVector->data(), sigValueVector->size()) >>
+      boolSink(resultVector);
+  }
+  BOOST_CHECK_EQUAL(resultVector, true);
 
   tpm.deleteKey(ecKeyName);
   BOOST_CHECK_EQUAL(tpm.hasKey(ecKeyName), false);
@@ -207,14 +241,24 @@
   unique_ptr<KeyHandle> key = tpm.createKey(identity, HmacKeyParams());
   Name hmacKeyName = key->getKeyName();
 
-  const uint8_t content[] = {0x01, 0x02, 0x03, 0x04};
-  auto sigValue = key->sign(DigestAlgorithm::SHA256, content, sizeof(content));
-  BOOST_REQUIRE(sigValue != nullptr);
-  Block sigBlock(tlv::SignatureValue, sigValue);
+  // Sign and verify using single buffer API
+  const uint8_t content1[] = {0x01, 0x02, 0x03, 0x04};
+  auto sigValueSingle = key->sign(DigestAlgorithm::SHA256, content1, sizeof(content1));
+  BOOST_REQUIRE(sigValueSingle != nullptr);
+  bool resultSingle = key->verify(DigestAlgorithm::SHA256, content1, sizeof(content1),
+                                  sigValueSingle->data(), sigValueSingle->size());
+  BOOST_CHECK_EQUAL(resultSingle, true);
 
-  bool result = key->verify(DigestAlgorithm::SHA256, content, sizeof(content),
-                            sigBlock.value(), sigBlock.value_size());
-  BOOST_CHECK_EQUAL(result, true);
+  // Sign and verify using vectored API
+  const uint8_t content2[] = {0x05, 0x06, 0x07, 0x08};
+  auto sigValueVector = key->sign(DigestAlgorithm::SHA256, {{content1, sizeof(content1)},
+                                                            {content2, sizeof(content2)}});
+  BOOST_REQUIRE(sigValueVector != nullptr);
+  bool resultVector = key->verify(DigestAlgorithm::SHA256,
+                                  {{content1, sizeof(content1)},
+                                   {content2, sizeof(content2)}},
+                                  sigValueVector->data(), sigValueVector->size());
+  BOOST_CHECK_EQUAL(resultVector, true);
 
   tpm.deleteKey(hmacKeyName);
   BOOST_CHECK_EQUAL(tpm.hasKey(hmacKeyName), false);
diff --git a/tests/unit/security/transform/buffer-source.t.cpp b/tests/unit/security/transform/buffer-source.t.cpp
index 80b9d8d..8f1a23e 100644
--- a/tests/unit/security/transform/buffer-source.t.cpp
+++ b/tests/unit/security/transform/buffer-source.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-2020 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -43,7 +43,7 @@
   std::ostringstream os1;
   bufferSource(in, sizeof(in)) >> streamSink(os1);
   std::string out1 = os1.str();
-  BOOST_CHECK_EQUAL_COLLECTIONS(in, in + sizeof(in), out1.begin(), out1.end());
+  BOOST_CHECK_EQUAL_COLLECTIONS(out1.begin(), out1.end(), in, in + sizeof(in));
 
   std::string in2 =
     "0123456701234567012345670123456701234567012345670123456701234567"
@@ -70,13 +70,23 @@
   std::ostringstream os2;
   bufferSource(in2) >> streamSink(os2);
   std::string out2 = os2.str();
-  BOOST_CHECK_EQUAL_COLLECTIONS(in2.begin(), in2.end(), out2.begin(), out2.end());
+  BOOST_CHECK_EQUAL_COLLECTIONS(out2.begin(), out2.end(), in2.begin(), in2.end());
 
   Buffer in3(in, sizeof(in));
   std::ostringstream os3;
   bufferSource(in3) >> streamSink(os3);
   std::string out3 = os3.str();
-  BOOST_CHECK_EQUAL_COLLECTIONS(in3.begin(), in3.end(), out3.begin(), out3.end());
+  BOOST_CHECK_EQUAL_COLLECTIONS(out3.begin(), out3.end(), in3.begin(), in3.end());
+
+  InputBuffers in4{{in, sizeof(in)}, {reinterpret_cast<const uint8_t*>(in2.data()), in2.size()}};
+  std::ostringstream os4;
+  bufferSource(in4) >> streamSink(os4);
+  std::string out4 = os4.str();
+  BOOST_CHECK_EQUAL(out4.size(), sizeof(in) + in2.size());
+  BOOST_CHECK_EQUAL_COLLECTIONS(out4.begin(), out4.begin() + in4[0].second,
+                                in4[0].first, in4[0].first + in4[0].second);
+  BOOST_CHECK_EQUAL_COLLECTIONS(out4.begin() + in4[0].second, out4.end(),
+                                in4[1].first, in4[1].first + in4[1].second);
 }
 
 BOOST_AUTO_TEST_SUITE_END() // TestBufferSource