signature-info: add signed Interest elements

refs #4804

Change-Id: Ice910b7740212cf116ddac24e12634877f75c71e
diff --git a/ndn-cxx/data.cpp b/ndn-cxx/data.cpp
index fe34062..306a09e 100644
--- a/ndn-cxx/data.cpp
+++ b/ndn-cxx/data.cpp
@@ -65,7 +65,7 @@
   }
 
   // SignatureInfo
-  totalLength += m_signatureInfo.wireEncode(encoder);
+  totalLength += m_signatureInfo.wireEncode(encoder, SignatureInfo::Type::Data);
 
   // Content
   totalLength += encoder.prependBlock(getContent());
diff --git a/ndn-cxx/encoding/tlv.hpp b/ndn-cxx/encoding/tlv.hpp
index f27c1cb..b435d14 100644
--- a/ndn-cxx/encoding/tlv.hpp
+++ b/ndn-cxx/encoding/tlv.hpp
@@ -74,6 +74,8 @@
   InterestLifetime                = 12,
   HopLimit                        = 34,
   ApplicationParameters           = 36,
+  InterestSignatureInfo           = 44,
+  InterestSignatureValue          = 46,
   MetaInfo                        = 20,
   Content                         = 21,
   SignatureInfo                   = 22,
@@ -84,6 +86,9 @@
   SignatureType                   = 27,
   KeyLocator                      = 28,
   KeyDigest                       = 29,
+  SignatureNonce                  = 38,
+  SignatureTime                   = 40,
+  SignatureSeqNum                 = 42,
   LinkDelegation                  = 31,
   LinkPreference                  = 30,
 
@@ -135,7 +140,7 @@
 std::ostream&
 operator<<(std::ostream& os, SignatureTypeValue st);
 
-/** @brief TLV-TYPE numbers for SignatureInfo features
+/** @brief TLV-TYPE numbers for SignatureInfo extensions
  *  @sa docs/specs/certificate-format.rst
  */
 enum {
diff --git a/ndn-cxx/security/v2/certificate.cpp b/ndn-cxx/security/v2/certificate.cpp
index f5ef157..ae3ae30 100644
--- a/ndn-cxx/security/v2/certificate.cpp
+++ b/ndn-cxx/security/v2/certificate.cpp
@@ -121,10 +121,14 @@
   return getSignatureInfo().getValidityPeriod().isValid(ts);
 }
 
-const Block&
+Block
 Certificate::getExtension(uint32_t type) const
 {
-  return getSignatureInfo().getTypeSpecificTlv(type);
+  auto block = getSignatureInfo().getCustomTlv(type);
+  if (!block) {
+    NDN_THROW(Error("TLV-TYPE " + to_string(type) + " sub-element does not exist in SignatureInfo"));
+  }
+  return *block;
 }
 
 bool
@@ -146,16 +150,13 @@
     os << "  NotAfter: "  << time::toIsoString(cert.getValidityPeriod().getPeriod().second)  << "\n";
   }
 
