update certificate request data structure

Change-Id: Id73428d07e6a0315dcdf78da8aba380f429a8f46
diff --git a/src/ca-detail/ca-sqlite.cpp b/src/ca-detail/ca-sqlite.cpp
index 941b8b0..a73f46c 100644
--- a/src/ca-detail/ca-sqlite.cpp
+++ b/src/ca-detail/ca-sqlite.cpp
@@ -20,10 +20,11 @@
 
 #include "ca-sqlite.hpp"
 
+#include <sqlite3.h>
+
+#include <boost/filesystem.hpp>
 #include <ndn-cxx/security/v2/validation-policy.hpp>
 #include <ndn-cxx/util/sqlite3-statement.hpp>
-#include <sqlite3.h>
-#include <boost/filesystem.hpp>
 
 namespace ndn {
 namespace ndncert {
@@ -42,15 +43,14 @@
     ca_name BLOB NOT NULL,
     request_type INTEGER NOT NULL,
     status INTEGER NOT NULL,
-    challenge_status TEXT,
     cert_key_name BLOB NOT NULL,
     cert_request BLOB NOT NULL,
     challenge_type TEXT,
-    challenge_secrets TEXT,
+    challenge_status TEXT,
     challenge_tp TEXT,
     remaining_tries INTEGER,
     remaining_time INTEGER,
-    probe_token BLOB
+    challenge_secrets TEXT
   );
 CREATE UNIQUE INDEX IF NOT EXISTS
   CertRequestIdIndex ON CertRequests(request_id);
@@ -71,7 +71,7 @@
 )_DBTEXT_";
 
 CaSqlite::CaSqlite(const std::string& location)
-  : CaStorage()
+    : CaStorage()
 {
   // Determine the path of sqlite db
   boost::filesystem::path dbDir;
@@ -94,7 +94,7 @@
 #else
                                nullptr
 #endif
-                               );
+  );
   if (result != SQLITE_OK)
     BOOST_THROW_EXCEPTION(Error("CaSqlite DB cannot be opened/created: " + dbDir.string()));
 
