remove JSON parameter list from challenge module and add unified return interfaces for challenge modules

Change-Id: I0e69f7f8ef2ea36f38f11815aa980928b049cdc7
diff --git a/src/ca-config.cpp b/src/ca-config.cpp
index e6ab0f6..c8bc298 100644
--- a/src/ca-config.cpp
+++ b/src/ca-config.cpp
@@ -97,7 +97,7 @@
     if (challengeType == "") {
       BOOST_THROW_EXCEPTION(std::runtime_error("Cannot read type in supported-challenges from the config file"));
     }
-    if (!ChallengeModule::supportChallenge(challengeType)) {
+    if (!ChallengeModule::isChallengeSupported(challengeType)) {
       BOOST_THROW_EXCEPTION(std::runtime_error("Does not support challenge read from the config file"));
     }
     m_supportedChallenges.push_back(challengeType);
diff --git a/src/challenge-module.cpp b/src/challenge-module.cpp
index 9288330..47dca5d 100644
--- a/src/challenge-module.cpp
+++ b/src/challenge-module.cpp
@@ -32,7 +32,7 @@
 ChallengeModule::~ChallengeModule() = default;
 
 bool
-ChallengeModule::supportChallenge(const std::string& challengeType)
+ChallengeModule::isChallengeSupported(const std::string& challengeType)
 {
   ChallengeFactory& factory = getFactory();
   auto i = factory.find(challengeType);
@@ -70,16 +70,45 @@
   return result;
 }
 
-void
-ChallengeModule::updateRequestOnChallengeEnd(CertificateRequest& request)
+std::tuple<Error, std::string>
+ChallengeModule::returnWithError(CertificateRequest& request, Error errorCode, std::string&& errorInfo)
 {
+  request.m_status = Status::FAILURE;
+  request.m_challengeType = "";
+  request.m_challengeStatus = "";
   request.m_challengeSecrets = JsonSection();
   request.m_challengeTp = "";
-  request.m_challengeType = "";
   request.m_remainingTime = 0;
   request.m_remainingTries = 0;
+  return std::make_tuple(errorCode, std::move(errorInfo));
 }
 
+std::tuple<Error, std::string>
+ChallengeModule::returnWithNewChallengeStatus(CertificateRequest& request, const std::string& challengeStatus,
+                                              JsonSection&& challengeSecret, size_t remainingTries, size_t remainingTime)
+{
+  request.m_status = Status::CHALLENGE;
+  request.m_challengeType = CHALLENGE_TYPE;
+  request.m_challengeStatus = std::move(challengeStatus);
+  request.m_challengeSecrets = std::move(challengeSecret);
+  request.m_challengeTp = time::toIsoString(time::system_clock::now());
+  request.m_remainingTime = remainingTries;
+  request.m_remainingTries = remainingTime;
+  return std::make_tuple(Error::NO_ERROR, "");
+}
+
+std::tuple<Error, std::string>
+ChallengeModule::returnWithSuccess(CertificateRequest& request)
+{
+  request.m_status = Status::PENDING;
+  request.m_challengeType = CHALLENGE_TYPE;
+  request.m_challengeStatus = "";
+  request.m_challengeSecrets = JsonSection();
+  request.m_challengeTp = "";
+  request.m_remainingTime = 0;
+  request.m_remainingTries = 0;
+  return std::make_tuple(Error::NO_ERROR, "");
+}
 
 } // namespace ndncert
 } // namespace ndn
diff --git a/src/challenge-module.hpp b/src/challenge-module.hpp
index fb687e1..660ff15 100644
--- a/src/challenge-module.hpp
+++ b/src/challenge-module.hpp
@@ -21,35 +21,21 @@
 #ifndef NDNCERT_CHALLENGE_MODULE_HPP
 #define NDNCERT_CHALLENGE_MODULE_HPP
 
-#include "ndncert-common.hpp"
+#include <tuple>
+
 #include "certificate-request.hpp"
+#include "ndncert-common.hpp"
 
 namespace ndn {
 namespace ndncert {
 
-class ChallengeModule : noncopyable
-{
+class ChallengeModule : noncopyable {
 public:
-  /**
-   * @brief Error that can be thrown from ChallengeModule
-   *
-   * ChallengeModule should throw Error to notice CA there's an Error. In this case, CA will
-   * generate an Error JSON file back to end entity.
-   */
-  class Error : public std::runtime_error
-  {
-  public:
-    using std::runtime_error::runtime_error;
-  };
+  explicit ChallengeModule(const std::string& uniqueType);
 
-public:
-  explicit
-  ChallengeModule(const std::string& uniqueType);
+  virtual ~ChallengeModule();
 
-  virtual
-  ~ChallengeModule();
-
-  template<class ChallengeType>
+  template <class ChallengeType>
   static void
   registerChallengeModule(const std::string& typeName)
   {
@@ -59,56 +45,60 @@
   }
 
   static bool
-  supportChallenge(const std::string& challengeType);
+  isChallengeSupported(const std::string& challengeType);
 
   static unique_ptr<ChallengeModule>
   createChallengeModule(const std::string& challengeType);
 
   // For CA
-  virtual void
+  virtual std::tuple<Error, std::string>
   handleChallengeRequest(const Block& params, CertificateRequest& request) = 0;
 
   // For Client
-  virtual JsonSection
-  getRequirementForChallenge(Status status, const std::string& challengeStatus) = 0;
-
-  virtual JsonSection
-  genChallengeRequestJson(Status status, const std::string& challengeStatus, const JsonSection& params) = 0;
+  virtual std::vector<std::tuple<std::string, std::string>>
+  getRequestedParameterList(Status status, const std::string& challengeStatus) = 0;
 
   virtual Block
-  genChallengeRequestTLV(Status status, const std::string& challengeStatus, const JsonSection& params) = 0;
+  genChallengeRequestTLV(Status status, const std::string& challengeStatus,
+                         std::vector<std::tuple<std::string, std::string>>&& params) = 0;
 
   // helpers
   static std::string
   generateSecretCode();
 
 protected:
+  // used by challenge modules
+  std::tuple<Error, std::string>
+  returnWithError(CertificateRequest& request, Error errorCode, std::string&& errorInfo);
 
-  void
-  updateRequestOnChallengeEnd(CertificateRequest& request);
+  std::tuple<Error, std::string>
+  returnWithNewChallengeStatus(CertificateRequest& request, const std::string& challengeStatus,
+                               JsonSection&& challengeSecret, size_t remainingTries, size_t remainingTime);
+
+  std::tuple<Error, std::string>
+  returnWithSuccess(CertificateRequest& request);
 
 public:
   const std::string CHALLENGE_TYPE;
 
 private:
-  typedef function<unique_ptr<ChallengeModule> ()> ChallengeCreateFunc;
+  typedef function<unique_ptr<ChallengeModule>()> ChallengeCreateFunc;
   typedef std::map<std::string, ChallengeCreateFunc> ChallengeFactory;
 
   static ChallengeFactory&
   getFactory();
 };
 
-#define NDNCERT_REGISTER_CHALLENGE(C, T)                           \
-static class NdnCert ## C ## ChallengeRegistrationClass            \
-{                                                                  \
-public:                                                            \
-  NdnCert ## C ## ChallengeRegistrationClass()                     \
-  {                                                                \
-    ::ndn::ndncert::ChallengeModule::registerChallengeModule<C>(T);\
-  }                                                                \
-} g_NdnCert ## C ## ChallengeRegistrationVariable
+#define NDNCERT_REGISTER_CHALLENGE(C, T)                              \
+  static class NdnCert##C##ChallengeRegistrationClass {               \
+  public:                                                             \
+    NdnCert##C##ChallengeRegistrationClass()                          \
+    {                                                                 \
+      ::ndn::ndncert::ChallengeModule::registerChallengeModule<C>(T); \
+    }                                                                 \
+  } g_NdnCert##C##ChallengeRegistrationVariable
 
-} // namespace ndncert
-} // namespace ndn
+}  // namespace ndncert
+}  // namespace ndn
 
-#endif // NDNCERT_CHALLENGE_MODULE_HPP
+#endif  // NDNCERT_CHALLENGE_MODULE_HPP
diff --git a/src/challenge-module/challenge-credential.cpp b/src/challenge-module/challenge-credential.cpp
index da24fef..fe120eb 100644
--- a/src/challenge-module/challenge-credential.cpp
+++ b/src/challenge-module/challenge-credential.cpp
@@ -28,15 +28,11 @@
 namespace ndn {
 namespace ndncert {
 
-_LOG_INIT(ndncert.ChallengeCredential);
-
+_LOG_INIT(ndncert.challenge.credential);
 NDNCERT_REGISTER_CHALLENGE(ChallengeCredential, "Credential");
 
-const std::string ChallengeCredential::FAILURE_INVALID_FORMAT_CREDENTIAL = "failure-cannot-parse-credential";
-const std::string ChallengeCredential::FAILURE_INVALID_FORMAT_SELF_SIGNED = "failure-cannot-parse-self-signed";
-const std::string ChallengeCredential::FAILURE_INVALID_CREDENTIAL = "failure-invalid-credential";
-const std::string ChallengeCredential::JSON_CREDENTIAL_CERT = "issued-cert";
-const std::string ChallengeCredential::JSON_PROOF_OF_PRIVATE_KEY = "proof-of-private-key";
+const std::string ChallengeCredential::PARAMETER_KEY_CREDENTIAL_CERT = "issued-cert";
+const std::string ChallengeCredential::PARAMETER_KEY_PROOF_OF_PRIVATE_KEY = "proof-of-private-key";
 
 ChallengeCredential::ChallengeCredential(const std::string& configPath)
     : ChallengeModule("Credential")
@@ -57,12 +53,12 @@
     boost::property_tree::read_json(m_configFile, config);
   }
   catch (const boost::property_tree::info_parser_error& error) {
-    BOOST_THROW_EXCEPTION(Error("Failed to parse configuration file " + m_configFile +
-                                " " + error.message() + " line " + std::to_string(error.line())));
+    BOOST_THROW_EXCEPTION(std::runtime_error("Failed to parse configuration file " + m_configFile +
+                                             " " + error.message() + " line " + std::to_string(error.line())));
   }
 
   if (config.begin() == config.end()) {
-    BOOST_THROW_EXCEPTION(Error("Error processing configuration file: " + m_configFile + " no data"));
+    BOOST_THROW_EXCEPTION(std::runtime_error("Error processing configuration file: " + m_configFile + " no data"));
   }
 
   m_trustAnchors.clear();
@@ -80,7 +76,7 @@
 }
 
 // For CA
