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