@@ -117,27 +117,33 @@
 CaSqlite::getRequest(const std::string& requestId)
 {
   Sqlite3Statement statement(m_database,
-                             R"_SQLTEXT_(SELECT id, request_id, ca_name, status,
-                             challenge_status, cert_key_name, cert_request, challenge_type, challenge_secrets,
-                             challenge_tp, remaining_tries, remaining_time, request_type, probe_token
+                             R"_SQLTEXT_(SELECT id, ca_name, status,
+                             challenge_status, cert_request,
+                             challenge_type, challenge_secrets,
+                             challenge_tp, remaining_tries, remaining_time, request_type
                              FROM CertRequests where request_id = ?)_SQLTEXT_");
   statement.bind(1, requestId, SQLITE_TRANSIENT);
 
   if (statement.step() == SQLITE_ROW) {
-    Name caName(statement.getBlock(2));
-    auto status = static_cast<Status>(statement.getInt(3));
-    auto challengeStatus = statement.getString(4);
-    security::v2::Certificate cert(statement.getBlock(6));
-    auto challengeType = statement.getString(7);
-    auto challengeSecrets = statement.getString(8);
-    auto challengeTp = statement.getString(9);
-    auto remainingTries = statement.getInt(10);
-    auto remainingTime = statement.getInt(11);
-    auto requestType = static_cast<RequestType>(statement.getInt(12));
-    CertificateRequest request(caName, requestId, requestType, status, challengeStatus, challengeType,
-                              challengeTp, remainingTime, remainingTries,
-                              convertString2Json(challengeSecrets), cert);
-    return request;
+    Name caName(statement.getBlock(1));
+    auto status = static_cast<Status>(statement.getInt(2));
+    auto challengeStatus = statement.getString(3);
+    security::v2::Certificate cert(statement.getBlock(4));
+    auto challengeType = statement.getString(5);
+    auto challengeSecrets = statement.getString(6);
+    auto challengeTp = statement.getString(7);
+    auto remainingTries = statement.getInt(8);
+    auto remainingTime = statement.getInt(9);
+    auto requestType = static_cast<RequestType>(statement.getInt(10));
+    if (challengeType != "") {
+      return CertificateRequest(caName, requestId, requestType, status, cert,
+                                challengeType, challengeStatus, time::fromIsoString(challengeTp),
+                                remainingTries, time::seconds(remainingTime),
+                                convertString2Json(challengeSecrets));
+    }
+    else {
+      return CertificateRequest(caName, requestId, requestType, status, cert);
+    }
   }
   else {
     BOOST_THROW_EXCEPTION(Error("Request " + requestId + " cannot be fetched from database"));
@@ -148,43 +154,43 @@
 CaSqlite::addRequest(const CertificateRequest& request)
 {
   // check whether request is there already
+  auto keyNameTlv = request.m_cert.getKeyName().wireEncode();
   Sqlite3Statement statement1(m_database,
                               R"_SQLTEXT_(SELECT * FROM CertRequests where cert_key_name = ?)_SQLTEXT_");
-  statement1.bind(1, request.m_cert.getKeyName().wireEncode(), SQLITE_TRANSIENT);
+  statement1.bind(1, keyNameTlv, SQLITE_TRANSIENT);
   if (statement1.step() == SQLITE_ROW) {
     BOOST_THROW_EXCEPTION(Error("Request for " + request.m_cert.getKeyName().toUri() + " already exists"));
-    return;
   }
 
   // check whether certificate is already issued
   Sqlite3Statement statement2(m_database,
                               R"_SQLTEXT_(SELECT * FROM IssuedCerts where cert_key_name = ?)_SQLTEXT_");
-  statement2.bind(1, request.m_cert.getKeyName().wireEncode(), SQLITE_TRANSIENT);
+  statement2.bind(1, keyNameTlv, SQLITE_TRANSIENT);
   if (statement2.step() == SQLITE_ROW) {
     BOOST_THROW_EXCEPTION(Error("Cert for " + request.m_cert.getKeyName().toUri() + " already exists"));
-    return;
   }
 
   Sqlite3Statement statement(
       m_database,
-      R"_SQLTEXT_(INSERT INTO CertRequests (request_id, ca_name, status,
-                  challenge_status, cert_key_name, cert_request, challenge_type, challenge_secrets,
-                  challenge_tp, remaining_tries, remaining_time, request_type)
+      R"_SQLTEXT_(INSERT INTO CertRequests (request_id, ca_name, status, request_type,
+                  cert_key_name, cert_request, challenge_type, challenge_status, challenge_secrets,
+                  challenge_tp, remaining_tries, remaining_time)
                   values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?))_SQLTEXT_");
   statement.bind(1, request.m_requestId, SQLITE_TRANSIENT);
   statement.bind(2, request.m_caPrefix.wireEncode(), SQLITE_TRANSIENT);
   statement.bind(3, static_cast<int>(request.m_status));
-  statement.bind(4, request.m_challengeStatus, SQLITE_TRANSIENT);
-  statement.bind(5, request.m_cert.getKeyName().wireEncode(),
-                  SQLITE_TRANSIENT);
+  statement.bind(4, static_cast<int>(request.m_requestType));
+  statement.bind(5, keyNameTlv, SQLITE_TRANSIENT);
   statement.bind(6, request.m_cert.wireEncode(), SQLITE_TRANSIENT);
-  statement.bind(7, request.m_challengeType, SQLITE_TRANSIENT);
-  statement.bind(8, convertJson2String(request.m_challengeSecrets),
-                  SQLITE_TRANSIENT);
-  statement.bind(9, request.m_challengeTp, SQLITE_TRANSIENT);
-  statement.bind(10, request.m_remainingTries);
-  statement.bind(11, request.m_remainingTime);
-  statement.bind(12, static_cast<int>(request.m_requestType));
+  if (request.m_challengeState) {
+    statement.bind(7, request.m_challengeType, SQLITE_TRANSIENT);
+    statement.bind(8, request.m_challengeState->m_challengeStatus, SQLITE_TRANSIENT);
+    statement.bind(9, convertJson2String(request.m_challengeState->m_secrets),
+                   SQLITE_TRANSIENT);
+    statement.bind(10, time::toIsoString(request.m_challengeState->m_timestamp), SQLITE_TRANSIENT);
+    statement.bind(11, request.m_challengeState->m_remainingTries);
+    statement.bind(12, request.m_challengeState->m_remainingTime.count());
+  }
   if (statement.step() != SQLITE_DONE) {
     BOOST_THROW_EXCEPTION(Error("Request " + request.m_requestId + " cannot be added to database"));
   }