-void
+std::tuple<Error, std::string>
 ChallengeCredential::handleChallengeRequest(const Block& params, CertificateRequest& request)
 {
   params.parse();
@@ -91,26 +87,20 @@
   auto& elements = params.elements();
   for (size_t i = 0; i < elements.size(); i++) {
     if (elements[i].type() == tlv_parameter_key) {
-      if (readString(elements[i]) == JSON_CREDENTIAL_CERT) {
+      if (readString(elements[i]) == PARAMETER_KEY_CREDENTIAL_CERT) {
         std::istringstream ss(readString(params.elements()[i + 1]));
         credential = io::load<security::v2::Certificate>(ss);
         if (credential == nullptr) {
-          _LOG_ERROR("Cannot load credential parameter: cert");
-          request.m_status = Status::FAILURE;
-          request.m_challengeStatus = FAILURE_INVALID_FORMAT_CREDENTIAL;
-          updateRequestOnChallengeEnd(request);
-          return;
+          _LOG_ERROR("Cannot load challenge parameter: credential");
+          return returnWithError(request, Error::INVALID_PARAMETER, "Cannot challenge credential: credential.");
         }
       }
-      else if (readString(elements[i]) == JSON_PROOF_OF_PRIVATE_KEY) {
+      else if (readString(elements[i]) == PARAMETER_KEY_PROOF_OF_PRIVATE_KEY) {
         std::istringstream ss(readString(params.elements()[i + 1]));
         selfSigned = io::load<security::v2::Certificate>(ss);
         if (selfSigned == nullptr) {
-          _LOG_ERROR("Cannot load credential parameter: cert");
-          request.m_status = Status::FAILURE;
-          request.m_challengeStatus = FAILURE_INVALID_FORMAT_SELF_SIGNED;
-          updateRequestOnChallengeEnd(request);
-          return;
+          _LOG_ERROR("Cannot load challenge parameter: proof of private key");
+          return returnWithError(request, Error::INVALID_PARAMETER, "Cannot load challenge parameter: proof of private key.");
         }
       }
       else {
@@ -126,63 +116,56 @@
       if (security::verifySignature(*selfSigned, anchor) &&
           security::verifySignature(*selfSigned, *credential) &&
           readString(selfSigned->getContent()) == request.m_requestId) {
-        request.m_status = Status::PENDING;
-        request.m_challengeStatus = CHALLENGE_STATUS_SUCCESS;
-        updateRequestOnChallengeEnd(request);
-        return;
+        return returnWithSuccess(request);
       }
     }
   }
 
-  _LOG_TRACE("Cannot verify the credential + self-signed Data + data content");
-  request.m_status = Status::FAILURE;
-  request.m_challengeStatus = FAILURE_INVALID_CREDENTIAL;
-  updateRequestOnChallengeEnd(request);
-  return;
+  _LOG_TRACE("Cannot verify the proof of private key against credential");
+  return returnWithError(request, Error::INVALID_PARAMETER, "Cannot verify the proof of private key against credential.");
 }
 
 // For Client
-JsonSection
-ChallengeCredential::getRequirementForChallenge(Status status, const std::string& challengeStatus)
+std::vector<std::tuple<std::string, std::string>>
+ChallengeCredential::getRequestedParameterList(Status status, const std::string& challengeStatus)
 {
-  JsonSection result;
-  if (status == Status::BEFORE_CHALLENGE && challengeStatus == "") {
-    result.put(JSON_CREDENTIAL_CERT, "Please_copy_anchor_signed_cert_here");
-    result.put(JSON_PROOF_OF_PRIVATE_KEY, "Please_copy_key_signed_request_id_data_here");
+  std::vector<std::tuple<std::string, std::string>> result;
+  if (status == Status::BEFORE_CHALLENGE) {
+    result.push_back(std::make_tuple(PARAMETER_KEY_CREDENTIAL_CERT, "Please provide the certificate issued by a trusted CA."));
+    result.push_back(std::make_tuple(PARAMETER_KEY_PROOF_OF_PRIVATE_KEY, "Please sign a Data packet with request ID as the content."));
   }
   else {
-    _LOG_ERROR("Client's status and challenge status are wrong");
-  }
-  return result;
-}
-
-JsonSection
-ChallengeCredential::genChallengeRequestJson(Status status, const std::string& challengeStatus, const JsonSection& params)
-{
-  JsonSection result;
-  if (status == Status::BEFORE_CHALLENGE && challengeStatus == "") {
-    result.put(JSON_CREDENTIAL_CERT, params.get(JSON_CREDENTIAL_CERT, ""));
-    result.put(JSON_PROOF_OF_PRIVATE_KEY, params.get(JSON_PROOF_OF_PRIVATE_KEY, ""));
-  }
-  else {
-    _LOG_ERROR("Client's status and challenge status are wrong");
+    BOOST_THROW_EXCEPTION(std::runtime_error("Unexpected status or challenge status."));
   }
   return result;
 }
 
 Block
-ChallengeCredential::genChallengeRequestTLV(Status status, const std::string& challengeStatus, const JsonSection& params)
+ChallengeCredential::genChallengeRequestTLV(Status status, const std::string& challengeStatus,
+                                            std::vector<std::tuple<std::string, std::string>>&& params)
 {
   Block request = makeEmptyBlock(tlv_encrypted_payload);
-  if (status == Status::BEFORE_CHALLENGE && challengeStatus == "") {
+  if (status == Status::BEFORE_CHALLENGE) {
+    if (params.size() != 2) {
+      BOOST_THROW_EXCEPTION(std::runtime_error("Wrong parameter provided."));
+    }
     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_PROOF_OF_PRIVATE_KEY));
-    request.push_back(makeStringBlock(tlv_parameter_value, params.get(JSON_PROOF_OF_PRIVATE_KEY, "")));
+    for (const auto& item : params) {
+      if (std::get<0>(item) == PARAMETER_KEY_CREDENTIAL_CERT) {
+        request.push_back(makeStringBlock(tlv_parameter_key, PARAMETER_KEY_CREDENTIAL_CERT));
+        request.push_back(makeStringBlock(tlv_parameter_value, std::get<1>(item)));
+      }
+      else if (std::get<0>(item) == PARAMETER_KEY_PROOF_OF_PRIVATE_KEY) {
+        request.push_back(makeStringBlock(tlv_parameter_key, PARAMETER_KEY_PROOF_OF_PRIVATE_KEY));
+        request.push_back(makeStringBlock(tlv_parameter_value, std::get<1>(item)));
+      }
+      else {
+        BOOST_THROW_EXCEPTION(std::runtime_error("Wrong parameter provided."));
+      }
+    }
   }
   else {
-    _LOG_ERROR("Client's status and challenge status are wrong");
+    BOOST_THROW_EXCEPTION(std::runtime_error("Unexpected status or challenge status."));
   }
   request.encode();
   return request;
diff --git a/src/challenge-module/challenge-credential.hpp b/src/challenge-module/challenge-credential.hpp
index c0e0e48..2afe38e 100644
--- a/src/challenge-module/challenge-credential.hpp
+++ b/src/challenge-module/challenge-credential.hpp
@@ -52,31 +52,26 @@
   ChallengeCredential(const std::string& configPath = "");
 
   // For CA
-  void
+  std::tuple<Error, std::string>
   handleChallengeRequest(const Block& params, CertificateRequest& request) override;
 
   // For Client
-  JsonSection
-  getRequirementForChallenge(Status status, const std::string& challengeStatus) override;
-
-  JsonSection
-  genChallengeRequestJson(Status status, const std::string& challengeStatus, const JsonSection& params) override;
+  std::vector<std::tuple<std::string, std::string>>
+  getRequestedParameterList(Status status, const std::string& challengeStatus) override;
 
   Block
-  genChallengeRequestTLV(Status status, const std::string& challengeStatus, const JsonSection& params) override;
+  genChallengeRequestTLV(Status status, const std::string& challengeStatus,
+                         std::vector<std::tuple<std::string, std::string>>&& params) override;
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   void
   parseConfigFile();
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
-  static const std::string FAILURE_INVALID_CREDENTIAL;
-  static const std::string FAILURE_INVALID_FORMAT_CREDENTIAL;
-  static const std::string FAILURE_INVALID_FORMAT_SELF_SIGNED;
-  static const std::string JSON_CREDENTIAL_CERT;
-  static const std::string JSON_PROOF_OF_PRIVATE_KEY;
+  // parameters
+  static const std::string PARAMETER_KEY_CREDENTIAL_CERT;
+  static const std::string PARAMETER_KEY_PROOF_OF_PRIVATE_KEY;
 
-PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   std::list<security::v2::Certificate> m_trustAnchors;
   std::string m_configFile;
 };
diff --git a/src/challenge-module/challenge-email.cpp b/src/challenge-module/challenge-email.cpp
index cd08d9b..7e0134a 100644
--- a/src/challenge-module/challenge-email.cpp
+++ b/src/challenge-module/challenge-email.cpp
@@ -19,187 +19,130 @@
  */
 
 #include "challenge-email.hpp"
+
+#include <regex>
+
 #include "../ca-module.hpp"
 #include "../logging.hpp"
-#include <regex>
 
 namespace ndn {
 namespace ndncert {
 
-_LOG_INIT(ndncert.ChallengeEmail);
-
+_LOG_INIT(ndncert.challenge.email);
 NDNCERT_REGISTER_CHALLENGE(ChallengeEmail, "email");
 
 const std::string ChallengeEmail::NEED_CODE = "need-code";
 const std::string ChallengeEmail::WRONG_CODE = "wrong-code";
-const std::string ChallengeEmail::FAILURE_INVALID_EMAIL = "failure-invalid-email";
-const std::string ChallengeEmail::JSON_EMAIL = "email";
-const std::string ChallengeEmail::JSON_CODE = "code";
+const std::string ChallengeEmail::PARAMETER_KEY_EMAIL = "email";
+const std::string ChallengeEmail::PARAMETER_KEY_CODE = "code";
 
 ChallengeEmail::ChallengeEmail(const std::string& scriptPath,
                                const size_t& maxAttemptTimes,
                                const time::seconds secretLifetime)
-  : ChallengeModule("email")
-  , m_sendEmailScript(scriptPath)
-  , m_maxAttemptTimes(maxAttemptTimes)
-  , m_secretLifetime(secretLifetime)
+    : ChallengeModule("email")
+    , m_sendEmailScript(scriptPath)
+    , m_maxAttemptTimes(maxAttemptTimes)
+    , m_secretLifetime(secretLifetime)
 {
 }
 
 // For CA
-void
+std::tuple<Error, std::string>
 ChallengeEmail::handleChallengeRequest(const Block& params, CertificateRequest& request)
 {
   params.parse();
   auto currentTime = time::system_clock::now();
-  if (request.m_challengeStatus == "") {
+  if (request.m_status == Status::BEFORE_CHALLENGE) {
     // for the first time, init the challenge
     std::string emailAddress = readString(params.get(tlv_parameter_value));
     if (!isValidEmailAddress(emailAddress)) {
-      request.m_status = Status::FAILURE;
-      request.m_challengeStatus = FAILURE_INVALID_EMAIL;
-      return;
+      return returnWithError(request, Error::INVALID_PARAMETER, "Invalid email address format.");
     }
-    // check whether this email is the same as the one used in PROBE
-    if (request.m_probeToken != nullptr) {
-      const auto& content = request.m_probeToken->getContent();
-      const auto& json = CaModule::jsonFromBlock(content);
-      const auto& expectedEmail = json.get("email", "");
-      Name expectedPrefix(json.get(JSON_CA_NAME, ""));
-      if (expectedEmail != emailAddress || !expectedPrefix.isPrefixOf(request.m_cert.getName())) {
-        _LOG_ERROR("Cannot match with the PROBE token. Input email: " << emailAddress
-                   << " Email in Token: " << expectedEmail
-                   << " Requested Cert Name: " << request.m_cert.getName()
-                   << " Identity Name got from Token: " << expectedPrefix);
-        return;
-      }
+    auto lastComponentRequested = readString(request.m_cert.getIdentity().get(-1));
+    if (lastComponentRequested != emailAddress) {
+      _LOG_TRACE("Email and requested name do not match. Email " << emailAddress << "requested last component " << lastComponentRequested);
     }
-    request.m_status = Status::CHALLENGE;
-    request.m_challengeStatus = NEED_CODE;
-    request.m_challengeType = CHALLENGE_TYPE;
     std::string emailCode = generateSecretCode();
     JsonSection secretJson;
-    secretJson.add(JSON_CODE, emailCode);
-    request.m_challengeSecrets = secretJson;
-    request.m_challengeTp = time::toIsoString(currentTime);
-    request.m_remainingTime = m_secretLifetime.count();
-    request.m_remainingTries = m_maxAttemptTimes;
+    secretJson.add(PARAMETER_KEY_CODE, emailCode);
     // send out the email
     sendEmail(emailAddress, emailCode, request);
     _LOG_TRACE("Secret for request " << request.m_requestId << " : " << emailCode);
-    return;
+    return returnWithNewChallengeStatus(request, NEED_CODE, std::move(secretJson), m_maxAttemptTimes, m_secretLifetime.count());
   }
-  else if (request.m_challengeStatus == NEED_CODE || request.m_challengeStatus == WRONG_CODE) {
+
+  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 = readString(params.get(tlv_parameter_value));
-    const auto realCode = request.m_challengeSecrets.get<std::string>(JSON_CODE);
+    auto secret = request.m_challengeSecrets;
+    // check if run out of time
     if (currentTime - time::fromIsoString(request.m_challengeTp) >= m_secretLifetime) {
-      // secret expires
-      request.m_status = Status::FAILURE;
-      request.m_challengeStatus = CHALLENGE_STATUS_FAILURE_TIMEOUT;
-      updateRequestOnChallengeEnd(request);
-      _LOG_TRACE("Secret expired. Challenge failed.");
-      return;
+      return returnWithError(request, Error::OUT_OF_TIME, "Secret expired.");
     }
-    else if (givenCode == realCode) {
+    // check if provided secret is correct
+    if (givenCode == secret.get<std::string>(PARAMETER_KEY_CODE)) {
       // the code is correct
-      request.m_status = Status::PENDING;
-      request.m_challengeStatus = CHALLENGE_STATUS_SUCCESS;
-      updateRequestOnChallengeEnd(request);
-      _LOG_TRACE("Secret code matched. Challenge succeeded.");
-      return;
+      _LOG_TRACE("Correct secret code. Challenge succeeded.");
+      return returnWithSuccess(request);
+    }
+    // otherwise, check remaining attempt times
+    if (request.m_remainingTries > 1) {
+      auto remainTime = m_secretLifetime - (currentTime - time::fromIsoString(request.m_challengeTp));
+      _LOG_TRACE("Wrong secret code provided. Remaining Tries - 1.");
+      return returnWithNewChallengeStatus(request, WRONG_CODE, std::move(secret), request.m_remainingTries - 1, remainTime.count());
     }
     else {
-      // check rest attempt times
-      if (request.m_remainingTries > 1) {
-        request.m_challengeStatus = WRONG_CODE;
-        request.m_remainingTries = request.m_remainingTries - 1;
-        auto remainTime = m_secretLifetime - (currentTime - time::fromIsoString(request.m_challengeTp));
-        request.m_remainingTime = remainTime.count();
-        _LOG_TRACE("Secret code didn't match. Remaining Tries - 1.");
-        return;
-      }
-      else {
-        // run out times
-        request.m_status = Status::FAILURE;
-        request.m_challengeStatus = CHALLENGE_STATUS_FAILURE_MAXRETRY;
-        updateRequestOnChallengeEnd(request);
-        _LOG_TRACE("Secret code didn't match. Ran out tires. Challenge failed.");
-        return;
-      }
+      // run out times
+      _LOG_TRACE("Wrong secret code provided. Ran out tires. Challenge failed.");
+      return returnWithError(request, Error::OUT_OF_TRIES, "Ran out tires.");
     }
   }
-  else {
-    _LOG_ERROR("The challenge status is wrong");
-    request.m_status = Status::FAILURE;
-    return;
-  }
+  return returnWithError(request, Error::INVALID_PARAMETER, "Unexpected status or challenge status");
 }
 
 // For Client
-JsonSection
-ChallengeEmail::getRequirementForChallenge(Status status, const std::string& challengeStatus)
+std::vector<std::tuple<std::string, std::string>>
+ChallengeEmail::getRequestedParameterList(Status status, const std::string& challengeStatus)
 {
-  JsonSection result;
+  std::vector<std::tuple<std::string, std::string>> result;
   if (status == Status::BEFORE_CHALLENGE && challengeStatus == "") {
-    result.put(JSON_EMAIL, "Please_input_your_email_address");
+    result.push_back(std::make_tuple(PARAMETER_KEY_EMAIL, "Please input your email address"));
   }
   else if (status == Status::CHALLENGE && challengeStatus == NEED_CODE) {
-    result.put(JSON_CODE, "Please_input_your_verification_code");
+    result.push_back(std::make_tuple(PARAMETER_KEY_CODE, "Please input your verification code"));
   }
   else if (status == Status::CHALLENGE && challengeStatus == WRONG_CODE) {
-    result.put(JSON_CODE, "Incorrect_code_please_try_again");
+    result.push_back(std::make_tuple(PARAMETER_KEY_CODE, "Incorrect code, please try again"));
   }
   else {
-    _LOG_ERROR("CA's status and challenge status are wrong");
-  }
-  return result;
-}
-
-JsonSection
-ChallengeEmail::genChallengeRequestJson(Status status, const std::string& challengeStatus, const JsonSection& params)
-{
-  JsonSection result;
-  if (status == Status::BEFORE_CHALLENGE && challengeStatus == "") {
-    result.put(JSON_CLIENT_SELECTED_CHALLENGE, CHALLENGE_TYPE);
-    result.put(JSON_EMAIL, params.get(JSON_EMAIL, ""));
-  }
-  else if (status == Status::CHALLENGE && challengeStatus == NEED_CODE) {
-    result.put(JSON_CLIENT_SELECTED_CHALLENGE, CHALLENGE_TYPE);
-    result.put(JSON_CODE, params.get(JSON_CODE, ""));
-  }
-  else if (status == Status::CHALLENGE && challengeStatus == WRONG_CODE) {
-    result.put(JSON_CLIENT_SELECTED_CHALLENGE, CHALLENGE_TYPE);
-    result.put(JSON_CODE, params.get(JSON_CODE, ""));
-  }
-  else {
-    _LOG_ERROR("Client's status and challenge status are wrong");
+    BOOST_THROW_EXCEPTION(std::runtime_error("Unexpected status or challenge status."));
   }
   return result;
 }
 
 Block
-ChallengeEmail::genChallengeRequestTLV(Status status, const std::string& challengeStatus, const JsonSection& params)
+ChallengeEmail::genChallengeRequestTLV(Status status, const std::string& challengeStatus, std::vector<std::tuple<std::string, std::string>>&& params)
 {
   Block request = makeEmptyBlock(tlv_encrypted_payload);
-  if (status == Status::BEFORE_CHALLENGE && challengeStatus == "") {
+  if (status == Status::BEFORE_CHALLENGE) {
+    if (params.size() != 1 || std::get<0>(params[0]) != PARAMETER_KEY_EMAIL) {
+      BOOST_THROW_EXCEPTION(std::runtime_error("Wrong parameter provided."));
+    }
     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,"")));
+    request.push_back(makeStringBlock(tlv_parameter_key, PARAMETER_KEY_EMAIL));
+    request.push_back(makeStringBlock(tlv_parameter_value, std::get<1>(params[0])));
   }
-  else if (status == Status::CHALLENGE && challengeStatus == NEED_CODE) {
+  else if (status == Status::CHALLENGE && (challengeStatus == NEED_CODE || challengeStatus == WRONG_CODE)) {
+    if (params.size() != 1 || std::get<0>(params[0]) != PARAMETER_KEY_CODE) {
+      BOOST_THROW_EXCEPTION(std::runtime_error("Wrong parameter provided."));
+    }
     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,"")));
+    request.push_back(makeStringBlock(tlv_parameter_key, PARAMETER_KEY_CODE));
+    request.push_back(makeStringBlock(tlv_parameter_value, std::get<1>(params[0])));
   }
   else {
-    _LOG_ERROR("Client's status and challenge status are wrong");
+    BOOST_THROW_EXCEPTION(std::runtime_error("Unexpected status or challenge status."));
   }
   request.encode();
   return request;
@@ -218,8 +161,7 @@
                           const CertificateRequest& request) const
 {
   std::string command = m_sendEmailScript;
-  command += " \"" + emailAddress + "\" \"" + secret + "\" \""
-    + request.m_caPrefix.toUri() + "\" \"" + request.m_cert.getName().toUri()  + "\"";
+  command += " \"" + emailAddress + "\" \"" + secret + "\" \"" + request.m_caPrefix.toUri() + "\" \"" + request.m_cert.getName().toUri() + "\"";
   int result = system(command.c_str());
   if (result == -1) {
     _LOG_TRACE("EmailSending Script " + m_sendEmailScript + " fails.");
@@ -229,5 +171,5 @@
   return;
 }
 
-} // namespace ndncert
-} // namespace ndn
+}  // namespace ndncert
+}  // namespace ndn
diff --git a/src/challenge-module/challenge-email.hpp b/src/challenge-module/challenge-email.hpp
index b43a3de..f148d06 100644
--- a/src/challenge-module/challenge-email.hpp
+++ b/src/challenge-module/challenge-email.hpp
@@ -58,18 +58,16 @@
                  const time::seconds secretLifetime = time::minutes(20));
 
   // For CA