-  try {
-    const Block& info = cert.getSignatureInfo().getTypeSpecificTlv(tlv::AdditionalDescription);
+  auto additionalDescription = cert.getSignatureInfo().getCustomTlv(tlv::AdditionalDescription);
+  if (additionalDescription) {
     os << "Additional Description:\n";
-    for (const auto& item : v2::AdditionalDescription(info)) {
+    for (const auto& item : v2::AdditionalDescription(*additionalDescription)) {
       os << "  " << item.first << ": " << item.second << "\n";
     }
   }
-  catch (const SignatureInfo::Error&) {
-    // ignore
-  }
 
   os << "Public key bits:\n";
   {
diff --git a/ndn-cxx/security/v2/certificate.hpp b/ndn-cxx/security/v2/certificate.hpp
index 12621ea..8d939b2 100644
--- a/ndn-cxx/security/v2/certificate.hpp
+++ b/ndn-cxx/security/v2/certificate.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).
  *
@@ -149,9 +149,9 @@
 
   /**
    * @brief Get extension with TLV @p type
-   * @throw ndn::SignatureInfo::Error if the specified block type does not exist
+   * @throw Error The specified block type does not exist
    */
-  const Block&
+  Block
   getExtension(uint32_t type) const;
 
   // @TODO Implement extension enumeration (Issue #3907)
diff --git a/ndn-cxx/signature-info.cpp b/ndn-cxx/signature-info.cpp
index 9c96cda..59c93bc 100644
--- a/ndn-cxx/signature-info.cpp
+++ b/ndn-cxx/signature-info.cpp
@@ -22,6 +22,9 @@
 #include "ndn-cxx/signature-info.hpp"
 #include "ndn-cxx/encoding/block-helpers.hpp"
 #include "ndn-cxx/util/concepts.hpp"
+#include "ndn-cxx/util/string-helper.hpp"
+
+#include <boost/range/adaptor/reversed.hpp>
 
 namespace ndn {
 
@@ -40,14 +43,14 @@
 {
 }
 
-SignatureInfo::SignatureInfo(const Block& block)
+SignatureInfo::SignatureInfo(const Block& block, SignatureInfo::Type type)
 {
-  wireDecode(block);
+  wireDecode(block, type);
 }
 
 template<encoding::Tag TAG>
 size_t
-SignatureInfo::wireEncode(EncodingImpl<TAG>& encoder) const
+SignatureInfo::wireEncode(EncodingImpl<TAG>& encoder, SignatureInfo::Type type) const
 {
   if (m_type == -1) {
     NDN_THROW(Error("Cannot encode invalid SignatureInfo"));
@@ -56,13 +59,23 @@
   // SignatureInfo = SIGNATURE-INFO-TYPE TLV-LENGTH
   //                   SignatureType
   //                   [KeyLocator]
-  //                   [ValidityPeriod] ; if present, stored as first item of m_otherTlvs
+  //                   [ValidityPeriod]
   //                   *OtherSubelements
 
+  // InterestSignatureInfo = INTEREST-SIGNATURE-INFO-TYPE TLV-LENGTH
+  //                           SignatureType
+  //                           [KeyLocator]
+  //                           [SignatureNonce]
+  //                           [SignatureTime]
+  //                           [SignatureSeqNum]
+  //                           *OtherSubelements
+
   size_t totalLength = 0;
 
-  for (auto i = m_otherTlvs.rbegin(); i != m_otherTlvs.rend(); i++) {
-    totalLength += encoder.prependBlock(*i);
+  // m_otherTlvs contains (if set) SignatureNonce, SignatureTime, SignatureSeqNum, ValidityPeriod,
+  // and AdditionalDescription, as well as any custom elements added by the user
+  for (const auto& block : m_otherTlvs | boost::adaptors::reversed) {
+    totalLength += encoder.prependBlock(block);
   }
 
   if (m_keyLocator) {
@@ -71,32 +84,37 @@
 
   totalLength += prependNonNegativeIntegerBlock(encoder, tlv::SignatureType,
                                                 static_cast<uint64_t>(m_type));
+
   totalLength += encoder.prependVarNumber(totalLength);
-  totalLength += encoder.prependVarNumber(tlv::SignatureInfo);
+  totalLength += encoder.prependVarNumber(to_underlying(type));
 
   return totalLength;
 }
 
-NDN_CXX_DEFINE_WIRE_ENCODE_INSTANTIATIONS(SignatureInfo);
+template size_t
+SignatureInfo::wireEncode<encoding::EncoderTag>(encoding::EncodingBuffer&, SignatureInfo::Type) const;
+
+template size_t
+SignatureInfo::wireEncode<encoding::EstimatorTag>(encoding::EncodingEstimator&, SignatureInfo::Type) const;
 
 const Block&
-SignatureInfo::wireEncode() const
+SignatureInfo::wireEncode(SignatureInfo::Type type) const
 {
   if (m_wire.hasWire())
     return m_wire;
 
   EncodingEstimator estimator;
-  size_t estimatedSize = wireEncode(estimator);
+  size_t estimatedSize = wireEncode(estimator, type);
 
   EncodingBuffer buffer(estimatedSize, 0);
-  wireEncode(buffer);
+  wireEncode(buffer, type);
 
   m_wire = buffer.block();
   return m_wire;
 }
 
 void
-SignatureInfo::wireDecode(const Block& wire)
+SignatureInfo::wireDecode(const Block& wire, SignatureInfo::Type type)
 {
   m_type = -1;
   m_keyLocator = nullopt;
@@ -105,28 +123,56 @@
   m_wire = wire;
   m_wire.parse();
 
-  if (m_wire.type() != tlv::SignatureInfo)
+  if (m_wire.type() != to_underlying(type)) {
     NDN_THROW(Error("SignatureInfo", m_wire.type()));
-
-  auto it = m_wire.elements_begin();
-
-  // the first sub-element must be SignatureType
-  if (it == m_wire.elements_end() || it->type() != tlv::SignatureType)
-    NDN_THROW(Error("Missing SignatureType in SignatureInfo"));
-
-  m_type = readNonNegativeIntegerAs<tlv::SignatureTypeValue>(*it);
-  ++it;
-
-  // the second sub-element could be KeyLocator
-  if (it != m_wire.elements_end() && it->type() == tlv::KeyLocator) {
-    m_keyLocator.emplace(*it);
-    ++it;
   }
 
-  // store SignatureType-specific sub-elements, if any
-  while (it != m_wire.elements_end()) {
-    m_otherTlvs.push_back(*it);
-    ++it;
+  size_t lastCriticalElement = 0;
+  for (const auto& element : m_wire.elements()) {
+    switch (element.type()) {
+      case tlv::SignatureType: {
+        if (lastCriticalElement > 0) {
+          NDN_THROW(Error("SignatureType element is repeated or out-of-order"));
+        }
+        m_type = readNonNegativeIntegerAs<tlv::SignatureTypeValue>(element);
+        lastCriticalElement = 1;
+        break;
+      }
+      case tlv::KeyLocator: {
+        if (lastCriticalElement > 1) {
+          NDN_THROW(Error("KeyLocator element is repeated or out-of-order"));
+        }
+        m_keyLocator.emplace(element);
+        lastCriticalElement = 2;
+        break;
+      }
+      case tlv::SignatureNonce: {
+        // Must handle SignatureNonce specifically because we must check that its length is >0
+        if (element.value_size() < 1) {
+          NDN_THROW(Error("SignatureNonce element cannot be empty"));
+        }
+        m_otherTlvs.push_back(element);
+        break;
+      }
+      case tlv::ValidityPeriod:
+        // ValidityPeriod is treated differently than other "extension" TLVs for historical reasons:
+        // It is intended to be non-critical, but its TLV-TYPE is in the critical range. Therefore,
+        // we must handle it specifically.
+        m_otherTlvs.push_back(element);
+        break;
+      default: {
+        // If the TLV-TYPE is unrecognized and critical, abort decoding
+        if (tlv::isCriticalType(element.type())) {
+          NDN_THROW(Error("Unrecognized element of critical type " + to_string(element.type())));
+        }
+        // Otherwise, store in m_otherTlvs
+        m_otherTlvs.push_back(element);
+      }
+    }
+  }
+
+  if (m_type == -1) {
+    NDN_THROW(Error("Missing SignatureType in SignatureInfo"));
   }
 }
 
@@ -168,29 +214,21 @@
 security::ValidityPeriod
 SignatureInfo::getValidityPeriod() const
 {
-  if (!hasValidityPeriod()) {
+  auto it = findOtherTlv(tlv::ValidityPeriod);
+  if (it == m_otherTlvs.end()) {
     NDN_THROW(Error("ValidityPeriod does not exist in SignatureInfo"));
   }
-  return security::ValidityPeriod(m_otherTlvs.front());
+  return security::ValidityPeriod(*it);
 }
 
 SignatureInfo&
 SignatureInfo::setValidityPeriod(optional<security::ValidityPeriod> validityPeriod)
 {
-  if (validityPeriod) {
-    auto block = validityPeriod->wireEncode();
-    if (!hasValidityPeriod()) {
-      m_otherTlvs.push_front(std::move(block));
-      m_wire.reset();
-    }
-    else if (m_otherTlvs.front() != block) {
-      m_otherTlvs.front() = std::move(block);
-      m_wire.reset();
-    }
+  if (!validityPeriod) {
+    removeCustomTlv(tlv::ValidityPeriod);
   }
-  else if (hasValidityPeriod()) {
-    m_otherTlvs.pop_front();
-    m_wire.reset();
+  else {
+    addCustomTlv(validityPeriod->wireEncode());
   }
   return *this;
 }
@@ -201,22 +239,133 @@
   setValidityPeriod(nullopt);
 }
 
+optional<std::vector<uint8_t>>
+SignatureInfo::getNonce() const
+{
+  auto it = findOtherTlv(tlv::SignatureNonce);
+  if (it == m_otherTlvs.end()) {
+    return nullopt;
+  }
+  return std::vector<uint8_t>(it->value_begin(), it->value_end());
+}
+
+SignatureInfo&
+SignatureInfo::setNonce(optional<std::vector<uint8_t>> nonce)
+{
+  if (!nonce) {
+    removeCustomTlv(tlv::SignatureNonce);
+  }
+  else {
+    addCustomTlv(makeBinaryBlock(tlv::SignatureNonce, nonce->data(), nonce->size()));
+  }
+  return *this;
+}
+
+optional<time::system_clock::time_point>
+SignatureInfo::getTime() const
+{
+  auto it = findOtherTlv(tlv::SignatureTime);
+  if (it == m_otherTlvs.end()) {
+    return nullopt;
+  }
+  return time::fromUnixTimestamp(time::milliseconds(readNonNegativeInteger(*it)));
+}
+
+SignatureInfo&
+SignatureInfo::setTime(optional<time::system_clock::time_point> time)
+{
+  if (!time) {
+    removeCustomTlv(tlv::SignatureTime);
+  }
+  else {
+    addCustomTlv(makeNonNegativeIntegerBlock(tlv::SignatureTime, time::toUnixTimestamp(*time).count()));
+  }
+  return *this;
+}
+
+optional<uint64_t>
+SignatureInfo::getSeqNum() const
+{
+  auto it = findOtherTlv(tlv::SignatureSeqNum);
+  if (it == m_otherTlvs.end()) {
+    return nullopt;
+  }
+  return readNonNegativeInteger(*it);
+}
+
+SignatureInfo&
+SignatureInfo::setSeqNum(optional<uint64_t> seqNum)
+{
+  if (!seqNum) {
+    removeCustomTlv(tlv::SignatureSeqNum);
+  }
+  else {
+    addCustomTlv(makeNonNegativeIntegerBlock(tlv::SignatureSeqNum, *seqNum));
+  }
+  return *this;
+}
+
+optional<Block>
+SignatureInfo::getCustomTlv(uint32_t type) const
+{
+  auto it = findOtherTlv(type);
+  if (it == m_otherTlvs.end()) {
+    return nullopt;
+  }
+  return *it;
+}
+
+void
+SignatureInfo::addCustomTlv(Block block)
+{
+  auto existingIt = std::find_if(m_otherTlvs.begin(), m_otherTlvs.end(), [&block] (const Block& b) {
+    return b.type() == block.type();
+  });
+  if (existingIt == m_otherTlvs.end()) {
+    m_otherTlvs.push_back(std::move(block));
+    m_wire.reset();
+  }
+  else if (*existingIt != block) {
+    *existingIt = std::move(block);
+    m_wire.reset();
+  }
+}
+
+void
+SignatureInfo::removeCustomTlv(uint32_t type)
+{
+  auto it = std::remove_if(m_otherTlvs.begin(), m_otherTlvs.end(), [type] (const Block& block) {
+    return block.type() == type;
+  });
+
+  if (it != m_otherTlvs.end()) {
+    m_otherTlvs.erase(it, m_otherTlvs.end());
+    m_wire.reset();
+  }
+}
+
 const Block&
 SignatureInfo::getTypeSpecificTlv(uint32_t type) const
 {
-  for (const Block& block : m_otherTlvs) {
-    if (block.type() == type)
-      return block;
+  auto it = findOtherTlv(type);
+  if (it == m_otherTlvs.end()) {
+    NDN_THROW(Error("TLV-TYPE " + to_string(type) + " sub-element does not exist in SignatureInfo"));
   }
-
-  NDN_THROW(Error("TLV-TYPE " + to_string(type) + " sub-element does not exist in SignatureInfo"));
+  return *it;
 }
 
 void
 SignatureInfo::appendTypeSpecificTlv(const Block& block)
 {
-  m_otherTlvs.push_back(block);
-  m_wire.reset();
+  addCustomTlv(block);
+}
+
+std::vector<Block>::const_iterator
+SignatureInfo::findOtherTlv(uint32_t type) const
+{
+  return std::find_if(m_otherTlvs.begin(), m_otherTlvs.end(), [type] (const Block& block) {
+    return block.type() == type;
+  });
 }
 
 bool
@@ -230,8 +379,9 @@
 std::ostream&
 operator<<(std::ostream& os, const SignatureInfo& info)
 {
-  if (info.getSignatureType() == -1)
+  if (info.getSignatureType() == -1) {
     return os << "Invalid SignatureInfo";
+  }
 
   os << static_cast<tlv::SignatureTypeValue>(info.getSignatureType());
   if (info.hasKeyLocator()) {
@@ -240,7 +390,27 @@
   if (!info.m_otherTlvs.empty()) {
     os << " { ";
     for (const auto& block : info.m_otherTlvs) {
-      os << block.type() << " ";
+      switch (block.type()) {
+        case tlv::SignatureNonce: {
+          os << "Nonce=";
+          auto nonce = *info.getNonce();
+          printHex(os, nonce.data(), nonce.size(), false);
+          os << " ";
+          break;
+        }
+        case tlv::SignatureTime:
+          os << "Time=" << time::toUnixTimestamp(*info.getTime()).count() << " ";
+          break;
+        case tlv::SignatureSeqNum:
+          os << "SeqNum=" << *info.getSeqNum() << " ";
+          break;
+        case tlv::ValidityPeriod:
+          os << "ValidityPeriod=" << info.getValidityPeriod() << " ";
+          break;
+        default:
+          os << block.type() << " ";
+          break;
+      }
     }
     os << "}";
   }
diff --git a/ndn-cxx/signature-info.hpp b/ndn-cxx/signature-info.hpp
index 9e7b42e..bcfa2ee 100644
--- a/ndn-cxx/signature-info.hpp
+++ b/ndn-cxx/signature-info.hpp
@@ -25,11 +25,9 @@
 #include "ndn-cxx/key-locator.hpp"
 #include "ndn-cxx/security/validity-period.hpp"
 
-#include <list>
-
 namespace ndn {
 
-/** @brief Represents a SignatureInfo TLV element
+/** @brief Represents a SignatureInfo or InterestSignatureInfo TLV element
  */
 class SignatureInfo
 {
@@ -40,8 +38,12 @@
     using tlv::Error::Error;
   };
 
-  /** @brief Create an invalid SignatureInfo
-   */
+  enum class Type : uint32_t {
+    Data = tlv::SignatureInfo,
+    Interest = tlv::InterestSignatureInfo
+  };
+
+public:
   SignatureInfo();
 
   /** @brief Create with the specified type and KeyLocator
@@ -50,10 +52,12 @@
   SignatureInfo(tlv::SignatureTypeValue type, optional<KeyLocator> keyLocator = nullopt);
 
   /** @brief Create from wire encoding
-   *  @throw tlv::Error decode error
+   *  @param wire Wire to decode from
+   *  @param type Which type of SignatureInfo block decoding should expect
+   *  @throw tlv::Error Decode error
    */
   explicit
-  SignatureInfo(const Block& wire);
+  SignatureInfo(const Block& wire, Type type = Type::Data);
 
   /** @brief Determine whether SignatureInfo is valid
    */
@@ -65,21 +69,33 @@
 
   /** @brief Fast encoding or block size estimation
    *  @param encoder EncodingEstimator or EncodingBuffer instance
+   *  @param type Which type of SignatureInfo block to encode
+   *
+   *  Elements are encoded in the following order: SignatureType, KeyLocator (if present), and
+   *  other elements in the order they were set (changing the value of an already present element
+   *  will not change that element's encoding order).
    */
   template<encoding::Tag TAG>
   size_t
-  wireEncode(EncodingImpl<TAG>& encoder) const;
+  wireEncode(EncodingImpl<TAG>& encoder, Type type = Type::Data) const;
 
   /** @brief Encode to wire format
+   *  @param type Which type of SignatureInfo block to encode
+   *
+   *  Elements are encoded in the following order: SignatureType, KeyLocator (if present), and
+   *  other elements in the order they were set (changing the value of an already present element
+   *  will not change that element's encoding order).
    */
   const Block&
-  wireEncode() const;
+  wireEncode(Type type = Type::Data) const;
 
   /** @brief Decode from wire format
-   *  @throw tlv::Error decode error
+   *  @param wire Wire to decode from
+   *  @param type Which type of SignatureInfo block decoding should expect
+   *  @throw tlv::Error Decode error
    */
   void
-  wireDecode(const Block& wire);
+  wireDecode(const Block& wire, Type type = Type::Data);
 
   /** @brief Check if this instance has cached wire encoding.
    */
@@ -91,7 +107,7 @@
 
 public: // field access
   /** @brief Get SignatureType
-   *  @return tlv::SignatureTypeValue, or -1 to indicate invalid SignatureInfo
+   *  @return tlv::SignatureTypeValue, or -1 to indicate an invalid SignatureInfo
    */
   int32_t
   getSignatureType() const noexcept
@@ -100,7 +116,7 @@
   }
 
   /** @brief Set SignatureType
-   *  @return a reference to this SignatureInfo, to allow chaining
+   *  @return A reference to this SignatureInfo, to allow chaining
    */
   SignatureInfo&
   setSignatureType(tlv::SignatureTypeValue type);
@@ -120,7 +136,7 @@
   getKeyLocator() const;
 
   /** @brief Set KeyLocator
-   *  @return a reference to this SignatureInfo, to allow chaining
+   *  @return A reference to this SignatureInfo, to allow chaining
    *
    *  Passing `nullopt` will remove the KeyLocator.
    */
@@ -134,22 +150,14 @@
   void
   unsetKeyLocator();
 
-  /** @brief Check if ValidityPeriod is present
-   */
-  bool
-  hasValidityPeriod() const noexcept
-  {
-    return !m_otherTlvs.empty() && m_otherTlvs.front().type() == tlv::ValidityPeriod;
-  }
-
   /** @brief Get ValidityPeriod
    *  @throw Error This SignatureInfo does not contain a ValidityPeriod
    */
   security::ValidityPeriod
   getValidityPeriod() const;
 
-  /** @brief Set ValidityPeriod
-   *  @return a reference to this SignatureInfo, to allow chaining
+  /** @brief Append or replace ValidityPeriod
+   *  @return A reference to this SignatureInfo, to allow chaining
    *
    *  Passing `nullopt` will remove the ValidityPeriod.
    */
@@ -163,22 +171,92 @@
   void
   unsetValidityPeriod();
 
-  /** @brief Get SignatureType-specific sub-element
-   *  @param type TLV-TYPE of sub-element
-   *  @throw Error sub-element of specified type does not exist
+  /** @brief Get SignatureNonce
+   *  @retval nullopt SignatureNonce is not set
    */
+  optional<std::vector<uint8_t>>
+  getNonce() const;
+
+  /** @brief Append or replace SignatureNonce
+   *  @return A reference to this SignatureInfo, to allow chaining
+   *
+   *  Passing `nullopt` will remove the SignatureNonce.
+   */
+  SignatureInfo&
+  setNonce(optional<std::vector<uint8_t>> nonce);
+
+  /** @brief Get SignatureTime
+   *  @retval nullopt SignatureTime is not set
+   */
+  optional<time::system_clock::time_point>
+  getTime() const;
+
+  /** @brief Append or replace SignatureTime
+   *  @return A reference to this SignatureInfo, to allow chaining
+   *
+   *  Passing `nullopt` will remove the SignatureTime.
+   */
+  SignatureInfo&
+  setTime(optional<time::system_clock::time_point> time = time::system_clock::now());
+
+  /** @brief Get SignatureSeqNum
+   *  @retval nullopt SignatureSeqNum is not set
+   */
+  optional<uint64_t>
+  getSeqNum() const;
+
+  /** @brief Append or replace SignatureSeqNum
+   *  @return A reference to this SignatureInfo, to allow chaining
+   *
+   *  Passing `nullopt` will remove the SignatureSeqNum.
+   */
+  SignatureInfo&
+  setSeqNum(optional<uint64_t> seqNum);
+
+  /** @brief Get first custom TLV element with the specified TLV-TYPE
+   *  @param type TLV-TYPE of element to get
+   *  @retval nullopt No custom TLV elements with the specified TLV-TYPE exist
+   */
+  optional<Block>
+  getCustomTlv(uint32_t type) const;
+
+  /** @brief Append an arbitrary TLV element to this SignatureInfo
+   *
+   *  If an element of the same TLV-TYPE already exists, it will be replaced by the new element.
+   */
+  void
+  addCustomTlv(Block block);
+
+  /** @brief Remove all arbitrary TLV elements with the specified TLV-TYPE from this SignatureInfo
+   *  @param type TLV-TYPE of elements to remove
+   */
+  void
+  removeCustomTlv(uint32_t type);
+
+  /** @brief Get SignatureType-specific sub-element
+   *  @deprecated Use getCustomTlv
+   *  @param type TLV-TYPE of sub-element
+   *  @throw Error Sub-element of specified type does not exist
+   */
+  [[deprecated("use getCustomTlv")]]
   const Block&
   getTypeSpecificTlv(uint32_t type) const;
 
   /** @brief Append SignatureType-specific sub-element
+   *  @deprecated Use addCustomTlv
    */
+  [[deprecated("use addCustomTlv")]]
   void
-  appendTypeSpecificTlv(const Block& element);
+  appendTypeSpecificTlv(const Block& block);
+
+private:
+  std::vector<Block>::const_iterator
+  findOtherTlv(uint32_t type) const;
 
 private:
   int32_t m_type = -1;
   optional<KeyLocator> m_keyLocator;
-  std::list<Block> m_otherTlvs;
+  std::vector<Block> m_otherTlvs;
 
   mutable Block m_wire;
 
@@ -189,7 +267,11 @@
   operator<<(std::ostream& os, const SignatureInfo& info);
 };
 
-NDN_CXX_DECLARE_WIRE_ENCODE_INSTANTIATIONS(SignatureInfo);
+extern template size_t
+SignatureInfo::wireEncode<encoding::EncoderTag>(EncodingBuffer&, SignatureInfo::Type) const;
+
+extern template size_t
+SignatureInfo::wireEncode<encoding::EstimatorTag>(EncodingEstimator&, SignatureInfo::Type) const;
 
 bool
 operator==(const SignatureInfo& lhs, const SignatureInfo& rhs);
diff --git a/tests/identity-management-fixture.cpp b/tests/identity-management-fixture.cpp
index 781b2a1..3877a23 100644
--- a/tests/identity-management-fixture.cpp
+++ b/tests/identity-management-fixture.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).
  *
@@ -93,7 +93,7 @@
 
   v2::AdditionalDescription description;
   description.set("type", "sub-certificate");
-  info.appendTypeSpecificTlv(description.wireEncode());
+  info.addCustomTlv(description.wireEncode());
 
   m_keyChain.sign(request, signingByIdentity(issuer).setSignatureInfo(info));
   m_keyChain.setDefaultCertificate(subIdentity.getDefaultKey(), request);
diff --git a/tests/unit/security/v2/certificate.t.cpp b/tests/unit/security/v2/certificate.t.cpp
index 0d848fd..91bb5c1 100644
--- a/tests/unit/security/v2/certificate.t.cpp
+++ b/tests/unit/security/v2/certificate.t.cpp
@@ -151,7 +151,7 @@
   BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(certificate.getValidityPeriod()),
                     "(20150814T223739, 20150818T223738)");
 
-  BOOST_CHECK_THROW(certificate.getExtension(12345), ndn::SignatureInfo::Error);
+  BOOST_CHECK_THROW(certificate.getExtension(12345), ndn::Data::Error);
   BOOST_CHECK_NO_THROW(certificate.getPublicKey());
 
   Data data(block);
@@ -177,7 +177,7 @@
   BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(certificate.getValidityPeriod()),
                     "(20141111T050000, 20141111T060000)");
 
-  BOOST_CHECK_THROW(certificate.getExtension(12345), ndn::SignatureInfo::Error);
+  BOOST_CHECK_THROW(certificate.getExtension(12345), ndn::Data::Error);
   BOOST_CHECK_NO_THROW(certificate.getPublicKey());
 }
 
diff --git a/tests/unit/signature-info.t.cpp b/tests/unit/signature-info.t.cpp
index 1b0fe58..ee8c826 100644
--- a/tests/unit/signature-info.t.cpp
+++ b/tests/unit/signature-info.t.cpp
@@ -30,7 +30,7 @@
 
 BOOST_AUTO_TEST_SUITE(TestSignatureInfo)
 
-const uint8_t sigInfoRsa[] = {
+const uint8_t sigInfoDataRsa[] = {
   0x16, 0x1b, // SignatureInfo
     0x1b, 0x01, // SignatureType
       0x01, // Sha256WithRsa
@@ -44,96 +44,56 @@
           0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72
 };
 
+const uint8_t sigInfoInterestRsa[] = {
+  0x2c, 0x33, // InterestSignatureInfo
+    0x1b, 0x01, // SignatureType
+      0x01, // Sha256WithRsa
+    0x1c, 0x16, // KeyLocator
+      0x07, 0x14, // Name
+        0x08, 0x04,
+          0x74, 0x65, 0x73, 0x74,
+        0x08, 0x03,
+          0x6b, 0x65, 0x79,
+        0x08, 0x07,
+          0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72,
+    0x26, 0x08, // SignatureNonce
+      0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+    0x28, 0x08, // SignatureTime
+      0x00, 0x00, 0x01, 0x72, 0x3d, 0x77, 0x00, 0x00,
+    0x2a, 0x02, // SignatureSeqNum
+      0x10, 0x20,
+};
+
 BOOST_AUTO_TEST_CASE(Constructor)
 {
   SignatureInfo info;
   BOOST_CHECK_EQUAL(info.getSignatureType(), -1);
   BOOST_CHECK_EQUAL(info.hasKeyLocator(), false);
   BOOST_CHECK_THROW(info.getKeyLocator(), SignatureInfo::Error);
-  BOOST_CHECK_EQUAL(info.hasValidityPeriod(), false);
   BOOST_CHECK_THROW(info.getValidityPeriod(), SignatureInfo::Error);
-
-  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(info), "Invalid SignatureInfo");
+  BOOST_CHECK(!info.getNonce());
+  BOOST_CHECK(!info.getTime());
+  BOOST_CHECK(!info.getSeqNum());
 
   SignatureInfo sha256Info(tlv::DigestSha256);
   BOOST_CHECK_EQUAL(sha256Info.getSignatureType(), tlv::DigestSha256);
-  BOOST_CHECK_EQUAL(sha256Info.hasKeyLocator(), false);
   BOOST_CHECK_THROW(sha256Info.getKeyLocator(), SignatureInfo::Error);
-
-  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(sha256Info), "DigestSha256");
+  BOOST_CHECK(!info.getNonce());
+  BOOST_CHECK(!info.getTime());
+  BOOST_CHECK(!info.getSeqNum());
 
   KeyLocator keyLocator("/test/key/locator");
   SignatureInfo sha256RsaInfo(tlv::SignatureSha256WithRsa, keyLocator);
   BOOST_CHECK_EQUAL(sha256RsaInfo.getSignatureType(), tlv::SignatureSha256WithRsa);
   BOOST_CHECK_EQUAL(sha256RsaInfo.hasKeyLocator(), true);
   BOOST_CHECK_EQUAL(sha256RsaInfo.getKeyLocator().getName(), Name("/test/key/locator"));
-  BOOST_CHECK_EQUAL(sha256RsaInfo.hasValidityPeriod(), false);
   BOOST_CHECK_THROW(sha256RsaInfo.getValidityPeriod(), SignatureInfo::Error);
-
-  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(sha256RsaInfo),
-                    "SignatureSha256WithRsa Name=/test/key/locator");
-
-  auto encoded = sha256RsaInfo.wireEncode();
-  Block sigInfoBlock(sigInfoRsa, sizeof(sigInfoRsa));
-  BOOST_CHECK_EQUAL_COLLECTIONS(sigInfoBlock.wire(), sigInfoBlock.wire() + sigInfoBlock.size(),
-                                encoded.wire(), encoded.wire() + encoded.size());
-
-  sha256RsaInfo = SignatureInfo(sigInfoBlock);
-  BOOST_CHECK_EQUAL(sha256RsaInfo.getSignatureType(), tlv::SignatureSha256WithRsa);
-  BOOST_CHECK_EQUAL(sha256RsaInfo.hasKeyLocator(), true);
-  BOOST_CHECK_EQUAL(sha256RsaInfo.getKeyLocator().getName(), Name("/test/key/locator"));
+  BOOST_CHECK(!info.getNonce());
+  BOOST_CHECK(!info.getTime());
+  BOOST_CHECK(!info.getSeqNum());
 }
 
-BOOST_AUTO_TEST_CASE(ConstructorError)
-{
-  const uint8_t error1[] = {
-    0x15, 0x1b, // Wrong SignatureInfo (0x16, 0x1b)
-      0x1b, 0x01, // SignatureType
-        0x01, // Sha256WithRsa
-      0x1c, 0x16, // KeyLocator
-        0x07, 0x14, // Name
-          0x08, 0x04,
-            0x74, 0x65, 0x73, 0x74,
-          0x08, 0x03,
-            0x6b, 0x65, 0x79,
-          0x08, 0x07,
-            0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72
-  };
-  Block errorBlock1(error1, sizeof(error1));
-  BOOST_CHECK_THROW(SignatureInfo info(errorBlock1), tlv::Error);
-
-  const uint8_t error2[] = {
-    0x16, 0x01, // SignatureInfo
-      0x01 // Wrong SignatureInfo value
-  };
-  Block errorBlock2(error2, sizeof(error2));
-  BOOST_CHECK_THROW(SignatureInfo info(errorBlock2), tlv::Error);
-
-  const uint8_t error3[] = {
-    0x16, 0x01, // SignatureInfo
-      0x1a, 0x01, // Wrong SignatureType (0x1b, 0x1b)
-        0x01, // Sha256WithRsa
-      0x1c, 0x16, // KeyLocator
-        0x07, 0x14, // Name
-          0x08, 0x04,
-            0x74, 0x65, 0x73, 0x74,
-          0x08, 0x03,
-            0x6b, 0x65, 0x79,
-          0x08, 0x07,
-            0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72
-  };
-  Block errorBlock3(error3, sizeof(error3));
-  BOOST_CHECK_THROW(SignatureInfo info(errorBlock3), tlv::Error);
-
-  const uint8_t error4[] = {
-    0x16, 0x00 // Empty SignatureInfo
-  };
-  Block errorBlock4(error4, sizeof(error4));
-  BOOST_CHECK_THROW(SignatureInfo info(errorBlock4), tlv::Error);
-
-}
-
-BOOST_AUTO_TEST_CASE(GetSetSignatureType)
+BOOST_AUTO_TEST_CASE(SignatureType)
 {
   SignatureInfo info;
   BOOST_CHECK_EQUAL(info.getSignatureType(), -1);
@@ -153,7 +113,8 @@
   BOOST_CHECK_EQUAL(info.hasWire(), false);
 }
 
-BOOST_AUTO_TEST_CASE(GetSetKeyLocator)
+// We must name this test case differently to avoid naming conflicts
+BOOST_AUTO_TEST_CASE(KeyLocatorField)
 {
   SignatureInfo info(tlv::SignatureSha256WithEcdsa);
   BOOST_CHECK_EQUAL(info.hasKeyLocator(), false);
@@ -179,6 +140,230 @@
   BOOST_CHECK_EQUAL(info.hasWire(), false);
 }
 
+BOOST_AUTO_TEST_CASE(SignatureNonce)
+{
+  SignatureInfo info(tlv::SignatureSha256WithEcdsa);
+  BOOST_CHECK(!info.getNonce());
+  info.wireEncode();
+  BOOST_CHECK_EQUAL(info.hasWire(), true);
+  std::vector<uint8_t> nonce{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
+  info.setNonce(nonce);
+  BOOST_CHECK_EQUAL(info.hasWire(), false);
+  BOOST_CHECK(info.getNonce() == nonce);
+
+  info.wireEncode();
+  info.setNonce(nonce);
+  BOOST_CHECK_EQUAL(info.hasWire(), true);
+  nonce[2] = 0xFF;
+  info.setNonce(nonce);
+  BOOST_CHECK_EQUAL(info.hasWire(), false);
+  BOOST_CHECK(info.getNonce() == nonce);
+
+  info.wireEncode();
+  BOOST_CHECK_EQUAL(info.hasWire(), true);
+  info.setNonce(nullopt);
+  BOOST_CHECK_EQUAL(info.hasWire(), false);
+  BOOST_CHECK(!info.getNonce());
+}
+
+BOOST_AUTO_TEST_CASE(SignatureTime)
+{
+  SignatureInfo info(tlv::SignatureSha256WithEcdsa);
+  BOOST_CHECK(!info.getTime());
+  info.wireEncode();
+  BOOST_CHECK_EQUAL(info.hasWire(), true);
+  time::system_clock::time_point timePoint(1590169108480_ms);
+  info.setTime(timePoint);
+  BOOST_CHECK_EQUAL(info.hasWire(), false);
+  BOOST_CHECK(info.getTime() == timePoint);
+
+  info.wireEncode();
+  info.setTime(timePoint);
+  BOOST_CHECK_EQUAL(info.hasWire(), true);
+  info.setTime(timePoint + 2_s);
+  BOOST_CHECK_EQUAL(info.hasWire(), false);
+  BOOST_CHECK(info.getTime() == timePoint + 2_s);
+
+  info.wireEncode();
+  BOOST_CHECK_EQUAL(info.hasWire(), true);
+  info.setTime(nullopt);
+  BOOST_CHECK_EQUAL(info.hasWire(), false);
+  BOOST_CHECK(!info.getTime());
+}
+
+BOOST_AUTO_TEST_CASE(SignatureSeqNum)
+{
+  SignatureInfo info(tlv::SignatureSha256WithEcdsa);
+  BOOST_CHECK(!info.getSeqNum());
+  info.wireEncode();
+  BOOST_CHECK_EQUAL(info.hasWire(), true);
+  info.setSeqNum(256);
+  BOOST_CHECK_EQUAL(info.hasWire(), false);
+  BOOST_CHECK(info.getSeqNum() == 256UL);
+
+  info.wireEncode();
+  info.setSeqNum(256);
+  BOOST_CHECK_EQUAL(info.hasWire(), true);
+  info.setSeqNum(512);
+  BOOST_CHECK_EQUAL(info.hasWire(), false);
+  BOOST_CHECK(info.getSeqNum() == 512UL);
+
+  info.wireEncode();
+  BOOST_CHECK_EQUAL(info.hasWire(), true);
+  info.setSeqNum(nullopt);
+  BOOST_CHECK_EQUAL(info.hasWire(), false);
+  BOOST_CHECK(!info.getSeqNum());
+}
+
+BOOST_AUTO_TEST_CASE(EncodeDecode)
+{
+  KeyLocator keyLocator("/test/key/locator");
+  SignatureInfo info(tlv::SignatureSha256WithRsa, keyLocator);
+
+  // Encode as SignatureInfo (for Data packets)
+  auto encodedData = info.wireEncode(SignatureInfo::Type::Data);
+  Block sigInfoDataBlock(sigInfoDataRsa, sizeof(sigInfoDataRsa));
+
+  BOOST_CHECK_EQUAL_COLLECTIONS(sigInfoDataBlock.wire(), sigInfoDataBlock.wire() + sigInfoDataBlock.size(),
+                                encodedData.wire(), encodedData.wire() + encodedData.size());
+
+  // Decode as SignatureInfo (for Data packets)
+  info = SignatureInfo(sigInfoDataBlock, SignatureInfo::Type::Data);
+  BOOST_CHECK_EQUAL(info.getSignatureType(), tlv::SignatureSha256WithRsa);
+  BOOST_CHECK_EQUAL(info.hasKeyLocator(), true);
+  BOOST_CHECK_EQUAL(info.getKeyLocator().getName(), Name("/test/key/locator"));
+
+  BOOST_CHECK(!info.getNonce());
+  BOOST_CHECK(!info.getTime());
+  BOOST_CHECK(!info.getSeqNum());
+
+  // Encode as InterestSignatureInfo
+  std::vector<uint8_t> nonce{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
+  info.setNonce(nonce);
+  time::system_clock::time_point timePoint(1590169108480_ms);
+  info.setTime(timePoint);
+  info.setSeqNum(0x1020);
+  auto encodedInterest = info.wireEncode(SignatureInfo::Type::Interest);
+  Block sigInfoInterestBlock(sigInfoInterestRsa, sizeof(sigInfoInterestRsa));
+
+  BOOST_CHECK_EQUAL_COLLECTIONS(sigInfoInterestBlock.wire(),
+                                sigInfoInterestBlock.wire() + sigInfoInterestBlock.size(),
+                                encodedInterest.wire(),
+                                encodedInterest.wire() + encodedInterest.size());
+
+  // Decode as InterestSignatureInfo
+  info = SignatureInfo(sigInfoInterestBlock, SignatureInfo::Type::Interest);
+  BOOST_CHECK_EQUAL(info.getSignatureType(), tlv::SignatureSha256WithRsa);
+  BOOST_CHECK_EQUAL(info.hasKeyLocator(), true);
+  BOOST_CHECK_EQUAL(info.getKeyLocator().getName(), Name("/test/key/locator"));
+  BOOST_CHECK(info.getNonce() == nonce);
+  BOOST_CHECK(info.getTime() == timePoint);
+  BOOST_CHECK(info.getSeqNum() == 0x1020UL);
+}
+
+BOOST_AUTO_TEST_CASE(DecodeError)
+{
+  const uint8_t error1[] = {
+    0x15, 0x1b, // Wrong SignatureInfo (0x16, 0x1b)
+      0x1b, 0x01, // SignatureType
+        0x01, // Sha256WithRsa
+      0x1c, 0x16, // KeyLocator
+        0x07, 0x14, // Name
+          0x08, 0x04,
+            0x74, 0x65, 0x73, 0x74,
+          0x08, 0x03,
+            0x6b, 0x65, 0x79,
+          0x08, 0x07,
+            0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72
+  };
+  Block errorBlock1(error1, sizeof(error1));
+  BOOST_CHECK_THROW(SignatureInfo(errorBlock1, SignatureInfo::Type::Data), tlv::Error);
+
+  const uint8_t error2[] = {
+    0x16, 0x05, // SignatureInfo
+      0x1b, 0x01, // SignatureType
+        0x01, // Sha256WithRsa
+      0x83, 0x00, // Unrecognized critical TLV
+  };
+  Block errorBlock2(error2, sizeof(error2));
+  BOOST_CHECK_THROW(SignatureInfo(errorBlock2, SignatureInfo::Type::Data), tlv::Error);
+
+  const uint8_t error3[] = {
+    0x16, 0x00 // Empty SignatureInfo
+  };
+  Block errorBlock3(error3, sizeof(error3));
+  BOOST_CHECK_THROW(SignatureInfo(errorBlock3, SignatureInfo::Type::Data), tlv::Error);
+
+  // Encoding is correct for SignatureInfo, but decoder is expecting InterestSignatureInfo
+  const uint8_t error4[] = {
+    0x16, 0x1b, // SignatureInfo
+      0x1b, 0x01, // SignatureType
+        0x01, // Sha256WithRsa
+      0x1c, 0x16, // KeyLocator
+        0x07, 0x14, // Name
+          0x08, 0x04,
+            0x74, 0x65, 0x73, 0x74,
+          0x08, 0x03,
+            0x6b, 0x65, 0x79,
+          0x08, 0x07,
+            0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72
+  };
+  Block errorBlock4(error4, sizeof(error4));
+  BOOST_CHECK_THROW(SignatureInfo(errorBlock4, SignatureInfo::Type::Interest), tlv::Error);
+
+  // SignatureType and KeyLocator out-of-order
+  const uint8_t error5[] = {
+    0x2c, 0x1b, // SignatureInfo
+      0x1c, 0x16, // KeyLocator
+        0x07, 0x14, // Name
+          0x08, 0x04,
+            0x74, 0x65, 0x73, 0x74,
+          0x08, 0x03,
+            0x6b, 0x65, 0x79,
+          0x08, 0x07,
+            0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72,
+      0x1b, 0x01, // SignatureType
+        0x01, // Sha256WithRsa
+  };
+  Block errorBlock5(error5, sizeof(error5));
+  BOOST_CHECK_THROW(SignatureInfo(errorBlock5, SignatureInfo::Type::Interest), tlv::Error);
+
+  // Repeated KeyLocator
+  const uint8_t error6[] = {
+    0x2c, 0x33, // SignatureInfo
+      0x1b, 0x01, // SignatureType
+        0x01, // Sha256WithRsa
+      0x1c, 0x16, // KeyLocator
+        0x07, 0x14, // Name
+          0x08, 0x04,
+            0x74, 0x65, 0x73, 0x74,
+          0x08, 0x03,
+            0x6b, 0x65, 0x79,
+          0x08, 0x07,
+            0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72,
+      0x1c, 0x16, // KeyLocator
+        0x07, 0x14, // Name
+          0x08, 0x04,
+            0x74, 0x65, 0x73, 0x74,
+          0x08, 0x03,
+            0x6b, 0x65, 0x79,
+          0x08, 0x07,
+            0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72
+  };
+  Block errorBlock6(error6, sizeof(error6));
+  BOOST_CHECK_THROW(SignatureInfo(errorBlock6, SignatureInfo::Type::Interest), tlv::Error);
+
+  // Zero-length SignatureNonce
+  const uint8_t error7[] = {
+    0x2c, 0x05, // SignatureInfo
+      0x1b, 0x01, // SignatureType
+        0x01, // Sha256WithRsa
+      0x26, 0x00 // SignatureNonce
+  };
+  Block errorBlock7(error7, sizeof(error7));
+  BOOST_CHECK_THROW(SignatureInfo(errorBlock7, SignatureInfo::Type::Interest), tlv::Error);
+}
+
 BOOST_AUTO_TEST_CASE(ValidityPeriod)
 {
   const uint8_t sigInfo[] = {
@@ -207,12 +392,10 @@
   security::ValidityPeriod vp1(notBefore, notAfter);
 
   SignatureInfo info(tlv::SignatureSha256WithRsa, KeyLocator("/test/key/locator"));
-  BOOST_CHECK_EQUAL(info.hasValidityPeriod(), false);
   BOOST_CHECK_THROW(info.getValidityPeriod(), SignatureInfo::Error);
 
   info.wireEncode();
   info.setValidityPeriod(vp1);
-  BOOST_CHECK_EQUAL(info.hasValidityPeriod(), true);
   BOOST_CHECK_EQUAL(info.getValidityPeriod(), vp1);
   BOOST_CHECK_EQUAL(info.hasWire(), false);
 
@@ -227,13 +410,11 @@
 
   // decode
   Block block(sigInfo, sizeof(sigInfo));
-  SignatureInfo info2(block);
-  BOOST_CHECK_EQUAL(info2.hasValidityPeriod(), true);
+  SignatureInfo info2(block, SignatureInfo::Type::Data);
   BOOST_CHECK_EQUAL(info2.getValidityPeriod(), vp1);
   BOOST_CHECK_EQUAL(info2.hasWire(), true);
 
   info2.setValidityPeriod(security::ValidityPeriod(notBefore, notBefore + 42_days));
-  BOOST_CHECK_EQUAL(info2.hasValidityPeriod(), true);
   BOOST_CHECK_NE(info2.getValidityPeriod(), vp1);
   BOOST_CHECK(info2.getValidityPeriod().getPeriod() == std::make_pair(notBefore, notBefore + 42_days));
   BOOST_CHECK_EQUAL(info2.hasWire(), false);
@@ -241,51 +422,93 @@
   info2.wireEncode();
   BOOST_CHECK_EQUAL(info2.hasWire(), true);
   info2.setValidityPeriod(nullopt);
-  BOOST_CHECK_EQUAL(info2.hasValidityPeriod(), false);
   BOOST_CHECK_THROW(info2.getValidityPeriod(), SignatureInfo::Error);
   BOOST_CHECK_EQUAL(info2.hasWire(), false);
 }
 
-BOOST_AUTO_TEST_CASE(OtherTlvs)
+BOOST_AUTO_TEST_CASE(CustomTlvs)
 {
   SignatureInfo info(tlv::SignatureSha256WithEcdsa);
-  info.appendTypeSpecificTlv("810101"_block);
-  BOOST_CHECK_THROW(info.getTypeSpecificTlv(0x82), SignatureInfo::Error);
-  BOOST_CHECK_EQUAL(info.getTypeSpecificTlv(0x81).type(), 0x81);
+  info.addCustomTlv("810101"_block);
+  BOOST_CHECK(!info.getCustomTlv(0x82));
+  BOOST_REQUIRE(info.getCustomTlv(0x81));
+  BOOST_CHECK_EQUAL(info.getCustomTlv(0x81)->type(), 0x81);
 
   info.wireEncode();
   BOOST_CHECK_EQUAL(info.hasWire(), true);
-  info.appendTypeSpecificTlv("82020202"_block);
+  info.addCustomTlv("82020202"_block);
   BOOST_CHECK_EQUAL(info.hasWire(), false);
+
+  info.wireEncode();
+  BOOST_CHECK_EQUAL(info.hasWire(), true);
+  info.removeCustomTlv(0x81);
+  BOOST_CHECK_EQUAL(info.hasWire(), false);
+  BOOST_CHECK(!info.getCustomTlv(0x81));
 }
 
-BOOST_AUTO_TEST_CASE(OtherTlvsEncoding) // Bug #3914
+BOOST_AUTO_TEST_CASE(CustomTlvsEncoding) // Bug #3914
 {
   SignatureInfo info1(tlv::SignatureSha256WithRsa);
-  info1.appendTypeSpecificTlv(makeStringBlock(101, "First"));
-  info1.appendTypeSpecificTlv(makeStringBlock(102, "Second"));
-  info1.appendTypeSpecificTlv(makeStringBlock(103, "Third"));
+  info1.appendTypeSpecificTlv(makeStringBlock(102, "First"));
+  info1.appendTypeSpecificTlv(makeStringBlock(104, "Second"));
+  info1.appendTypeSpecificTlv(makeStringBlock(106, "Third"));
 
-  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(info1), "SignatureSha256WithRsa { 101 102 103 }");
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(info1), "SignatureSha256WithRsa { 102 104 106 }");
 
   SignatureInfo info2;
-  info2.wireDecode(info1.wireEncode());
+  info2.wireDecode(info1.wireEncode(), SignatureInfo::Type::Data);
   BOOST_CHECK_EQUAL(info1, info2);
 
   const uint8_t infoBytes[] = {
     0x16, 0x19, // SignatureInfo
           0x1b, 0x01, 0x01, // SignatureType=1
-          0x65, 0x05, 0x46, 0x69, 0x72, 0x73, 0x74, // 101 "First"
-          0x66, 0x06, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, // 102 "Second"
-          0x67, 0x05, 0x54, 0x68, 0x69, 0x72, 0x64 // 103 "Third"
+          0x66, 0x05, 0x46, 0x69, 0x72, 0x73, 0x74, // 102 "First"
+          0x68, 0x06, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, // 104 "Second"
+          0x6a, 0x05, 0x54, 0x68, 0x69, 0x72, 0x64 // 106 "Third"
   };
 
-  SignatureInfo info3(Block(infoBytes, sizeof(infoBytes)));
+  SignatureInfo info3(Block(infoBytes, sizeof(infoBytes)), SignatureInfo::Type::Data);
   BOOST_CHECK_EQUAL(info3, info1);
   BOOST_CHECK_EQUAL_COLLECTIONS(infoBytes, infoBytes + sizeof(infoBytes),
                                 info1.wireEncode().begin(), info1.wireEncode().end());
 }
 
+BOOST_AUTO_TEST_CASE(OutputStream)
+{
+  SignatureInfo info;
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(info), "Invalid SignatureInfo");
+
+  info.setSignatureType(tlv::DigestSha256);
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(info), "DigestSha256");
+
+  info.setSignatureType(tlv::SignatureSha256WithRsa);
+  info.setKeyLocator(KeyLocator("/test/key/locator"));
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(info),
+                    "SignatureSha256WithRsa Name=/test/key/locator");
+
+  info.setNonce(std::vector<uint8_t>{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08});
+  info.setTime(time::system_clock::time_point(1590169108480_ms));
+  info.setSeqNum(0x1020);
+
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(info),
+                    "SignatureSha256WithRsa Name=/test/key/locator "
+                    "{ Nonce=0102030405060708 Time=1590169108480 SeqNum=4128 }");
+
+  info.setValidityPeriod(security::ValidityPeriod(time::getUnixEpoch(), time::getUnixEpoch() + 31_days));
+
+  info.addCustomTlv("82020102"_block);
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(info),
+                    "SignatureSha256WithRsa Name=/test/key/locator "
+                    "{ Nonce=0102030405060708 Time=1590169108480 SeqNum=4128 "
+                    "ValidityPeriod=(19700101T000000, 19700201T000000) 130 }");
+
+  info.addCustomTlv("84020102"_block);
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(info),
+                    "SignatureSha256WithRsa Name=/test/key/locator "
+                    "{ Nonce=0102030405060708 Time=1590169108480 SeqNum=4128 "
+                    "ValidityPeriod=(19700101T000000, 19700201T000000) 130 132 }");
+}
+
 BOOST_AUTO_TEST_SUITE_END() // TestSignatureInfo
 
 } // namespace tests
diff --git a/tools/ndnsec/cert-gen.cpp b/tools/ndnsec/cert-gen.cpp
index 1749b2b..c526654 100644
--- a/tools/ndnsec/cert-gen.cpp
+++ b/tools/ndnsec/cert-gen.cpp
@@ -152,7 +152,7 @@
   SignatureInfo signatureInfo;
   signatureInfo.setValidityPeriod(security::ValidityPeriod(notBefore, notAfter));
   if (!additionalDescription.empty()) {
-    signatureInfo.appendTypeSpecificTlv(additionalDescription.wireEncode());
+    signatureInfo.addCustomTlv(additionalDescription.wireEncode());
   }
 
   security::Identity identity;