@@ -195,18 +201,27 @@
 {
   Sqlite3Statement statement(m_database,
                              R"_SQLTEXT_(UPDATE CertRequests
-                             SET status = ?, challenge_status = ?, challenge_type = ?, challenge_secrets = ?,
-                             challenge_tp = ?, remaining_tries = ?, remaining_time = ?, request_type = ?
+                             SET status = ?, challenge_type = ?, challenge_status = ?, challenge_secrets = ?,
+                             challenge_tp = ?, remaining_tries = ?, remaining_time = ?
                              WHERE request_id = ?)_SQLTEXT_");
   statement.bind(1, static_cast<int>(request.m_status));
-  statement.bind(2, request.m_challengeStatus, SQLITE_TRANSIENT);
-  statement.bind(3, request.m_challengeType, SQLITE_TRANSIENT);
-  statement.bind(4, convertJson2String(request.m_challengeSecrets), SQLITE_TRANSIENT);
-  statement.bind(5, request.m_challengeTp, SQLITE_TRANSIENT);
-  statement.bind(6, request.m_remainingTries);
-  statement.bind(7, request.m_remainingTime);
-  statement.bind(8, static_cast<int>(request.m_requestType));
-  statement.bind(9, request.m_requestId, SQLITE_TRANSIENT);
+  statement.bind(2, request.m_challengeType, SQLITE_TRANSIENT);
+  if (request.m_challengeState) {
+    statement.bind(3, request.m_challengeState->m_challengeStatus, SQLITE_TRANSIENT);
+    statement.bind(4, convertJson2String(request.m_challengeState->m_secrets),
+                   SQLITE_TRANSIENT);
+    statement.bind(5, time::toIsoString(request.m_challengeState->m_timestamp), SQLITE_TRANSIENT);
+    statement.bind(6, request.m_challengeState->m_remainingTries);
+    statement.bind(7, request.m_challengeState->m_remainingTime.count());
+  }
+  else {
+    statement.bind(3, "", SQLITE_TRANSIENT);
+    statement.bind(4, "", SQLITE_TRANSIENT);
+    statement.bind(5, "", SQLITE_TRANSIENT);
+    statement.bind(6, 0);
+    statement.bind(7, 0);
+  }
+  statement.bind(8, request.m_requestId, SQLITE_TRANSIENT);
 
   if (statement.step() != SQLITE_DONE) {
     addRequest(request);
@@ -221,7 +236,6 @@
                              challenge_status, cert_key_name, cert_request, challenge_type, challenge_secrets,
                              challenge_tp, remaining_tries, remaining_time, request_type
                              FROM CertRequests)_SQLTEXT_");
-
   while (statement.step() == SQLITE_ROW) {
     auto requestId = statement.getString(1);
     Name caName(statement.getBlock(2));
@@ -234,10 +248,15 @@
     auto remainingTries = statement.getInt(10);
     auto remainingTime = statement.getInt(11);
     auto requestType = static_cast<RequestType>(statement.getInt(12));
-    CertificateRequest entry(caName, requestId, requestType, status, challengeStatus, challengeType,
-                             challengeTp, remainingTime, remainingTries,
-                             convertString2Json(challengeSecrets), cert);
-    result.push_back(entry);
+    if (challengeType != "") {
+      result.push_back(CertificateRequest(caName, requestId, requestType, status, cert,
+                                          challengeType, challengeStatus, time::fromIsoString(challengeTp),
+                                          remainingTries, time::seconds(remainingTime),
+                                          convertString2Json(challengeSecrets)));
+    }
+    else {
+      result.push_back(CertificateRequest(caName, requestId, requestType, status, cert));
+    }
   }
   return result;
 }