-  void
+  std::tuple<Error, std::string>
   handleChallengeRequest(const Block& params, CertificateRequest& request) override;
 
   // For Client
-  JsonSection
-  getRequirementForChallenge(Status status, const std::string& challengeStatus) override;
-
-  JsonSection
-  genChallengeRequestJson(Status status, const std::string& challengeStatus, const JsonSection& params) override;
+  std::vector<std::tuple<std::string, std::string>>
+  getRequestedParameterList(Status status, const std::string& challengeStatus) override;
 
   Block
-  genChallengeRequestTLV(Status status, const std::string& challengeStatus, const JsonSection& params) override;
+  genChallengeRequestTLV(Status status, const std::string& challengeStatus,
+                         std::vector<std::tuple<std::string, std::string>>&& params) override;
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   static bool
@@ -83,10 +81,9 @@
   // challenge status
   static const std::string NEED_CODE;
   static const std::string WRONG_CODE;
-  static const std::string FAILURE_INVALID_EMAIL;
-  // JSON attribute
-  static const std::string JSON_EMAIL;
-  static const std::string JSON_CODE;
+  // parameters
+  static const std::string PARAMETER_KEY_EMAIL;
+  static const std::string PARAMETER_KEY_CODE;
 
 private:
   std::string m_sendEmailScript;
