name: Add support for ImplicitSha256DigestComponent

This support includes the following new API functions:

- name::Component::isImplicitSha256Digest()
- name::Component::fromImplicitSha256Digest(...)
- Name::appendImplicitSha256Digest(...)
- Updated toUri()/fromUri(...) to support "sha256digest=..." URI
  representation of the ImplicitSha256Digest component

Change-Id: I756c4b94196cf031da98b5689bd60630533dfeb3
Refs: #1640, #2088
diff --git a/src/encoding/tlv.hpp b/src/encoding/tlv.hpp
index bb0f1c5..284ca26 100644
--- a/src/encoding/tlv.hpp
+++ b/src/encoding/tlv.hpp
@@ -61,6 +61,7 @@
   Interest      = 5,
   Data          = 6,
   Name          = 7,
+  ImplicitSha256DigestComponent = 1,
   NameComponent = 8,
   Selectors     = 9,
   Nonce         = 10,
diff --git a/src/name-component.cpp b/src/name-component.cpp
index 893369f..8aad977 100644
--- a/src/name-component.cpp
+++ b/src/name-component.cpp
@@ -28,8 +28,12 @@
 #include "encoding/block-helpers.hpp"
 #include "encoding/encoding-buffer.hpp"
 #include "util/string-helper.hpp"
+#include "security/cryptopp.hpp"
+#include "util/crypto.hpp"
 #include "util/concepts.hpp"
 
