security: string representation for SigningInfo

Change-Id: I92b227a0d0cb89654536043f703cea9e7b40fa64
refs: #3281
diff --git a/src/security/signing-info.cpp b/src/security/signing-info.cpp
index c231cc5..283484b 100644
--- a/src/security/signing-info.cpp
+++ b/src/security/signing-info.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "signing-info.hpp"
+#include "key-chain.hpp"
 
 namespace ndn {
 namespace security {
@@ -37,6 +38,41 @@
 {
 }
 
+SigningInfo::SigningInfo(const std::string& signingStr)
+{
+  if (signingStr.empty()) {
+    *this = SigningInfo();
+    return;
+  }
+
+  size_t pos = signingStr.find(':');
+
+  if (pos == std::string::npos) {
+    BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid signing string cannot represent SigningInfo"));
+  }
+
+  std::string scheme = signingStr.substr(0, pos);
+  std::string nameArg = signingStr.substr(pos + 1);
+
+  if (scheme == "id") {
+    if (nameArg == KeyChain::DIGEST_SHA256_IDENTITY.toUri()) {
+      setSha256Signing();
+    }
+    else {
+      setSigningIdentity(nameArg);
+    }
+  }
+  else if (scheme == "key") {
+    setSigningKeyName(nameArg);
+  }
+  else if (scheme == "cert") {
+    setSigningCertName(nameArg);
+  }
+  else {
+    BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid signing string scheme"));
+  }
+}
+
 void
 SigningInfo::setSigningIdentity(const Name& identity)
 {
@@ -70,5 +106,31 @@
   m_info = signatureInfo;
 }
 
-} // namespace ndn
+std::ostream&
+operator<<(std::ostream& os, const SigningInfo& si)
+{
+  switch (si.getSignerType()) {
+    case SigningInfo::SIGNER_TYPE_NULL:
+      return os;
+    case SigningInfo::SIGNER_TYPE_ID:
+      os << "id:";
+      break;
+    case SigningInfo::SIGNER_TYPE_KEY:
+      os << "key:";
+      break;
+    case SigningInfo::SIGNER_TYPE_CERT:
+      os << "cert:";
+      break;
+    case SigningInfo::SIGNER_TYPE_SHA256:
+      os << "id:" << KeyChain::DIGEST_SHA256_IDENTITY;
+      return os;
+    default:
+      BOOST_THROW_EXCEPTION(std::invalid_argument("Unknown signer type"));
+  }
+
+  os << si.getSignerName();
+  return os;
+}
+
 } // namespace security
+} // namespace ndn
diff --git a/src/security/signing-info.hpp b/src/security/signing-info.hpp
index 661276f..aaa2770 100644
--- a/src/security/signing-info.hpp
+++ b/src/security/signing-info.hpp
@@ -75,6 +75,21 @@
               const SignatureInfo& signatureInfo = EMPTY_SIGNATURE_INFO);
 
   /**
+   * @brief Construct SigningInfo from its string representation
+   *
+   * @param signingStr The representative signing string for SigningInfo signing method
+   *
+   * Structure of the representative string is as follows:
+   * - default signing: "" (empty string)
+   * - signing with a default certificate of a default key for the identity: `id:/my-identity`
+   * - signing with a default certificate of the key: `key:/my-identity/ksk-1`
+   * - signing with the certificate: `cert:/my-identity/KEY/ksk-1/ID-CERT/%FD%01`
+   * - signing with sha256 digest: `id:/localhost/identity/digest-sha256`
+   */
+  explicit
+  SigningInfo(const std::string& signingStr);
+
+  /**
    * @brief Set signer as an identity with name @p identity
    * @post Change the signerType to SIGNER_TYPE_ID
    */
@@ -166,6 +181,9 @@
   SignatureInfo m_info;
 };
 
+std::ostream&
+operator<<(std::ostream& os, const SigningInfo& si);
+
 } // namespace security
 } // namespace ndn
 
diff --git a/tests/unit-tests/security/signing-info.t.cpp b/tests/unit-tests/security/signing-info.t.cpp
index d3311be..2481747 100644
--- a/tests/unit-tests/security/signing-info.t.cpp
+++ b/tests/unit-tests/security/signing-info.t.cpp
@@ -20,6 +20,10 @@
  */
 
 #include "security/signing-info.hpp"
+#include "security/key-chain.hpp"
+
+#include <sstream>
+#include <boost/lexical_cast.hpp>
 
 #include "boost-test.hpp"
 
@@ -31,9 +35,9 @@
 
 BOOST_AUTO_TEST_CASE(Basic)
 {
-  Name id("/id");
-  Name key("/key");
-  Name cert("/cert");
+  Name id("/my-identity");
+  Name key("/my-key");
+  Name cert("/my-cert");
 
   SigningInfo info;
 
@@ -93,6 +97,47 @@
   BOOST_CHECK(info2.getSignatureInfo() == si);
 }
 
+BOOST_AUTO_TEST_CASE(FromString)
+{
+  SigningInfo infoDefault("");
+  BOOST_CHECK_EQUAL(infoDefault.getSignerType(), SigningInfo::SIGNER_TYPE_NULL);
+  BOOST_CHECK_EQUAL(infoDefault.getSignerName(), SigningInfo::EMPTY_NAME);
+
+  SigningInfo infoId("id:/my-identity");
+  BOOST_CHECK_EQUAL(infoId.getSignerType(), SigningInfo::SIGNER_TYPE_ID);
+  BOOST_CHECK_EQUAL(infoId.getSignerName(), "/my-identity");
+
+  SigningInfo infoKey("key:/my-key");
+  BOOST_CHECK_EQUAL(infoKey.getSignerType(), SigningInfo::SIGNER_TYPE_KEY);
+  BOOST_CHECK_EQUAL(infoKey.getSignerName(), "/my-key");
+
+  SigningInfo infoCert("cert:/my-cert");
+  BOOST_CHECK_EQUAL(infoCert.getSignerType(), SigningInfo::SIGNER_TYPE_CERT);
+  BOOST_CHECK_EQUAL(infoCert.getSignerName(), "/my-cert");
+
+  SigningInfo infoSha("id:/localhost/identity/digest-sha256");
+  BOOST_CHECK_EQUAL(infoSha.getSignerType(), SigningInfo::SIGNER_TYPE_SHA256);
+  BOOST_CHECK_EQUAL(infoSha.getSignerName(), SigningInfo::EMPTY_NAME);
+}
+
+BOOST_AUTO_TEST_CASE(ToString)
+{
+  // We can't use lexical_cast due to Boost Bug 6298.
+  std::stringstream ss;
+  ss << SigningInfo();
+  BOOST_CHECK_EQUAL(ss.str(), "");
+
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(
+                    SigningInfo(SigningInfo::SIGNER_TYPE_ID, "/my-identity")), "id:/my-identity");
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(
+                    SigningInfo(SigningInfo::SIGNER_TYPE_KEY, "/my-key")), "key:/my-key");
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(
+                    SigningInfo(SigningInfo::SIGNER_TYPE_CERT, "/my-cert")), "cert:/my-cert");
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(
+                    SigningInfo(SigningInfo::SIGNER_TYPE_SHA256)),
+                    "id:/localhost/identity/digest-sha256");
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace tests