diff --git a/src/challenge-module/challenge-pin.cpp b/src/challenge-module/challenge-pin.cpp
index 55b9435..cd20c51 100644
--- a/src/challenge-module/challenge-pin.cpp
+++ b/src/challenge-module/challenge-pin.cpp
@@ -19,162 +19,111 @@
  */
 
 #include "challenge-pin.hpp"
-#include "logging.hpp"
+
 #include <ndn-cxx/util/random.hpp>
 
+#include "logging.hpp"
+
 namespace ndn {
 namespace ndncert {
 
-_LOG_INIT(ndncert.challenge-pin);
-
+_LOG_INIT(ndncert.challenge.pin);
 NDNCERT_REGISTER_CHALLENGE(ChallengePin, "pin");
 
 const std::string ChallengePin::NEED_CODE = "need-code";
 const std::string ChallengePin::WRONG_CODE = "wrong-code";
-const std::string ChallengePin::JSON_PIN_CODE = "pin-code";
+const std::string ChallengePin::PARAMETER_KEY_CODE = "pin-code";
 
 ChallengePin::ChallengePin(const size_t& maxAttemptTimes, const time::seconds& secretLifetime)
-  : ChallengeModule("pin")
-  , m_secretLifetime(secretLifetime)
-  , m_maxAttemptTimes(maxAttemptTimes)
+    : ChallengeModule("pin")
+    , m_secretLifetime(secretLifetime)
+    , m_maxAttemptTimes(maxAttemptTimes)
 {
 }
 
 // For CA
-void
+std::tuple<Error, std::string>
 ChallengePin::handleChallengeRequest(const Block& params, CertificateRequest& request)
 {
   params.parse();
   auto currentTime = time::system_clock::now();
-  if (request.m_challengeStatus == "") {
+  if (request.m_status == Status::BEFORE_CHALLENGE) {
     _LOG_TRACE("Challenge Interest arrives. Init the challenge");
     // for the first time, init the challenge
-    request.m_status = Status::CHALLENGE;
-    request.m_challengeStatus = NEED_CODE;
-    request.m_challengeType = CHALLENGE_TYPE;
     std::string secretCode = generateSecretCode();
     JsonSection secretJson;
-    secretJson.add(JSON_PIN_CODE, secretCode);
-    request.m_challengeSecrets = secretJson;
-    request.m_challengeTp = time::toIsoString(currentTime);
-    request.m_remainingTime = m_secretLifetime.count();
-    request.m_remainingTries = m_maxAttemptTimes;
+    secretJson.add(PARAMETER_KEY_CODE, secretCode);
     _LOG_TRACE("Secret for request " << request.m_requestId << " : " << secretCode);
-    return;
+    return returnWithNewChallengeStatus(request, NEED_CODE, std::move(secretJson), m_maxAttemptTimes, m_secretLifetime.count());
   }
-  else if (request.m_challengeStatus == NEED_CODE || request.m_challengeStatus == WRONG_CODE) {
+
+  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 = readString(params.get(tlv_parameter_value));
-    const auto realCode = request.m_challengeSecrets.get<std::string>(JSON_PIN_CODE);
+    auto secret = request.m_challengeSecrets;
     if (currentTime - time::fromIsoString(request.m_challengeTp) >= m_secretLifetime) {
-      // secret expires
-      request.m_status = Status::FAILURE;
-      request.m_challengeStatus = CHALLENGE_STATUS_FAILURE_TIMEOUT;
-      updateRequestOnChallengeEnd(request);
-      _LOG_TRACE("Secret expired. Challenge failed.");
-      return;
+      return returnWithError(request, Error::OUT_OF_TIME, "Secret expired.");
     }
-    else if (givenCode == realCode) {
-      // the code is correct
-      request.m_status = Status::PENDING;
-      request.m_challengeStatus = CHALLENGE_STATUS_SUCCESS;
-      updateRequestOnChallengeEnd(request);
-      _LOG_TRACE("PIN code matched. Challenge succeeded.");
-      return;
+    if (givenCode == secret.get<std::string>(PARAMETER_KEY_CODE)) {
+      _LOG_TRACE("Correct PIN code. Challenge succeeded.");
+      return returnWithSuccess(request);
+    }
+    // check rest attempt times
+    if (request.m_remainingTries > 1) {
+      auto remainTime = m_secretLifetime - (currentTime - time::fromIsoString(request.m_challengeTp));
+      _LOG_TRACE("Wrong PIN code provided. Remaining Tries - 1.");
+      return returnWithNewChallengeStatus(request, WRONG_CODE, std::move(secret), request.m_remainingTries - 1, remainTime.count());
     }
     else {
-      // check rest attempt times
-      if (request.m_remainingTries > 1) {
-        request.m_challengeStatus = WRONG_CODE;
-        request.m_remainingTries = request.m_remainingTries - 1;
-        auto remainTime = m_secretLifetime - (currentTime - time::fromIsoString(request.m_challengeTp));
-        request.m_remainingTime = remainTime.count();
-        _LOG_TRACE("PIN code didn't match. Remaining Tries - 1.");
-        return;
-      }
-      else {
-        // run out times
-        request.m_status = Status::FAILURE;
-        request.m_challengeStatus = CHALLENGE_STATUS_FAILURE_MAXRETRY;
-        updateRequestOnChallengeEnd(request);
-        _LOG_TRACE("PIN code didn't match. Ran out tires. Challenge failed.");
-        return;
-      }
+      // run out times
+      _LOG_TRACE("Wrong PIN code provided. Ran out tires. Challenge failed.");
+      return returnWithError(request, Error::OUT_OF_TRIES, "Ran out tires.");
     }
   }
-  else {
-    _LOG_ERROR("The challenge status is wrong");
-    request.m_status = Status::FAILURE;
-    return;
-  }
+  return returnWithError(request, Error::INVALID_PARAMETER, "Unexpected status or challenge status");
 }
 
 // For Client
-JsonSection
-ChallengePin::getRequirementForChallenge(Status status, const std::string& challengeStatus)
+std::vector<std::tuple<std::string, std::string>>
+ChallengePin::getRequestedParameterList(Status status, const std::string& challengeStatus)
 {
-  JsonSection result;
-  if (status == Status::BEFORE_CHALLENGE && challengeStatus == "") {
+  std::vector<std::tuple<std::string, std::string>> result;
+  if (status == Status::BEFORE_CHALLENGE) {
     // do nothing
   }
   else if (status == Status::CHALLENGE && challengeStatus == NEED_CODE) {
-    result.put(JSON_PIN_CODE, "Please_input_your_verification_code");
+    result.push_back(std::make_tuple(PARAMETER_KEY_CODE, "Please input your PIN code"));
   }
   else if (status == Status::CHALLENGE && challengeStatus == WRONG_CODE) {
-    result.put(JSON_PIN_CODE, "Incorrect_PIN_code_please_try_again");
+    result.push_back(std::make_tuple(PARAMETER_KEY_CODE, "Incorrect PIN code, please try again"));
   }
   else {
-    _LOG_ERROR("Client's status and challenge status are wrong");
-  }
-  return result;
-}
-
-JsonSection
-ChallengePin::genChallengeRequestJson(Status status, const std::string& challengeStatus, const JsonSection& params)
-{
-  JsonSection result;
-  if (status == Status::BEFORE_CHALLENGE && challengeStatus == "") {
-    // do nothing
-    result.put(JSON_CLIENT_SELECTED_CHALLENGE, CHALLENGE_TYPE);
-  }
-  else if (status == Status::CHALLENGE && challengeStatus == NEED_CODE) {
-    result.put(JSON_CLIENT_SELECTED_CHALLENGE, CHALLENGE_TYPE);
-    result.put(JSON_PIN_CODE, params.get(JSON_PIN_CODE, ""));
-  }
-  else if (status == Status::CHALLENGE && challengeStatus == WRONG_CODE) {
-    result.put(JSON_CLIENT_SELECTED_CHALLENGE, CHALLENGE_TYPE);
-    result.put(JSON_PIN_CODE, params.get(JSON_PIN_CODE, ""));
-  }
-  else {
-    _LOG_ERROR("Client's status and challenge status are wrong");
+    BOOST_THROW_EXCEPTION(std::runtime_error("Unexpected status or challenge status."));
   }
   return result;
 }
 
 Block
-ChallengePin::genChallengeRequestTLV(Status status, const std::string& challengeStatus, const JsonSection& params)
+ChallengePin::genChallengeRequestTLV(Status status, const std::string& challengeStatus, std::vector<std::tuple<std::string, std::string>>&& params)
 {
   Block request = makeEmptyBlock(tlv_encrypted_payload);
-  if (status == Status::BEFORE_CHALLENGE && challengeStatus == "") {
-    // do nothing
+  if (status == Status::BEFORE_CHALLENGE) {
     request.push_back(makeStringBlock(tlv_selected_challenge, CHALLENGE_TYPE));
   }
-  else if (status == Status::CHALLENGE && challengeStatus == NEED_CODE) {
+  else if (status == Status::CHALLENGE && (challengeStatus == NEED_CODE || challengeStatus == WRONG_CODE)) {
+    if (params.size() != 1 || std::get<0>(params[0]) != PARAMETER_KEY_CODE) {
+      BOOST_THROW_EXCEPTION(std::runtime_error("Wrong parameter provided."));
+    }
     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,"")));
+    request.push_back(makeStringBlock(tlv_parameter_key, PARAMETER_KEY_CODE));
+    request.push_back(makeStringBlock(tlv_parameter_value, std::get<1>(params[0])));
   }
   else {
-    _LOG_ERROR("Client's status and challenge status are wrong");
+    BOOST_THROW_EXCEPTION(std::runtime_error("Unexpected status or challenge status."));
   }
   request.encode();
   return request;
 }
-} // namespace ndncert
-} // namespace ndn
+}  // namespace ndncert
+}  // namespace ndn
diff --git a/src/challenge-module/challenge-pin.hpp b/src/challenge-module/challenge-pin.hpp
index 44db7d8..88cf9bd 100644
--- a/src/challenge-module/challenge-pin.hpp
+++ b/src/challenge-module/challenge-pin.hpp
@@ -52,26 +52,24 @@
                const time::seconds& secretLifetime = time::seconds(3600));
 
   // For CA
