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;
};
/**