security: Implement operator<< for v2::Certificate

This commit also implements operator<< for SignatureInfoValue and
KeyLocator.

Change-Id: I71d3840ab63bacf1278d755b5eed9630c5a2f48f
diff --git a/src/encoding/tlv.cpp b/src/encoding/tlv.cpp
new file mode 100644
index 0000000..4de73a1
--- /dev/null
+++ b/src/encoding/tlv.cpp
@@ -0,0 +1,42 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 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 "tlv.hpp"
+
+namespace ndn {
+namespace tlv {
+
+std::ostream&
+operator<<(std::ostream& os, const SignatureTypeValue& signatureType)
+{
+  switch (signatureType) {
+    case SignatureTypeValue::DigestSha256:
+      return os << "DigestSha256";
+    case SignatureTypeValue::SignatureSha256WithRsa:
+      return os << "SignatureSha256WithRsa";
+    case SignatureTypeValue::SignatureSha256WithEcdsa:
+      return os << "SignatureSha256WithEcdsa";
+  }
+  return os << "Unknown Signature Type";
+}
+
+} // namespace tlv
+} // namespace ndn
diff --git a/src/encoding/tlv.hpp b/src/encoding/tlv.hpp
index ad964e2..c9a6520 100644
--- a/src/encoding/tlv.hpp
+++ b/src/encoding/tlv.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2015 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -99,6 +99,9 @@
   SignatureSha256WithEcdsa = 3
 };
 
+std::ostream&
+operator<<(std::ostream& os, const SignatureTypeValue& signatureType);
+
 /** @brief TLV codes for SignatureInfo features
  *  @sa docs/tutorials/certificate-format.rst
  */
diff --git a/src/key-locator.cpp b/src/key-locator.cpp
index 151a704..8eef8cc 100644
--- a/src/key-locator.cpp
+++ b/src/key-locator.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2015 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -21,6 +21,7 @@
 
 #include "key-locator.hpp"
 #include "encoding/block-helpers.hpp"
+#include "util/string-helper.hpp"
 
 namespace ndn {
 
@@ -189,4 +190,30 @@
   return wireEncode() == other.wireEncode();
 }
 
+std::ostream&
+operator<<(std::ostream& os, const KeyLocator& keyLocator)
+{
+  switch (keyLocator.getType()) {
+    case KeyLocator::KeyLocator_Name: {
+      return os << "Name=" << keyLocator.getName();
+    }
+    case KeyLocator::KeyLocator_KeyDigest: {
+      const size_t MAX_DIGEST_OCTETS_TO_SHOW = 5;
+      const Block& digest = keyLocator.getKeyDigest();
+      os << "KeyDigest=" << toHex(digest.value(), digest.value_size()).substr(0, MAX_DIGEST_OCTETS_TO_SHOW * 2);
+      if (digest.value_size() > MAX_DIGEST_OCTETS_TO_SHOW) {
+        os << "...";
+      }
+      return os;
+    }
+    case KeyLocator::KeyLocator_None: {
+      return os << "None";
+    }
+    case KeyLocator::KeyLocator_Unknown: {
+      return os << "Unknown";
+    }
+  }
+  return os << "Unknown";
+}
+
 } // namespace ndn
diff --git a/src/key-locator.hpp b/src/key-locator.hpp
index 8c206e4..b442854 100644
--- a/src/key-locator.hpp
+++ b/src/key-locator.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2015 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -162,6 +162,9 @@
   mutable Block m_wire;
 };
 
+std::ostream&
+operator<<(std::ostream& os, const KeyLocator& keyLocator);
+
 } // namespace ndn
 
 #endif // NDN_KEY_LOCATOR_HPP
diff --git a/src/security/v2/certificate.cpp b/src/security/v2/certificate.cpp
index b6a08ab..dcd6ae0 100644
--- a/src/security/v2/certificate.cpp
+++ b/src/security/v2/certificate.cpp
@@ -23,7 +23,10 @@
  */
 
 #include "certificate.hpp"
+#include "additional-description.hpp"
 #include "../../encoding/block-helpers.hpp"
+#include "../../util/indented-stream.hpp"
+#include "../transform.hpp"
 
 namespace ndn {
 namespace security {
@@ -132,6 +135,52 @@
           certName.get(Certificate::KEY_COMPONENT_OFFSET) == Certificate::KEY_COMPONENT);
 }
 