-  void
+  std::tuple<Error, std::string>
   handleChallengeRequest(const Block& params, CertificateRequest& request) override;
 
   // For Client
-  JsonSection
-  getRequirementForChallenge(Status status, const std::string& challengeStatus) override;
-
-  JsonSection
-  genChallengeRequestJson(Status status, const std::string& challengeStatus, const JsonSection& params) override;
+  std::vector<std::tuple<std::string, std::string>>
+  getRequestedParameterList(Status status, const std::string& challengeStatus) override;
 
   Block
-  genChallengeRequestTLV(Status status, const std::string& challengeStatus, const JsonSection& params) override;
+  genChallengeRequestTLV(Status status, const std::string& challengeStatus,
+                         std::vector<std::tuple<std::string, std::string>>&& params) override;
 
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   // challenge status
   static const std::string NEED_CODE;
   static const std::string WRONG_CODE;
-  // JSON attribute
-  static const std::string JSON_PIN_CODE;
+  // parameters
+  static const std::string PARAMETER_KEY_CODE;
 
 private:
   time::seconds m_secretLifetime;
diff --git a/src/challenge-module/challenge-private-key.cpp b/src/challenge-module/challenge-private-key.cpp
deleted file mode 100644
index 74af413..0000000
--- a/src/challenge-module/challenge-private-key.cpp
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * 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-private-key.hpp"
-
-#include <iostream>
-#include <ndn-cxx/security/verification-helpers.hpp>
-#include <ndn-cxx/util/io.hpp>
-
-#include "../logging.hpp"
-
-namespace ndn {
-namespace ndncert {
-
-_LOG_INIT(ndncert.ChallengePrivateKey);
-
-NDNCERT_REGISTER_CHALLENGE(ChallengePrivateKey, "Private");
-
-const std::string ChallengePrivateKey::FAILURE_INVALID_REQUEST_TYPE = "failure-invalid-request-type";
-const std::string ChallengePrivateKey::FAILURE_INVALID_FORMAT_SELF_SIGNED = "failure-cannot-parse-self-signed";
-const std::string ChallengePrivateKey::FAILURE_INVALID_CREDENTIAL = "failure-invalid-credential";
-const std::string ChallengePrivateKey::JSON_PROOF_OF_PRIVATE_KEY = "proof-of-private-key";
-
-ChallengePrivateKey::ChallengePrivateKey()
-    : ChallengeModule("PrivateKey")
-{
-}
-
-// For CA
-void
-ChallengePrivateKey::handleChallengeRequest(const Block& params, CertificateRequest& request)
-{
-  if (request.m_requestType == REQUEST_TYPE_NEW) {
-      _LOG_TRACE("Cannot use this private key challenge for new certificate request");
-      request.m_status = Status::FAILURE;
-      request.m_challengeStatus = FAILURE_INVALID_REQUEST_TYPE;
-      updateRequestOnChallengeEnd(request);
-  }
-  params.parse();
-  shared_ptr<security::v2::Certificate> selfSigned;
-  auto& elements = params.elements();
-  for (size_t i = 0; i < elements.size(); i++) {
-    if (elements[i].type() == tlv_parameter_key) {
-      if (readString(elements[i]) == JSON_PROOF_OF_PRIVATE_KEY) {
-        std::istringstream ss(readString(params.elements()[i + 1]));
-        selfSigned = io::load<security::v2::Certificate>(ss);
-        if (selfSigned == nullptr) {
-          _LOG_ERROR("Cannot load credential parameter: cert");
-          request.m_status = Status::FAILURE;
-          request.m_challengeStatus = FAILURE_INVALID_FORMAT_SELF_SIGNED;
-          updateRequestOnChallengeEnd(request);
-          return;
-        }
-      }
-      else {
-        continue;
-      }
-    }
-  }
-
-  // verify the credential and the self-signed cert
-  if (security::verifySignature(*selfSigned, request.m_cert) &&
-    readString(selfSigned->getContent()) == request.m_requestId) {
-    request.m_status = Status::PENDING;
-    request.m_challengeStatus = CHALLENGE_STATUS_SUCCESS;
-    updateRequestOnChallengeEnd(request);
-    return;
-  }
-
-  _LOG_TRACE("Cannot verify the credential + self-signed Data + data content");
-  request.m_status = Status::FAILURE;
-  request.m_challengeStatus = FAILURE_INVALID_CREDENTIAL;
-  updateRequestOnChallengeEnd(request);
-}
-
-// For Client
-JsonSection
-ChallengePrivateKey::getRequirementForChallenge(Status status, const std::string& challengeStatus)
-{
-  JsonSection result;
-  if (status == Status::BEFORE_CHALLENGE && challengeStatus == "") {
-    result.put(JSON_PROOF_OF_PRIVATE_KEY, "Please_copy_key_signed_request_id_data_here");
-  }
-  else {
-    _LOG_ERROR("Client's status and challenge status are wrong");
-  }
-  return result;
-}
-
-JsonSection
-ChallengePrivateKey::genChallengeRequestJson(Status status, const std::string& challengeStatus, const JsonSection& params)
-{
-  JsonSection result;
-  if (status == Status::BEFORE_CHALLENGE && challengeStatus == "") {
-    result.put(JSON_PROOF_OF_PRIVATE_KEY, params.get(JSON_PROOF_OF_PRIVATE_KEY, ""));
-  }
-  else {
-    _LOG_ERROR("Client's status and challenge status are wrong");
-  }
-  return result;
-}
-
-Block
-ChallengePrivateKey::genChallengeRequestTLV(Status 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_PROOF_OF_PRIVATE_KEY));
-    request.push_back(makeStringBlock(tlv_parameter_value, params.get(JSON_PROOF_OF_PRIVATE_KEY, "")));
-  }
-  else {
-    _LOG_ERROR("Client's status and challenge status are wrong");
-  }
-  request.encode();
-  return request;
-}
-}  // namespace ndncert
-}  // namespace ndn
diff --git a/src/challenge-module/challenge-private-key.hpp b/src/challenge-module/challenge-private-key.hpp
deleted file mode 100644
index 5f0b936..0000000
--- a/src/challenge-module/challenge-private-key.hpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
- * 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_CHALLENGE_PRIVATE_KEY_HPP
-#define NDNCERT_CHALLENGE_PRIVATE_KEY_HPP
-
-#include "../challenge-module.hpp"
-
-namespace ndn {
-namespace ndncert {
-
-/**
- * @brief Private Key based challenge (for renewal and revocation)
- *
- * Once the requester could proof his/her possession of the private key corresponds to
- * the current CA's previous issued certificate, the requester could finish the challenge.
- *
- * The requester needs to provide the proof of the possession the private for the certificate
- * for the previous cerificate. The challenge require the requester to a BASE64 Data packet
- * signed by the credential pub key and whose content is the request id.
- *
- * The main process of this challenge module is:
- *   1. The requester sign a Data packet which content is the request id.
- *   2. The challenge module will verify the signature of the credential.
- *
- * Failure info when application fails:
- *   FAILURE_INVALID_CREDENTIAL: When the signature cannot be validated.
- *   FAILURE_INVALID_FORMAT: When the credential format is wrong.
- */
-class ChallengePrivateKey : public ChallengeModule
-{
-public:
-  ChallengePrivateKey();
-
-  // For CA
-  void
-  handleChallengeRequest(const Block& params, CertificateRequest& request) override;
-
-  // For Client
-  JsonSection
-  getRequirementForChallenge(Status status, const std::string& challengeStatus) override;
-
-  JsonSection
-  genChallengeRequestJson(Status status, const std::string& challengeStatus, const JsonSection& params) override;
-
-  Block
-  genChallengeRequestTLV(Status status, const std::string& challengeStatus, const JsonSection& params) override;
-
-PUBLIC_WITH_TESTS_ELSE_PRIVATE:
-  static const std::string FAILURE_INVALID_REQUEST_TYPE;
-  static const std::string FAILURE_INVALID_CREDENTIAL;
-  static const std::string FAILURE_INVALID_FORMAT_SELF_SIGNED;
-  static const std::string JSON_PROOF_OF_PRIVATE_KEY;
-};
-
-} // namespace ndncert
-} // namespace ndn
-
-#endif // NDNCERT_CHALLENGE_PRIVATE_KEY_HPP
diff --git a/src/ndncert-common.hpp b/src/ndncert-common.hpp
index cbbba39..6727a88 100644
--- a/src/ndncert-common.hpp
+++ b/src/ndncert-common.hpp
@@ -149,6 +149,7 @@
 std::string statusToString(Status status);
 
 enum class Error : uint16_t {
+  NO_ERROR = 0,
   BAD_INTEREST_FORMAT = 1,
   BAD_PARAMETER_FORMAT = 2,
   BAD_SIGNATURE = 3,
diff --git a/src/protocol-detail/error.cpp b/src/protocol-detail/error.cpp
new file mode 100644
index 0000000..0b24eec
--- /dev/null
+++ b/src/protocol-detail/error.cpp
@@ -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.
+ */
+
+#include "error.hpp"
+
+namespace ndn {
+namespace ndncert {
+
+Block
+ERROR::encodeDataContent(Error errorCode, const std::string& description)
+{
+  Block response = makeEmptyBlock(tlv::Content);
+  response.push_back(makeNonNegativeIntegerBlock(tlv_error_code, static_cast<size_t>(errorCode)));
+  response.push_back(makeStringBlock(tlv_error_info, description));
+  response.encode();
+  return response;
+}
+
+std::tuple<Error, std::string>
+ERROR::decodefromDataContent(const Block& block)
+{
+  block.parse();
+  Error error = static_cast<Error>(readNonNegativeInteger(block.get(tlv_error_code)));
+  auto description = readString(block.get(tlv_error_info));
+  return std::make_tuple(error, description);
+}
+
+}  // namespace ndncert
+}  // namespace ndn
diff --git a/src/protocol-detail/error.hpp b/src/protocol-detail/error.hpp
new file mode 100644
index 0000000..c03ec05
--- /dev/null
+++ b/src/protocol-detail/error.hpp
@@ -0,0 +1,48 @@
+/* -*- 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_ERROR_HPP
+#define NDNCERT_PROTOCOL_DETAIL_ERROR_HPP
+
+#include "../ca-config.hpp"
+#include "../client-config.hpp"
+
+namespace ndn {
+namespace ndncert {
+
+class ERROR {
+public:
+  /**
+   * Encode error information into a Data content TLV
+   */
+  static Block
+  encodeDataContent(Error errorCode, const std::string& description);
+
+  /**
+   * Decode error information from Data content TLV
+   */
+  static std::tuple<Error, std::string>
+  decodefromDataContent(const Block& block);
+};
+
+}  // namespace ndncert
+}  // namespace ndn
+
+#endif // NDNCERT_PROTOCOL_ERROR_HPP
\ No newline at end of file
diff --git a/tests/unit-tests/bench.t.cpp b/tests/unit-tests/bench.t.cpp
index a8602ef..81f9cf4 100644
--- a/tests/unit-tests/bench.t.cpp
+++ b/tests/unit-tests/bench.t.cpp
@@ -110,10 +110,10 @@
     if (Name("/ndn/CA/NEW").isPrefixOf(response.getName())) {
       std::cout << "NEW Data Size: " << response.wireEncode().size() << std::endl;
       client.onNewResponse(response);
-      auto paramJson = pinChallenge.getRequirementForChallenge(client.m_status, client.m_challengeStatus);
+      auto paramList = pinChallenge.getRequestedParameterList(client.m_status, client.m_challengeStatus);
       challengeInterest = client.generateChallengeInterest(pinChallenge.genChallengeRequestTLV(client.m_status,
                                                                                                client.m_challengeStatus,
-                                                                                               paramJson));
+                                                                                               std::move(paramList)));
     }
     else if (Name("/ndn/CA/CHALLENGE").isPrefixOf(response.getName()) && count == 0) {
       count++;
@@ -123,10 +123,10 @@
       BOOST_CHECK(client.m_status == Status::CHALLENGE);
       BOOST_CHECK_EQUAL(client.m_challengeStatus, ChallengePin::NEED_CODE);
 
-      auto paramJson = pinChallenge.getRequirementForChallenge(client.m_status, client.m_challengeStatus);
+      auto paramList = pinChallenge.getRequestedParameterList(client.m_status, client.m_challengeStatus);
       challengeInterest2 = client.generateChallengeInterest(pinChallenge.genChallengeRequestTLV(client.m_status,
                                                                                                 client.m_challengeStatus,
-                                                                                                paramJson));
+                                                                                                std::move(paramList)));
     }
     else if (Name("/ndn/CA/CHALLENGE").isPrefixOf(response.getName()) && count == 1) {
       count++;
@@ -136,16 +136,13 @@
       BOOST_CHECK(client.m_status == Status::CHALLENGE);
       BOOST_CHECK_EQUAL(client.m_challengeStatus, ChallengePin::WRONG_CODE);
 
-      auto paramJson = pinChallenge.getRequirementForChallenge(client.m_status, client.m_challengeStatus);
+      auto paramList = pinChallenge.getRequestedParameterList(client.m_status, client.m_challengeStatus);
       auto request = ca.getCertificateRequest(*challengeInterest2);
-      auto secret = request.m_challengeSecrets.get(ChallengePin::JSON_PIN_CODE, "");
-      for (auto& i : paramJson) {
-        if (i.first == ChallengePin::JSON_PIN_CODE)
-          i.second.put("", secret);
-      }
+      auto secret = request.m_challengeSecrets.get(ChallengePin::PARAMETER_KEY_CODE, "");
+      std::get<1>(paramList[0]) = secret;
       challengeInterest3 = client.generateChallengeInterest(pinChallenge.genChallengeRequestTLV(client.m_status,
                                                                                                 client.m_challengeStatus,
-                                                                                                paramJson));
+                                                                                                std::move(paramList)));
       std::cout << "CHALLENGE Interest Size: " << challengeInterest3->wireEncode().size() << std::endl;
     }
     else if (Name("/ndn/CA/CHALLENGE").isPrefixOf(response.getName()) && count == 2) {
@@ -155,7 +152,6 @@
 
       client.onChallengeResponse(response);
       BOOST_CHECK(client.m_status == Status::SUCCESS);
-      BOOST_CHECK_EQUAL(client.m_challengeStatus, CHALLENGE_STATUS_SUCCESS);
     }
   });
 
