TLV encoding to replace JSON format message
diff --git a/src/ca-config.hpp b/src/ca-config.hpp
index 5bf4f44..4ff3fe1 100644
--- a/src/ca-config.hpp
+++ b/src/ca-config.hpp
@@ -42,7 +42,7 @@
  * @p vector, input, a list of parameter key-value pair used for name assignment.
  * @return a vector containing the possible namespaces derived from the parameters.
  */
-using ProbeHandler = function<std::string /*identity name*/ (const JsonSection& json /*requester input*/)>;
+using ProbeHandler = function<std::string /*identity name*/ (const Block& tlv /*requester input*/)>;
 using NameAssignmentFunc = function<std::vector<std::string>(const std::vector<std::tuple<std::string, std::string>>)>;
 
 /**
diff --git a/src/ca-module.cpp b/src/ca-module.cpp
index be44e11..275e1f8 100644
--- a/src/ca-module.cpp
+++ b/src/ca-module.cpp
@@ -22,7 +22,10 @@
 #include "challenge-module.hpp"
 #include "logging.hpp"
 #include "crypto-support/enc-tlv.hpp"
-#include "tlv.hpp"
+#include "protocol-detail/info.hpp"
+#include "protocol-detail/probe.hpp"
+#include "protocol-detail/new.hpp"
+#include "protocol-detail/challenge.hpp"
 #include <ndn-cxx/util/io.hpp>
 #include <ndn-cxx/security/verification-helpers.hpp>
 #include <ndn-cxx/security/signing-helpers.hpp>
@@ -120,7 +123,11 @@
 CaModule::onInfo(const Interest& request)
 {
   _LOG_TRACE("Received INFO request");
-  Block contentTLV = genInfoResponseTLV();
+
+  const auto& pib = m_keyChain.getPib();
+  const auto& identity = pib.getIdentity(m_config.m_caName);
+  const auto& cert = identity.getDefaultKey().getDefaultCertificate();
+  Block contentTLV = INFO::encodeContentFromCAConfig(m_config, cert);
   Data result;
 
   result.setName(request.getName());
@@ -138,19 +145,19 @@
 {
   // PROBE Naming Convention: /<CA-Prefix>/CA/PROBE/[ParametersSha256DigestComponent]
   _LOG_TRACE("Received PROBE request");
-  JsonSection contentJson;
 
   // process PROBE requests: find an available name
   std::string availableId;
-  const auto& parameterJson = jsonFromBlock(request.getApplicationParameters());
-  if (parameterJson.empty()) {
-    _LOG_ERROR("Empty JSON obtained from the Interest parameter.");
+  const auto& parameterTLV = request.getApplicationParameters();
+  if (!parameterTLV.hasValue()) {
+    _LOG_ERROR("Empty TLV obtained from the Interest parameter.");
     return;
   }
   //std::string probeInfoStr = parameterJson.get(JSON_CLIENT_PROBE_INFO, "");
+  // TODO: m_probeHandler is never set
   if (m_config.m_probeHandler) {
     try {
-      availableId = m_config.m_probeHandler(parameterJson);
+      availableId = m_config.m_probeHandler(parameterTLV);
     }
     catch (const std::exception& e) {
       _LOG_TRACE("Cannot find PROBE input from PROBE parameters: " << e.what());
@@ -164,11 +171,11 @@
   Name newIdentityName = m_config.m_caName;
   newIdentityName.append(availableId);
   _LOG_TRACE("Handle PROBE: generate an identity " << newIdentityName);
-  contentJson = genProbeResponseJson(newIdentityName.toUri(), m_config.m_probe, parameterJson);
+  Block contentTLV = PROBE::encodeDataContent(newIdentityName.toUri(), m_config.m_probe, parameterTLV);
 
   Data result;
   result.setName(request.getName());
-  result.setContent(dataContentFromJson(contentJson));
+  result.setContent(contentTLV);
   result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
   m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
   m_face.put(result);
@@ -180,14 +187,16 @@
 {
   // NEW Naming Convention: /<CA-prefix>/CA/NEW/[SignedInterestParameters_Digest]
   // get ECDH pub key and cert request
-  const auto& parameterJson = jsonFromBlock(request.getApplicationParameters());
-  if (parameterJson.empty()) {
-    _LOG_ERROR("Empty JSON obtained from the Interest parameter.");
+  const auto& parameterTLV = request.getApplicationParameters();
+  if (!parameterTLV.hasValue()) {
+    _LOG_ERROR("Empty TLV obtained from the Interest parameter.");
     return;
   }
-  std::string peerKeyBase64 = parameterJson.get(JSON_CLIENT_ECDH, "");
+
+  std::string peerKeyBase64 = readString(parameterTLV.get(tlv_ecdh_pub));
+
   if (peerKeyBase64 == "") {
-    _LOG_ERROR("Empty JSON_CLIENT_ECDH obtained from the Interest parameter.");
+    _LOG_ERROR("Empty ECDH PUB obtained from the Interest parameter.");
     return;
   }
 
@@ -207,11 +216,10 @@
        (uint8_t*)&saltInt, sizeof(saltInt), m_aesKey, sizeof(m_aesKey));
 
   // parse certificate request
-  std::string certRequestStr = parameterJson.get(JSON_CLIENT_CERT_REQ, "");
+  Block cert_req = parameterTLV.get(tlv_cert_request);
   shared_ptr<security::v2::Certificate> clientCert = nullptr;
   try {
-    std::stringstream ss(certRequestStr);
-    clientCert = io::load<security::v2::Certificate>(ss);
+    clientCert->wireDecode(cert_req);
   }
   catch (const std::exception& e) {
     _LOG_ERROR("Unrecognized certificate request: " << e.what());
@@ -230,34 +238,6 @@
     return;
   }
 
-  // parse probe token if any
-  std::string probeTokenStr = parameterJson.get("probe-token", "");
-  shared_ptr<Data> probeToken = nullptr;
-  if (probeTokenStr != "") {
-    try {
-      std::stringstream ss(probeTokenStr);
-      probeToken = io::load<Data>(ss);
-    }
-    catch (const std::exception& e) {
-      _LOG_ERROR("Unrecognized probe token: " << e.what());
-      return;
-    }
-  }
-  if (probeToken == nullptr && m_config.m_probe != "") {
-    // the CA requires PROBE before NEW
-    _LOG_ERROR("CA requires PROBE but no PROBE token is found in NEW Interest.");
-    return;
-  }
-  else if (probeToken != nullptr) {
-    // check whether the carried probe token is a PROBE Data packet
-    Name prefix = m_config.m_caName;
-    prefix.append("CA").append("PROBE");
-    if (!prefix.isPrefixOf(probeToken->getName())) {
-      _LOG_ERROR("Carried PROBE token is not a valid PROBE Data packet.");
-      return;
-    }
-  }
-
   // verify the self-signed certificate, the request, and the token
   if (!m_config.m_caName.isPrefixOf(clientCert->getName()) // under ca prefix
       || !security::v2::Certificate::isValidName(clientCert->getName()) // is valid cert name
@@ -273,22 +253,11 @@
     _LOG_ERROR("Interest with bad signature.");
     return;
   }
-  if (probeToken != nullptr) {
-    const auto& pib = m_keyChain.getPib();
-    const auto& key = pib.getIdentity(m_config.m_caName).getDefaultKey();
-    const auto& caCert = key.getDefaultCertificate();
-    if (!security::verifySignature(*probeToken, caCert)) {
-      _LOG_ERROR("PROBE Token with bad signature.");
-      return;
-    }
-  }
 
   // create new request instance
   std::string requestId = std::to_string(random::generateWord64());
   CertificateRequest certRequest(m_config.m_caName, requestId, STATUS_BEFORE_CHALLENGE, *clientCert);
-  if (probeToken != nullptr) {
-    certRequest.setProbeToken(probeToken);
-  }
+
   try {
     m_storage->addRequest(certRequest);
   }
@@ -300,10 +269,10 @@
   Data result;
   result.setName(request.getName());
   result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
-  result.setContent(dataContentFromJson(genNewResponseJson(myEcdhPubKeyBase64,
-                                                           std::to_string(saltInt),
-                                                           certRequest,
-                                                           m_config.m_supportedChallenges)));
+  result.setContent(NEW::encodeDataContent(myEcdhPubKeyBase64,
+                                      std::to_string(saltInt),
+                                      certRequest,
+                                      m_config.m_supportedChallenges));
   m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
   m_face.put(result);
 
@@ -328,48 +297,45 @@
     return;
   }
   // decrypt the parameters
-  Buffer paramJsonPayload;
+  Buffer paramTLVPayload;
   try {
-    paramJsonPayload = decodeBlockWithAesGcm128(request.getApplicationParameters(), m_aesKey,
+    paramTLVPayload = decodeBlockWithAesGcm128(request.getApplicationParameters(), m_aesKey,
                                                 (uint8_t*)"test", strlen("test"));
   }
   catch (const std::exception& e) {
     _LOG_ERROR("Cannot successfully decrypt the Interest parameters: " << e.what());
     return;
   }
-  if (paramJsonPayload.size() == 0) {
+  if (paramTLVPayload.size() == 0) {
     _LOG_ERROR("Got an empty buffer from content decryption.");
     return;
   }
-  std::string paramJsonStr((const char*)paramJsonPayload.data(), paramJsonPayload.size());
-  std::istringstream ss(paramJsonStr);
-  JsonSection paramJson;
-  try {
-    boost::property_tree::json_parser::read_json(ss, paramJson);
-  }
-  catch (const std::exception& e) {
-    _LOG_ERROR("Cannot read JSON from decrypted content: " << e.what());
-    return;
-  }
+
+  // TODO: any simpler method?
+  bool isSucess;
+  Block paramTLV;
+  std::tie<bool, Block>(isSucess, paramTLV) =  Block::fromBuffer(paramTLVPayload.data(), paramTLVPayload.size());
 
   // load the corresponding challenge module
-  std::string challengeType = paramJson.get(JSON_CLIENT_SELECTED_CHALLENGE, "");
+  std::string challengeType = readString(paramTLV.get(tlv_selected_challenge));
   auto challenge = ChallengeModule::createChallengeModule(challengeType);
-  JsonSection contentJson;
+
+  Block contentTLV;
+
   if (challenge == nullptr) {
     _LOG_TRACE("Unrecognized challenge type " << challengeType);
     certRequest.m_status = STATUS_FAILURE;
     certRequest.m_challengeStatus = CHALLENGE_STATUS_UNKNOWN_CHALLENGE;
-    contentJson = genChallengeResponseJson(certRequest);
+    contentTLV = CHALLENGE::encodeDataPayload(certRequest);
   }
   else {
     _LOG_TRACE("CHALLENGE module to be load: " << challengeType);
     // let challenge module handle the request
-    challenge->handleChallengeRequest(paramJson, certRequest);
+    challenge->handleChallengeRequest(paramTLV, certRequest);
     if (certRequest.m_status == STATUS_FAILURE) {
       // if challenge failed
       m_storage->deleteRequest(certRequest.m_requestId);
-      contentJson = genChallengeResponseJson(certRequest);
+      contentTLV = CHALLENGE::encodeDataPayload(certRequest);
       _LOG_TRACE("Challenge failed");
     }
     else if (certRequest.m_status == STATUS_PENDING) {
@@ -389,9 +355,12 @@
       if (m_config.m_statusUpdateCallback) {
         m_config.m_statusUpdateCallback(certRequest);
       }
-      contentJson = genChallengeResponseJson(certRequest);
-      contentJson.add(JSON_CA_CERT_ID, readString(issuedCert.getName().at(-1)));
-      contentJson.add(JSON_CHALLENGE_ISSUED_CERT_NAME, issuedCert.getName().toUri());
+
+      contentTLV = CHALLENGE::encodeDataPayload(certRequest);
+      contentTLV.push_back(makeNestedBlock(tlv_issued_cert_name, issuedCert.getName()));
+      contentTLV.parse();
+
+      //contentJson.add(JSON_CA_CERT_ID, readString(issuedCert.getName().at(-1)));
       _LOG_TRACE("Challenge succeeded. Certificate has been issued");
     }
     else {
@@ -402,7 +371,7 @@
         _LOG_TRACE("Cannot update request instance: " << e.what());
         return;
       }
-      contentJson = genChallengeResponseJson(certRequest);
+      contentTLV = CHALLENGE::encodeDataPayload(certRequest);
       _LOG_TRACE("No failure no success. Challenge moves on");
     }
   }
@@ -412,11 +381,9 @@
   result.setFreshnessPeriod(DEFAULT_DATA_FRESHNESS_PERIOD);
 
   // encrypt the content
-  std::stringstream ss2;
-  boost::property_tree::write_json(ss2, contentJson);
-  auto payload = ss2.str();
-  auto contentBlock = encodeBlockWithAesGcm128(tlv::Content, m_aesKey, (const uint8_t*)payload.c_str(),
-                                               payload.size(), (uint8_t*)"test", strlen("test"));
+  auto payload = contentTLV.getBuffer();
+  auto contentBlock = encodeBlockWithAesGcm128(tlv::Content, m_aesKey, payload->data(),
+                                               payload->size(), (uint8_t*)"test", strlen("test"));
   result.setContent(contentBlock);
   m_keyChain.sign(result, signingByIdentity(m_config.m_caName));
   m_face.put(result);
@@ -552,46 +519,6 @@
   return root;
 }
 
-Block
-CaModule::genInfoResponseTLV()
-{
-  Block response;
-  // ca-prefix
-  Name caName = m_config.m_caName;
-  // response = makeStringBlock(CAPrefix, caName.toUri());
-  response = makeNestedBlock(CAPrefix, caName);
-
-  // ca-info
-  const auto& pib = m_keyChain.getPib();
-  const auto& identity = pib.getIdentity(m_config.m_caName);
-  const auto& cert = identity.getDefaultKey().getDefaultCertificate();
-  std::string caInfo = "";
-  if (m_config.m_caInfo == "") {
-    caInfo = "Issued by " + cert.getSignature().getKeyLocator().getName().toUri();
-  }
-  else {
-    caInfo = m_config.m_caInfo;
-  }
-
-  response.push_back(makeStringBlock(CAInfo, caInfo));
-
-
-  // parameter-key (Not implemented yet)
-  for() {
-    response.push_back(makeStringBlock(ParameterKey, ""));
-  }
-
-  // TODO: need to convert from days to seconds
-  response.push_back(makeNonNegativeIntegerBlock(MaxValidityPeriod, m_validityPeriod));
-
-  // certificate
-  response.push_back(makeNestedBlock(CACertificate, cert));
-  response.parse();
-
-  return response;
-}
-
-
 JsonSection
 CaModule::genNewResponseJson(const std::string& ecdhKey, const std::string& salt,
                              const CertificateRequest& request,
diff --git a/src/challenge-module.hpp b/src/challenge-module.hpp
index 80670f4..4cc5fa9 100644
--- a/src/challenge-module.hpp
+++ b/src/challenge-module.hpp
@@ -66,7 +66,7 @@
 
   // For CA
   virtual void
-  handleChallengeRequest(const JsonSection& params, CertificateRequest& request) = 0;
+  handleChallengeRequest(const Block& params, CertificateRequest& request) = 0;
 
   // For Client
   virtual JsonSection
@@ -75,6 +75,9 @@
   virtual JsonSection
   genChallengeRequestJson(int status, const std::string& challengeStatus, const JsonSection& params) = 0;
 
+  virtual Block
+  genChallengeRequestTLV(int status, const std::string& challengeStatus, const JsonSection& params) = 0;
+
   // helpers
   static std::string
   generateSecretCode();
diff --git a/src/challenge-module/challenge-credential.cpp b/src/challenge-module/challenge-credential.cpp
index 69a6bbe..a4ca557 100644
--- a/src/challenge-module/challenge-credential.cpp
+++ b/src/challenge-module/challenge-credential.cpp
@@ -78,13 +78,14 @@
 
 // For CA
 void
-ChallengeCredential::handleChallengeRequest(const JsonSection& params, CertificateRequest& request)
+ChallengeCredential::handleChallengeRequest(const Block& params, CertificateRequest& request)
 {
   if (m_trustAnchors.empty()) {
     parseConfigFile();
   }
   // load credential parameter
-  std::istringstream ss1(params.get(JSON_CREDENTIAL_CERT, ""));
+  // TODO: instead of string, should pass certificate byte value directly
+  std::istringstream ss1(readString(params.elements().at(1)));
   auto cert = io::load<security::v2::Certificate>(ss1);
   if (cert == nullptr) {
     _LOG_ERROR("Cannot load credential parameter: cert");
@@ -95,8 +96,9 @@
   }
   ss1.str("");
   ss1.clear();
+
   // load self-signed data
-  std::istringstream ss2(params.get(JSON_CREDENTIAL_SELF, ""));
+  std::istringstream ss2(readString(params.elements().at(3)));
   auto self = io::load<Data>(ss2);
   if (self == nullptr) {
     _LOG_TRACE("Cannot load credential parameter: self-signed cert");
@@ -158,5 +160,24 @@
   return result;
 }
 
+Block
+ChallengeCredential::genChallengeRequestTLV(int status, const std::string& challengeStatus, const JsonSection& params)
+{
+  Block request = makeEmptyBlock(tlv_encrypted_payload);
+  if (status == STATUS_BEFORE_CHALLENGE && challengeStatus == "") {
+    request.push_back(makeStringBlock(tlv_selected_challenge, CHALLENGE_TYPE));
+    request.push_back(makeStringBlock(tlv_parameter_key, JSON_CREDENTIAL_CERT));
+    request.push_back(makeStringBlock(tlv_parameter_value, params.get(JSON_CREDENTIAL_CERT,"")));
+    request.push_back(makeStringBlock(tlv_parameter_key, JSON_CREDENTIAL_SELF));
+    request.push_back(makeStringBlock(tlv_parameter_value, params.get(JSON_CREDENTIAL_SELF,"")));
+  }
+  else {
+    _LOG_ERROR("Client's status and challenge status are wrong");
+  }
+  request.parse();
+  return request;
+}
+
+
 } // namespace ndncert
 } // namespace ndn
diff --git a/src/challenge-module/challenge-credential.hpp b/src/challenge-module/challenge-credential.hpp
index 5d4a93c..bf53175 100644
--- a/src/challenge-module/challenge-credential.hpp
+++ b/src/challenge-module/challenge-credential.hpp
@@ -53,7 +53,7 @@
 
   // For CA
   void
-  handleChallengeRequest(const JsonSection& params, CertificateRequest& request) override;
+  handleChallengeRequest(const Block& params, CertificateRequest& request) override;
 
   // For Client
   JsonSection
@@ -62,6 +62,9 @@
   JsonSection
   genChallengeRequestJson(int status, const std::string& challengeStatus, const JsonSection& params) override;
 
+  Block
+  genChallengeRequestTLV(int status, const std::string& challengeStatus, const JsonSection& params) override;
+
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   void
   parseConfigFile();
diff --git a/src/challenge-module/challenge-email.cpp b/src/challenge-module/challenge-email.cpp
index b9ca344..db069c8 100644
--- a/src/challenge-module/challenge-email.cpp
+++ b/src/challenge-module/challenge-email.cpp
@@ -48,12 +48,12 @@
 
 // For CA
 void
-ChallengeEmail::handleChallengeRequest(const JsonSection& params, CertificateRequest& request)
+ChallengeEmail::handleChallengeRequest(const Block& params, CertificateRequest& request)
 {
   auto currentTime = time::system_clock::now();
   if (request.m_challengeStatus == "") {
     // for the first time, init the challenge
-    std::string emailAddress = params.get(JSON_EMAIL, "");
+    std::string emailAddress = readString(params.get(tlv_parameter_value));
     if (!isValidEmailAddress(emailAddress)) {
       request.m_status = STATUS_FAILURE;
       request.m_challengeStatus = FAILURE_INVALID_EMAIL;
@@ -91,7 +91,7 @@
   else if (request.m_challengeStatus == NEED_CODE || request.m_challengeStatus == WRONG_CODE) {
     _LOG_TRACE("Challenge Interest arrives. Challenge Status: " << request.m_challengeStatus);
     // the incoming interest should bring the pin code
-    std::string givenCode = params.get(JSON_CODE, "");
+    std::string givenCode = readString(params.get(tlv_parameter_value));
     const auto realCode = request.m_challengeSecrets.get<std::string>(JSON_CODE);
     if (currentTime - time::fromIsoString(request.m_challengeTp) >= m_secretLifetime) {
       // secret expires
@@ -178,6 +178,32 @@
   return result;
 }
 
+Block
+ChallengeEmail::genChallengeRequestTLV(int status, const std::string& challengeStatus, const JsonSection& params)
+{
+  Block request = makeEmptyBlock(tlv_encrypted_payload);
+  if (status == STATUS_BEFORE_CHALLENGE && challengeStatus == "") {
+    request.push_back(makeStringBlock(tlv_selected_challenge, CHALLENGE_TYPE));
+    request.push_back(makeStringBlock(tlv_parameter_key, JSON_EMAIL));
+    request.push_back(makeStringBlock(tlv_parameter_value, params.get(JSON_EMAIL,"")));
+  }
+  else if (status == STATUS_CHALLENGE && challengeStatus == NEED_CODE) {
+    request.push_back(makeStringBlock(tlv_selected_challenge, CHALLENGE_TYPE));
+    request.push_back(makeStringBlock(tlv_parameter_key, JSON_CODE));
+    request.push_back(makeStringBlock(tlv_parameter_value, params.get(JSON_CODE,"")));
+  }
+  else if (status == STATUS_CHALLENGE && challengeStatus == WRONG_CODE) {
+    request.push_back(makeStringBlock(tlv_selected_challenge, CHALLENGE_TYPE));
+    request.push_back(makeStringBlock(tlv_parameter_key, JSON_CODE));
+    request.push_back(makeStringBlock(tlv_parameter_value, params.get(JSON_CODE,"")));
+  }
+  else {
+    _LOG_ERROR("Client's status and challenge status are wrong");
+  }
+  request.parse();
+  return request;
+}
+
 bool
 ChallengeEmail::isValidEmailAddress(const std::string& emailAddress)
 {
diff --git a/src/challenge-module/challenge-email.hpp b/src/challenge-module/challenge-email.hpp
index 2f40f1f..4421bd3 100644
--- a/src/challenge-module/challenge-email.hpp
+++ b/src/challenge-module/challenge-email.hpp
@@ -23,6 +23,7 @@
 
 #include "../challenge-module.hpp"
 #include <ndn-cxx/util/time.hpp>
+#include <ndn-cxx/encoding/block.hpp>
 
 namespace ndn {
 namespace ndncert {
@@ -58,7 +59,7 @@
 
   // For CA
   void
-  handleChallengeRequest(const JsonSection& params, CertificateRequest& request) override;
+  handleChallengeRequest(const Block& params, CertificateRequest& request) override;
 
   // For Client
   JsonSection
@@ -67,6 +68,9 @@
   JsonSection
   genChallengeRequestJson(int status, const std::string& challengeStatus, const JsonSection& params) override;
 
+  Block
+  genChallengeRequestTLV(int status, const std::string& challengeStatus, const JsonSection& params) override;
+
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   static bool
   isValidEmailAddress(const std::string& emailAddress);
diff --git a/src/challenge-module/challenge-pin.cpp b/src/challenge-module/challenge-pin.cpp
index 87f36cc..f31ca77 100644
--- a/src/challenge-module/challenge-pin.cpp
+++ b/src/challenge-module/challenge-pin.cpp
@@ -42,7 +42,7 @@
 
 // For CA
 void
-ChallengePin::handleChallengeRequest(const JsonSection& params, CertificateRequest& request)
+ChallengePin::handleChallengeRequest(const Block& params, CertificateRequest& request)
 {
   auto currentTime = time::system_clock::now();
   if (request.m_challengeStatus == "") {
@@ -64,7 +64,7 @@
   else if (request.m_challengeStatus == NEED_CODE || request.m_challengeStatus == WRONG_CODE) {
     _LOG_TRACE("Challenge Interest arrives. Challenge Status: " << request.m_challengeStatus);
     // the incoming interest should bring the pin code
-    std::string givenCode = params.get(JSON_PIN_CODE, "");
+    std::string givenCode = readString(params.get(tlv_parameter_value));
     const auto realCode = request.m_challengeSecrets.get<std::string>(JSON_PIN_CODE);
     if (currentTime - time::fromIsoString(request.m_challengeTp) >= m_secretLifetime) {
       // secret expires
@@ -151,5 +151,29 @@
   return result;
 }
 
+Block
+ChallengePin::genChallengeRequestTLV(int status, const std::string& challengeStatus, const JsonSection& params)
+{
+  Block request = makeEmptyBlock(tlv_encrypted_payload);
+  if (status == STATUS_BEFORE_CHALLENGE && challengeStatus == "") {
+    // do nothing
+    request.push_back(makeStringBlock(tlv_selected_challenge, CHALLENGE_TYPE));
+  }
+  else if (status == STATUS_CHALLENGE && challengeStatus == NEED_CODE) {
+    request.push_back(makeStringBlock(tlv_selected_challenge, CHALLENGE_TYPE));
+    request.push_back(makeStringBlock(tlv_parameter_key, JSON_PIN_CODE));
+    request.push_back(makeStringBlock(tlv_parameter_value, params.get(JSON_PIN_CODE,"")));
+  }
+  else if (status == STATUS_CHALLENGE && challengeStatus == WRONG_CODE) {
+    request.push_back(makeStringBlock(tlv_selected_challenge, CHALLENGE_TYPE));
+    request.push_back(makeStringBlock(tlv_parameter_key, JSON_PIN_CODE));
+    request.push_back(makeStringBlock(tlv_parameter_value, params.get(JSON_PIN_CODE,"")));
+  }
+  else {
+    _LOG_ERROR("Client's status and challenge status are wrong");
+  }
+  request.parse();
+  return request;
+}
 } // namespace ndncert
 } // namespace ndn
diff --git a/src/challenge-module/challenge-pin.hpp b/src/challenge-module/challenge-pin.hpp
index 8eef944..7ae1d75 100644
--- a/src/challenge-module/challenge-pin.hpp
+++ b/src/challenge-module/challenge-pin.hpp
@@ -53,7 +53,7 @@
 
   // For CA
   void
-  handleChallengeRequest(const JsonSection& params, CertificateRequest& request) override;
+  handleChallengeRequest(const Block& params, CertificateRequest& request) override;
 
   // For Client
   JsonSection
@@ -62,6 +62,10 @@
   JsonSection
   genChallengeRequestJson(int status, const std::string& challengeStatus, const JsonSection& params) override;
 
+  Block
+  genChallengeRequestTLV(int status, const std::string& challengeStatus, const JsonSection& params) override;
+
+
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   // challenge status
   static const std::string NEED_CODE;
diff --git a/src/client-config.cpp b/src/client-config.cpp
index 9273760..47115bb 100644
--- a/src/client-config.cpp
+++ b/src/client-config.cpp
@@ -84,21 +84,17 @@
 }
 
 ClientCaItem
-ClientConfig::extractCaItem(const Block& contentBlock)
+ClientConfig::extractCaItem(const JsonSection& configSection)
 {
   ClientCaItem item;
-  item.m_caName = Name(readString(contentBlock.get(CAPrefix)));
+  item.m_caName = Name(configSection.get("ca-prefix", ""));
   if (item.m_caName.empty()) {
     BOOST_THROW_EXCEPTION(Error("Cannot read ca-prefix from the config file"));
   }
-  item.m_caInfo = readString(contentBlock.get(CAInfo));
-  // item.m_probe = configSection.get("probe", "");
-
-  security::v2::Certificate anchor = contentBlock.get(CACertificate);
-
-  //std::istringstream ss(configSection.get("certificate", ""));
-  //auto anchor = io::load<security::v2::Certificate>(ss);
-
+  item.m_caInfo = configSection.get("ca-info", "");
+  item.m_probe = configSection.get("probe", "");
+  std::istringstream ss(configSection.get("certificate", ""));
+  auto anchor = io::load<security::v2::Certificate>(ss);
   if (anchor == nullptr) {
     BOOST_THROW_EXCEPTION(Error("Cannot load the certificate from config file"));
   }
@@ -106,6 +102,28 @@
   return item;
 }
 
+ClientCaItem
+ClientConfig::extractCaItem(const Block& contentBlock)
+{
+  ClientCaItem item;
+  item.m_caName = Name(readString(contentBlock.get(tlv_ca_prefix)));
+  if (item.m_caName.empty()) {
+    BOOST_THROW_EXCEPTION(Error("Cannot read ca-prefix from the config file"));
+  }
+  item.m_caInfo = readString(contentBlock.get(tlv_ca_info));
+  // item.m_probe = configSection.get("probe", "");
+
+  if (!contentBlock.get(tlv_ca_certificate).hasValue()) {
+    BOOST_THROW_EXCEPTION(Error("Cannot load the certificate from config file"));
+  }
+
+  security::v2::Certificate anchor;
+  anchor.wireDecode(contentBlock.get(tlv_ca_certificate));
+  item.m_anchor = anchor;
+
+  return item;
+}
+
 void
 ClientConfig::removeCaItem(const Name& caName)
 {
diff --git a/src/client-config.hpp b/src/client-config.hpp
index 0195c48..876535d 100644
--- a/src/client-config.hpp
+++ b/src/client-config.hpp
@@ -106,6 +106,9 @@
   static ClientCaItem
   extractCaItem(const JsonSection& configSection);
 
+  static ClientCaItem
+  extractCaItem(const Block& configSection);
+
 public:
   std::list<ClientCaItem> m_caItems;
   std::string m_localNdncertAnchor;
diff --git a/src/client-module.cpp b/src/client-module.cpp
index 1789632..7049961 100644
--- a/src/client-module.cpp
+++ b/src/client-module.cpp
@@ -22,6 +22,10 @@
 #include "logging.hpp"
 #include "challenge-module.hpp"
 #include "crypto-support/enc-tlv.hpp"
+#include "protocol-detail/info.hpp"
+#include "protocol-detail/probe.hpp"
+#include "protocol-detail/new.hpp"
+#include "protocol-detail/challenge.hpp"
 #include <ndn-cxx/util/io.hpp>
 #include <ndn-cxx/security/signing-helpers.hpp>
 #include <ndn-cxx/security/verification-helpers.hpp>
@@ -59,10 +63,10 @@
 }
 
 bool
-ClientModule::verifyProbeInfoResponse(const Block& contentBlock)
+ClientModule::verifyInfoResponse(const Data& reply)
 {
   // parse the ca item
-  auto caItem = ClientConfig::extractCaItem(contentBlock);
+  auto caItem = INFO::decodeClientConfigFromContent(reply.getContent());
 
   // verify the probe Data's sig
   if (!security::verifySignature(reply, caItem.m_anchor)) {
@@ -73,12 +77,12 @@
 }
 
 void
-ClientModule::addCaFromProbeInfoResponse(const Data& reply)
+ClientModule::addCaFromInfoResponse(const Data& reply)
 {
   const Block& contentBlock = reply.getContent();
 
   // parse the ca item
-  auto caItem = ClientConfig::extractCaItem(contentBlock);
+  auto caItem = INFO::decodeClientConfigFromContent(contentBlock);
 
   // update the local config
   bool findItem = false;
@@ -101,8 +105,9 @@
   auto interest = make_shared<Interest>(interestName);
   interest->setMustBeFresh(true);
   interest->setCanBePrefix(false);
-  auto paramJson = genProbeRequestJson(ca, probeInfo);
-  interest->setApplicationParameters(paramFromJson(paramJson));
+  interest->setApplicationParameters(
+    PROBE::encodeApplicationParametersFromProbeInfo(ca, probeInfo)
+  );
 
   // update local state
   m_ca = ca;
@@ -116,12 +121,12 @@
     _LOG_ERROR("Cannot verify data signature from " << m_ca.m_caName.toUri());
     return;
   }
-  auto contentJson = getJsonFromData(reply);
+
+  auto contentTLV = reply.getContent();
 
   // read the available name and put it into the state
-  auto nameUri = contentJson.get(JSON_CA_NAME, "");
-  if (nameUri != "") {
-    m_identityName = Name(nameUri);
+  if (contentTLV.get(tlv_probe_response).hasValue()) {
+    m_identityName.wireDecode(contentTLV.get(tlv_probe_response));
   }
   else {
     NDN_LOG_TRACE("The JSON_CA_NAME is empty.");
@@ -194,7 +199,9 @@
   auto interest = make_shared<Interest>(interestName);
   interest->setMustBeFresh(true);
   interest->setCanBePrefix(false);
-  interest->setApplicationParameters(paramFromJson(genNewRequestJson(m_ecdh.getBase64PubKey(), certRequest, probeToken)));
+  interest->setApplicationParameters(
+    NEW::encodeApplicationParameters(m_ecdh.getBase64PubKey(), certRequest, probeToken)
+  );
 
   // sign the Interest packet
   m_keyChain.sign(*interest, signingByKey(m_key.getName()));
@@ -208,11 +215,11 @@
     _LOG_ERROR("Cannot verify data signature from " << m_ca.m_caName.toUri());
     return std::list<std::string>();
   }
-  auto contentJson = getJsonFromData(reply);
+  auto contentTLV = reply.getContent();
 
   // ECDH
-  const auto& peerKeyBase64Str = contentJson.get(JSON_CA_ECDH, "");
-  const auto& saltStr = contentJson.get(JSON_CA_SALT, "");
+  const auto& peerKeyBase64Str = readString(contentTLV.get(tlv_ecdh_pub));  
+  const auto& saltStr = readString(contentTLV.get(tlv_salt));
   uint64_t saltInt = std::stoull(saltStr);
   m_ecdh.deriveSecret(peerKeyBase64Str);
 
@@ -221,22 +228,22 @@
        (uint8_t*)&saltInt, sizeof(saltInt), m_aesKey, sizeof(m_aesKey));
 
   // update state
-  m_status = contentJson.get(JSON_CA_STATUS, 0);
-  m_requestId = contentJson.get(JSON_CA_REQUEST_ID, "");
-
-  auto challengesJson = contentJson.get_child(JSON_CA_CHALLENGES);
+  m_status = readNonNegativeInteger(contentTLV.get(tlv_status));
+  m_requestId = readString(contentTLV.get(tlv_request_id));
   m_challengeList.clear();
-  for (const auto& challengeJson : challengesJson) {
-    m_challengeList.push_back(challengeJson.second.get(JSON_CA_CHALLENGE_ID, ""));
+  for (auto const& element : contentTLV.elements()) {
+    if (element.type() == tlv_challenge) {
+      m_challengeList.push_back(readString(element));
+    }
   }
   return m_challengeList;
 }
 
 shared_ptr<Interest>
-ClientModule::generateChallengeInterest(const JsonSection& paramJson)
+ClientModule::generateChallengeInterest(const Block& challengeRequest)
 {
-  m_challengeType = paramJson.get(JSON_CLIENT_SELECTED_CHALLENGE, "");
-
+  m_challengeType = readString(challengeRequest.get(tlv_selected_challenge));
+  
   Name interestName = m_ca.m_caName;
   interestName.append("CA").append("CHALLENGE").append(m_requestId);
   auto interest = make_shared<Interest>(interestName);
@@ -244,11 +251,9 @@
   interest->setCanBePrefix(false);
 
   // encrypt the Interest parameters
-  std::stringstream ss;
-  boost::property_tree::write_json(ss, paramJson);
-  auto payload = ss.str();
+  auto payload = challengeRequest.getBuffer();
   auto paramBlock = encodeBlockWithAesGcm128(tlv::ApplicationParameters, m_aesKey,
-                                             (const uint8_t*)payload.c_str(), payload.size(), (const uint8_t*)"test", strlen("test"));
+                                             payload->data(), payload->size(), (const uint8_t*)"test", strlen("test"));
   interest->setApplicationParameters(paramBlock);
 
   m_keyChain.sign(*interest, signingByKey(m_key.getName()));
@@ -263,17 +268,19 @@
     return;
   }
   auto result = decodeBlockWithAesGcm128(reply.getContent(), m_aesKey, (const uint8_t*)"test", strlen("test"));
-  std::string payload((const char*)result.data(), result.size());
-  std::istringstream ss(payload);
-  JsonSection contentJson;
-  boost::property_tree::json_parser::read_json(ss, contentJson);
+  bool isSuccess;
+  Block contentTLV;
+
+  std::tie<bool, Block>(isSuccess, contentTLV) = Block::fromBuffer(result.data(), result.size());
 
   // update state
-  m_status = contentJson.get(JSON_CA_STATUS, 0);
-  m_challengeStatus = contentJson.get(JSON_CHALLENGE_STATUS, "");
-  m_remainingTries = contentJson.get(JSON_CHALLENGE_REMAINING_TRIES, 0);
-  m_freshBefore = time::system_clock::now() + time::seconds(contentJson.get(JSON_CHALLENGE_REMAINING_TIME, 0));
-  m_issuedCertName = contentJson.get(JSON_CHALLENGE_ISSUED_CERT_NAME, "");
+  m_status = readNonNegativeInteger(contentTLV.get(tlv_status));
+  m_challengeStatus = readString(contentTLV.get(tlv_challenge_status));
+  m_remainingTries = readNonNegativeInteger(contentTLV.get(tlv_remaining_tries));
+  m_freshBefore = time::system_clock::now() +
+                  time::seconds(readNonNegativeInteger(contentTLV.get(tlv_remaining_time)));
+
+  m_issuedCertName.wireDecode(contentTLV.get(tlv_issued_cert_name));
 }
 
 shared_ptr<Interest>
diff --git a/src/client-module.hpp b/src/client-module.hpp
index 57ae01f..f45b034 100644
--- a/src/client-module.hpp
+++ b/src/client-module.hpp
@@ -71,7 +71,7 @@
   generateInfoInterest(const Name& caName);
 
   bool
-  verifyProbeInfoResponse(const Data& reply);
+  verifyInfoResponse(const Data& reply);
 
   /**
    * @brief Process the replied PROBE INFO Data packet
@@ -80,7 +80,7 @@
    * can be verified in later challenge phase.
    */
   void