+std::ostream&
+operator<<(std::ostream& os, const Certificate& cert)
+{
+  os << "Certificate name:\n";
+  os << "  " << cert.getName() << "\n";
+  os << "Validity:\n";
+  {
+    os << "  NotBefore: " << time::toIsoString(cert.getValidityPeriod().getPeriod().first) << "\n";
+    os << "  NotAfter: "  << time::toIsoString(cert.getValidityPeriod().getPeriod().second)  << "\n";
+  }
+
+  try {
+    const Block& info = cert.getSignature().getSignatureInfo().getTypeSpecificTlv(tlv::AdditionalDescription);
+    os << "Additional Description:\n";
+    for (const auto& item : v2::AdditionalDescription(info)) {
+      os << "  " << item.first << ": " << item.second << "\n";
+    }
+  }
+  catch (const SignatureInfo::Error&) {
+    // ignore
+  }
+
+  os << "Public key bits:\n";
+  {
+    util::IndentedStream os2(os, "  ");
+    namespace t = ndn::security::transform;
+    t::bufferSource(cert.getPublicKey().buf(), cert.getPublicKey().size()) >> t::base64Encode() >> t::streamSink(os2);
+  }
+
+  os << "Signature Information:\n";
+  {
+    os << "  Signature Type: " << static_cast<tlv::SignatureTypeValue>(cert.getSignature().getType()) << "\n";
+
+    if (cert.getSignature().hasKeyLocator()) {
+      os << "  Key Locator: ";
+      const KeyLocator& keyLocator = cert.getSignature().getKeyLocator();
+      if (keyLocator.getType() == KeyLocator::KeyLocator_Name && keyLocator.getName() == cert.getKeyName()) {
+        os << "Self-Signed ";
+      }
+      os << keyLocator << "\n";
+    }
+  }
+
+  return os;
+}
+
 Name
 extractIdentityFromCertName(const Name& certName)
 {
diff --git a/src/security/v2/certificate.hpp b/src/security/v2/certificate.hpp
index c451277..c5e2c97 100644
--- a/src/security/v2/certificate.hpp
+++ b/src/security/v2/certificate.hpp
@@ -172,6 +172,9 @@
   static const name::Component KEY_COMPONENT;
 };
 
+std::ostream&
+operator<<(std::ostream& os, const Certificate& cert);
+
 /**
  * @brief Extract identity namespace from the certificate name @p certName
  */
diff --git a/tests/unit-tests/encoding/tlv.t.cpp b/tests/unit-tests/encoding/tlv.t.cpp
index 635aecf..6576e04 100644
--- a/tests/unit-tests/encoding/tlv.t.cpp
+++ b/tests/unit-tests/encoding/tlv.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -25,6 +25,7 @@
 
 #include <boost/iostreams/stream.hpp>
 #include <boost/iostreams/device/array.hpp>
+#include <boost/lexical_cast.hpp>
 
 namespace ndn {
 namespace tlv {
@@ -401,6 +402,26 @@
 
 BOOST_AUTO_TEST_SUITE_END() // NonNegativeInteger
 
+BOOST_AUTO_TEST_SUITE(PrintHelpers)
+
+BOOST_AUTO_TEST_CASE(PrintSignatureTypeValue)
+{
+  SignatureTypeValue value = DigestSha256;
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(value), "DigestSha256");
+
+  value = SignatureSha256WithRsa;
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(value), "SignatureSha256WithRsa");
+
+  value = SignatureSha256WithEcdsa;
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(value), "SignatureSha256WithEcdsa");
+
+  value = static_cast<SignatureTypeValue>(-1);
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(value), "Unknown Signature Type");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestTlv
+
+
 BOOST_AUTO_TEST_SUITE_END() // TestTlv
 BOOST_AUTO_TEST_SUITE_END() // Encoding
 
diff --git a/tests/unit-tests/key-locator.t.cpp b/tests/unit-tests/key-locator.t.cpp
index 72e4d66..8ae8d48 100644
--- a/tests/unit-tests/key-locator.t.cpp
+++ b/tests/unit-tests/key-locator.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -23,6 +23,7 @@
 #include "encoding/block-helpers.hpp"
 
 #include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
 
 namespace ndn {
 namespace tests {
@@ -56,6 +57,8 @@
   BOOST_CHECK_EQUAL(b.getType(), KeyLocator::KeyLocator_None);
   BOOST_CHECK_THROW(b.getName(), KeyLocator::Error);
   BOOST_CHECK_THROW(b.getKeyDigest(), KeyLocator::Error);
+
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(b), "None");
 }
 
 BOOST_AUTO_TEST_CASE(TypeName)
