security: Add base64 and hex encoding/decoding support in transformation

Change-Id: I11d5c8921abcf04411aac38382dc285269f553ef
Refs: #3009
diff --git a/src/security/openssl.hpp b/src/security/detail/openssl.hpp
similarity index 75%
rename from src/security/openssl.hpp
rename to src/security/detail/openssl.hpp
index c753497..09a6f68 100644
--- a/src/security/openssl.hpp
+++ b/src/security/detail/openssl.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2014 Regents of the University of California.
+ * Copyright (c) 2013-2016 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -19,8 +19,8 @@
  * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
  */
 
-#ifndef NDN_SECURITY_OPENSSL_HPP
-#define NDN_SECURITY_OPENSSL_HPP
+#ifndef NDN_SECURITY_DETAIL_OPENSSL_HPP
+#define NDN_SECURITY_DETAIL_OPENSSL_HPP
 
 // suppress deprecation warnings in OSX >= 10.7
 
@@ -30,16 +30,16 @@
 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
 #endif // __clang__
 
-#ifdef __GNUC__
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#endif // __GNUC__
+#endif // __APPLE__
 
-#endif
-
-
-#include <openssl/ssl.h>
-#include <openssl/sha.h>
+#include <openssl/rand.h>
+#include <openssl/bio.h>
+#include <openssl/evp.h>
 #include <openssl/rsa.h>
+#include <openssl/ec.h>
+#include <openssl/pem.h>
+#include <openssl/hmac.h>
+#include <openssl/x509.h>
 
 
-#endif // NDN_SECURITY_OPENSSL_HPP
+#endif // NDN_SECURITY_DETAIL_OPENSSL_HPP
diff --git a/src/security/transform.hpp b/src/security/transform.hpp
index b29f5c6..b9a960c 100644
--- a/src/security/transform.hpp
+++ b/src/security/transform.hpp
@@ -29,4 +29,9 @@
 #include "transform/bool-sink.hpp"
 #include "transform/stream-sink.hpp"
 
+#include "transform/hex-encode.hpp"
+#include "transform/hex-decode.hpp"
+#include "transform/base64-encode.hpp"
+#include "transform/base64-decode.hpp"
+
 #endif // NDN_CXX_SECURITY_TRANSFORM_HPP