diff --git a/tests/unit-tests/ca-module.t.cpp b/tests/unit-tests/ca-module.t.cpp
index 9e0289a..3860060 100644
--- a/tests/unit-tests/ca-module.t.cpp
+++ b/tests/unit-tests/ca-module.t.cpp
@@ -46,49 +46,51 @@
 
   advanceClocks(time::milliseconds(20), 60);
   BOOST_CHECK_EQUAL(ca.m_registeredPrefixHandles.size(), 2);
-  BOOST_CHECK_EQUAL(ca.m_interestFilterHandles.size(), 5); // onInfo, onProbe, onNew, onChallenge, onRevoke
+  BOOST_CHECK_EQUAL(ca.m_interestFilterHandles.size(), 5);  // onInfo, onProbe, onNew, onChallenge, onRevoke
 }
 
-// BOOST_AUTO_TEST_CASE(HandleProbe)
-// {
-//   auto identity = addIdentity(Name("/ndn"));
-//   auto key = identity.getDefaultKey();
-//   auto cert = key.getDefaultCertificate();
+BOOST_AUTO_TEST_CASE(HandleProbe)
+{
+  auto identity = addIdentity(Name("/ndn"));
+  auto key = identity.getDefaultKey();
+  auto cert = key.getDefaultCertificate();
 
-//   util::DummyClientFace face(io, m_keyChain, {true, true});
-//   CaModule ca(face, m_keyChain, "tests/unit-tests/ca.conf.test", "ca-storage-memory");
-//   ca.setProbeHandler([&](const Block& probeInfo) {
-//     return "example";
-//   });
-//   advanceClocks(time::milliseconds(20), 60);
+  util::DummyClientFace face(io, m_keyChain, {true, true});
+  CaModule ca(face, m_keyChain, "tests/unit-tests/ca.conf.test", "ca-storage-memory");
+  ca.setNameAssignmentFunction([&](const std::vector<std::tuple<std::string, std::string>>) -> std::vector<std::string> {
+    std::vector<std::string> result;
+    result.push_back("example");
+    return result;
+  });
+  advanceClocks(time::milliseconds(20), 60);
 
-//   Interest interest("/ndn/CA/PROBE");
-//   interest.setCanBePrefix(false);
+  Interest interest("/ndn/CA/PROBE");
+  interest.setCanBePrefix(false);
 
-//   Block paramTLV = makeEmptyBlock(tlv::ApplicationParameters);
-//   paramTLV.push_back(makeStringBlock(tlv_parameter_key, JSON_CLIENT_PROBE_INFO));
-//   paramTLV.push_back(makeStringBlock(tlv_parameter_value, "zhiyi"));
-//   paramTLV.encode();
+  Block paramTLV = makeEmptyBlock(tlv::ApplicationParameters);
+  paramTLV.push_back(makeStringBlock(tlv_parameter_key, JSON_CLIENT_PROBE_INFO));
+  paramTLV.push_back(makeStringBlock(tlv_parameter_value, "zhiyi"));
+  paramTLV.encode();
 
-//   interest.setApplicationParameters(paramTLV);
+  interest.setApplicationParameters(paramTLV);
 
-//   int count = 0;
-//   face.onSendData.connect([&](const Data& response) {
-//     count++;
-//     BOOST_CHECK(security::verifySignature(response, cert));
-//     Block contentBlock = response.getContent();
-//     contentBlock.parse();
-//     Block probeResponse = contentBlock.get(tlv_probe_response);
-//     probeResponse.parse();
-//     Name caName;
-//     caName.wireDecode(probeResponse.get(tlv::Name));
-//     BOOST_CHECK_EQUAL(caName, "/ndn/example");
-//   });
-//   face.receive(interest);
+  int count = 0;
+  face.onSendData.connect([&](const Data& response) {
+    count++;
+    BOOST_CHECK(security::verifySignature(response, cert));
+    Block contentBlock = response.getContent();
+    contentBlock.parse();
+    Block probeResponse = contentBlock.get(tlv_probe_response);
+    probeResponse.parse();
+    Name caName;
+    caName.wireDecode(probeResponse.get(tlv::Name));
+    BOOST_CHECK_EQUAL(caName, "/ndn/example");
+  });
+  face.receive(interest);
 
-//   advanceClocks(time::milliseconds(20), 60);
-//   BOOST_CHECK_EQUAL(count, 1);
-// }
+  advanceClocks(time::milliseconds(20), 60);
+  BOOST_CHECK_EQUAL(count, 1);
+}
 
 BOOST_AUTO_TEST_CASE(HandleInfo)
 {
@@ -305,10 +307,10 @@
   face.onSendData.connect([&](const Data& response) {
     if (Name("/ndn/CA/NEW").isPrefixOf(response.getName())) {
       client.onNewResponse(response);
-      auto paramJson = pinChallenge.getRequirementForChallenge(client.m_status, client.m_challengeStatus);
+      auto paramList = pinChallenge.getRequestedParameterList(client.m_status, client.m_challengeStatus);
       challengeInterest = client.generateChallengeInterest(pinChallenge.genChallengeRequestTLV(client.m_status,
                                                                                                client.m_challengeStatus,
-                                                                                               paramJson));
+                                                                                               std::move(paramList)));
     }
     else if (Name("/ndn/CA/CHALLENGE").isPrefixOf(response.getName()) && count == 0) {
       count++;
@@ -318,10 +320,10 @@
       BOOST_CHECK(client.m_status == Status::CHALLENGE);
       BOOST_CHECK_EQUAL(client.m_challengeStatus, ChallengePin::NEED_CODE);
 
-      auto paramJson = pinChallenge.getRequirementForChallenge(client.m_status, client.m_challengeStatus);
+      auto paramList = pinChallenge.getRequestedParameterList(client.m_status, client.m_challengeStatus);
       challengeInterest2 = client.generateChallengeInterest(pinChallenge.genChallengeRequestTLV(client.m_status,
                                                                                                 client.m_challengeStatus,
-                                                                                                paramJson));
+                                                                                                std::move(paramList)));
     }
     else if (Name("/ndn/CA/CHALLENGE").isPrefixOf(response.getName()) && count == 1) {
       count++;
@@ -331,16 +333,13 @@
       BOOST_CHECK(client.m_status == Status::CHALLENGE);
       BOOST_CHECK_EQUAL(client.m_challengeStatus, ChallengePin::WRONG_CODE);
 
-      auto paramJson = pinChallenge.getRequirementForChallenge(client.m_status, client.m_challengeStatus);
+      auto paramList = pinChallenge.getRequestedParameterList(client.m_status, client.m_challengeStatus);
       auto request = ca.getCertificateRequest(*challengeInterest2);
-      auto secret = request.m_challengeSecrets.get(ChallengePin::JSON_PIN_CODE, "");
-      for (auto& i : paramJson) {
-        if (i.first == ChallengePin::JSON_PIN_CODE)
-          i.second.put("", secret);
-      }
+      auto secret = request.m_challengeSecrets.get(ChallengePin::PARAMETER_KEY_CODE, "");
+      std::get<1>(paramList[0]) = secret;
       challengeInterest3 = client.generateChallengeInterest(pinChallenge.genChallengeRequestTLV(client.m_status,
                                                                                                 client.m_challengeStatus,
-                                                                                                paramJson));
+                                                                                                std::move(paramList)));
     }
     else if (Name("/ndn/CA/CHALLENGE").isPrefixOf(response.getName()) && count == 2) {
       count++;
@@ -348,7 +347,6 @@
 
       client.onChallengeResponse(response);
       BOOST_CHECK(client.m_status == Status::SUCCESS);
-      BOOST_CHECK_EQUAL(client.m_challengeStatus, CHALLENGE_STATUS_SUCCESS);
     }
   });
 
