list func: update ca config format

Change-Id: I761f6c27d15a7909450a921785923ccbf9b6a597
diff --git a/ca.conf.sample b/ca.conf.sample
index 50b160c..1a7db2e 100644
--- a/ca.conf.sample
+++ b/ca.conf.sample
@@ -2,10 +2,29 @@
   "ca-list":
   [
     {
+        "ca-prefix": "/ndn",
+        "issuing-freshness": "720",
+        "validity-period": "360",
+        "ca-info": "NDN Testbed CA",
+
+        "probe": "Use the university/organization name as input",
+
+        "targeted-list": "Use your email address (edu preferred) as input",
+        "related-ca-list":
+        [
+          { "ca-prefix": "/ndn/edu/arizona" },
+          { "ca-prefix": "/ndn/edu/memphis" }
+        ],
+
+        "supported-challenges":
+        [
+            { "type": "PIN" }
+        ]
+    },
+    {
         "ca-prefix": "/example",
         "issuing-freshness": "720",
         "validity-period": "360",
-        "ca-anchor": "/example/KEY/%9A%E0%C6%C6%09%7C%92i/self/%FD%00%00%01Z%B0%2AJ%B4",
         "supported-challenges":
         [
             { "type": "PIN" }
diff --git a/src/ca-config.cpp b/src/ca-config.cpp
index 1032709..0f4edc8 100644
--- a/src/ca-config.cpp
+++ b/src/ca-config.cpp
@@ -28,37 +28,51 @@
 void
 CaConfig::load(const std::string& fileName)
 {
+  JsonSection configJson;
   try {
-    boost::property_tree::read_json(fileName, m_config);
+    boost::property_tree::read_json(fileName, configJson);
   }
   catch (const boost::property_tree::info_parser_error& error) {
     BOOST_THROW_EXCEPTION(Error("Failed to parse configuration file " + fileName +
                                 " " + error.message() + " line " + std::to_string(error.line())));
   }
 
-  if (m_config.begin() == m_config.end()) {
+  if (configJson.begin() == configJson.end()) {
     BOOST_THROW_EXCEPTION(Error("Error processing configuration file: " + fileName + " no data"));
   }
 
-  parse();
+  parse(configJson);
 }
 
 void
-CaConfig::parse()
+CaConfig::parse(const JsonSection& configJson)
 {
   m_caItems.clear();
-  auto caList = m_config.get_child("ca-list");
+  auto caList = configJson.get_child("ca-list");
   auto it = caList.begin();
   for (; it != caList.end(); it++) {
     CaItem item;
-    item.m_caName = Name(it->second.get<std::string>("ca-prefix"));
-    item.m_probe = it->second.get("probe", false);
-    item.m_freshnessPeriod = time::seconds(it->second.get<uint64_t>("issuing-freshness"));
-    item.m_validityPeriod = time::days(it->second.get<uint64_t>("validity-period"));
 
+    // essential info
+    item.m_caName = Name(it->second.get<std::string>("ca-prefix"));
+    item.m_freshnessPeriod = time::seconds(it->second.get("issuing-freshness", 720));
+    item.m_validityPeriod = time::days(it->second.get("validity-period", 360));
+
+    // optional info
+    item.m_probe = it->second.get("probe", "");
+    item.m_caInfo = it->second.get("ca-info", "");
+    item.m_targetedList = it->second.get("targeted-list", "");
+
+    // optional supported challenges
     auto challengeList = it->second.get_child("supported-challenges");
     item.m_supportedChallenges = parseChallengeList(challengeList);
-    item.m_anchor = Name(it->second.get<std::string>("ca-anchor"));
+
+    // related cas
+    auto relatedCaList = it->second.get_child_optional("related-ca-list");
+    if (relatedCaList) {
+      item.m_relatedCaList = parseRelatedCaList(*relatedCaList);
+    }
+
     m_caItems.push_back(item);
   }
 }
@@ -74,5 +88,18 @@
   return result;
 }
 
+std::list<ClientCaItem>
+CaConfig::parseRelatedCaList(const JsonSection& section)
+{
+  std::list<ClientCaItem> result;
+  auto it = section.begin();
+  for (; it != section.end(); it++) {
+    ClientCaItem item;
+    item.m_caName = Name(it->second.get<std::string>("ca-prefix"));
+    result.push_back(item);
+  }
+  return result;
+}
+
 } // namespace ndncert
 } // namespace ndn
diff --git a/src/ca-config.hpp b/src/ca-config.hpp
index 12cc396..b117ac3 100644
--- a/src/ca-config.hpp
+++ b/src/ca-config.hpp
@@ -22,6 +22,7 @@
 #define NDNCERT_CA_CONFIG_HPP
 
 #include "certificate-request.hpp"
+#include "client-config.hpp"
 #include <ndn-cxx/security/v2/certificate.hpp>
 
 namespace ndn {
@@ -30,16 +31,30 @@
 class CaItem
 {
 public:
+  // basic info
   Name m_caName;
-  bool m_probe;
+
+  // related CAs
+  std::list<ClientCaItem> m_relatedCaList;
+
+  // essential config
   time::seconds m_freshnessPeriod;
   time::days m_validityPeriod;
   std::list<std::string> m_supportedChallenges;
-  Name m_anchor;
+
+  // optional parameters
+  std::string m_probe;
+  std::string m_targetedList;
+  std::string m_caInfo;
 };
 
 /**
  * @brief Represents a CA configuration instance
+ *
+ * For CA configuration format, please refer to:
+ *   https://github.com/named-data/ndncert/wiki/Ca-Configuration-Sample
+ *
+ * @note Changes made to CaConfig won't be written back to the config
  */
 class CaConfig
 {
@@ -59,16 +74,16 @@
 
 private:
   void
-  parse();
+  parse(const JsonSection& configJson);
 
   std::list<std::string>
   parseChallengeList(const JsonSection& configSection);
 
+  std::list<ClientCaItem>
+  parseRelatedCaList(const JsonSection& section);
+
 public:
   std::list<CaItem> m_caItems;
-
-PUBLIC_WITH_TESTS_ELSE_PRIVATE:
-  JsonSection m_config;
 };
 
 } // namespace ndncert
diff --git a/src/ca-module.cpp b/src/ca-module.cpp
index 94fb2bc..904886d 100644
--- a/src/ca-module.cpp
+++ b/src/ca-module.cpp
@@ -55,30 +55,32 @@
     try {
       const RegisteredPrefixId* prefixId = m_face.registerPrefix(prefix,
         [&] (const Name& name) {
-          const InterestFilterId* filterId = m_face.setInterestFilter(Name(name).append("_PROBE"),
-                                                                      bind(&CaModule::handleProbe, this, _2, item));
-          m_interestFilterIds.push_back(filterId);
-
-          filterId = m_face.setInterestFilter(Name(name).append("_NEW"),
+          // NEW
+          const InterestFilterId* filterId = m_face.setInterestFilter(Name(name).append("_NEW"),
                                               bind(&CaModule::handleNew, this, _2, item));
           m_interestFilterIds.push_back(filterId);
-
+          // SELECT
           filterId = m_face.setInterestFilter(Name(name).append("_SELECT"),
                                               bind(&CaModule::handleSelect, this, _2, item));
           m_interestFilterIds.push_back(filterId);
-
+          // VALIDATE
           filterId = m_face.setInterestFilter(Name(name).append("_VALIDATE"),
                                               bind(&CaModule::handleValidate, this, _2, item));
           m_interestFilterIds.push_back(filterId);
-
+          // STATUS
           filterId = m_face.setInterestFilter(Name(name).append("_STATUS"),
                                               bind(&CaModule::handleStatus, this, _2, item));
           m_interestFilterIds.push_back(filterId);
-
+          // DOWNLOAD
           filterId = m_face.setInterestFilter(Name(name).append("_DOWNLOAD"),
                                               bind(&CaModule::handleDownload, this, _2, item));
           m_interestFilterIds.push_back(filterId);
-
+          // PROBE
+          if (item.m_probe != "") {
+            filterId = m_face.setInterestFilter(Name(name).append("_PROBE"),
+                                                bind(&CaModule::handleProbe, this, _2, item));
+            m_interestFilterIds.push_back(filterId);
+          }
           _LOG_TRACE("Prefix " << name << " got registered");
         },
         bind(&CaModule::onRegisterFailed, this, _2));
@@ -120,7 +122,7 @@
   Data result;
   result.setName(request.getName());
   result.setContent(dataContentFromJson(genResponseProbeJson(identityName, "")));
-  m_keyChain.sign(result, signingByCertificate(caItem.m_anchor));
+  m_keyChain.sign(result, signingByIdentity(caItem.m_caName));
   m_face.put(result);
 
   _LOG_TRACE("Handle PROBE: generate identity " << identityName);
@@ -165,7 +167,7 @@
   result.setName(request.getName());
   result.setContent(dataContentFromJson(genResponseNewJson(requestId, certRequest.getStatus(),
                                                            caItem.m_supportedChallenges)));
-  m_keyChain.sign(result, signingByCertificate(caItem.m_anchor));
+  m_keyChain.sign(result, signingByIdentity(caItem.m_caName));
   m_face.put(result);
 
   m_requestUpdateCallback(certRequest);
@@ -219,7 +221,7 @@
   Data result;
   result.setName(request.getName());
   result.setContent(dataContentFromJson(contentJson));
-  m_keyChain.sign(result, signingByCertificate(caItem.m_anchor));
+  m_keyChain.sign(result, signingByIdentity(caItem.m_caName));
   m_face.put(result);
 
   m_requestUpdateCallback(certRequest);
@@ -264,7 +266,7 @@
   Data result;
   result.setName(request.getName());
   result.setContent(dataContentFromJson(contentJson));
-  m_keyChain.sign(result, signingByCertificate(caItem.m_anchor));
+  m_keyChain.sign(result, signingByIdentity(caItem.m_caName));
   m_face.put(result);
 
   m_requestUpdateCallback(certRequest);
@@ -301,7 +303,7 @@
   Data result;
   result.setName(request.getName());
   result.setContent(dataContentFromJson(contentJson));
-  m_keyChain.sign(result, signingByCertificate(caItem.m_anchor));
+  m_keyChain.sign(result, signingByIdentity(caItem.m_caName));
   m_face.put(result);
 }
 
@@ -325,7 +327,7 @@
   Data result;
   result.setName(request.getName());
   result.setContent(signedCert.wireEncode());
-  m_keyChain.sign(result, signingByCertificate(caItem.m_anchor));
+  m_keyChain.sign(result, signingByIdentity(caItem.m_caName));
   m_face.put(result);
 }
 
@@ -342,8 +344,8 @@
   security::ValidityPeriod period(time::system_clock::now(),
                                   time::system_clock::now() + caItem.m_validityPeriod);
   signatureInfo.setValidityPeriod(period);
-  security::SigningInfo signingInfo(security::SigningInfo::SIGNER_TYPE_CERT,
-                                    caItem.m_anchor, signatureInfo);
+  security::SigningInfo signingInfo(security::SigningInfo::SIGNER_TYPE_ID,
+                                    caItem.m_caName, signatureInfo);
   newCert.setFreshnessPeriod(caItem.m_freshnessPeriod);
 
   m_keyChain.sign(newCert, signingInfo);
diff --git a/src/ca-module.hpp b/src/ca-module.hpp
index 0e768da..5388fe6 100644
--- a/src/ca-module.hpp
+++ b/src/ca-module.hpp
@@ -70,6 +70,7 @@
     return m_storage;
   }
 