@@ -265,10 +284,15 @@
     auto remainingTries = statement.getInt(10);
     auto remainingTime = statement.getInt(11);
     auto requestType = static_cast<RequestType>(statement.getInt(12));
-    CertificateRequest entry(caName, requestId, requestType, status, challengeStatus, challengeType,
-                             challengeTp, remainingTime, remainingTries,
-                             convertString2Json(challengeSecrets), cert);
-    result.push_back(entry);
+    if (challengeType != "") {
+      result.push_back(CertificateRequest(caName, requestId, requestType, status, cert,
+                                          challengeType, challengeStatus, time::fromIsoString(challengeTp),
+                                          remainingTries, time::seconds(remainingTime),
+                                          convertString2Json(challengeSecrets)));
+    }
+    else {
+      result.push_back(CertificateRequest(caName, requestId, requestType, status, cert));
+    }
   }
   return result;
 }
@@ -360,22 +384,5 @@
   return result;
 }
 
-std::string
-CaSqlite::convertJson2String(const JsonSection& json)
-{
-  std::stringstream ss;
-  boost::property_tree::write_json(ss, json);
-  return ss.str();
-}
-
-JsonSection
-CaSqlite::convertString2Json(const std::string& jsonContent)
-{
-  std::istringstream ss(jsonContent);
-  JsonSection json;
-  boost::property_tree::json_parser::read_json(ss, json);
-  return json;
-}
-
-} // namespace ndncert
-} // namespace ndn
+}  // namespace ndncert
+}  // namespace ndn
diff --git a/src/ca-detail/ca-sqlite.hpp b/src/ca-detail/ca-sqlite.hpp
index f0039f8..200361d 100644
--- a/src/ca-detail/ca-sqlite.hpp
+++ b/src/ca-detail/ca-sqlite.hpp
@@ -78,13 +78,6 @@
   std::list<security::v2::Certificate>
   listAllIssuedCertificates(const Name& caName) override;
 
-PUBLIC_WITH_TESTS_ELSE_PRIVATE:
-  static std::string
-  convertJson2String(const JsonSection& json);
-
-  static JsonSection
-  convertString2Json(const std::string& jsonContent);
-
 private:
   sqlite3* m_database;
 };
diff --git a/src/certificate-request.cpp b/src/certificate-request.cpp
index be1ccb5..36ee00b 100644
--- a/src/certificate-request.cpp
+++ b/src/certificate-request.cpp
@@ -24,7 +24,23 @@
 namespace ndn {
 namespace ndncert {
 
-CertificateRequest::CertificateRequest() = default;
+ChallengeState::ChallengeState(const std::string& challengeStatus,
+                               const system_clock::TimePoint& challengeTp,
+                               size_t remainingTries, time::seconds remainingTime,
+                               JsonSection&& challengeSecrets)
+    : m_challengeStatus(challengeStatus)
+    , m_timestamp(challengeTp)
+    , m_remainingTries(remainingTries)
+    , m_remainingTime(remainingTime)
+    , m_secrets(std::move(challengeSecrets))
+{
+}
+
+CertificateRequest::CertificateRequest()
+    : m_requestType(RequestType::NOTINITIALIZED)
+    , m_status(Status::NOT_STARTED)
+{
+}
 
 CertificateRequest::CertificateRequest(const Name& caName, const std::string& requestId, RequestType requestType, Status status,
                                        const security::v2::Certificate& cert)
@@ -37,39 +53,41 @@
 }
 
 CertificateRequest::CertificateRequest(const Name& caName, const std::string& requestId, RequestType requestType, Status status,
-                                       const std::string& challengeStatus, const std::string& challengeType,
-                                       const std::string& challengeTp, int remainingTime, int remainingTries,
-                                       const JsonSection& challengeSecrets, const security::v2::Certificate& cert)
+                                       const security::v2::Certificate& cert, const std::string& challengeType,
+                                       const std::string& challengeStatus, const system_clock::TimePoint& challengeTp,
+                                       size_t remainingTries, time::seconds remainingTime, JsonSection&& challengeSecrets)
     : m_caPrefix(caName)
     , m_requestId(requestId)
     , m_requestType(requestType)
     , m_status(status)
     , m_cert(cert)