diff --git a/src/security/transform/base64-decode.cpp b/src/security/transform/base64-decode.cpp
new file mode 100644
index 0000000..7f3cf54
--- /dev/null
+++ b/src/security/transform/base64-decode.cpp
@@ -0,0 +1,135 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "base64-decode.hpp"
+#include "../../encoding/buffer.hpp"
+#include "../detail/openssl.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+
+/**
+ * @brief The implementation class which contains the internal state of the filter
+ *        which includes openssl specific structures.
+ */
+class Base64Decode::Impl
+{
+public:
+  Impl()
+    : m_base64(BIO_new(BIO_f_base64()))
+    , m_source(BIO_new(BIO_s_mem()))
+  {
+    // Input may not be written in a single time.
+    // Do not return EOF when source is empty unless explicitly requested
+    BIO_set_mem_eof_return(m_source, -1);
+
+    // connect base64 transform to the data source.
+    BIO_push(m_base64, m_source);
+  }
+
+  ~Impl()
+  {
+    BIO_free_all(m_source);
+  }
+
+public:
+  BIO* m_base64;
+  BIO* m_source; // BIO_f_base64 alone does not work without a source
+};
+
+static const size_t BUFFER_LENGTH = 1024;
+
+Base64Decode::Base64Decode(bool expectNewlineEvery64Bytes)
+  : m_impl(new Impl)
+{
+  if (!expectNewlineEvery64Bytes)
+    BIO_set_flags(m_impl->m_base64, BIO_FLAGS_BASE64_NO_NL);
+}
+
+void
+Base64Decode::preTransform()
+{
+  while (isOutputBufferEmpty()) {
+    fillOutputBuffer();
+    if (isOutputBufferEmpty()) // nothing to read from BIO, return
+      return;
+
+    flushOutputBuffer();
+  }
+}
+
+size_t
+Base64Decode::convert(const uint8_t* buf, size_t size)
+{
+  int wLen = BIO_write(m_impl->m_source, buf, size);
+
+  if (wLen <= 0) { // fail to write data
+    if (!BIO_should_retry(m_impl->m_source)) {
+      // 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;
+  }
+}
+
+void
+Base64Decode::finalize()
+{
+  BIO_set_mem_eof_return(m_impl->m_source, 0);
+
+  fillOutputBuffer();
+
+  while (!isOutputBufferEmpty()) {
+    flushOutputBuffer();
+    if (isOutputBufferEmpty())
+      fillOutputBuffer();
+  }
+}
+
+void
+Base64Decode::fillOutputBuffer()
+{
+  // OpenSSL base64 BIO cannot give us the number bytes of partial decoded result,
+  // so we just try to read a chunk.
+  auto buffer = make_unique<OBuffer>(BUFFER_LENGTH);
+  int rLen = BIO_read(m_impl->m_base64, &(*buffer)[0], buffer->size());
+  if (rLen <= 0)
+    return;
+
+  if (static_cast<size_t>(rLen) < buffer->size())
+    buffer->erase(buffer->begin() + rLen, buffer->end());
+
+  setOutputBuffer(std::move(buffer));
+}
+
+unique_ptr<Transform>
+base64Decode(bool expectNewlineEvery64Bytes)
+{
+  return make_unique<Base64Decode>(expectNewlineEvery64Bytes);
+}
+
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/src/security/transform/base64-decode.hpp b/src/security/transform/base64-decode.hpp
new file mode 100644
index 0000000..cfd5876
--- /dev/null
+++ b/src/security/transform/base64-decode.hpp
@@ -0,0 +1,89 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_CXX_SECURITY_TRANSFORM_BASE64_DECODE_HPP
+#define NDN_CXX_SECURITY_TRANSFORM_BASE64_DECODE_HPP
+
+#include "transform-base.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+
+/**
+ * @brief The module to perform Base64 decoding transformation.
+ */
+class Base64Decode : public Transform
+{
+public:
+  /**
+   * @brief Create a base64 decoding module
+   *
+   * @p expectNewlineEvery64Bytes if true, expect newline after every 64 bytes, otherwise expect
+   *                              all input to be in a single line. Output is undefined if input
+   *                              does not conform to the requirement.
+   */
+  explicit
+  Base64Decode(bool expectNewlineEvery64Bytes = true);
+
+private:
+
+  /**
+   * @brief Read partial transformation results into output buffer and write them into next module.
+   */
+  virtual void
+  preTransform() final;
+
+  /**
+   * @brief Decode data @p buf in base64 format
+   *
+   * @return number of bytes that have been accepted by the converter
+   */
+  virtual size_t
+  convert(const uint8_t* buf, size_t size) final;
+
+  /**
+   * @brief Finalize base64 decoding
+   *
+   * This method with read all decoding results from the converter and write them into next module.
+   */
+  virtual void
+  finalize() final;
+
+  /**
+   * @brief Try to fill partial decoding result into output buffer.
+   */
+  void
+  fillOutputBuffer();
+
+private:
+  class Impl;
+  unique_ptr<Impl> m_impl;
+};
+
+unique_ptr<Transform>
+base64Decode(bool expectNewlineEvery64Bytes = true);
+
+} // namespace transform
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_CXX_SECURITY_TRANSFORM_BASE64_DECODE_HPP
diff --git a/src/security/transform/base64-encode.cpp b/src/security/transform/base64-encode.cpp
new file mode 100644
index 0000000..0a27bed
--- /dev/null
+++ b/src/security/transform/base64-encode.cpp
@@ -0,0 +1,135 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "base64-encode.hpp"
+#include "../../encoding/buffer.hpp"
+#include "../detail/openssl.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+
+/**
+ * @brief The implementation class which contains the internal state of the filter
+ *        which includes openssl specific structures.
+ */
+class Base64Encode::Impl
+{
+public:
+  Impl()
+    : m_base64(BIO_new(BIO_f_base64()))
+    , m_sink(BIO_new(BIO_s_mem()))
+  {
+    // connect base64 transform to the data sink.
+    BIO_push(m_base64, m_sink);
+  }
+
+  ~Impl()
+  {
+    BIO_free_all(m_sink);
+  }
+
+public:
+  BIO* m_base64;
+  BIO* m_sink; // BIO_f_base64 alone does not work without a sink
+};
+
+Base64Encode::Base64Encode(bool needBreak)
+  : m_impl(new Impl)
+{
+  if (!needBreak)
+    BIO_set_flags(m_impl->m_base64, BIO_FLAGS_BASE64_NO_NL);
+}
+
+void
+Base64Encode::preTransform()
+{
+  fillOutputBuffer();
+}
+
+size_t
+Base64Encode::convert(const uint8_t* data, size_t dataLen)
+{
+  if (dataLen == 0)
+    return 0;
+
+  int wLen = BIO_write(m_impl->m_base64, data, dataLen);
+
+  if (wLen <= 0) { // fail to write data
+    if (!BIO_should_retry(m_impl->m_base64)) {
+      // 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
+    fillOutputBuffer();
+    return wLen;
+  }
+}
+
+void
+Base64Encode::finalize()
+{
+  if (BIO_flush(m_impl->m_base64) != 1)
+    BOOST_THROW_EXCEPTION(Error(getIndex(), "Failed to flush"));
+
+  while (!isConverterEmpty()) {
+    fillOutputBuffer();
+    while (!isOutputBufferEmpty()) {
+      flushOutputBuffer();
+    }
+  }
+}
+
+void
+Base64Encode::fillOutputBuffer()
+{
+  int nRead = BIO_pending(m_impl->m_sink);
+  if (nRead <= 0)
+    return;
+
+  // there is something to read from BIO
+  auto buffer = make_unique<OBuffer>(nRead);
+  int rLen = BIO_read(m_impl->m_sink, &(*buffer)[0], nRead);
+  if (rLen < 0)
+    return;
+
+  if (rLen < nRead)
+    buffer->erase(buffer->begin() + rLen, buffer->end());
+  setOutputBuffer(std::move(buffer));
+}
+
+bool
+Base64Encode::isConverterEmpty()
+{
+  return (BIO_pending(m_impl->m_sink) <= 0);
+}
+
+unique_ptr<Transform>
+base64Encode(bool needBreak)
+{
+  return make_unique<Base64Encode>(needBreak);
+}
+
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/src/security/transform/base64-encode.hpp b/src/security/transform/base64-encode.hpp
new file mode 100644
index 0000000..ac9c664
--- /dev/null
+++ b/src/security/transform/base64-encode.hpp
@@ -0,0 +1,91 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_CXX_SECURITY_TRANSFORM_BASE64_ENCODE_HPP
+#define NDN_CXX_SECURITY_TRANSFORM_BASE64_ENCODE_HPP
+
+#include "transform-base.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+
+/**
+ * @brief The module to perform Base64 encoding transformation.
+ */
+class Base64Encode : public Transform
+{
+public:
+  /**
+   * @brief Create a base64 encoding module
+   *
+   * @p needBreak if true, insert newline after every 64 bytes, otherwise no newline is inserted
+   */
+  explicit
+  Base64Encode(bool needBreak = true);
+
+private:
+  /**
+   * @brief Read partial transformation result (if exists) from BIO
+   */
+  virtual void
+  preTransform() final;
+
+  /**
+   * @brief Encode @data into base64 format.
+   * @return The number of input bytes that have been accepted by the converter.
+   */
+  virtual size_t
+  convert(const uint8_t* data, size_t dataLen) final;
+
+  /**
+   * @brief Finalize base64 encoding
+   *
+   * This method with read all encoding results from the converter and write them into next module.
+   */
+  virtual void
+  finalize() final;
+
+  /**
+   * @brief Fill output buffer with the transformation result from BIO.
+   */
+  void
+  fillOutputBuffer();
+
+  /**
+   * @return true if converter does not have partial result.
+   */
+  bool
+  isConverterEmpty();
+
+private:
+  class Impl;
+  unique_ptr<Impl> m_impl;
+};
+
+unique_ptr<Transform>
+base64Encode(bool needBreak = true);
+
+} // namespace transform
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_CXX_SECURITY_TRANSFORM_BASE64_ENCODE_HPP
diff --git a/src/security/transform/hex-decode.cpp b/src/security/transform/hex-decode.cpp
new file mode 100644
index 0000000..aa82461
--- /dev/null
+++ b/src/security/transform/hex-decode.cpp
@@ -0,0 +1,118 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "hex-decode.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+
+static const int8_t C2H[256] = { // hex decoding pad.
+// 0   1   2   3   4   5   6   7   8   9   10  11  12  13  14  15
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0-15
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16-31
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32-47
+   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1, // 48-63
+  -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 64-79
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80-95
+  -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 96-111
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 112-127
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128-143
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144-159
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160-175
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176-191
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192-207
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208-223
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224-239
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 240-255
+};
+
+HexDecode::HexDecode()
+  : m_hasOddByte(false)
+  , m_oddByte(0)
+{
+}
+
+size_t
+HexDecode::convert(const uint8_t* hex, size_t hexLen)
+{
+  if (hexLen == 0)
+    return 0;
+
+  setOutputBuffer(toBytes(hex, hexLen));
+
+  size_t totalDecodedLen = hexLen + (m_hasOddByte ? 1 : 0);
+  if (totalDecodedLen % 2 == 1) {
+    m_oddByte = hex[hexLen - 1];
+    m_hasOddByte = true;
+  }
+  else
+    m_hasOddByte = false;
+
+  return hexLen;
+}
+
+void
+HexDecode::finalize()
+{
+  if (m_hasOddByte)
+    BOOST_THROW_EXCEPTION(Error(getIndex(), "Incomplete input"));
+}
+
+unique_ptr<Transform::OBuffer>
+HexDecode::toBytes(const uint8_t* hex, size_t hexLen)
+{
+  size_t bufferSize = (hexLen + (m_hasOddByte ? 1 : 0)) >> 1;
+  auto buffer = make_unique<OBuffer>(bufferSize);
+  uint8_t* buf = &buffer->front();
+
+  if (m_hasOddByte) {
+    if (C2H[hex[0]] < 0 || C2H[m_oddByte] < 0)
+      BOOST_THROW_EXCEPTION(Error(getIndex(), "Wrong input byte"));
+
+    buf[0] = (C2H[m_oddByte] << 4) + (C2H[hex[0]]);
+    buf += 1;
+    hex += 1;
+    hexLen -= 1;
+  }
+
+  while (hexLen > 1) {
+    if (C2H[hex[0]] < 0 || C2H[hex[1]] < 0)
+      BOOST_THROW_EXCEPTION(Error(getIndex(), "Wrong input byte"));
+
+    buf[0] = (C2H[hex[0]] << 4) + (C2H[hex[1]]);
+    buf += 1;
+    hex += 2;
+    hexLen -= 2;
+  }
+
+  return buffer;
+}
+
+unique_ptr<Transform>
+hexDecode()
+{
+  return make_unique<HexDecode>();
+}
+
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/src/security/transform/hex-decode.hpp b/src/security/transform/hex-decode.hpp
new file mode 100644
index 0000000..77d477d
--- /dev/null
+++ b/src/security/transform/hex-decode.hpp
@@ -0,0 +1,80 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_CXX_SECURITY_TRANSFORM_HEX_DECODE_HPP
+#define NDN_CXX_SECURITY_TRANSFORM_HEX_DECODE_HPP
+
+#include "transform-base.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+
+/**
+ * @brief The module to perform hexadecimal decoding transformation.
+ *
+ * For example, if the input is a string "012345", the output will be
+ * a byte stream: 0x01, 0x23, 0x45.
+ *
+ * If the total length of input is not even (2n + 1), the module will throw Error.
+ */
+class HexDecode : public Transform
+{
+public:
+  /**
+   * @brief Create a hex decoding module
+   */
+  HexDecode();
+
+private:
+  /**
+   * @brief Decode data @p buf, and write the result into output buffer directly.
+   *
+   * @return number of input bytes that are accepted
+   */
+  virtual size_t
+  convert(const uint8_t* buf, size_t size) final;
+
+  /**
+   * @throws Error if pending byte exists.
+   */
+  virtual void
+  finalize() final;
+
+  /**
+   * @return results of decoding concatenation of @p oddByte and @p hex.
+   */
+  unique_ptr<Transform::OBuffer>
+  toBytes(const uint8_t* hex, size_t hexLen);
+
+private:
+  bool m_hasOddByte;
+  uint8_t m_oddByte;
+};
+
+unique_ptr<Transform>
+hexDecode();
+
+} // namespace transform
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_CXX_SECURITY_TRANSFORM_HEX_DECODE_HPP
diff --git a/src/security/transform/hex-encode.cpp b/src/security/transform/hex-encode.cpp
new file mode 100644
index 0000000..6d54f0e
--- /dev/null
+++ b/src/security/transform/hex-encode.cpp
@@ -0,0 +1,76 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "hex-encode.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+
+static const char H2CL[16] = {
+  '0', '1', '2', '3', '4', '5', '6', '7',
+  '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+};
+
+static const char H2CU[16] = {
+  '0', '1', '2', '3', '4', '5', '6', '7',
+  '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+};
+
+HexEncode::HexEncode(bool useUpperCase)
+  : m_useUpperCase(useUpperCase)
+{
+}
+
+size_t
+HexEncode::convert(const uint8_t* data, size_t dataLen)
+{
+  setOutputBuffer(toHex(data, dataLen));
+  return dataLen;
+}
+
+unique_ptr<Transform::OBuffer>
+HexEncode::toHex(const uint8_t* data, size_t dataLen)
+{
+  const char* encodePad = (m_useUpperCase) ? H2CU : H2CL;
+
+  auto encoded = make_unique<OBuffer>(dataLen * 2);
+  uint8_t* buf = &encoded->front();
+  for (size_t i = 0; i < dataLen; i++) {
+    buf[0] = encodePad[((data[i] >> 4) & 0x0F)];
+    buf++;
+    buf[0] = encodePad[(data[i] & 0x0F)];
+    buf++;
+  }
+  return encoded;
+}
+
+
+
+unique_ptr<Transform>
+hexEncode(bool useUpperCase)
+{
+  return make_unique<HexEncode>(useUpperCase);
+}
+
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/src/security/transform/hex-encode.hpp b/src/security/transform/hex-encode.hpp
new file mode 100644
index 0000000..95fc6e8
--- /dev/null
+++ b/src/security/transform/hex-encode.hpp
@@ -0,0 +1,74 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_CXX_SECURITY_TRANSFORM_HEX_ENCODE_HPP
+#define NDN_CXX_SECURITY_TRANSFORM_HEX_ENCODE_HPP
+
+#include "transform-base.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+
+/**
+ * @brief The module to perform hexadecimal encoding transformation.
+ *
+ * For example, if the input is a byte stream 0x01, 0x23, 0x45,
+ * the output will be a string "012345".
+ */
+class HexEncode : public Transform
+{
+public:
+  /**
+   * @brief Create a hex encoding module
+   *
+   * @param useUpperCase if true, use upper case letters, otherwise lower case
+   */
+  explicit
+  HexEncode(bool useUpperCase = false);
+
+private:
+  /**
+   * @brief Encode @p data, and write the result into next module directly.
+   *
+   * @return The number of input bytes that have been accepted by the converter.
+   */
+  virtual size_t
+  convert(const uint8_t* data, size_t dataLen) final;
+
+  /**
+   * @return results of encoding @p data
+   */
+  unique_ptr<Transform::OBuffer>
+  toHex(const uint8_t* data, size_t dataLen);
+
+private:
+  bool m_useUpperCase;
+};
+
+unique_ptr<Transform>
+hexEncode(bool useUpperCase = false);
+
+} // namespace transform
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_CXX_SECURITY_TRANSFORM_HEX_ENCODE_HPP
diff --git a/src/security/transform/transform-base.cpp b/src/security/transform/transform-base.cpp
index 85df6b9..936e0ae 100644
--- a/src/security/transform/transform-base.cpp
+++ b/src/security/transform/transform-base.cpp
@@ -74,6 +74,76 @@
   }
 }
 