@@ -86,13 +89,15 @@
   BOOST_CHECK_EQUAL(b.getType(), KeyLocator::KeyLocator_Name);
   BOOST_CHECK_EQUAL(b.getName(), Name("/N"));
   BOOST_CHECK_THROW(b.getKeyDigest(), KeyLocator::Error);
+
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(b), "Name=/N");
 }
 
 BOOST_AUTO_TEST_CASE(TypeKeyDigest)
 {
-  char digestOctets[] = "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD";
-  ConstBufferPtr digestBuffer = make_shared<Buffer>(digestOctets, 8);
-  Block expectedDigestBlock = makeBinaryBlock(tlv::KeyDigest, digestOctets, 8);
+  std::string digestOctets = "\x12\x34\x56\x78\x9a\xbc\xde\xf1\x23\x45";
+  ConstBufferPtr digestBuffer = make_shared<Buffer>(digestOctets.c_str(), digestOctets.size());
+  Block expectedDigestBlock = makeBinaryBlock(tlv::KeyDigest, digestOctets.c_str(), digestOctets.size());
 
   KeyLocator a;
   a.setKeyDigest(digestBuffer);
@@ -109,7 +114,7 @@
   //   printf("0x%02x, ", *it);
   // }
   static const uint8_t expected[] = {
-    0x1c, 0x0a, 0x1d, 0x08, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd
+    0x1c, 0x0c, 0x1d, 0x0a, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf1, 0x23, 0x45
   };
   BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
                                 wire.begin(), wire.end());
@@ -120,6 +125,12 @@
   BOOST_CHECK_EQUAL(b.getType(), KeyLocator::KeyLocator_KeyDigest);
   BOOST_CHECK(b.getKeyDigest() == expectedDigestBlock);
   BOOST_CHECK_THROW(b.getName(), KeyLocator::Error);
+
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(b), "KeyDigest=123456789A...");
+
+  std::string shortDigest = "\xbc\xde\xf1";
+  b.setKeyDigest(make_shared<Buffer>(shortDigest.c_str(), shortDigest.size()));
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(b), "KeyDigest=BCDEF1");
 }
 
 BOOST_AUTO_TEST_CASE(Equality)
@@ -167,6 +178,8 @@
   BOOST_CHECK_EQUAL(a == b, true);
   BOOST_CHECK_EQUAL(a != b, false);
 
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(b), "Unknown");
+
   b.setName("/N");
   BOOST_CHECK_EQUAL(a == b, false);
   BOOST_CHECK_EQUAL(a != b, true);
diff --git a/tests/unit-tests/security/v2/certificate.t.cpp b/tests/unit-tests/security/v2/certificate.t.cpp
index 8836e05..9522b55 100644
--- a/tests/unit-tests/security/v2/certificate.t.cpp
+++ b/tests/unit-tests/security/v2/certificate.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -256,6 +256,30 @@
   BOOST_CHECK_THROW(cert.getPublicKey(), Certificate::Error);
 }
 
+BOOST_AUTO_TEST_CASE(PrintCertificateInfo)
+{
+  const std::string expectedCertificateInfo = std::string(R"INFO(
+Certificate name:
+  /ndn/site1/KEY/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B
+Validity:
+  NotBefore: 20150814T223739
+  NotAfter: 20150818T223738
+Public key bits:
+  MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCeBj5HhbI0N6qFR6wDJIO1nKgF
+  OiQe64kBu+mbssMirGjj8GwCzmimxNCnBpCcqhsIHYtDmjNnRG0hoxuImpdeWcQV
+  C9ksvVEHYYKtwbjXv5vPfSTCY/OXF+v+YiW6W02Kwnq9Q4qPuPLxxWow01CMyJrf
+  7+0153pi6nZ8uwgmxwIBEQ==
+Signature Information:
+  Signature Type: SignatureSha256WithRsa
+  Key Locator: Name=/ndn/site1/KEY/ksk-2516425377094
+)INFO").substr(1);
+
+  Certificate certificate(Block(CERT, sizeof(CERT)));
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(certificate), expectedCertificateInfo);
+
+  // @todo Check output formats of other certificates
+}
+
 BOOST_AUTO_TEST_SUITE_END() // TestCertificate
 BOOST_AUTO_TEST_SUITE_END() // V2
 BOOST_AUTO_TEST_SUITE_END() // Security