Update the NDNCERT library to version NDNCERT v2

spec:[https://github.com/named-data/ndncert/wiki/NDNCERT-Protocol-new]

Change-Id: Ia480a8e70c4b38ca170dfe2fcf50d1265ab65f46
diff --git a/src/challenge-module/challenge-credential.cpp b/src/challenge-module/challenge-credential.cpp
index 2a47e7a..839ccae 100644
--- a/src/challenge-module/challenge-credential.cpp
+++ b/src/challenge-module/challenge-credential.cpp
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2017, Regents of the University of California.
+ * Copyright (c) 2017-2019, Regents of the University of California.
  *
  * This file is part of ndncert, a certificate management system based on NDN.
  *
@@ -29,9 +29,9 @@
 
 NDNCERT_REGISTER_CHALLENGE(ChallengeCredential, "Credential");
 
-const std::string ChallengeCredential::FAILURE_INVALID_FORMAT = "failure-invalid-format";
+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_CREDENTIAL_SELF = "self-signed";
 
@@ -71,101 +71,91 @@
   }
 }
 
-JsonSection
-ChallengeCredential::processSelectInterest(const Interest& interest, CertificateRequest& request)
+// For CA
+void
+ChallengeCredential::handleChallengeRequest(const JsonSection& params, CertificateRequest& request)
 {
   if (m_trustAnchors.empty()) {
     parseConfigFile();
   }
-
-  // interest format: /caName/CA/_SELECT/{"request-id":"id"}/CREDENTIAL/{"credential":"..."}/<signature>
-  request.setChallengeType(CHALLENGE_TYPE);
-  JsonSection credentialJson = getJsonFromNameComponent(interest.getName(), request.getCaName().size() + 4);
-
-  // load credential parameters
-  std::istringstream ss1(credentialJson.get<std::string>(JSON_CREDENTIAL_CERT));
+  // load credential parameter
+  std::istringstream ss1(params.get<std::string>(JSON_CREDENTIAL_CERT));
   security::v2::Certificate cert;
   try {
     cert = *(io::load<security::v2::Certificate>(ss1));
   }
   catch (const std::exception& e) {
-    _LOG_TRACE("Cannot load credential parameter: cert" << e.what());
-    request.setStatus(FAILURE_INVALID_FORMAT);
-    return genFailureJson(request.getRequestId(), CHALLENGE_TYPE, FAILURE, FAILURE_INVALID_FORMAT);
+    _LOG_ERROR("Cannot load credential parameter: cert" << e.what());
+    request.m_status = STATUS_FAILURE;
+    request.m_challengeStatus = FAILURE_INVALID_FORMAT_CREDENTIAL;
+    updateRequestOnChallengeEnd(request);
+    return;
   }
   ss1.str("");
   ss1.clear();
-
-  std::istringstream ss2(credentialJson.get<std::string>(JSON_CREDENTIAL_SELF));
-  security::v2::Certificate self;
+  // load self-signed data
+  std::istringstream ss2(params.get<std::string>(JSON_CREDENTIAL_SELF));
+  Data self;
   try {
-    self = *(io::load<security::v2::Certificate>(ss2));
+    self = *(io::load<Data>(ss2));
   }
   catch (const std::exception& e) {
     _LOG_TRACE("Cannot load credential parameter: self-signed cert" << e.what());
-    request.setStatus(FAILURE_INVALID_FORMAT);
-    return genFailureJson(request.getRequestId(), CHALLENGE_TYPE, FAILURE, FAILURE_INVALID_FORMAT);
+    request.m_status = STATUS_FAILURE;
+    request.m_challengeStatus = FAILURE_INVALID_FORMAT_SELF_SIGNED;
+    updateRequestOnChallengeEnd(request);
+    return;
   }
   ss2.str("");
   ss2.clear();
 
-  // verify two parameters
+  // verify the credential and the self-signed cert
   Name signingKeyName = cert.getSignature().getKeyLocator().getName();
   for (auto anchor : m_trustAnchors) {
     if (anchor.getKeyName() == signingKeyName) {
-      if (security::verifySignature(cert, anchor) && security::verifySignature(self, cert)) {
-        request.setStatus(SUCCESS);
-        return genResponseChallengeJson(request.getRequestId(), CHALLENGE_TYPE, SUCCESS);
+      if (security::verifySignature(cert, anchor) && security::verifySignature(self, cert)
+          && readString(self.getContent()) == request.m_requestId) {
+        request.m_status = STATUS_PENDING;
+        request.m_challengeStatus = CHALLENGE_STATUS_SUCCESS;
+        updateRequestOnChallengeEnd(request);
+        return;
       }
     }
   }
 
-  request.setStatus(FAILURE_INVALID_CREDENTIAL);
-  return genResponseChallengeJson(request.getRequestId(), CHALLENGE_TYPE, FAILURE_INVALID_CREDENTIAL);
+  _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;
 }
 
+// For Client
 JsonSection
-ChallengeCredential::processValidateInterest(const Interest& interest, CertificateRequest& request)
-{
-  // there is no validate request here, do nothing
-  return genFailureJson(request.getRequestId(), CHALLENGE_TYPE, FAILURE, FAILURE_INVALID_FORMAT);
-}
-
-std::list<std::string>
-ChallengeCredential::getSelectRequirements()
-{
-  std::list<std::string> result;
-  result.push_back("Please input the bytes of a certificate issued by the trusted CA");
-  result.push_back("Please input the bytes of a self-signed certificate for the corresponding key");
-  return result;
-}
-
-std::list<std::string>
-ChallengeCredential::getValidateRequirements(const std::string& status)
-{
-  // there is no validate request here, do nothing
-  std::list<std::string> result;
-  return result;
-}
-
-JsonSection
-ChallengeCredential::doGenSelectParamsJson(const std::string& status,
-                                           const std::list<std::string>& paramList)
+ChallengeCredential::getRequirementForChallenge(int status, const std::string& challengeStatus)
 {
   JsonSection result;
-  BOOST_ASSERT(status == WAIT_SELECTION);
-  BOOST_ASSERT(paramList.size() == 2);
-  result.put(JSON_CREDENTIAL_CERT, paramList.front());
-  result.put(JSON_CREDENTIAL_SELF, paramList.back());
+  if (status == STATUS_BEFORE_CHALLENGE && challengeStatus == "") {
+    result.put(JSON_CREDENTIAL_CERT, "Please_copy_anchor_signed_cert_here");
+    result.put(JSON_CREDENTIAL_SELF, "Please_copy_key_signed_request_id_data_here");
+  }
+  else {
+    _LOG_ERROR("Client's status and challenge status are wrong");
+  }
   return result;
 }
 
 JsonSection
-ChallengeCredential::doGenValidateParamsJson(const std::string& status,
-                                             const std::list<std::string>& paramList)
+ChallengeCredential::genChallengeRequestJson(int status, const std::string& challengeStatus, const JsonSection& params)
 {
   JsonSection result;
-  BOOST_ASSERT(paramList.size() == 0);
+  if (status == STATUS_BEFORE_CHALLENGE && challengeStatus == "") {
+    result.put(JSON_CREDENTIAL_CERT, params.get<std::string>(JSON_CREDENTIAL_CERT, ""));
+    result.put(JSON_CREDENTIAL_SELF, params.get<std::string>(JSON_CREDENTIAL_SELF, ""));
+  }
+  else {
+    _LOG_ERROR("Client's status and challenge status are wrong");
+  }
   return result;
 }
 
diff --git a/src/challenge-module/challenge-credential.hpp b/src/challenge-module/challenge-credential.hpp
index bcd8b5d..5d4a93c 100644
--- a/src/challenge-module/challenge-credential.hpp
+++ b/src/challenge-module/challenge-credential.hpp
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2017, Regents of the University of California.
+ * Copyright (c) 2017-2019, Regents of the University of California.
  *
  * This file is part of ndncert, a certificate management system based on NDN.
  *
@@ -29,16 +29,17 @@
  * @brief Provide Credential based challenge
  *
  * Credential here means the certificate issued by a trust anchor. Once the requester
- * could proof his/her possession of an existing certificate from other certificate, th
- * requester could finish the challenge.
+ * could proof his/her possession of an existing certificate from other certificate issuer,
+ * the requester could finish the challenge.
  *
  * The requester needs to provide the proof of the possession of a certificate issued by
  * a trust anchor. The challenge require the requester to pass the BASE64 certificate and
- * a BASE64 self-signed certificate whose key is the same as the key in certificate.
+ * 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. Requester provides a certificate signed by that trusted certificate as credential.
  *   2. The challenge module will verify the signature of the credential.
+ *   3. The content of the signed Data is the request id
  *
  * Failure info when application fails:
  *   FAILURE_INVALID_CREDENTIAL: When the cert issued from trust anchor or self-signed cert
@@ -50,26 +51,16 @@
 public:
   ChallengeCredential(const std::string& configPath = "");
 
-PUBLIC_WITH_TESTS_ELSE_PROTECTED:
+  // For CA
+  void
+  handleChallengeRequest(const JsonSection& params, CertificateRequest& request) override;
+
+  // For Client
   JsonSection
-  processSelectInterest(const Interest& interest, CertificateRequest& request) override;
+  getRequirementForChallenge(int status, const std::string& challengeStatus) override;
 
   JsonSection
-  processValidateInterest(const Interest& interest, CertificateRequest& request) override;
-
-  std::list<std::string>
-  getSelectRequirements() override;
-
-  std::list<std::string>
-  getValidateRequirements(const std::string& status) override;
-
-  JsonSection
-  doGenSelectParamsJson(const std::string& status,
-                        const std::list<std::string>& paramList) override;
-
-  JsonSection
-  doGenValidateParamsJson(const std::string& status,
-                          const std::list<std::string>& paramList) override;
+  genChallengeRequestJson(int status, const std::string& challengeStatus, const JsonSection& params) override;
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   void
@@ -77,8 +68,8 @@
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   static const std::string FAILURE_INVALID_CREDENTIAL;
-  static const std::string FAILURE_INVALID_FORMAT;
-
+  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_CREDENTIAL_SELF;
 
diff --git a/src/challenge-module/challenge-email.cpp b/src/challenge-module/challenge-email.cpp
index b65d331..dbd3b2c 100644
--- a/src/challenge-module/challenge-email.cpp
+++ b/src/challenge-module/challenge-email.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2017-2018, Regents of the University of California.
+ * Copyright (c) 2017-2019, Regents of the University of California.
  *
  * This file is part of ndncert, a certificate management system based on NDN.
  *
@@ -31,15 +31,9 @@
 
 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::FAILURE_TIMEOUT = "timeout";
-const std::string ChallengeEmail::FAILURE_MAXRETRY = "max-retry";
-
 const std::string ChallengeEmail::JSON_EMAIL = "email";
-const std::string ChallengeEmail::JSON_CODE_TP = "code-timepoint";
 const std::string ChallengeEmail::JSON_CODE = "code";
-const std::string ChallengeEmail::JSON_ATTEMPT_TIMES = "attempt-times";
 
 ChallengeEmail::ChallengeEmail(const std::string& scriptPath,
                                const size_t& maxAttemptTimes,
@@ -51,131 +45,123 @@
 {
 }
 
-JsonSection
-ChallengeEmail::processSelectInterest(const Interest& interest, CertificateRequest& request)
+// For CA
+void
+ChallengeEmail::handleChallengeRequest(const JsonSection& params, CertificateRequest& request)
 {
-  // interest format: /caName/CA/_SELECT/{"request-id":"id"}/EMAIL/{"Email":"email"}/<signature>
-  JsonSection emailJson = getJsonFromNameComponent(interest.getName(),
-                                                   request.getCaName().size() + 4);
-  std::string emailAddress = emailJson.get<std::string>(JSON_EMAIL);
-  if (!isValidEmailAddress(emailAddress)) {
-    request.setStatus(FAILURE_INVALID_EMAIL);
-    request.setChallengeType(CHALLENGE_TYPE);
-    return genFailureJson(request.getRequestId(), CHALLENGE_TYPE, FAILURE, FAILURE_INVALID_EMAIL);
+  if (request.m_challengeStatus == "") {
+    // for the first time, init the challenge
+    std::string emailAddress = params.get<std::string>(JSON_EMAIL);
+    if (!isValidEmailAddress(emailAddress)) {
+      request.m_status = STATUS_FAILURE;
+      request.m_challengeStatus = FAILURE_INVALID_EMAIL;
+      return;
+    }
+    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(time::system_clock::now());
+    request.m_remainingTime = m_secretLifetime.count();
+    request.m_remainingTries = m_maxAttemptTimes;
+    // send out the email
+    sendEmail(emailAddress, emailCode, request);
+    _LOG_TRACE("Secret for request " << request.m_requestId << " : " << emailCode);
+    return;
   }
-
-  std::string emailCode = generateSecretCode();
-  sendEmail(emailAddress, emailCode, request.getCaName().toUri());
-
-  request.setStatus(NEED_CODE);
-  request.setChallengeType(CHALLENGE_TYPE);
-  request.setChallengeSecrets(generateStoredSecrets(time::system_clock::now(),
-                                                    emailCode, m_maxAttemptTimes));
-  return genResponseChallengeJson(request.getRequestId(), CHALLENGE_TYPE, NEED_CODE);
-}
-
-JsonSection
-ChallengeEmail::processValidateInterest(const Interest& interest, CertificateRequest& request)
-{
-  // interest format: /caName/CA/_VALIDATION/{"request-id":"id"}/EMAIL/{"code":"code"}/<signature>
-  JsonSection infoJson = getJsonFromNameComponent(interest.getName(), request.getCaName().size() + 4);
-  std::string givenCode = infoJson.get<std::string>(JSON_CODE);
-
-  const auto parsedSecret = parseStoredSecrets(request.getChallengeSecrets());
-  if (time::system_clock::now() - std::get<0>(parsedSecret) >= m_secretLifetime) {
-    // secret expires
-    request.setStatus(FAILURE_TIMEOUT);
-    request.setChallengeSecrets(JsonSection());
-    return genFailureJson(request.getRequestId(), CHALLENGE_TYPE, FAILURE, FAILURE_TIMEOUT);
-  }
-  else if (givenCode == std::get<1>(parsedSecret)) {
-    request.setStatus(SUCCESS);
-    request.setChallengeSecrets(JsonSection());
-    Name downloadName = genDownloadName(request.getCaName(), request.getRequestId());
-    return genResponseChallengeJson(request.getRequestId(), CHALLENGE_TYPE, SUCCESS, downloadName);
-  }
-  else {
-    // check rest attempt times
-    if (std::get<2>(parsedSecret) > 1) {
-      int restAttemptTimes = std::get<2>(parsedSecret) - 1;
-      request.setStatus(WRONG_CODE);
-      request.setChallengeSecrets(generateStoredSecrets(std::get<0>(parsedSecret),
-                                                        std::get<1>(parsedSecret),
-                                                        restAttemptTimes));
-      return genResponseChallengeJson(request.getRequestId(), CHALLENGE_TYPE, WRONG_CODE);
+  else if (request.m_challengeStatus == NEED_CODE || request.m_challengeStatus == WRONG_CODE) {
+    _LOG_TRACE("Challenge Interest arrives. Challenge Status: " << request.m_challengeStatus);
+    // the incoming interest should bring the pin code
+    std::string givenCode = params.get<std::string>(JSON_CODE);
+    const auto realCode = request.m_challengeSecrets.get<std::string>(JSON_CODE);
+    if (time::system_clock::now() - 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;
+    }
+    else if (givenCode == realCode) {
+      // 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;
     }
     else {
-      // run out times
-      request.setStatus(FAILURE_MAXRETRY);
-      request.setChallengeSecrets(JsonSection());
-      return genFailureJson(request.getRequestId(), CHALLENGE_TYPE, FAILURE, FAILURE_MAXRETRY);
+      // 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 - (time::system_clock::now() - 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;
+      }
     }
   }
-}
-
-std::list<std::string>
-ChallengeEmail::getSelectRequirements()
-{
-  std::list<std::string> result;
-  result.push_back("Please input your email address:");
-  return result;
-}
-
-std::list<std::string>
-ChallengeEmail::getValidateRequirements(const std::string& status)
-{
-  std::list<std::string> result;
-  if (status == NEED_CODE) {
-    result.push_back("Please input your verification code:");
+  else {
+    _LOG_ERROR("The challenge status is wrong");
+    request.m_status = STATUS_FAILURE;
+    return;
   }
-  else if (status == WRONG_CODE) {
-    result.push_back("Incorrect PIN code, please try again and input your verification code:");
+}
+
+// For Client
+JsonSection
+ChallengeEmail::getRequirementForChallenge(int status, const std::string& challengeStatus)
+{
+  JsonSection result;
+  if (status == STATUS_BEFORE_CHALLENGE && challengeStatus == "") {
+    result.put(JSON_EMAIL, "Please_input_your_email_address");
+  }
+  else if (status == STATUS_CHALLENGE && challengeStatus == NEED_CODE) {
+    result.put(JSON_CODE, "Please_input_your_verification_code");
+  }
+  else if (status == STATUS_CHALLENGE && challengeStatus == WRONG_CODE) {
+    result.put(JSON_CODE, "Incorrect_code_please_try_again");
+  }
+  else {
+    _LOG_ERROR("CA's status and challenge status are wrong");
   }
   return result;
 }
 
 JsonSection
-ChallengeEmail::doGenSelectParamsJson(const std::string& status,
-                                      const std::list<std::string>& paramList)
+ChallengeEmail::genChallengeRequestJson(int status, const std::string& challengeStatus, const JsonSection& params)
 {
   JsonSection result;
-  BOOST_ASSERT(status == WAIT_SELECTION);
-  BOOST_ASSERT(paramList.size() == 1);
-  result.put(JSON_EMAIL, paramList.front());
+  if (status == STATUS_BEFORE_CHALLENGE && challengeStatus == "") {
+    result.put(JSON_CLIENT_SELECTED_CHALLENGE, CHALLENGE_TYPE);
+    result.put(JSON_EMAIL, params.get<std::string>(JSON_EMAIL, ""));
+  }
+  else if (status == STATUS_CHALLENGE && challengeStatus == NEED_CODE) {
+    result.put(JSON_CLIENT_SELECTED_CHALLENGE, CHALLENGE_TYPE);
+    result.put(JSON_CODE, params.get<std::string>(JSON_CODE, ""));
+  }
+  else if (status == STATUS_CHALLENGE && challengeStatus == WRONG_CODE) {
+    result.put(JSON_CLIENT_SELECTED_CHALLENGE, CHALLENGE_TYPE);
+    result.put(JSON_CODE, params.get<std::string>(JSON_CODE, ""));
+  }
+  else {
+    _LOG_ERROR("Client's status and challenge status are wrong");
+  }
   return result;
 }
 
-JsonSection
-ChallengeEmail::doGenValidateParamsJson(const std::string& status,
-                                        const std::list<std::string>& paramList)
-{
-  JsonSection result;
-  BOOST_ASSERT(paramList.size() == 1);
-  result.put(JSON_CODE, paramList.front());
-  return result;
-}
-
-std::tuple<time::system_clock::TimePoint, std::string, int>
-ChallengeEmail::parseStoredSecrets(const JsonSection& storedSecrets)
-{
-  auto tp = time::fromIsoString(storedSecrets.get<std::string>(JSON_CODE_TP));
-  std::string rightCode= storedSecrets.get<std::string>(JSON_CODE);
-  int attemptTimes = std::stoi(storedSecrets.get<std::string>(JSON_ATTEMPT_TIMES));
-
-  return std::make_tuple(tp, rightCode, attemptTimes);
-}
-
-JsonSection
-ChallengeEmail::generateStoredSecrets(const time::system_clock::TimePoint& tp,
-                                    const std::string& secretCode, int attempTimes)
-{
-  JsonSection json;
-  json.put(JSON_CODE_TP, time::toIsoString(tp));
-  json.put(JSON_CODE, secretCode);
-  json.put(JSON_ATTEMPT_TIMES, std::to_string(attempTimes));
-  return json;
-}
-
 bool
 ChallengeEmail::isValidEmailAddress(const std::string& emailAddress)
 {
@@ -186,10 +172,10 @@
 
 void
 ChallengeEmail::sendEmail(const std::string& emailAddress, const std::string& secret,
-                          const std::string& caName) const
+                          const CertificateRequest& request) const
 {
   std::string command = m_sendEmailScript;
-  command += " \"" + emailAddress + "\" \"" + secret + "\" \"" + caName + "\"";
+  command += " \"" + emailAddress + "\" \"" + secret + "\" \"" + request.m_caName.toUri() + "\"";
   int result = system(command.c_str());
   if (result == -1) {
     _LOG_TRACE("EmailSending Script " + m_sendEmailScript + " fails.");
diff --git a/src/challenge-module/challenge-email.hpp b/src/challenge-module/challenge-email.hpp
index a014d00..2f40f1f 100644
--- a/src/challenge-module/challenge-email.hpp
+++ b/src/challenge-module/challenge-email.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2017, Regents of the University of California.
+ * Copyright (c) 2017-2019, Regents of the University of California.
  *
  * This file is part of ndncert, a certificate management system based on NDN.
  *
@@ -56,26 +56,16 @@
                  const size_t& maxAttemptTimes = 3,
                  const time::seconds secretLifetime = time::minutes(20));
 
-PUBLIC_WITH_TESTS_ELSE_PROTECTED:
+  // For CA
+  void
+  handleChallengeRequest(const JsonSection& params, CertificateRequest& request) override;
+
+  // For Client
   JsonSection
-  processSelectInterest(const Interest& interest, CertificateRequest& request) override;
+  getRequirementForChallenge(int status, const std::string& challengeStatus) override;
 
   JsonSection
-  processValidateInterest(const Interest& interest, CertificateRequest& request) override;
-
-  std::list<std::string>
-  getSelectRequirements() override;
-
-  std::list<std::string>
-  getValidateRequirements(const std::string& status) override;
-
-  JsonSection
-  doGenSelectParamsJson(const std::string& status,
-                        const std::list<std::string>& paramList) override;
-
-  JsonSection
-  doGenValidateParamsJson(const std::string& status,
-                          const std::list<std::string>& paramList) override;
+  genChallengeRequestJson(int status, const std::string& challengeStatus, const JsonSection& params) override;
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   static bool
@@ -83,28 +73,16 @@
 
   void
   sendEmail(const std::string& emailAddress, const std::string& secret,
-            const std::string& caName) const;
+            const CertificateRequest& request) const;
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
-  static std::tuple<time::system_clock::TimePoint, std::string, int>
-  parseStoredSecrets(const JsonSection& storedSecret);
-
-  static JsonSection
-  generateStoredSecrets(const time::system_clock::TimePoint& tp, const std::string& secretCode,
-                        int attempTimes);
-
-PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  // challenge status
   static const std::string NEED_CODE;
   static const std::string WRONG_CODE;
-
-  static const std::string FAILURE_TIMEOUT;
   static const std::string FAILURE_INVALID_EMAIL;
-  static const std::string FAILURE_MAXRETRY;
-
+  // JSON attribute
   static const std::string JSON_EMAIL;
-  static const std::string JSON_CODE_TP;
   static const std::string JSON_CODE;
-  static const std::string JSON_ATTEMPT_TIMES;
 
 private:
   std::string m_sendEmailScript;
diff --git a/src/challenge-module/challenge-pin.cpp b/src/challenge-module/challenge-pin.cpp
index 2bb81ea..7de32f0 100644
--- a/src/challenge-module/challenge-pin.cpp
+++ b/src/challenge-module/challenge-pin.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2017, Regents of the University of California.
+ * Copyright (c) 2017-2019, Regents of the University of California.
  *
  * This file is part of ndncert, a certificate management system based on NDN.
  *
@@ -20,7 +20,6 @@
 
 #include "challenge-pin.hpp"
 #include "logging.hpp"
-#include "json-helper.hpp"
 #include <ndn-cxx/util/random.hpp>
 
 namespace ndn {
@@ -32,13 +31,7 @@
 
 const std::string ChallengePin::NEED_CODE = "need-code";
 const std::string ChallengePin::WRONG_CODE = "wrong-code";
-
-const std::string ChallengePin::FAILURE_TIMEOUT = "failure-timeout";
-const std::string ChallengePin::FAILURE_MAXRETRY = "failure-max-retry";
-
-const std::string ChallengePin::JSON_CODE_TP = "code-timepoint";
-const std::string ChallengePin::JSON_PIN_CODE = "code";
-const std::string ChallengePin::JSON_ATTEMPT_TIMES = "attempt-times";
+const std::string ChallengePin::JSON_PIN_CODE = "pin-code";
 
 ChallengePin::ChallengePin(const size_t& maxAttemptTimes, const time::seconds& secretLifetime)
   : ChallengeModule("PIN")
@@ -47,119 +40,115 @@
 {
 }
 
-JsonSection
-ChallengePin::processSelectInterest(const Interest& interest, CertificateRequest& request)
+// For CA
+void
+ChallengePin::handleChallengeRequest(const JsonSection& params, CertificateRequest& request)
 {
-  // interest format: /caName/CA/_SELECT/{"request-id":"id"}/PIN/<signature>
-  request.setStatus(NEED_CODE);
-  request.setChallengeType(CHALLENGE_TYPE);
-  std::string secretCode = generateSecretCode();
-  request.setChallengeSecrets(generateStoredSecrets(time::system_clock::now(),
-                                                    secretCode,
-                                                    m_maxAttemptTimes));
-  _LOG_TRACE("Secret for request " << request.getRequestId() << " : " << secretCode);
-  return genResponseChallengeJson(request.getRequestId(), CHALLENGE_TYPE, NEED_CODE);
-}
-
-JsonSection
-ChallengePin::processValidateInterest(const Interest& interest, CertificateRequest& request)
-{
-  // interest format: /caName/CA/_VALIDATION/{"request-id":"id"}/PIN/{"code":"code"}/<signature>
-  JsonSection infoJson = getJsonFromNameComponent(interest.getName(), request.getCaName().size() + 4);
-  std::string givenCode = infoJson.get<std::string>(JSON_PIN_CODE);
-
-  const auto parsedSecret = parseStoredSecrets(request.getChallengeSecrets());
-  if (time::system_clock::now() - std::get<0>(parsedSecret) >= m_secretLifetime) {
-    // secret expires
-    request.setStatus(FAILURE_TIMEOUT);
-    request.setChallengeSecrets(JsonSection());
-    return genFailureJson(request.getRequestId(), CHALLENGE_TYPE, FAILURE, FAILURE_TIMEOUT);
+  if (request.m_challengeStatus == "") {
+    _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(time::system_clock::now());
+    request.m_remainingTime = m_secretLifetime.count();
+    request.m_remainingTries = m_maxAttemptTimes;
+    _LOG_TRACE("Secret for request " << request.m_requestId << " : " << secretCode);
+    return;
   }
-  else if (givenCode == std::get<1>(parsedSecret)) {
-    request.setStatus(SUCCESS);
-    request.setChallengeSecrets(JsonSection());
-    Name downloadName = genDownloadName(request.getCaName(), request.getRequestId());
-    return genResponseChallengeJson(request.getRequestId(), CHALLENGE_TYPE, SUCCESS, downloadName);
-  }
-  else {
-    // check rest attempt times
-    if (std::get<2>(parsedSecret) > 1) {
-      int restAttemptTimes = std::get<2>(parsedSecret) - 1;
-      request.setStatus(WRONG_CODE);
-      request.setChallengeSecrets(generateStoredSecrets(std::get<0>(parsedSecret),
-                                                        std::get<1>(parsedSecret),
-                                                        restAttemptTimes));
-      return genResponseChallengeJson(request.getRequestId(), CHALLENGE_TYPE, WRONG_CODE);
+  else if (request.m_challengeStatus == NEED_CODE || request.m_challengeStatus == WRONG_CODE) {
+    _LOG_TRACE("Challenge Interest arrives. Challenge Status: " << request.m_challengeStatus);
+    // the incoming interest should bring the pin code
+    std::string givenCode = params.get<std::string>(JSON_PIN_CODE);
+    const auto realCode = request.m_challengeSecrets.get<std::string>(JSON_PIN_CODE);
+    if (time::system_clock::now() - 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;
+    }
+    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;
     }
     else {
-      // run out times
-      request.setStatus(FAILURE_MAXRETRY);
-      request.setChallengeSecrets(JsonSection());
-      return genFailureJson(request.getRequestId(), CHALLENGE_TYPE, FAILURE, FAILURE_MAXRETRY);
+      // 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 - (time::system_clock::now() - 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;
+      }
     }
   }
-}
-
-std::list<std::string>
-ChallengePin::getSelectRequirements()
-{
-  std::list<std::string> result;
-  return result;
-}
-
-std::list<std::string>
-ChallengePin::getValidateRequirements(const std::string& status)
-{
-  std::list<std::string> result;
-  if (status == NEED_CODE) {
-    result.push_back("Please input your verification code:");
+  else {
+    _LOG_ERROR("The challenge status is wrong");
+    request.m_status = STATUS_FAILURE;
+    return;
   }
-  else if (status == WRONG_CODE) {
-    result.push_back("Incorrect PIN code, please try again and input your verification code:");
+}
+
+// For Client
+JsonSection
+ChallengePin::getRequirementForChallenge(int status, const std::string& challengeStatus)
+{
+  JsonSection result;
+  if (status == STATUS_BEFORE_CHALLENGE && challengeStatus == "") {
+    // do nothing
+  }
+  else if (status == STATUS_CHALLENGE && challengeStatus == NEED_CODE) {
+    result.put(JSON_PIN_CODE, "Please_input_your_verification_code");
+  }
+  else if (status == STATUS_CHALLENGE && challengeStatus == WRONG_CODE) {
+    result.put(JSON_PIN_CODE, "Incorrect_PIN_code_please_try_again");
+  }
+  else {
+    _LOG_ERROR("Client's status and challenge status are wrong");
   }
   return result;
 }
 
 JsonSection
-ChallengePin::doGenSelectParamsJson(const std::string& status,
-                                    const std::list<std::string>& paramList)
+ChallengePin::genChallengeRequestJson(int status, const std::string& challengeStatus, const JsonSection& params)
 {
   JsonSection result;
-  BOOST_ASSERT(status == WAIT_SELECTION);
-  BOOST_ASSERT(paramList.size() == 0);
+  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<std::string>(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<std::string>(JSON_PIN_CODE, ""));
+  }
+  else {
+    _LOG_ERROR("Client's status and challenge status are wrong");
+  }
   return result;
 }
 
-JsonSection
-ChallengePin::doGenValidateParamsJson(const std::string& status,
-                                      const std::list<std::string>& paramList)
-{
-  JsonSection result;
-  BOOST_ASSERT(paramList.size() == 1);
-  result.put(JSON_PIN_CODE, paramList.front());
-  return result;
-}
-
-std::tuple<time::system_clock::TimePoint, std::string, int>
-ChallengePin::parseStoredSecrets(const JsonSection& storedSecrets)
-{
-  auto tp = time::fromIsoString(storedSecrets.get<std::string>(JSON_CODE_TP));
-  std::string rightCode= storedSecrets.get<std::string>(JSON_PIN_CODE);
-  int attemptTimes = std::stoi(storedSecrets.get<std::string>(JSON_ATTEMPT_TIMES));
-
-  return std::make_tuple(tp, rightCode, attemptTimes);
-}
-
-JsonSection
-ChallengePin::generateStoredSecrets(const time::system_clock::TimePoint& tp,
-                                    const std::string& secretCode, int attempTimes)
-{
-  JsonSection json;
-  json.put(JSON_CODE_TP, time::toIsoString(tp));
-  json.put(JSON_PIN_CODE, secretCode);
-  json.put(JSON_ATTEMPT_TIMES, std::to_string(attempTimes));
-  return json;
-}
-
 } // namespace ndncert
 } // namespace ndn
diff --git a/src/challenge-module/challenge-pin.hpp b/src/challenge-module/challenge-pin.hpp
index 982ed4e..8eef944 100644
--- a/src/challenge-module/challenge-pin.hpp
+++ b/src/challenge-module/challenge-pin.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2017, Regents of the University of California.
+ * Copyright (c) 2017-2019, Regents of the University of California.
  *
  * This file is part of ndncert, a certificate management system based on NDN.
  *
@@ -51,45 +51,23 @@
   ChallengePin(const size_t& maxAttemptTimes = 3,
                const time::seconds& secretLifetime = time::seconds(3600));
 
-PUBLIC_WITH_TESTS_ELSE_PROTECTED:
+  // For CA
+  void
+  handleChallengeRequest(const JsonSection& params, CertificateRequest& request) override;
+
+  // For Client
   JsonSection
-  processSelectInterest(const Interest& interest, CertificateRequest& request) override;
+  getRequirementForChallenge(int status, const std::string& challengeStatus) override;
 
   JsonSection
-  processValidateInterest(const Interest& interest, CertificateRequest& request) override;
-
-  std::list<std::string>
-  getSelectRequirements() override;
-
-  std::list<std::string>
-  getValidateRequirements(const std::string& status) override;
-
-  JsonSection
-  doGenSelectParamsJson(const std::string& status,
-                        const std::list<std::string>& paramList) override;
-
-  JsonSection
-  doGenValidateParamsJson(const std::string& status,
-                          const std::list<std::string>& paramList) override;
+  genChallengeRequestJson(int status, const std::string& challengeStatus, const JsonSection& params) override;
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
-  static std::tuple<time::system_clock::TimePoint, std::string, int>
-  parseStoredSecrets(const JsonSection& storedSecret);
-
-  static JsonSection
-  generateStoredSecrets(const time::system_clock::TimePoint& tp, const std::string& secretCode,
-                        int attempTimes);
-
-PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  // challenge status
   static const std::string NEED_CODE;
   static const std::string WRONG_CODE;
-
-  static const std::string FAILURE_TIMEOUT;
-  static const std::string FAILURE_MAXRETRY;
-
-  static const std::string JSON_CODE_TP;
+  // JSON attribute
   static const std::string JSON_PIN_CODE;
-  static const std::string JSON_ATTEMPT_TIMES;
 
 private:
   time::seconds m_secretLifetime;