+Transform::Transform()
+  : m_oBuffer(nullptr)
+  , m_outputOffset(0)
+{
+}
+
+void
+Transform::flushOutputBuffer()
+{
+  if (isOutputBufferEmpty())
+    return;
+
+  size_t nWritten = m_next->write(&(*m_oBuffer)[m_outputOffset],
+                                  m_oBuffer->size() - m_outputOffset);
+  m_outputOffset += nWritten;
+}
+
+void
+Transform::setOutputBuffer(unique_ptr<OBuffer> buffer)
+{
+  BOOST_ASSERT(isOutputBufferEmpty());
+  m_oBuffer = std::move(buffer);
+  m_outputOffset = 0;
+}
+
+bool
+Transform::isOutputBufferEmpty() const
+{
+  return (m_oBuffer == nullptr || m_oBuffer->size() == m_outputOffset);
+}
+
+size_t
+Transform::doWrite(const uint8_t* data, size_t dataLen)
+{
+  flushOutputBuffer();
+  if (!isOutputBufferEmpty())
+    return 0;
+
+  preTransform();
+  flushOutputBuffer();
+  if (!isOutputBufferEmpty())
+    return 0;
+
+  size_t nConverted = convert(data, dataLen);
+
+  flushOutputBuffer();
+
+  return nConverted;
+}
+
+void
+Transform::doEnd()
+{
+  finalize();
+  m_next->end();
+}
+
+void
+Transform::preTransform()
+{
+}
+
+void
+Transform::finalize()
+{
+  while (!isOutputBufferEmpty()) {
+    flushOutputBuffer();
+  }
+}
+
 Source::Source()
   : m_nModules(1) // source per se is counted as one module
 {
diff --git a/src/security/transform/transform-base.hpp b/src/security/transform/transform-base.hpp
index 7f64145..5e3a72b 100644
--- a/src/security/transform/transform-base.hpp
+++ b/src/security/transform/transform-base.hpp
@@ -23,6 +23,7 @@
 #define NDN_CXX_SECURITY_TRANSFORM_BASE_HPP
 
 #include "../../common.hpp"
+#include <vector>
 
 namespace ndn {
 namespace security {
@@ -178,6 +179,80 @@
                   public Downstream,
                   noncopyable
 {
+protected:
+  typedef std::vector<uint8_t> OBuffer;
+
+  Transform();
+
+  /**
+   * @brief Read the content from output buffer and write it into next module.
+   */
+  void
+  flushOutputBuffer();
+
+  /**
+   * @brief Set output buffer to @p buffer
+   */
+  void
+  setOutputBuffer(unique_ptr<OBuffer> buffer);
+
+  /**
+   * @brief Check if output buffer is empty
+   */
+  bool
+  isOutputBufferEmpty() const;
+
+private:
+
+  /**
+   * @brief Abstraction of data processing in an intermediate module
+   */
+  virtual size_t
+  doWrite(const uint8_t* data, size_t dataLen) final;
+
+  /**
+   * @brief Finalize transformation in this module
+   *
+   * This method will not return until all transformation result is written into next module
+   */
+  virtual void
+  doEnd() final;
+
+  /**
+   * @brief Process before transformation.
+   *
+   * @pre output buffer is empty.
+   *
+   * This method is invoked before every convert(...) invocation.
+   *
+   * This implementation does nothing.  A subclass can override this method to perform
+   * specific pre-transformation procedure, e.g., read partial transformation results into
+   * output buffer.
+   */
+  virtual void
+  preTransform();
+
+  /**
+   * @brief Convert input @p data.
+   *
+   * @return The number of input bytes that have been accepted by the converter.
+   */
+  virtual size_t
+  convert(const uint8_t* data, size_t dataLen) = 0;
+
+  /**
+   * @brief Finalize the transformation.
+   *
+   * This implementation only flushes content in output buffer into next module.
+   * A subclass can override this method to perform specific finalization procedure, i.e.,
+   * finalize the transformation and flush the result into next module.
+   */
+  virtual void
+  finalize();
+
+private:
+  unique_ptr<OBuffer> m_oBuffer;
+  size_t m_outputOffset;
 };
 
 /**