-    , m_challengeStatus(challengeStatus)
     , m_challengeType(challengeType)
-    , m_challengeTp(challengeTp)
-    , m_remainingTime(remainingTime)
-    , m_remainingTries(remainingTries)
-    , m_challengeSecrets(challengeSecrets)
+    , m_challengeState(ChallengeState(challengeStatus, challengeTp, remainingTries, remainingTime, std::move(challengeSecrets)))
 {
 }
 
 std::ostream&
 operator<<(std::ostream& os, const CertificateRequest& request)
 {
-  os << "Request CA name:\n";
+  os << "Request's CA name:\n";
   os << "  " << request.m_caPrefix << "\n";
-  os << "Request ID:\n";
+  os << "Request's request ID:\n";
   os << "  " << request.m_requestId << "\n";
-  os << "Request Status:\n";
+  os << "Request's status:\n";
   os << "  " << statusToString(request.m_status) << "\n";
-  if (request.m_challengeStatus != "") {
+  os << "Request's challenge type:\n";
+  os << "  " << request.m_challengeType << "\n";
+  if (request.m_challengeState) {
     os << "Challenge Status:\n";
-    os << "  " << request.m_challengeStatus << "\n";
-  }
-  if (request.m_challengeType != "") {
-    os << "Request Challenge Type:\n";
-    os << "  " << request.m_challengeType << "\n";
+    os << "  " << request.m_challengeState->m_challengeStatus << "\n";
+    os << "Challenge remaining tries:\n";
+    os << "  " << request.m_challengeState->m_remainingTries << " times\n";
+    os << "Challenge remaining time:\n";
+    os << "  " << request.m_challengeState->m_remainingTime.count() << " seconds\n";
+    os << "Challenge last update:\n";
+    os << "  " << time::toIsoString(request.m_challengeState->m_timestamp) << "\n";
+    os << "Challenge secret:\n";
+    os << "  " << convertJson2String(request.m_challengeState->m_secrets) << "\n";
   }
   os << "Certificate:\n";
   util::IndentedStream os2(os, "  ");
diff --git a/src/certificate-request.hpp b/src/certificate-request.hpp
index b377587..ee52218 100644
--- a/src/certificate-request.hpp
+++ b/src/certificate-request.hpp
@@ -22,15 +22,20 @@
 #define NDNCERT_CERTIFICATE_REQUEST_HPP
 
 #include "ndncert-common.hpp"
-#include <ndn-cxx/security/v2/certificate.hpp>
-#include <boost/property_tree/info_parser.hpp>
-#include <boost/property_tree/json_parser.hpp>
-#include <boost/property_tree/ptree.hpp>
 
 namespace ndn {
 namespace ndncert {
 
-typedef boost::property_tree::ptree JsonSection;
+struct ChallengeState {
+  ChallengeState(const std::string& challengeStatus, const system_clock::TimePoint& challengeTp,
+                 size_t remainingTries, time::seconds remainingTime,
+                 JsonSection&& challengeSecrets);
+  std::string m_challengeStatus;
+  system_clock::TimePoint m_timestamp;
+  size_t m_remainingTries;
+  time::seconds m_remainingTime;
+  JsonSection m_secrets;
+};
 
 /**
  * @brief Represents a certificate request instance.
@@ -45,23 +50,19 @@
   CertificateRequest(const Name& caName, const std::string& requestId, RequestType requestType, Status status,
                      const security::v2::Certificate& cert);
   CertificateRequest(const Name& caName, const std::string& requestId, RequestType requestType, Status status,
-                     const std::string& challengeStatus, const std::string& challengeType,
-                     const std::string& challengeTp, int remainingTime, int remainingTries,
-                     const JsonSection& challengeSecrets, const security::v2::Certificate& cert);
+                     const security::v2::Certificate& cert, const std::string& challengeType,
+                     const std::string& challengeStatus, const system_clock::TimePoint& challengeTp,
+                     size_t remainingTries, time::seconds remainingTime, JsonSection&& challengeSecrets);
 
 public:
   Name m_caPrefix;
-  std::string m_requestId = "";
-  RequestType m_requestType = RequestType::NOTINITIALIZED;
-  Status m_status = Status::NOT_STARTED;
+  std::string m_requestId;
+  RequestType m_requestType;
+  Status m_status;
   security::v2::Certificate m_cert;
 
-  std::string m_challengeStatus = "";
-  std::string m_challengeType = "";
-  std::string m_challengeTp = "";
-  int m_remainingTime = 0;
-  int m_remainingTries = 0;
-  JsonSection m_challengeSecrets;
+  std::string m_challengeType;
+  boost::optional<ChallengeState> m_challengeState;
 };
 
 std::ostream&
diff --git a/src/challenge-module.cpp b/src/challenge-module.cpp
index a54a3e7..38cd284 100644
--- a/src/challenge-module.cpp
+++ b/src/challenge-module.cpp
@@ -75,25 +75,17 @@
 {
   request.m_status = Status::FAILURE;
   request.m_challengeType = "";
-  request.m_challengeStatus = "";
-  request.m_challengeSecrets = JsonSection();
-  request.m_challengeTp = "";
-  request.m_remainingTime = 0;
-  request.m_remainingTries = 0;
+  request.m_challengeState = boost::none;
   return std::make_tuple(errorCode, std::move(errorInfo));
 }
 
 std::tuple<ErrorCode, std::string>
 ChallengeModule::returnWithNewChallengeStatus(CertificateRequest& request, const std::string& challengeStatus,
-                                              JsonSection&& challengeSecret, size_t remainingTries, size_t remainingTime)
+                                              JsonSection&& challengeSecret, size_t remainingTries, time::seconds 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;
+  request.m_challengeState = ChallengeState(challengeStatus, time::system_clock::now(), remainingTries, remainingTime, std::move(challengeSecret));
   return std::make_tuple(ErrorCode::NO_ERROR, "");
 }
 
@@ -102,11 +94,7 @@
 {
   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;
+  request.m_challengeState = boost::none;
   return std::make_tuple(ErrorCode::NO_ERROR, "");
 }
 
diff --git a/src/challenge-module.hpp b/src/challenge-module.hpp
index 37fd611..be17e10 100644
--- a/src/challenge-module.hpp
+++ b/src/challenge-module.hpp
@@ -73,7 +73,7 @@
 
   std::tuple<ErrorCode, std::string>
   returnWithNewChallengeStatus(CertificateRequest& request, const std::string& challengeStatus,
-                               JsonSection&& challengeSecret, size_t remainingTries, size_t remainingTime);
+                               JsonSection&& challengeSecret, size_t remainingTries, time::seconds remainingTime);
 
   std::tuple<ErrorCode, std::string>
   returnWithSuccess(CertificateRequest& request);
diff --git a/src/challenge-module/challenge-email.cpp b/src/challenge-module/challenge-email.cpp
index aec7859..2e9e133 100644
--- a/src/challenge-module/challenge-email.cpp
+++ b/src/challenge-module/challenge-email.cpp
@@ -65,34 +65,38 @@
     // send out the email
     sendEmail(emailAddress, emailCode, request);
     _LOG_TRACE("Secret for request " << request.m_requestId << " : " << emailCode);
-    return returnWithNewChallengeStatus(request, NEED_CODE, std::move(secretJson), m_maxAttemptTimes, m_secretLifetime.count());
+    return returnWithNewChallengeStatus(request, NEED_CODE, std::move(secretJson), m_maxAttemptTimes, m_secretLifetime);
   }
-
-  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));
-    auto secret = request.m_challengeSecrets;
-    // check if run out of time
-    if (currentTime - time::fromIsoString(request.m_challengeTp) >= m_secretLifetime) {
-      return returnWithError(request, ErrorCode::OUT_OF_TIME, "Secret expired.");
-    }
-    // check if provided secret is correct
-    if (givenCode == secret.get<std::string>(PARAMETER_KEY_CODE)) {
-      // the code is correct
-      _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 {
-      // run out times
-      _LOG_TRACE("Wrong secret code provided. Ran out tires. Challenge failed.");
-      return returnWithError(request, ErrorCode::OUT_OF_TRIES, "Ran out tires.");
+  if (request.m_challengeState) {
+    if (request.m_challengeState->m_challengeStatus == NEED_CODE ||
+        request.m_challengeState->m_challengeStatus == WRONG_CODE) {
+      _LOG_TRACE("Challenge Interest arrives. Challenge Status: " << request.m_challengeState->m_challengeStatus);
+      // the incoming interest should bring the pin code
+      std::string givenCode = readString(params.get(tlv_parameter_value));
+      auto secret = request.m_challengeState->m_secrets;
+      // check if run out of time
+      if (currentTime - request.m_challengeState->m_timestamp >= m_secretLifetime) {
+        return returnWithError(request, ErrorCode::OUT_OF_TIME, "Secret expired.");
+      }
+      // check if provided secret is correct
+      if (givenCode == secret.get<std::string>(PARAMETER_KEY_CODE)) {
+        // the code is correct
+        _LOG_TRACE("Correct secret code. Challenge succeeded.");
+        return returnWithSuccess(request);
+      }
+      // otherwise, check remaining attempt times
+      if (request.m_challengeState->m_remainingTries > 1) {
+        auto remainTime = m_secretLifetime - (currentTime - request.m_challengeState->m_timestamp);
+        _LOG_TRACE("Wrong secret code provided. Remaining Tries - 1.");
+        return returnWithNewChallengeStatus(request, WRONG_CODE, std::move(secret),
+                                            request.m_challengeState->m_remainingTries - 1,
+                                            time::duration_cast<time::seconds>(remainTime));
+      }
+      else {
+        // run out times
+        _LOG_TRACE("Wrong secret code provided. Ran out tires. Challenge failed.");
+        return returnWithError(request, ErrorCode::OUT_OF_TRIES, "Ran out tires.");
+      }
     }
   }
   return returnWithError(request, ErrorCode::INVALID_PARAMETER, "Unexpected status or challenge status");
diff --git a/src/challenge-module/challenge-pin.cpp b/src/challenge-module/challenge-pin.cpp
index 9302e42..caad97d 100644
--- a/src/challenge-module/challenge-pin.cpp
+++ b/src/challenge-module/challenge-pin.cpp
@@ -19,6 +19,7 @@
  */
 
 #include "challenge-pin.hpp"
+
 #include <ndn-cxx/util/random.hpp>
 
 namespace ndn {
@@ -51,31 +52,35 @@
     JsonSection secretJson;
     secretJson.add(PARAMETER_KEY_CODE, secretCode);
     _LOG_TRACE("Secret for request " << request.m_requestId << " : " << secretCode);
-    return returnWithNewChallengeStatus(request, NEED_CODE, std::move(secretJson), m_maxAttemptTimes, m_secretLifetime.count());
+    return returnWithNewChallengeStatus(request, NEED_CODE, std::move(secretJson), m_maxAttemptTimes, m_secretLifetime);
   }
-
-  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));
-    auto secret = request.m_challengeSecrets;
-    if (currentTime - time::fromIsoString(request.m_challengeTp) >= m_secretLifetime) {
-      return returnWithError(request, ErrorCode::OUT_OF_TIME, "Secret expired.");
-    }
-    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 {
-      // run out times
-      _LOG_TRACE("Wrong PIN code provided. Ran out tires. Challenge failed.");
-      return returnWithError(request, ErrorCode::OUT_OF_TRIES, "Ran out tires.");
+  if (request.m_challengeState) {
+    if (request.m_challengeState->m_challengeStatus == NEED_CODE ||
+        request.m_challengeState->m_challengeStatus == WRONG_CODE) {
+      _LOG_TRACE("Challenge Interest arrives. Challenge Status: " << request.m_challengeState->m_challengeStatus);
+      // the incoming interest should bring the pin code
+      std::string givenCode = readString(params.get(tlv_parameter_value));
+      auto secret = request.m_challengeState->m_secrets;
+      if (currentTime - request.m_challengeState->m_timestamp >= m_secretLifetime) {
+        return returnWithError(request, ErrorCode::OUT_OF_TIME, "Secret expired.");
+      }
+      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_challengeState->m_remainingTries > 1) {
+        auto remainTime = m_secretLifetime - (currentTime - request.m_challengeState->m_timestamp);
+        _LOG_TRACE("Wrong PIN code provided. Remaining Tries - 1.");
+        return returnWithNewChallengeStatus(request, WRONG_CODE, std::move(secret),
+                                            request.m_challengeState->m_remainingTries - 1,
+                                            time::duration_cast<time::seconds>(remainTime));
+      }
+      else {
+        // run out times
+        _LOG_TRACE("Wrong PIN code provided. Ran out tires. Challenge failed.");
+        return returnWithError(request, ErrorCode::OUT_OF_TRIES, "Ran out tires.");
+      }
     }
   }
   return returnWithError(request, ErrorCode::INVALID_PARAMETER, "Unexpected status or challenge status");
diff --git a/src/ndncert-common.cpp b/src/ndncert-common.cpp
index fc0e9c6..086d880 100644
--- a/src/ndncert-common.cpp
+++ b/src/ndncert-common.cpp
@@ -62,5 +62,22 @@
   }
 }
 