+#include <boost/lexical_cast.hpp>
+
 namespace ndn {
 namespace name {
 
@@ -39,6 +43,13 @@
 static_assert(std::is_base_of<tlv::Error, Component::Error>::value,
               "name::Component::Error must inherit from tlv::Error");
 
+static const std::string&
+getSha256DigestUriPrefix()
+{
+  static const std::string prefix { "sha256digest=" };
+  return prefix;
+}
+
 Component::Component()
   : Block(tlv::NameComponent)
 {
@@ -47,8 +58,9 @@
 Component::Component(const Block& wire)
   : Block(wire)
 {
-  if (type() != tlv::NameComponent)
-    throw Error("Cannot construct name::Component from not a NameComponent TLV wire block");
+  if (type() != tlv::NameComponent && type() != tlv::ImplicitSha256DigestComponent)
+    throw Error("Cannot construct name::Component from not a NameComponent "
+                "or ImplicitSha256DigestComponent TLV wire block");
 }
 
 Component::Component(const ConstBufferPtr& buffer)
@@ -82,62 +94,93 @@
 {
   std::string trimmedString(escapedString + beginOffset, escapedString + endOffset);
   trim(trimmedString);
-  std::string value = unescape(trimmedString);
 
-  if (value.find_first_not_of(".") == std::string::npos) {
-    // Special case for component of only periods.
-    if (value.size() <= 2)
-      // Zero, one or two periods is illegal.  Ignore this component.
-      throw Error("Illegal URI (name component cannot be . or ..)");
-    else
-      // Remove 3 periods.
-      return Component(reinterpret_cast<const uint8_t*>(&value[3]), value.size() - 3);
+  if (trimmedString.compare(0, getSha256DigestUriPrefix().size(),
+                            getSha256DigestUriPrefix()) == 0) {
+    if (trimmedString.size() != getSha256DigestUriPrefix().size() + crypto::SHA256_DIGEST_SIZE * 2)
+      throw Error("Cannot convert to ImplicitSha256DigestComponent"
+                  "(expected sha256 in hex encoding)");
+
+    try {
+      std::string value;
+      CryptoPP::StringSource(reinterpret_cast<const uint8_t*>(trimmedString.c_str()) +
+                               getSha256DigestUriPrefix().size(),
+                             trimmedString.size () - getSha256DigestUriPrefix().size(), true,
+                             new CryptoPP::HexDecoder(new CryptoPP::StringSink(value)));
+
+      return fromImplicitSha256Digest(reinterpret_cast<const uint8_t*>(value.c_str()),
+                                      value.size());
+    }
+    catch (CryptoPP::Exception& e) {
+      throw Error("Cannot convert to a ImplicitSha256DigestComponent (invalid hex encoding)");
+    }
   }
-  else
-    return Component(reinterpret_cast<const uint8_t*>(&value[0]), value.size());
+  else {
+    std::string value = unescape(trimmedString);
+
+    if (value.find_first_not_of(".") == std::string::npos) {
+      // Special case for component of only periods.
+      if (value.size() <= 2)
+        // Zero, one or two periods is illegal.  Ignore this component.
+        throw Error("Illegal URI (name component cannot be . or ..)");
+      else
+        // Remove 3 periods.
+        return Component(reinterpret_cast<const uint8_t*>(&value[3]), value.size() - 3);
+    }
+    else
+      return Component(reinterpret_cast<const uint8_t*>(&value[0]), value.size());
+  }
 }
 
 
 void
 Component::toUri(std::ostream& result) const
 {
-  const uint8_t* value = this->value();
-  size_t valueSize = value_size();
+  if (type() == tlv::ImplicitSha256DigestComponent) {
+    result << getSha256DigestUriPrefix();
 
-  bool gotNonDot = false;
-  for (size_t i = 0; i < valueSize; ++i) {
-    if (value[i] != 0x2e) {
-      gotNonDot = true;
-      break;
-    }
-  }
-  if (!gotNonDot) {
-    // Special case for component of zero or more periods.  Add 3 periods.
-    result << "...";
-    for (size_t i = 0; i < valueSize; ++i)
-      result << '.';
+    CryptoPP::StringSource(value(), value_size(), true,
+                           new CryptoPP::HexEncoder(new CryptoPP::FileSink(result), false));
   }
   else {
-    // In case we need to escape, set to upper case hex and save the previous flags.
-    std::ios::fmtflags saveFlags = result.flags(std::ios::hex | std::ios::uppercase);
+    const uint8_t* value = this->value();
+    size_t valueSize = value_size();
 
+    bool gotNonDot = false;
     for (size_t i = 0; i < valueSize; ++i) {
-      uint8_t x = value[i];
-      // Check for 0-9, A-Z, a-z, (+), (-), (.), (_)
-      if ((x >= 0x30 && x <= 0x39) || (x >= 0x41 && x <= 0x5a) ||
-          (x >= 0x61 && x <= 0x7a) || x == 0x2b || x == 0x2d ||
-          x == 0x2e || x == 0x5f)
-        result << x;
-      else {
-        result << '%';
-        if (x < 16)
-          result << '0';
-        result << static_cast<uint32_t>(x);
+      if (value[i] != 0x2e) {
+        gotNonDot = true;
+        break;
       }
     }
+    if (!gotNonDot) {
+      // Special case for component of zero or more periods.  Add 3 periods.
+      result << "...";
+      for (size_t i = 0; i < valueSize; ++i)
+        result << '.';
+    }
+    else {
+      // In case we need to escape, set to upper case hex and save the previous flags.
+      std::ios::fmtflags saveFlags = result.flags(std::ios::hex | std::ios::uppercase);
 
-    // Restore.
-    result.flags(saveFlags);
+      for (size_t i = 0; i < valueSize; ++i) {
+        uint8_t x = value[i];
+        // Check for 0-9, A-Z, a-z, (+), (-), (.), (_)
+        if ((x >= 0x30 && x <= 0x39) || (x >= 0x41 && x <= 0x5a) ||
+            (x >= 0x61 && x <= 0x7a) || x == 0x2b || x == 0x2d ||
+            x == 0x2e || x == 0x5f)
+          result << x;
+        else {
+          result << '%';
+          if (x < 16)
+            result << '0';
+          result << static_cast<uint32_t>(x);
+        }
+      }
+
+      // Restore.
+      result.flags(saveFlags);
+    }
   }
 }
 
@@ -149,6 +192,7 @@
   return os.str();
 }
 
+////////////////////////////////////////////////////////////////////////////////
 
 bool
 Component::isNumber() const
@@ -195,6 +239,7 @@
   return isNumberWithMarker(SEQUENCE_NUMBER_MARKER);
 }
 
+////////////////////////////////////////////////////////////////////////////////
 
 uint64_t
 Component::toNumber() const
@@ -247,11 +292,11 @@
   return toNumberWithMarker(SEQUENCE_NUMBER_MARKER);
 }
 
+////////////////////////////////////////////////////////////////////////////////
 
 Component
 Component::fromNumber(uint64_t number)
 {
-  /// \todo Change to tlv::NumberComponent
   return nonNegativeIntegerBlock(tlv::NameComponent, number);
 }
 
@@ -307,11 +352,52 @@
   return fromNumberWithMarker(SEQUENCE_NUMBER_MARKER, seqNo);
 }
 
+////////////////////////////////////////////////////////////////////////////////
+
+bool
+Component::isGeneric() const
+{
+  return (type() == tlv::NameComponent);
+}
+
+bool
+Component::isImplicitSha256Digest() const
+{
+  return (type() == tlv::ImplicitSha256DigestComponent &&
+          value_size() == crypto::SHA256_DIGEST_SIZE);
+}
+
+Component
+Component::fromImplicitSha256Digest(const ConstBufferPtr& digest)
+{
+  if (digest->size() != crypto::SHA256_DIGEST_SIZE)
+    throw Error("Cannot create ImplicitSha256DigestComponent (input digest must be " +
+                boost::lexical_cast<std::string>(crypto::SHA256_DIGEST_SIZE) + " octets)");
+
+  return Block(tlv::ImplicitSha256DigestComponent, digest);
+}
+
+Component
+Component::fromImplicitSha256Digest(const uint8_t* digest, size_t digestSize)
+{
+  if (digestSize != crypto::SHA256_DIGEST_SIZE)
+    throw Error("Cannot create ImplicitSha256DigestComponent (input digest must be " +
+                boost::lexical_cast<std::string>(crypto::SHA256_DIGEST_SIZE) + " octets)");
+
+  return dataBlock(tlv::ImplicitSha256DigestComponent, digest, digestSize);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
 int
 Component::compare(const Component& other) const
 {
   // Imitate ndn_Exclude_compareComponents.
-  if (value_size() < other.value_size())
+  if (type() < other.type())
+    return -1;
+  else if (type() > other.type())
+    return 1;
+  else if (value_size() < other.value_size())
     return -1;
   if (value_size() > other.value_size())
     return 1;
@@ -346,7 +432,7 @@
   }
 
   totalLength += encoder.prependVarNumber(totalLength);
-  totalLength += encoder.prependVarNumber(tlv::NameComponent);
+  totalLength += encoder.prependVarNumber(type());
 
   return encoder.block();
 }
@@ -360,7 +446,7 @@
   if (value_size() > 0)
     totalLength += block.prependByteArray(value(), value_size());
   totalLength += block.prependVarNumber(value_size());
-  totalLength += block.prependVarNumber(tlv::NameComponent);
+  totalLength += block.prependVarNumber(type());
   return totalLength;
 }
 
@@ -389,8 +475,9 @@
 void
 Component::wireDecode(const Block& wire)
 {
-  if (wire.type() != tlv::NameComponent)
-    throw Error("name::Component::wireDecode called on not a NameComponent TLV wire block");
+  if (wire.type() != tlv::NameComponent || wire.type() != tlv::ImplicitSha256DigestComponent)
+    throw Error("name::Component::wireDecode called on not a NameComponent "
+                "or ImplicitSha256DigestComponent TLV wire block");
 
   *this = wire;
 }
diff --git a/src/name-component.hpp b/src/name-component.hpp
index b8023b1..e49275a 100644
--- a/src/name-component.hpp
+++ b/src/name-component.hpp
@@ -463,6 +463,34 @@
   static Component
   fromSequenceNumber(uint64_t seqNo);
 
+  ////////////////////////////////////////////////////////////////////////////////
+
+  /**
+   * @brief Check if the component is GenericComponent
+   */
+  bool
+  isGeneric() const;
+
+  /**
+   * @brief Check if the component is ImplicitSha256DigestComponent
+   */
+  bool
+  isImplicitSha256Digest() const;
+
+  /**
+   * @brief Create ImplicitSha256DigestComponent component
+   */
+  static Component
+  fromImplicitSha256Digest(const ConstBufferPtr& digest);
+
+  /**
+   * @brief Create ImplicitSha256DigestComponent component
+   */
+  static Component
+  fromImplicitSha256Digest(const uint8_t* digest, size_t digestSize);
+
+  ////////////////////////////////////////////////////////////////////////////////
+
   bool
   empty() const
   {
diff --git a/src/name.cpp b/src/name.cpp
index 9090176..f9d91db 100644
--- a/src/name.cpp
+++ b/src/name.cpp
@@ -225,6 +225,19 @@
   return *this;
 }
 
+Name&
+Name::appendImplicitSha256Digest(const ConstBufferPtr& digest)
+{
+  m_nameBlock.push_back(Component::fromImplicitSha256Digest(digest));
+  return *this;
+}
+
+Name&
+Name::appendImplicitSha256Digest(const uint8_t* digest, size_t digestSize)
+{
+  m_nameBlock.push_back(Component::fromImplicitSha256Digest(digest, digestSize));
+  return *this;
+}
 
 Name
 Name::getSubName(size_t iStartComponent, size_t nComponents) const
diff --git a/src/name.hpp b/src/name.hpp
index 1719e92..9743a96 100644
--- a/src/name.hpp
+++ b/src/name.hpp
@@ -334,6 +334,18 @@
   appendSequenceNumber(uint64_t seqNo);
 
   /**
+   * @brief Append ImplicitSha256Digest
+   */
+  Name&
+  appendImplicitSha256Digest(const ConstBufferPtr& digest);
+
+  /**
+   * @brief Append ImplicitSha256Digest
+   */
+  Name&
+  appendImplicitSha256Digest(const uint8_t* digest, size_t digestSize);
+
+  /**
    * @brief Get the successor of a name
    *
    * The successor of a name is defined as follows:
diff --git a/tests/unit-tests/test-name.cpp b/tests/unit-tests/test-name.cpp
index f10cf0a..01bd884 100644
--- a/tests/unit-tests/test-name.cpp
+++ b/tests/unit-tests/test-name.cpp
@@ -223,6 +223,8 @@
     const Name& expected = it->template get<3>();
     BOOST_TEST_MESSAGE("Check " << expected[0].toUri());
 
+    BOOST_CHECK_EQUAL(expected[0].isGeneric(), true);
+
     name::Component actualComponent = it->template get<0>()(it->template get<4>());
     BOOST_CHECK_EQUAL(actualComponent, expected[0]);
 
@@ -290,6 +292,58 @@
   BOOST_CHECK_EQUAL(map[name3], 3);
 }
 
+BOOST_AUTO_TEST_CASE(ImplictSha256Digest)
+{
+  Name n;
+
+  static const uint8_t DIGEST[] = { 0x28, 0xba, 0xd4, 0xb5, 0x27, 0x5b, 0xd3, 0x92,
+                                    0xdb, 0xb6, 0x70, 0xc7, 0x5c, 0xf0, 0xb6, 0x6f,
+                                    0x13, 0xf7, 0x94, 0x2b, 0x21, 0xe8, 0x0f, 0x55,
+                                    0xc0, 0xe8, 0x6b, 0x37, 0x47, 0x53, 0xa5, 0x48 };
+
+  BOOST_REQUIRE_NO_THROW(n.appendImplicitSha256Digest(DIGEST, 32));
+  BOOST_REQUIRE_NO_THROW(n.appendImplicitSha256Digest(make_shared<Buffer>(DIGEST, 32)));
+  BOOST_CHECK_EQUAL(n.get(0), n.get(1));
+
+  BOOST_REQUIRE_THROW(n.appendImplicitSha256Digest(DIGEST, 34), name::Component::Error);
+  BOOST_REQUIRE_THROW(n.appendImplicitSha256Digest(DIGEST, 30), name::Component::Error);
+
+  n.append(DIGEST, 32);
+  BOOST_CHECK_LT(n.get(0), n.get(2));
+  BOOST_CHECK_EQUAL_COLLECTIONS(n.get(0).value_begin(), n.get(0).value_end(),
+                                n.get(2).value_begin(), n.get(2).value_end());
+
+  n.append(DIGEST + 1, 32);
+  BOOST_CHECK_LT(n.get(0), n.get(3));
+
+  n.append(DIGEST + 2, 32);
+  BOOST_CHECK_LT(n.get(0), n.get(4));
+
+  BOOST_CHECK_EQUAL(n.get(0).toUri(), "sha256digest="
+                    "28bad4b5275bd392dbb670c75cf0b66f13f7942b21e80f55c0e86b374753a548");
+
+  BOOST_CHECK_EQUAL(n.get(0).isImplicitSha256Digest(), true);
+  BOOST_CHECK_EQUAL(n.get(2).isImplicitSha256Digest(), false);
+
+  BOOST_CHECK_THROW(Name("/hello/sha256digest=hmm"), name::Component::Error);
+
+  Name n2;
+  // check canonical URI encoding (lower case)
+  BOOST_CHECK_NO_THROW(n2 = Name("/hello/sha256digest="
+                              "28bad4b5275bd392dbb670c75cf0b66f13f7942b21e80f55c0e86b374753a548"));
+  BOOST_CHECK_EQUAL(n.get(0), n2.get(1));
+
+  // will accept hex value in upper case too
+  BOOST_CHECK_NO_THROW(n2 = Name("/hello/sha256digest="
+                              "28BAD4B5275BD392DBB670C75CF0B66F13F7942B21E80F55C0E86B374753A548"));
+  BOOST_CHECK_EQUAL(n.get(0), n2.get(1));
+
+  // this is not valid sha256digest component, will be treated as generic component
+  BOOST_CHECK_NO_THROW(n2 = Name("/hello/SHA256DIGEST="
+                              "28BAD4B5275BD392DBB670C75CF0B66F13F7942B21E80F55C0E86B374753A548"));
+  BOOST_CHECK_NE(n.get(0), n2.get(1));
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace ndn
diff --git a/tools/tlvdump.cpp b/tools/tlvdump.cpp
index 0273861..ab9fcd4 100644
--- a/tools/tlvdump.cpp
+++ b/tools/tlvdump.cpp
@@ -27,11 +27,11 @@
 #include <iomanip>
 #include <fstream>
 
-const uint32_t TLV_DICT_SIZE = 30;
+namespace ndn {
 
-const std::string TLV_DICT[TLV_DICT_SIZE] = {
+const std::vector<std::string> TLV_DICT = {
   "RESERVED", //      = 0
-  "RESERVED", //      = 1
+  "ImplicitSha256DigestComponent", //      = 1
   "RESERVED", //      = 2
   "RESERVED", //      = 3
   "RESERVED", //      = 4
@@ -59,7 +59,7 @@
   "FinalBlockId" // = 26
   "SignatureType", // = 27,
   "KeyLocator", //    = 28,
-  "KeyLocatorDigest", // = 29
+  "KeyDigest" // = 29
 };
 
 void
@@ -67,16 +67,16 @@
 {
   std::cout << type << " (";
 
-  if (type < TLV_DICT_SIZE) {
+  if (type < TLV_DICT.size()) {
     std::cout << TLV_DICT[type];
   }
-  else if (TLV_DICT_SIZE <= type && type < 128) {
+  else if (TLV_DICT.size() <= type && type < tlv::AppPrivateBlock1) {
     std::cout << "RESERVED_1";
   }
-  else if (128 <= type && type < 253) {
+  else if (tlv::AppPrivateBlock1 <= type && type < 253) {
     std::cout << "APP_TAG_1";
   }
-  else if (253 <= type && type < 32767) {
+  else if (253 <= type && type < tlv::AppPrivateBlock2) {
     std::cout << "RESERVED_3";
   }
   else {
@@ -87,17 +87,17 @@
 
 
 void
-BlockPrinter(const ndn::Block& block, const std::string& indent = "")
+BlockPrinter(const Block& block, const std::string& indent = "")
 {
   std::cout << indent;
   printTypeInfo(block.type());
   std::cout << " (size: " << block.value_size() << ")";
 
   try {
-    // if (block.type() != ndn::tlv::Content && block.type() != ndn::tlv::SignatureValue)
+    // if (block.type() != tlv::Content && block.type() != tlv::SignatureValue)
     block.parse();
   }
-  catch (ndn::tlv::Error& e) {
+  catch (tlv::Error& e) {
     // pass (e.g., leaf block reached)
 
     // @todo: Figure how to deterministically figure out that value is not recursive TLV block
@@ -106,12 +106,12 @@
   if (block.elements().empty())
     {
       std::cout << " [[";
-      ndn::name::Component(block.value(), block.value_size()).toUri(std::cout);
+      name::Component(block.value(), block.value_size()).toUri(std::cout);
       std::cout<< "]]";
     }
   std::cout << std::endl;
 
-  for (ndn::Block::element_const_iterator i = block.elements_begin();
+  for (Block::element_const_iterator i = block.elements_begin();
        i != block.elements_end();
        ++i)
     {
@@ -120,10 +120,10 @@
 }
 
 void
-HexPrinter(const ndn::Block& block, const std::string& indent = "")
+HexPrinter(const Block& block, const std::string& indent = "")
 {
   std::cout << indent;
-  for (ndn::Buffer::const_iterator i = block.begin (); i != block.value_begin(); ++i)
+  for (Buffer::const_iterator i = block.begin (); i != block.value_begin(); ++i)
     {
       std::cout << "0x";
       std::cout << std::noshowbase << std::hex << std::setw(2) <<
@@ -135,7 +135,7 @@
   if (block.elements_size() == 0 && block.value_size() > 0)
     {
       std::cout << indent << "    ";
-      for (ndn::Buffer::const_iterator i = block.value_begin (); i != block.value_end(); ++i)
+      for (Buffer::const_iterator i = block.value_begin (); i != block.value_end(); ++i)
       {
         std::cout << "0x";
         std::cout << std::noshowbase << std::hex << std::setw(2) <<
@@ -146,7 +146,7 @@
     }
   else
     {
-      for (ndn::Block::element_const_iterator i = block.elements_begin();
+      for (Block::element_const_iterator i = block.elements_begin();
            i != block.elements_end();
            ++i)
         {
@@ -160,7 +160,7 @@
 {
   while (is.peek() != std::char_traits<char>::eof()) {
     try {
-      ndn::Block block = ndn::Block::fromStream(is);
+      Block block = Block::fromStream(is);
       BlockPrinter(block, "");
       // HexPrinter(block, "");
     }
@@ -171,17 +171,19 @@
 
 }
 
+} // namespace ndn
+
 int main(int argc, const char *argv[])
 {
   if (argc == 1 ||
       (argc == 2 && std::string(argv[1]) == "-"))
     {
-      parseBlocksFromStream(std::cin);
+      ndn::parseBlocksFromStream(std::cin);
     }
   else
     {
       std::ifstream file(argv[1]);
-      parseBlocksFromStream(file);
+      ndn::parseBlocksFromStream(file);
     }
 
   return 0;