-  addCaFromProbeInfoResponse(const Data& reply);
+  addCaFromInfoResponse(const Data& reply);
 
   shared_ptr<Interest>
   generateProbeInterest(const ClientCaItem& ca, const std::string& probeInfo);
@@ -97,7 +97,7 @@
   onNewResponse(const Data& reply);
 
   shared_ptr<Interest>
-  generateChallengeInterest(const JsonSection& paramJson);
+  generateChallengeInterest(const Block& paramTLV);
 
   void
   onChallengeResponse(const Data& reply);
@@ -145,7 +145,7 @@
   std::string m_challengeStatus = "";
   std::string m_challengeType = "";
   std::string m_certId = "";
-  std::string m_issuedCertName = "";
+  Name m_issuedCertName;
   std::list<std::string> m_challengeList;
   bool m_isCertInstalled = false;
   bool m_isNewlyCreatedIdentity = false;
diff --git a/src/protocol-detail/challenge.cpp b/src/protocol-detail/challenge.cpp
new file mode 100644
index 0000000..f1fed27
--- /dev/null
+++ b/src/protocol-detail/challenge.cpp
@@ -0,0 +1,44 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2017-2020, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert 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 General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#include "challenge.hpp"
+#include "../ndncert-common.hpp"
+#include "../certificate-request.hpp"
+
+namespace ndn {
+namespace ndncert {
+
+Block
+CHALLENGE::encodeDataPayload(const CertificateRequest& request)
+{
+  Block response = makeEmptyBlock(tlv_encrypted_payload);
+  makeNonNegativeIntegerBlock(tlv_status, request.m_status);
+  makeStringBlock(tlv_challenge_status, request.m_challengeStatus);
+  makeNonNegativeIntegerBlock(tlv_remaining_tries, request.m_remainingTries);
+  makeNonNegativeIntegerBlock(tlv_remaining_time, request.m_remainingTime);
+  response.parse();
+  return response;
+}
+
+} // namespace ndncert
+} // namespace ndn
+
+
+
diff --git a/src/protocol-detail/challenge.hpp b/src/protocol-detail/challenge.hpp
new file mode 100644
index 0000000..1ff74fd
--- /dev/null
+++ b/src/protocol-detail/challenge.hpp
@@ -0,0 +1,39 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2017-2020, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert 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 General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#ifndef NDNCERT_PROTOCOL_DETAIL_CHALLENGE_HPP
+#define NDNCERT_PROTOCOL_DETAIL_CHALLENGE_HPP
+
+#include "../certificate-request.hpp"
+#include <ndn-cxx/encoding/block.hpp>
+
+namespace ndn {
+namespace ndncert {
+
+class CHALLENGE {
+public:
+  static Block
+  encodeDataPayload(const CertificateRequest& request);
+};
+
+}  // namespace ndncert
+}  // namespace ndn
+
+#endif // NDNCERT_PROTOCOL_DETAIL_HPP
\ No newline at end of file
diff --git a/src/protocol-detail/info.cpp b/src/protocol-detail/info.cpp
index 93bd7b7..1463e04 100644
--- a/src/protocol-detail/info.cpp
+++ b/src/protocol-detail/info.cpp
@@ -28,7 +28,14 @@
 {
   auto content = makeEmptyBlock(tlv::Content);
   content.push_back(makeNestedBlock(tlv_ca_prefix, caConfig.m_caPrefix));
-  content.push_back(makeStringBlock(tlv_ca_info, caConfig.m_caInfo));
+  std::string caInfo = "";
+  if (caConfig.m_caInfo == "") {
+    caInfo = "Issued by " + certificate.getSignature().getKeyLocator().getName().toUri();
+  } else {
+    caInfo = caConfig.m_caInfo;
+  }
+  content.push_back(makeStringBlock(tlv_ca_info, caInfo));
+
   for (const auto& key : caConfig.m_probeParameterKeys) {
     content.push_back(makeStringBlock(tlv_parameter_key, key));
   }
diff --git a/src/protocol-detail/info.hpp b/src/protocol-detail/info.hpp
index 89a907b..9c190f2 100644
--- a/src/protocol-detail/info.hpp
+++ b/src/protocol-detail/info.hpp
@@ -18,8 +18,8 @@
  * See AUTHORS.md for complete list of ndncert authors and contributors.
  */
 
-#ifndef NDNCERT_PROTOCOL_DETAIL_HPP
-#define NDNCERT_PROTOCOL_DETAIL_HPP
+#ifndef NDNCERT_PROTOCOL_DETAIL_INFO_HPP
+#define NDNCERT_PROTOCOL_DETAIL_INFO_HPP
 
 #include "../ca-config.hpp"
 #include "../client-config.hpp"
diff --git a/src/protocol-detail/new.cpp b/src/protocol-detail/new.cpp
new file mode 100644
index 0000000..27bbf0a
--- /dev/null
+++ b/src/protocol-detail/new.cpp
@@ -0,0 +1,75 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2017-2020, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert 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 General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#include "new.hpp"
+#include "../logging.hpp"
+#include "../ndncert-common.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>
+#include <ndn-cxx/util/logger.hpp>
+
+namespace ndn {
+namespace ndncert {
+
+_LOG_INIT(ndncert.client);
+
+Block
+NEW::encodeApplicationParameters(const std::string& ecdhPub, const security::v2::Certificate& certRequest,
+                                const shared_ptr<Data>& probeToken)
+{
+  Block request = makeEmptyBlock(tlv::ApplicationParameters);
+  std::stringstream ss;
+  try {
+    security::transform::bufferSource(certRequest.wireEncode().wire(), certRequest.wireEncode().size())
+    >> security::transform::base64Encode(false)
+    >> security::transform::streamSink(ss);
+  }
+  catch (const security::transform::Error& e) {
+    _LOG_ERROR("Cannot convert self-signed cert into BASE64 string " << e.what());
+    return request;
+  }
+
+  request.push_back(makeStringBlock(tlv_ecdh_pub, ecdhPub));
+  request.push_back(makeNestedBlock(tlv_cert_request, certRequest));
+  request.parse();
+  return request;
+}
+
+Block
+NEW::encodeDataContent(const std::string& ecdhKey, const std::string& salt,
+                             const CertificateRequest& request,
+                             const std::list<std::string>& challenges)
+{
+  Block response = makeEmptyBlock(tlv::Content);
+  response.push_back(makeStringBlock(tlv_ecdh_pub, ecdhKey));
+  response.push_back(makeStringBlock(tlv_salt, salt));
+  response.push_back(makeStringBlock(tlv_request_id, request.m_requestId));
+  response.push_back(makeNonNegativeIntegerBlock(tlv_status, request.m_status));
+  for (const auto& entry: challenges) {
+    response.push_back(makeStringBlock(tlv_challenge, entry));
+  }
+  response.parse();
+  return response;
+}
+
+}  // namespace ndncert
+}  // namespace ndn
\ No newline at end of file
diff --git a/src/protocol-detail/new.hpp b/src/protocol-detail/new.hpp
new file mode 100644
index 0000000..ae95a15
--- /dev/null
+++ b/src/protocol-detail/new.hpp
@@ -0,0 +1,46 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2017-2020, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert 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 General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#ifndef NDNCERT_PROTOCOL_DETAIL_NEW_HPP
+#define NDNCERT_PROTOCOL_DETAIL_NEW_HPP
+
+#include "../certificate-request.hpp"
+#include "ndn-cxx/encoding/block.hpp"
+#include <ndn-cxx/security/v2/certificate.hpp>
+
+namespace ndn {
+namespace ndncert {
+
+class NEW {
+public:
+  static Block
+  encodeApplicationParameters(const std::string& ecdhPub, const security::v2::Certificate& certRequest,
+                                const shared_ptr<Data>& probeToken);
+
+  static Block
+  encodeDataContent(const std::string& ecdhKey, const std::string& salt,
+                             const CertificateRequest& request,
+                             const std::list<std::string>& challenges);
+};
+
+}  // namespace ndncert
+}  // namespace ndn
+
+#endif // NDNCERT_PROTOCOL_DETAIL_HPP
\ No newline at end of file
diff --git a/src/protocol-detail/probe.cpp b/src/protocol-detail/probe.cpp
new file mode 100644
index 0000000..b8034b1
--- /dev/null
+++ b/src/protocol-detail/probe.cpp
@@ -0,0 +1,99 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2017-2020, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert 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 General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#include "probe.hpp"
+#include "boost/throw_exception.hpp"
+#include "../logging.hpp"
+#include <ndn-cxx/encoding/tlv.hpp>
+
+namespace ndn {
+namespace ndncert {
+
+// For Client
+std::vector<std::string>
+PROBE::parseProbeComponents(const std::string& probe)
+{
+  std::vector<std::string> components;
+  std::string delimiter = ":";
+  size_t last = 0;
+  size_t next = 0;
+  while ((next = probe.find(delimiter, last)) != std::string::npos) {
+    components.push_back(probe.substr(last, next - last));
+    last = next + 1;
+  }
+  components.push_back(probe.substr(last));
+  return components;
+}
+
+Block
+PROBE::encodeApplicationParametersFromProbeInfo(const ClientCaItem& ca, const std::string& probeInfo)
+{
+  auto content = makeEmptyBlock(tlv::ApplicationParameters);
+
+  std::vector<std::string> fields = parseProbeComponents(ca.m_probe);
+  std::vector<std::string> arguments  = parseProbeComponents(probeInfo);;
+
+  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) {
+      content.push_back(
+        makeStringBlock(tlv_parameter_key, fields.at(i))
+      );
+      content.push_back(
+        makeStringBlock(tlv_parameter_value, arguments.at(i))
+      );
+  }
+  content.parse();
+  return content;
+}
+
+// For CA
+Block
+PROBE::encodeDataContent(const Name& identifier, const std::string& m_probe, const Block& parameterTLV)
+{
+  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));
+
+  Block content = makeEmptyBlock(tlv::Content);
+  
+  // TODO: Currently have no mechanism to utilize the given params to determine name
+  //for (size_t i = 0; i < fields.size(); ++i) {
+  //  root.put(fields.at(i), parameterJson.get(fields.at(i), ""));
+  //}
+
+  content.push_back(makeNestedBlock(tlv_probe_response, identifier));
+
+  // TODO: Must be determined based on CA config
+  content.push_back(makeEmptyBlock(tlv_allow_longer_name));
+  content.parse();
+  return content;
+}
+
+}  // namespace ndncert
+}  // namespace ndn
\ No newline at end of file
diff --git a/src/protocol-detail/probe.hpp b/src/protocol-detail/probe.hpp
new file mode 100644
index 0000000..6fbca8e
--- /dev/null
+++ b/src/protocol-detail/probe.hpp
@@ -0,0 +1,56 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2017-2020, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert 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 General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#ifndef NDNCERT_PROTOCOL_DETAIL_PROBE_HPP
+#define NDNCERT_PROTOCOL_DETAIL_PROBE_HPP
+
+#include "../ca-config.hpp"
+#include "../client-config.hpp"
+
+namespace ndn {
+namespace ndncert {
+
+class PROBE {
+
+public:
+  /**
+   * @brief Error that can be thrown from PROBE
+   */
+  class Error : public std::runtime_error
+  {
+    public:
+    using std::runtime_error::runtime_error;
+  };
+
+public:
+  static std::vector<std::string>
+  parseProbeComponents(const std::string& probe);
+
+  static Block
+  encodeApplicationParametersFromProbeInfo(const ClientCaItem& ca, const std::string& probeInfo);
+
+  static Block
+  encodeDataContent(const Name& identifier, const std::string& m_probe, const Block& parameterTLV);
+};
+
+}  // namespace ndncert
+}  // namespace ndn
+
+#endif // NDNCERT_PROTOCOL_DETAIL_HPP
\ No newline at end of file