+std::string
+convertJson2String(const JsonSection& json)
+{
+  std::stringstream ss;
+  boost::property_tree::write_json(ss, json);
+  return ss.str();
+}
+
+JsonSection
+convertString2Json(const std::string& jsonContent)
+{
+  std::istringstream ss(jsonContent);
+  JsonSection json;
+  boost::property_tree::json_parser::read_json(ss, json);
+  return json;
+}
+
 }  // namespace ndncert
 }  // namespace ndn
diff --git a/src/ndncert-common.hpp b/src/ndncert-common.hpp
index b5c8d0f..d052d62 100644
--- a/src/ndncert-common.hpp
+++ b/src/ndncert-common.hpp
@@ -51,6 +51,9 @@
 #include <boost/assert.hpp>
 #include <boost/noncopyable.hpp>
 #include <boost/throw_exception.hpp>
+#include <boost/property_tree/info_parser.hpp>
+#include <boost/property_tree/json_parser.hpp>
+#include <boost/property_tree/ptree.hpp>
 
 namespace ndn {
 namespace ndncert {
@@ -152,6 +155,14 @@
 
 std::string requestTypeToString(RequestType type);
 
+typedef boost::property_tree::ptree JsonSection;
+
+std::string
+convertJson2String(const JsonSection& json);
+
+JsonSection
+convertString2Json(const std::string& jsonContent);
+
 }  // namespace ndncert
 }  // namespace ndn
 
diff --git a/src/protocol-detail/challenge.cpp b/src/protocol-detail/challenge.cpp
index e4d5ecb..b8823e0 100644
--- a/src/protocol-detail/challenge.cpp
+++ b/src/protocol-detail/challenge.cpp
@@ -30,9 +30,9 @@
 {
   Block response = makeEmptyBlock(tlv_encrypted_payload);
   response.push_back(makeNonNegativeIntegerBlock(tlv_status, static_cast<size_t>(request.m_status)));
-  response.push_back(makeStringBlock(tlv_challenge_status, request.m_challengeStatus));
-  response.push_back(makeNonNegativeIntegerBlock(tlv_remaining_tries, request.m_remainingTries));
-  response.push_back(makeNonNegativeIntegerBlock(tlv_remaining_time, request.m_remainingTime));
+  response.push_back(makeStringBlock(tlv_challenge_status, request.m_challengeState->m_challengeStatus));
+  response.push_back(makeNonNegativeIntegerBlock(tlv_remaining_tries, request.m_challengeState->m_remainingTries));
+  response.push_back(makeNonNegativeIntegerBlock(tlv_remaining_time, request.m_challengeState->m_remainingTime.count()));
   response.encode();
   return response;
 }