+  // @Deprecated This function should be set for each CA
   void
   setProbeHandler(const ProbeHandler& handler)
   {
diff --git a/tests/unit-tests/ca-config.t.cpp b/tests/unit-tests/ca-config.t.cpp
index 532aeeb..24b8536 100644
--- a/tests/unit-tests/ca-config.t.cpp
+++ b/tests/unit-tests/ca-config.t.cpp
@@ -18,8 +18,9 @@
  * See AUTHORS.md for complete list of ndncert authors and contributors.
  */
 
-#include "identity-management-fixture.hpp"
 #include "ca-config.hpp"
+
+#include "identity-management-fixture.hpp"
 #include <ndn-cxx/security/transform/base64-encode.hpp>
 #include <ndn-cxx/security/transform/buffer-source.hpp>
 #include <ndn-cxx/security/transform/stream-sink.hpp>
@@ -34,23 +35,45 @@
 {
   CaConfig config;
   config.load("tests/unit-tests/ca.conf.test");
-  auto itemA = config.m_caItems.front();
-  BOOST_CHECK_EQUAL(itemA.m_caName.toUri(), "/ndn/edu/ucla/cs/zhiyi");
-  BOOST_CHECK(!itemA.m_probe);
-  BOOST_CHECK_EQUAL(itemA.m_freshnessPeriod, time::seconds(720));
-  BOOST_CHECK_EQUAL(itemA.m_validityPeriod, time::days(360));
-  BOOST_CHECK_EQUAL(itemA.m_anchor.toUri(),
-                    "/ndn/edu/ucla/cs/zhiyi/KEY/%9A%E0%C6%C6%09%7C%92i/self/%FD%00%00%01Z%B0%2AJ%B4");
-  BOOST_CHECK_EQUAL(itemA.m_supportedChallenges.size(), 1);
 
-  auto itemB = config.m_caItems.back();
-  BOOST_CHECK_EQUAL(itemB.m_caName.toUri(), "/ndn/site1");
-  BOOST_CHECK(itemB.m_probe);
-  BOOST_CHECK_EQUAL(itemB.m_freshnessPeriod, time::seconds(720));
-  BOOST_CHECK_EQUAL(itemB.m_validityPeriod, time::days(360));
-  BOOST_CHECK_EQUAL(itemB.m_anchor.toUri(),
-                    "/ndn/site1/KEY/%11%BC%22%F4c%15%FF%17/self/%FD%00%00%01Y%C8%14%D9%A5");
-  BOOST_CHECK_EQUAL(itemB.m_supportedChallenges.size(), 1);
+  int count = 0;
+  for (auto item : config.m_caItems) {
+    if (item.m_caName.toUri() == "/ndn") {
+      BOOST_CHECK_EQUAL(item.m_freshnessPeriod, time::seconds(720));
+      BOOST_CHECK_EQUAL(item.m_validityPeriod, time::days(360));
+      BOOST_CHECK_EQUAL(item.m_probe, "input email address");
+      BOOST_CHECK_EQUAL(item.m_caInfo, "ndn testbed ca");
+      BOOST_CHECK_EQUAL(item.m_targetedList,
+                        "Use your email address (edu preferred) as input");
+      BOOST_CHECK_EQUAL(item.m_relatedCaList.size(), 2);
+
+      // check related ca
+      auto relatedCaA = item.m_relatedCaList.front();
+      BOOST_CHECK_EQUAL(relatedCaA.m_caName.toUri(), "/ndn/edu/arizona");
+      auto relatedCaB = item.m_relatedCaList.back();
+      BOOST_CHECK_EQUAL(relatedCaB.m_caName.toUri(), "/ndn/edu/memphis");
+
+      BOOST_CHECK_EQUAL(count, 0);
+      count++;
+    }
+    else if (item.m_caName.toUri() == "/ndn/edu/ucla/cs/zhiyi") {
+      BOOST_CHECK_EQUAL(item.m_probe, "");
+      BOOST_CHECK_EQUAL(item.m_freshnessPeriod, time::seconds(720));
+      BOOST_CHECK_EQUAL(item.m_validityPeriod, time::days(360));
+      BOOST_CHECK_EQUAL(item.m_supportedChallenges.size(), 1);
+
+      BOOST_CHECK_EQUAL(count, 1);
+      count++;
+    }
+    else if (item.m_caName.toUri() == "/ndn/site1") {
+      BOOST_CHECK(item.m_probe != "");
+      BOOST_CHECK_EQUAL(item.m_freshnessPeriod, time::seconds(720));
+      BOOST_CHECK_EQUAL(item.m_validityPeriod, time::days(360));
+      BOOST_CHECK_EQUAL(item.m_supportedChallenges.size(), 1);
+
+      BOOST_CHECK_EQUAL(count, 2);
+    }
+  }
 }
 
 BOOST_AUTO_TEST_SUITE_END() // TestCaConfig
diff --git a/tests/unit-tests/ca-module.t.cpp b/tests/unit-tests/ca-module.t.cpp
index 94d636d..fbd7358 100644
--- a/tests/unit-tests/ca-module.t.cpp
+++ b/tests/unit-tests/ca-module.t.cpp
@@ -18,8 +18,9 @@
  * See AUTHORS.md for complete list of ndncert authors and contributors.
  */
 
-#include "database-fixture.hpp"
 #include "ca-module.hpp"
+
+#include "database-fixture.hpp"
 #include "client-module.hpp"
 #include "challenge-module.hpp"
 #include <ndn-cxx/util/dummy-client-face.hpp>
@@ -37,7 +38,7 @@
 {
   util::DummyClientFace face(m_io, {true, true});
   CaModule ca(face, m_keyChain, "tests/unit-tests/ca.conf.test");
-  BOOST_CHECK_EQUAL(ca.getCaConf().m_caItems.front().m_caName.toUri(), "/ndn/edu/ucla/cs/zhiyi");
+  BOOST_CHECK_EQUAL(ca.getCaConf().m_caItems.front().m_caName.toUri(), "/ndn");
   BOOST_CHECK_EQUAL(ca.getCaConf().m_caItems.back().m_caName.toUri(), "/ndn/site1");
 
   auto identity = addIdentity(Name("/ndn/site2"));
@@ -47,8 +48,8 @@
   BOOST_CHECK_EQUAL(ca.getCaStorage()->getCertificate("111").getIdentity(), Name("/ndn/site2"));
 
   advanceClocks(time::milliseconds(20), 60);
-  BOOST_CHECK_EQUAL(ca.m_registeredPrefixIds.size(), 2);
-  BOOST_CHECK_EQUAL(ca.m_interestFilterIds.size(), 12);
+  BOOST_CHECK_EQUAL(ca.m_registeredPrefixIds.size(), 3);
+  BOOST_CHECK_EQUAL(ca.m_interestFilterIds.size(), 17);
 }
 
 BOOST_AUTO_TEST_CASE(HandleProbe)
@@ -62,7 +63,6 @@
   ca.setProbeHandler([&] (const std::string& probeInfo) {
       return probeInfo;
     });
-  ca.getCaConf().m_caItems.back().m_anchor = cert.getName();
 
   advanceClocks(time::milliseconds(20), 60);
 
@@ -91,7 +91,6 @@
 
   util::DummyClientFace face(m_io, {true, true});
   CaModule ca(face, m_keyChain, "tests/unit-tests/ca.conf.test");
-  ca.getCaConf().m_caItems.back().m_anchor = cert.getName();
 
   advanceClocks(time::milliseconds(20), 60);
 
@@ -125,7 +124,6 @@
   ca.setProbeHandler([&] (const std::string& probeInfo) {
       return probeInfo;
     });
-  ca.getCaConf().m_caItems.back().m_anchor = cert.getName();
   advanceClocks(time::milliseconds(20), 60);
 
   Name identityName("/ndn/site1");
diff --git a/tests/unit-tests/ca.conf.test b/tests/unit-tests/ca.conf.test
index 87db072..5809613 100644
--- a/tests/unit-tests/ca.conf.test
+++ b/tests/unit-tests/ca.conf.test
@@ -2,10 +2,29 @@
   "ca-list":
   [
     {
+        "ca-prefix": "/ndn",
+        "issuing-freshness": "720",
+        "validity-period": "360",
+        "ca-info": "ndn testbed ca",
+
+        "probe": "input email address",
+
+        "targeted-list": "Use your email address (edu preferred) as input",
+        "related-ca-list":
+        [
+          { "ca-prefix": "/ndn/edu/arizona" },
+          { "ca-prefix": "/ndn/edu/memphis" }
+        ],
+
+        "supported-challenges":
+        [
+            { "type": "PIN" }
+        ]
+    },
+    {
         "ca-prefix": "/ndn/edu/ucla/cs/zhiyi",
         "issuing-freshness": "720",
         "validity-period": "360",
-        "ca-anchor": "/ndn/edu/ucla/cs/zhiyi/KEY/%9A%E0%C6%C6%09%7C%92i/self/%FD%00%00%01Z%B0%2AJ%B4",
         "supported-challenges":
         [
             { "type": "PIN" }
@@ -13,10 +32,9 @@
     },
     {
         "ca-prefix": "/ndn/site1",
-        "probe": "true",
+        "probe": "input email address",
         "issuing-freshness": "720",
         "validity-period": "360",
-        "ca-anchor": "/ndn/site1/KEY/%11%BC%22%F4c%15%FF%17/self/%FD%00%00%01Y%C8%14%D9%A5",
         "supported-challenges":
         [
             { "type": "PIN" }