Probe: make probe a token for better security

Change-Id: I079d70146b3e5c22e789d2ed754018fe562ddd6c
diff --git a/AUTHORS.md b/AUTHORS.md
index 2a0da5c..efe9a10 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -4,6 +4,7 @@
 ## The primary authors are (and/or have been):
 
 * Zhiyi Zhang            <https://zhiyi-zhang.com>
+* Yufeng Zhang            <yufeng@ucla.edu>             
 * Alexander Afanasyev    <http://lasr.cs.ucla.edu/afanasyev/index.html>
 * Lixia Zhang            <http://web.cs.ucla.edu/~lixia/>
 
diff --git a/src/ca-config.hpp b/src/ca-config.hpp
index 83bd821..e20b8b4 100644
--- a/src/ca-config.hpp
+++ b/src/ca-config.hpp
@@ -33,7 +33,7 @@
  *
  * The function should throw exceptions when there is an unexpected input.
  */
-using ProbeHandler = function<std::string/*identity name*/ (const std::string&/*requester input*/)>;
+using ProbeHandler = function<std::string/*identity name*/ (const JsonSection& json/*requester input*/)>;
 
 /**
  * @brief The function would be invoked whenever the certificate request status gets update
diff --git a/src/ca-module.cpp b/src/ca-module.cpp
index 7a575a9..b91a03c 100644
--- a/src/ca-module.cpp
+++ b/src/ca-module.cpp
@@ -127,10 +127,12 @@
     // if not a PROBE INFO, find an available name
     std::string availableId = "";
     const auto& parameterJson = jsonFromBlock(request.getApplicationParameters());
-    std::string probeInfoStr = parameterJson.get(JSON_CLIENT_PROBE_INFO, "");
+    //m_config.m_probe
+
+    //std::string probeInfoStr = parameterJson.get(JSON_CLIENT_PROBE_INFO, "");
     if (m_config.m_probeHandler) {
       try {
-        availableId = m_config.m_probeHandler(probeInfoStr);
+        availableId = m_config.m_probeHandler(parameterJson);
       }
       catch (const std::exception& e) {
         _LOG_TRACE("Cannot find PROBE input from PROBE parameters " << e.what());
@@ -144,7 +146,7 @@
     Name newIdentityName = m_config.m_caName;
     _LOG_TRACE("Handle PROBE: generate an identity " << newIdentityName);
     newIdentityName.append(availableId);
-    contentJson = genProbeResponseJson(newIdentityName.toUri());
+    contentJson = genProbeResponseJson(newIdentityName.toUri(), m_config.m_probe, parameterJson);
   }
 
   Data result;
@@ -404,14 +406,28 @@
  *
  * PROBE response JSON format:
  * {
- *   "name": "@p identifier",
- *   "ca-config": "@p caInformation"
+ *   "name": "@p identifier"
  * }
  */
 const JsonSection
-CaModule::genProbeResponseJson(const Name& identifier)
+CaModule::genProbeResponseJson(const Name& identifier, const std::string& m_probe, const JsonSection& parameterJson)
 {
+    std::vector<std::string> fields;
+    std::string delimiter = ":";
+    size_t last = 0;
+    size_t next = 0;
+    while ((next = m_probe.find(delimiter, last)) != std::string::npos) {
+      fields.push_back(m_probe.substr(last, next - last));
+      last = next + 1;
+    }
+    fields.push_back(m_probe.substr(last));
+
   JsonSection root;
+
+  for (size_t i = 0; i < fields.size(); ++i) {
+      root.put(fields.at(i), parameterJson.get(fields.at(i), ""));
+  }
+
   root.put(JSON_CA_NAME, identifier.toUri());
   return root;
 }
diff --git a/src/ca-module.hpp b/src/ca-module.hpp
index 47e30c9..9f40c24 100644
--- a/src/ca-module.hpp
+++ b/src/ca-module.hpp
@@ -97,7 +97,9 @@
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   const JsonSection
-  genProbeResponseJson(const Name& identifier);
+  genProbeResponseJson(const Name& identifier,
+                       const std::string& m_probe,
+                       const JsonSection& parameterJson);
 
   const JsonSection
   genProbeResponseJson();
diff --git a/src/client-config.hpp b/src/client-config.hpp
index b3aa0fc..ddbdc74 100644
--- a/src/client-config.hpp
+++ b/src/client-config.hpp
@@ -39,7 +39,7 @@
   // A brief introduction to the CA. Extracted from config field "ca-info"
   std::string m_caInfo;
   // An instruction for requesters to use _PROBE. Extracted from config field "probe"
-  std::string m_probe;
+  std::string m_probe; // "email::uid::name"
 
   // CA's certificate
   security::v2::Certificate m_anchor;
diff --git a/src/client-module.cpp b/src/client-module.cpp
index 5b03a23..29898bc 100644
--- a/src/client-module.cpp
+++ b/src/client-module.cpp
@@ -89,7 +89,7 @@
   auto interest = make_shared<Interest>(interestName);
   interest->setMustBeFresh(true);
   interest->setCanBePrefix(false);
-  auto paramJson = genProbeRequestJson(probeInfo);
+  auto paramJson = genProbeRequestJson(ca, probeInfo);
   interest->setApplicationParameters(paramFromJson(paramJson));
 
   // update local state
@@ -305,10 +305,38 @@
 }
 
 const JsonSection