@@ -369,7 +367,7 @@
   auto key = identity.getDefaultKey();
   auto cert = key.getDefaultCertificate();
 
-  util::DummyClientFace face(io, { true, true });
+  util::DummyClientFace face(io, {true, true});
   CaModule ca(face, m_keyChain, "tests/unit-tests/ca.conf.test", "ca-storage-memory");
   advanceClocks(time::milliseconds(20), 60);
 
@@ -383,7 +381,7 @@
   clientCert.setContent(clientKey.getPublicKey().data(), clientKey.getPublicKey().size());
   SignatureInfo signatureInfo;
   signatureInfo.setValidityPeriod(security::ValidityPeriod(time::system_clock::now(),
-          time::system_clock::now() + time::hours(10)));
+                                                           time::system_clock::now() + time::hours(10)));
   m_keyChain.sign(clientCert, signingByKey(clientKey.getName()).setSignatureInfo(signatureInfo));
   CertificateRequest certRequest(Name("/ndn"), "122", REQUEST_TYPE_NEW, Status::SUCCESS, clientCert);
   auto issuedCert = ca.issueCertificate(certRequest);
@@ -397,7 +395,7 @@
   auto interest = client.generateRevokeInterest(issuedCert);
 
   int count = 0;
-  face.onSendData.connect([&] (const Data& response) {
+  face.onSendData.connect([&](const Data& response) {
     count++;
     BOOST_CHECK(security::verifySignature(response, cert));
     auto contentBlock = response.getContent();
@@ -409,16 +407,16 @@
 
     auto challengeBlockCount = 0;
     for (auto const& element : contentBlock.elements()) {
-        if (element.type() == tlv_challenge) {
-            challengeBlockCount++;
-        }
+      if (element.type() == tlv_challenge) {
+        challengeBlockCount++;
+      }
     }
 
     BOOST_CHECK(challengeBlockCount != 0);
 
     client.onRevokeResponse(response);
     BOOST_CHECK_EQUAL_COLLECTIONS(client.m_aesKey, client.m_aesKey + sizeof(client.m_aesKey),
-            ca.m_aesKey, ca.m_aesKey + sizeof(ca.m_aesKey));
+                                  ca.m_aesKey, ca.m_aesKey + sizeof(ca.m_aesKey));
   });
   face.receive(*interest);
 
@@ -432,7 +430,7 @@
   auto key = identity.getDefaultKey();
   auto cert = key.getDefaultCertificate();
 
-  util::DummyClientFace face(io, { true, true });
+  util::DummyClientFace face(io, {true, true});
   CaModule ca(face, m_keyChain, "tests/unit-tests/ca.conf.test", "ca-storage-memory");
   advanceClocks(time::milliseconds(20), 60);
 
@@ -446,7 +444,7 @@
   clientCert.setContent(clientKey.getPublicKey().data(), clientKey.getPublicKey().size());
   SignatureInfo signatureInfo;
   signatureInfo.setValidityPeriod(security::ValidityPeriod(time::system_clock::now(),
-          time::system_clock::now() + time::hours(10)));
+                                                           time::system_clock::now() + time::hours(10)));
   m_keyChain.sign(clientCert, signingByKey(clientKey.getName()).setSignatureInfo(signatureInfo));
 
   ClientModule client(m_keyChain);
@@ -458,7 +456,7 @@
   auto interest = client.generateRevokeInterest(clientCert);
 
   int count = 0;
-  face.onSendData.connect([&] (const Data& response) {
+  face.onSendData.connect([&](const Data& response) {
     count++;
   });
   face.receive(*interest);
@@ -467,8 +465,7 @@
   BOOST_CHECK_EQUAL(count, 0);
 }
 
-
-BOOST_AUTO_TEST_SUITE_END() // TestCaModule
+BOOST_AUTO_TEST_SUITE_END()  // TestCaModule
 
 }  // namespace tests
 }  // namespace ndncert
diff --git a/tests/unit-tests/challenge-credential.t.cpp b/tests/unit-tests/challenge-credential.t.cpp
index e600e20..358175e 100644
--- a/tests/unit-tests/challenge-credential.t.cpp
+++ b/tests/unit-tests/challenge-credential.t.cpp
@@ -89,15 +89,14 @@
 
   Block params = makeEmptyBlock(tlv_encrypted_payload);
   params.push_back(makeStringBlock(tlv_selected_challenge, "Credential"));
-  params.push_back(makeStringBlock(tlv_parameter_key, ChallengeCredential::JSON_CREDENTIAL_CERT));
+  params.push_back(makeStringBlock(tlv_parameter_key, ChallengeCredential::PARAMETER_KEY_CREDENTIAL_CERT));
   params.push_back(makeStringBlock(tlv_parameter_value, credentialStr));
-  params.push_back(makeStringBlock(tlv_parameter_key, ChallengeCredential::JSON_PROOF_OF_PRIVATE_KEY));
+  params.push_back(makeStringBlock(tlv_parameter_key, ChallengeCredential::PARAMETER_KEY_PROOF_OF_PRIVATE_KEY));
   params.push_back(makeStringBlock(tlv_parameter_value, selfSignedStr));
   params.encode();
 
   challenge.handleChallengeRequest(params, request);
   BOOST_CHECK(request.m_status == Status::PENDING);
-  BOOST_CHECK_EQUAL(request.m_challengeStatus, CHALLENGE_STATUS_SUCCESS);
 }
 
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/unit-tests/challenge-email.t.cpp b/tests/unit-tests/challenge-email.t.cpp
index 4beb6b4..d397e58 100644
--- a/tests/unit-tests/challenge-email.t.cpp
+++ b/tests/unit-tests/challenge-email.t.cpp
@@ -48,7 +48,7 @@
   CertificateRequest request(Name("/ndn/site1"), "123", REQUEST_TYPE_NEW, Status::BEFORE_CHALLENGE, cert);
 
   Block paramTLV = makeEmptyBlock(tlv_encrypted_payload);
-  paramTLV.push_back(makeStringBlock(tlv_parameter_key, ChallengeEmail::JSON_EMAIL));
+  paramTLV.push_back(makeStringBlock(tlv_parameter_key, ChallengeEmail::PARAMETER_KEY_EMAIL));
   paramTLV.push_back(makeStringBlock(tlv_parameter_value, "zhiyi@cs.ucla.edu"));
 
   ChallengeEmail challenge("./tests/unit-tests/test-send-email.sh");
@@ -56,7 +56,7 @@
 
   BOOST_CHECK(request.m_status == Status::CHALLENGE);
   BOOST_CHECK_EQUAL(request.m_challengeStatus, ChallengeEmail::NEED_CODE);
-  BOOST_CHECK(request.m_challengeSecrets.get<std::string>(ChallengeEmail::JSON_CODE) != "");
+  BOOST_CHECK(request.m_challengeSecrets.get<std::string>(ChallengeEmail::PARAMETER_KEY_CODE) != "");
   BOOST_CHECK(request.m_remainingTime != 0);
   BOOST_CHECK(request.m_remainingTries != 0);
   BOOST_CHECK(request.m_challengeTp != "");
@@ -76,7 +76,7 @@
 
   end = line.find(delimiter);
   std::string secret = line.substr(0, end);
-  auto stored_secret = request.m_challengeSecrets.get<std::string>(ChallengeEmail::JSON_CODE);
+  auto stored_secret = request.m_challengeSecrets.get<std::string>(ChallengeEmail::PARAMETER_KEY_CODE);
   BOOST_CHECK_EQUAL(secret, stored_secret);
   line = line.substr(end + 1);
 
@@ -98,13 +98,12 @@
   CertificateRequest request(Name("/ndn/site1"), "123", REQUEST_TYPE_NEW, Status::BEFORE_CHALLENGE, cert);
 
   Block paramTLV = makeEmptyBlock(tlv_encrypted_payload);
-  paramTLV.push_back(makeStringBlock(tlv_parameter_key, ChallengeEmail::JSON_EMAIL));
+  paramTLV.push_back(makeStringBlock(tlv_parameter_key, ChallengeEmail::PARAMETER_KEY_EMAIL));
   paramTLV.push_back(makeStringBlock(tlv_parameter_value, "zhiyi@cs"));
 
   ChallengeEmail challenge;
   challenge.handleChallengeRequest(paramTLV, request);
 
-  BOOST_CHECK_EQUAL(request.m_challengeStatus, ChallengeEmail::FAILURE_INVALID_EMAIL);
   BOOST_CHECK(request.m_status == Status::FAILURE);
 }
 
@@ -114,18 +113,17 @@
   auto key = identity.getDefaultKey();
   auto cert = key.getDefaultCertificate();
   JsonSection json;
-  json.put(ChallengeEmail::JSON_CODE, "4567");
+  json.put(ChallengeEmail::PARAMETER_KEY_CODE, "4567");
   CertificateRequest request(Name("/ndn/site1"), "123", REQUEST_TYPE_NEW, Status::CHALLENGE, ChallengeEmail::NEED_CODE,
                              "Email", time::toIsoString(time::system_clock::now()), 3600, 3, json, cert);
 
   Block paramTLV = makeEmptyBlock(tlv_encrypted_payload);
-  paramTLV.push_back(makeStringBlock(tlv_parameter_key, ChallengeEmail::JSON_CODE));
+  paramTLV.push_back(makeStringBlock(tlv_parameter_key, ChallengeEmail::PARAMETER_KEY_CODE));
   paramTLV.push_back(makeStringBlock(tlv_parameter_value, "4567"));
 
   ChallengeEmail challenge;
   challenge.handleChallengeRequest(paramTLV, request);
 