-ClientModule::genProbeRequestJson(const std::string& probeInfo)
+ClientModule::genProbeRequestJson(const ClientCaItem& ca, const std::string& probeInfo)
 {
+  std::string delimiter = ":";
+  size_t last = 0;
+  size_t next = 0;
+
   JsonSection root;
-  root.put(JSON_CLIENT_PROBE_INFO, probeInfo);
+
+  std::vector<std::string> fields;
+  while ((next = ca.m_probe.find(delimiter, last)) != std::string::npos) {
+    fields.push_back(ca.m_probe.substr(last, next - last));
+    last = next + 1;
+  }
+  fields.push_back(ca.m_probe.substr(last));
+
+  std::vector<std::string> arguments;
+  last = 0;
+  next = 0;
+  while ((next = probeInfo.find(delimiter, last)) != std::string::npos) {
+    arguments.push_back(probeInfo.substr(last, next - last));
+    last = next + 1;
+  }
+  arguments.push_back(probeInfo.substr(last));
+
+  if (arguments.size() != fields.size()) {
+    BOOST_THROW_EXCEPTION(Error("Error in genProbeRequestJson: argument list does not match field list in the config file."));
+  }
+
+  for (size_t i = 0; i < fields.size(); ++i) {
+      root.put(fields.at(i), arguments.at(i));
+  }
+
   return root;
 }
 
diff --git a/src/client-module.hpp b/src/client-module.hpp
index bbf124c..30cd333 100644
--- a/src/client-module.hpp
+++ b/src/client-module.hpp
@@ -121,7 +121,7 @@
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   const JsonSection
-  genProbeRequestJson(const std::string& probeInfo);
+  genProbeRequestJson(const ClientCaItem& ca, const std::string& probeInfo);
 
   const JsonSection
   genNewRequestJson(const std::string& ecdhPub, const security::v2::Certificate& certRequest);
diff --git a/tests/unit-tests/ca-module.t.cpp b/tests/unit-tests/ca-module.t.cpp
index 34e2721..45600b8 100644
--- a/tests/unit-tests/ca-module.t.cpp
+++ b/tests/unit-tests/ca-module.t.cpp
@@ -61,8 +61,8 @@
 
   util::DummyClientFace face(m_io, {true, true});
   CaModule ca(face, m_keyChain, "tests/unit-tests/ca.conf.test");
-  ca.setProbeHandler([&] (const std::string& probeInfo) {
-      return probeInfo + "example";
+  ca.setProbeHandler([&] (const JsonSection& probeInfo) {
+      return "example";
     });
   advanceClocks(time::milliseconds(20), 60);
 
@@ -77,7 +77,7 @@
       count++;
       BOOST_CHECK(security::verifySignature(response, cert));
       auto contentJson = ClientModule::getJsonFromData(response);
-      BOOST_CHECK_EQUAL(contentJson.get<std::string>(JSON_CA_NAME), "/ndn/zhiyiexample");
+      BOOST_CHECK_EQUAL(contentJson.get<std::string>(JSON_CA_NAME), "/ndn/example");
     });
   face.receive(interest);
 
@@ -93,8 +93,8 @@
 
   util::DummyClientFace face(m_io, {true, true});
   CaModule ca(face, m_keyChain, "tests/unit-tests/ca.conf.test");
-  ca.setProbeHandler([&] (const std::string& probeInfo) {
-      return probeInfo + "example";
+  ca.setProbeHandler([&] (const JsonSection& probeInfo) {
+      return "example";
     });
   advanceClocks(time::milliseconds(20), 60);
 
diff --git a/tests/unit-tests/client-module.t.cpp b/tests/unit-tests/client-module.t.cpp
index da08d9b..1da13ce 100644
--- a/tests/unit-tests/client-module.t.cpp
+++ b/tests/unit-tests/client-module.t.cpp
@@ -50,16 +50,38 @@
   auto cert = key.getDefaultCertificate();
 
   ClientCaItem item;
+  item.m_probe = "email:uid:name";
   item.m_caName = Name("/site");
   item.m_anchor = cert;
   client.getClientConf().m_caItems.push_back(item);
 
-  auto firstInterest = client.generateProbeInterest(item, "zhiyi@cs.ucla.edu");
+  auto firstInterest = client.generateProbeInterest(item, "zhiyi@cs.ucla.edu:987654321:Zhiyi Zhang");
   BOOST_CHECK_EQUAL(firstInterest->getName().toUri(), "/site/CA/_PROBE");
-  BOOST_CHECK_EQUAL(CaModule::jsonFromBlock(firstInterest->getApplicationParameters()).get<std::string>(JSON_CLIENT_PROBE_INFO),
+  BOOST_CHECK_EQUAL(CaModule::jsonFromBlock(firstInterest->getApplicationParameters()).get<std::string>("email"),
                     "zhiyi@cs.ucla.edu");
 }
 
+BOOST_AUTO_TEST_CASE(genProbeRequestJson)
+{
+  ClientModule client(m_keyChain);
+  client.getClientConf().load("tests/unit-tests/client.conf.test");
+
+  auto identity = addIdentity(Name("/site"));
+  auto key = identity.getDefaultKey();
+  auto cert = key.getDefaultCertificate();
+
+  ClientCaItem item;
+  item.m_probe = "email:uid:name";
+  item.m_caName = Name("/site");
+  item.m_anchor = cert;
+  client.getClientConf().m_caItems.push_back(item);
+
+  auto interestPacket = client.genProbeRequestJson(item, "yufeng@ucla.edu:123456789:Yufeng Zhang");
+  BOOST_CHECK_EQUAL(interestPacket.get("email", ""), "yufeng@ucla.edu");
+  BOOST_CHECK_EQUAL(interestPacket.get("uid", ""), "123456789");
+  BOOST_CHECK_EQUAL(interestPacket.get("name", ""), "Yufeng Zhang");
+}
+
 BOOST_AUTO_TEST_SUITE_END() // TestClientModule
 
 } // namespace tests