-  BOOST_CHECK_EQUAL(request.m_challengeStatus, CHALLENGE_STATUS_SUCCESS);
   BOOST_CHECK(request.m_status == Status::PENDING);
   BOOST_CHECK_EQUAL(request.m_challengeSecrets.empty(), true);
 }
@@ -136,12 +134,12 @@
   auto key = identity.getDefaultKey();
   auto cert = key.getDefaultCertificate();
   JsonSection json;
-  json.put(ChallengeEmail::JSON_CODE, "4567");
+  json.put(ChallengeEmail::PARAMETER_KEY_CODE, "4567");
   CertificateRequest request(Name("/ndn/site1"), "123", REQUEST_TYPE_NEW, Status::CHALLENGE, ChallengeEmail::NEED_CODE,
                              "email", time::toIsoString(time::system_clock::now()), 3600, 3, json, cert);
 
   Block paramTLV = makeEmptyBlock(tlv_encrypted_payload);
-  paramTLV.push_back(makeStringBlock(tlv_parameter_key, ChallengeEmail::JSON_CODE));
+  paramTLV.push_back(makeStringBlock(tlv_parameter_key, ChallengeEmail::PARAMETER_KEY_CODE));
   paramTLV.push_back(makeStringBlock(tlv_parameter_value, "7890"));
 
   ChallengeEmail challenge;
diff --git a/tests/unit-tests/challenge-pin.t.cpp b/tests/unit-tests/challenge-pin.t.cpp
index ef0a572..a75444a 100644
--- a/tests/unit-tests/challenge-pin.t.cpp
+++ b/tests/unit-tests/challenge-pin.t.cpp
@@ -54,19 +54,18 @@
   auto key = identity.getDefaultKey();
   auto cert = key.getDefaultCertificate();
   JsonSection secret;
-  secret.add(ChallengePin::JSON_PIN_CODE, "12345");
+  secret.add(ChallengePin::PARAMETER_KEY_CODE, "12345");
   CertificateRequest request(Name("/ndn/site1"), "123", REQUEST_TYPE_NEW, Status::CHALLENGE, ChallengePin::NEED_CODE, "pin",
                              time::toIsoString(time::system_clock::now()), 3600, 3, secret, cert);
 
   Block paramTLV = makeEmptyBlock(tlv_encrypted_payload);
-  paramTLV.push_back(makeStringBlock(tlv_parameter_key, ChallengePin::JSON_PIN_CODE));
+  paramTLV.push_back(makeStringBlock(tlv_parameter_key, ChallengePin::PARAMETER_KEY_CODE));
   paramTLV.push_back(makeStringBlock(tlv_parameter_value, "12345"));
 
   ChallengePin challenge;
   challenge.handleChallengeRequest(paramTLV, request);
 
   BOOST_CHECK(request.m_status == Status::PENDING);
-  BOOST_CHECK_EQUAL(request.m_challengeStatus, CHALLENGE_STATUS_SUCCESS);
   BOOST_CHECK_EQUAL(request.m_challengeSecrets.empty(), true);
 }
 
@@ -76,12 +75,12 @@
   auto key = identity.getDefaultKey();
   auto cert = key.getDefaultCertificate();
   JsonSection secret;
-  secret.add(ChallengePin::JSON_PIN_CODE, "12345");
+  secret.add(ChallengePin::PARAMETER_KEY_CODE, "12345");
   CertificateRequest request(Name("/ndn/site1"), "123", REQUEST_TYPE_NEW, Status::CHALLENGE, ChallengePin::NEED_CODE, "pin",
                              time::toIsoString(time::system_clock::now()), 3600, 3, secret, cert);
 
   Block paramTLV = makeEmptyBlock(tlv_encrypted_payload);
-  paramTLV.push_back(makeStringBlock(tlv_parameter_key, ChallengePin::JSON_PIN_CODE));
+  paramTLV.push_back(makeStringBlock(tlv_parameter_key, ChallengePin::PARAMETER_KEY_CODE));
   paramTLV.push_back(makeStringBlock(tlv_parameter_value, "45678"));
 
   ChallengePin challenge;
diff --git a/tests/unit-tests/challenge-private-key.t.cpp b/tests/unit-tests/challenge-private-key.t.cpp
deleted file mode 100644
index 20eceaa..0000000
--- a/tests/unit-tests/challenge-private-key.t.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/* -*- 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-module/challenge-private-key.hpp"
-#include "test-common.hpp"
-#include <ndn-cxx/security/signing-helpers.hpp>
-#include <ndn-cxx/util/io.hpp>
-
-namespace ndn {
-namespace ndncert {
-namespace tests {
-
-BOOST_FIXTURE_TEST_SUITE(TestChallengeCredential, IdentityManagementFixture)
-
-BOOST_AUTO_TEST_CASE(HandlePrivateKeyChallengeRequest)
-{
-  // create trust anchor
-  ChallengePrivateKey challenge;
-
-  // create certificate request
-  auto identityA = addIdentity(Name("/example"));
-  auto keyA = identityA.getDefaultKey();
-  auto certA = keyA.getDefaultCertificate();
-  CertificateRequest request(Name("/example"), "123", REQUEST_TYPE_REVOKE, Status::BEFORE_CHALLENGE, certA);
-
-  security::v2::Certificate privateKeyProof;
-  privateKeyProof.setName(Name(keyA.getName()).append("proof-of-private-key").appendVersion());
-  privateKeyProof.setContent(makeStringBlock(tlv::Content, "123"));
-  m_keyChain.sign(privateKeyProof, signingByKey(keyA));
-
-  std::stringstream ss;
-  io::save<security::v2::Certificate>(privateKeyProof, ss);
-  auto checkCert = *(io::load<security::v2::Certificate>(ss));
-  BOOST_CHECK_EQUAL(checkCert, privateKeyProof);
-  ss.str("");
-  ss.clear();
-
-  io::save<security::v2::Certificate>(privateKeyProof, ss);
-  std::string selfSignedStr = ss.str();
-  ss.str("");
-  ss.clear();
-
-  Block params = makeEmptyBlock(tlv_encrypted_payload);
-  params.push_back(makeStringBlock(tlv_selected_challenge, "Private Key"));
-  params.push_back(makeStringBlock(tlv_parameter_key, ChallengePrivateKey::JSON_PROOF_OF_PRIVATE_KEY));
-  params.push_back(makeStringBlock(tlv_parameter_value, selfSignedStr));
-  params.encode();
-
-  challenge.handleChallengeRequest(params, request);
-  BOOST_CHECK(request.m_status == Status::PENDING);
-  BOOST_CHECK_EQUAL(request.m_challengeStatus, CHALLENGE_STATUS_SUCCESS);
-}
-
-BOOST_AUTO_TEST_SUITE_END()
-
-}  // namespace tests
-}  // namespace ndncert
-}  // namespace ndn
diff --git a/tools/ndncert-client.cpp b/tools/ndncert-client.cpp
index 52d0fdb..4b3b38a 100644
--- a/tools/ndncert-client.cpp
+++ b/tools/ndncert-client.cpp
@@ -43,24 +43,20 @@
 int validityPeriod = -1;
 ClientModule client(keyChain);
 
-static std::list<std::string>
-captureParams(const JsonSection& requirement)
+static void
+captureParams(std::vector<std::tuple<std::string, std::string>>& requirement)
 {
   std::list<std::string> results;
-  for (const auto& item : requirement) {
-    std::cerr << item.second.get<std::string>("") << std::endl;
-    std::cerr << "Please provide the argument: " << item.first << " : " << std::endl;
-    std::string tempParam;
-    getline(std::cin, tempParam);
-    results.push_back(tempParam);
+  for (auto& item : requirement) {
+    std::cerr << std::get<1>(item) << std::endl;
+    std::string captured;
+    getline(std::cin, captured);
+    std::get<1>(item) = captured;
   }
   std::cerr << "Got it. This is what you've provided:" << std::endl;
-  auto it1 = results.begin();
-  auto it2 = requirement.begin();
-  for (; it1 != results.end() && it2 != requirement.end(); it1++, it2++) {
-    std::cerr << it2->first << " : " << *it1 << std::endl;
+  for (const auto& item : requirement) {
+    std::cerr << std::get<0>(item) << " : " << std::get<1>(item) << std::endl;
   }
-  return results;
 }
 
 static std::list<std::string>
@@ -135,22 +131,15 @@
   }
 
   auto challenge = ChallengeModule::createChallengeModule(challengeType);
-  auto requirement = challenge->getRequirementForChallenge(client.getApplicationStatus(),
-                                                           client.getChallengeStatus());
+  auto requirement = challenge->getRequestedParameterList(client.getApplicationStatus(),
+                                                          client.getChallengeStatus());
   if (requirement.size() > 0) {
     std::cerr << "Step " << nStep++ << ": Please satisfy following instruction(s)\n";
-    std::string redo = "";
-    std::list<std::string> capturedParams;
-    capturedParams = captureParams(requirement);
-    auto it1 = capturedParams.begin();
-    auto it2 = requirement.begin();
-    for (; it1 != capturedParams.end() && it2 != requirement.end(); it1++, it2++) {
-      it2->second.put("", *it1);
-    }
+    captureParams(requirement);
   }
   face.expressInterest(*client.generateChallengeInterest(challenge->genChallengeRequestTLV(client.getApplicationStatus(),
                                                                                            client.getChallengeStatus(),
-                                                                                           requirement)),
+                                                                                           std::move(requirement))),
                        bind(&challengeCb, _2), bind(&onNackCb), bind(&timeoutCb));
 }
 
@@ -194,22 +183,15 @@
     std::cerr << "Error. Cannot load selected Challenge Module. Exit." << std::endl;
     return;
   }
-  auto requirement = challenge->getRequirementForChallenge(client.getApplicationStatus(),
-                                                           client.getChallengeStatus());
+  auto requirement = challenge->getRequestedParameterList(client.getApplicationStatus(),
+                                                          client.getChallengeStatus());
   if (requirement.size() > 0) {
-    std::cerr << "Step " << nStep++ << ": Please satisfy following instruction(s)\n";
-    std::string redo = "";
-    std::list<std::string> capturedParams;
-    capturedParams = captureParams(requirement);
-    auto it1 = capturedParams.begin();
-    auto it2 = requirement.begin();
-    for (; it1 != capturedParams.end() && it2 != requirement.end(); it1++, it2++) {
-      it2->second.put("", *it1);
-    }
+    std::cerr << "Step " << nStep++ << ": Please provide parameters used for Identity Verification Challenge\n";
+    captureParams(requirement);
   }
   face.expressInterest(*client.generateChallengeInterest(challenge->genChallengeRequestTLV(client.getApplicationStatus(),
                                                                                            client.getChallengeStatus(),
-                                                                                           requirement)),
+                                                                                           std::move(requirement))),
                        bind(&challengeCb, _2), bind(&onNackCb), bind(&timeoutCb));
 }