refactor client module to requester

Change-Id: I2b9835af7f03942bfdb6a886c95cfb4b907e2068
diff --git a/src/challenge-modules/challenge-email.cpp b/src/challenge-modules/challenge-email.cpp
index 327fb79..c12ba88 100644
--- a/src/challenge-modules/challenge-email.cpp
+++ b/src/challenge-modules/challenge-email.cpp
@@ -19,7 +19,6 @@
  */
 
 #include "challenge-email.hpp"
-#include "../ca-module.hpp"
 #include <regex>
 
 namespace ndn {
diff --git a/src/challenge-modules/challenge-pin.cpp b/src/challenge-modules/challenge-pin.cpp
index 4853eed..fd5b5a5 100644
--- a/src/challenge-modules/challenge-pin.cpp
+++ b/src/challenge-modules/challenge-pin.cpp
@@ -19,7 +19,6 @@
  */
 
 #include "challenge-pin.hpp"
-
 #include <ndn-cxx/util/random.hpp>
 
 namespace ndn {
diff --git a/src/client-module.cpp b/src/client-module.cpp
deleted file mode 100644
index ffda468..0000000
--- a/src/client-module.cpp
+++ /dev/null
@@ -1,406 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2017-2020, Regents of the University of California.
- *
- * This file is part of ndncert, a certificate management system based on NDN.
- *
- * ndncert is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * ndncert is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received copies of the GNU General Public License along with
- * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- *
- * See AUTHORS.md for complete list of ndncert authors and contributors.
- */
-
-#include "client-module.hpp"
-
-#include <ndn-cxx/security/signing-helpers.hpp>
-#include <ndn-cxx/security/transform/base64-encode.hpp>
-#include <ndn-cxx/security/transform/buffer-source.hpp>
-#include <ndn-cxx/security/transform/stream-sink.hpp>
-#include <ndn-cxx/security/verification-helpers.hpp>
-#include <ndn-cxx/util/io.hpp>
-#include <ndn-cxx/util/random.hpp>
-
-#include "challenge-module.hpp"
-#include "crypto-support/enc-tlv.hpp"
-#include "protocol-detail/challenge.hpp"
-#include "protocol-detail/info.hpp"
-#include "protocol-detail/new-renew-revoke.hpp"
-#include "protocol-detail/probe.hpp"
-#include "protocol-detail/error.hpp"
-#include "ndncert-common.hpp"
-
-namespace ndn {
-namespace ndncert {
-
-_LOG_INIT(ndncert.client);
-
-ClientModule::ClientModule(security::v2::KeyChain& keyChain)
-    : m_keyChain(keyChain)
-{
-}
-
-ClientModule::~ClientModule()
-{
-  endSession();
-}
-
-shared_ptr<Interest>
-ClientModule::generateInfoInterest(const Name& caName)
-{
-  Name interestName = caName;
-  if (readString(caName.at(-1)) != "CA")
-    interestName.append("CA");
-  interestName.append("INFO");
-  auto interest = make_shared<Interest>(interestName);
-  interest->setMustBeFresh(true);
-  interest->setCanBePrefix(false);
-  return interest;
-}
-
-bool
-ClientModule::verifyInfoResponse(const Data& reply)
-{
-  // parse the ca item
-  auto caItem = INFO::decodeDataContent(reply.getContent());
-
-  // verify the probe Data's sig
-  if (!security::verifySignature(reply, *caItem.m_cert)) {
-    _LOG_ERROR("Cannot verify data signature from " << m_ca.m_caPrefix.toUri());
-    return false;
-  }
-  return true;
-}
-
-void
-ClientModule::addCaFromInfoResponse(const Data& reply)
-{
-  const Block& contentBlock = reply.getContent();
-
-  // parse the ca item
-  auto caItem = INFO::decodeDataContent(contentBlock);
-
-  // update the local config
-  bool findItem = false;
-  for (auto& item : m_config.m_caItems) {
-    if (item.m_caPrefix == caItem.m_caPrefix) {
-      findItem = true;
-      item = caItem;
-    }
-  }
-  if (!findItem) {
-    m_config.m_caItems.push_back(caItem);
-  }
-}
-
-shared_ptr<Interest>
-ClientModule::generateProbeInterest(const CaConfigItem& ca,
-                                    std::vector<std::tuple<std::string, std::string>>&& probeInfo)
-{
-  Name interestName = ca.m_caPrefix;
-  interestName.append("CA").append("PROBE");
-  auto interest = make_shared<Interest>(interestName);
-  interest->setMustBeFresh(true);
-  interest->setCanBePrefix(false);
-  interest->setApplicationParameters(PROBE::encodeApplicationParameters(std::move(probeInfo)));
-
-  // update local state
-  m_ca = ca;
-  return interest;
-}
-
-void
-ClientModule::onProbeResponse(const Data& reply)
-{
-  if (!security::verifySignature(reply, *m_ca.m_cert)) {
-    _LOG_ERROR("Cannot verify data signature from " << m_ca.m_caPrefix.toUri());
-    return;
-  }
-
-  // error handling
-  processIfError(reply);
-
-  auto contentTLV = reply.getContent();
-  contentTLV.parse();
-
-  // read the available name and put it into the state
-  if (contentTLV.get(tlv_probe_response).hasValue()) {
-    Block probeResponseBlock = contentTLV.get(tlv_probe_response);
-    probeResponseBlock.parse();
-    m_identityName.wireDecode(probeResponseBlock.get(tlv::Name));
-  }
-  else {
-    NDN_LOG_TRACE("The JSON_CA_NAME is empty.");
-  }
-}
-
-shared_ptr<Interest>
-ClientModule::generateNewInterest(const time::system_clock::TimePoint& notBefore,
-                                  const time::system_clock::TimePoint& notAfter,
-                                  const Name& identityName)
-{
-  // Name requestedName = identityName;
-  if (!identityName.empty()) {  // if identityName is not empty, find the corresponding CA
-    bool findCa = false;
-    for (const auto& caItem : m_config.m_caItems) {
-      if (caItem.m_caPrefix.isPrefixOf(identityName)) {
-        m_ca = caItem;
-        findCa = true;
-      }
-    }
-    if (!findCa) {  // if cannot find, cannot proceed
-      return nullptr;
-    }
-    m_identityName = identityName;
-  }
-  else {  // if identityName is empty, check m_identityName or generate a random name
-    if (!m_identityName.empty()) {
-      // do nothing
-    }
-    else {
-      NDN_LOG_TRACE("Randomly create a new name because m_identityName is empty and the param is empty.");
-      auto id = std::to_string(random::generateSecureWord64());
-      m_identityName = m_ca.m_caPrefix;
-      m_identityName.append(id);
-    }
-  }
-
-  // generate a newly key pair or use an existing key
-  const auto& pib = m_keyChain.getPib();
-  security::pib::Identity identity;
-  try {
-    identity = pib.getIdentity(m_identityName);
-  }
-  catch (const security::Pib::Error& e) {
-    identity = m_keyChain.createIdentity(m_identityName);
-    m_isNewlyCreatedIdentity = true;
-    m_isNewlyCreatedKey = true;
-  }
-  try {
-    m_key = identity.getDefaultKey();
-  }
-  catch (const security::Pib::Error& e) {
-    m_key = m_keyChain.createKey(identity);
-    m_isNewlyCreatedKey = true;
-  }
-
-  // generate certificate request
-  security::v2::Certificate certRequest;
-  certRequest.setName(Name(m_key.getName()).append("cert-request").appendVersion());
-  certRequest.setContentType(tlv::ContentType_Key);
-  certRequest.setContent(m_key.getPublicKey().data(), m_key.getPublicKey().size());
-  SignatureInfo signatureInfo;
-  signatureInfo.setValidityPeriod(security::ValidityPeriod(notBefore, notAfter));
-  m_keyChain.sign(certRequest, signingByKey(m_key.getName()).setSignatureInfo(signatureInfo));
-
-  // generate Interest packet
-  Name interestName = m_ca.m_caPrefix;
-  interestName.append("CA").append("NEW");
-  auto interest = make_shared<Interest>(interestName);
-  interest->setMustBeFresh(true);
-  interest->setCanBePrefix(false);
-  interest->setApplicationParameters(
-      NEW_RENEW_REVOKE::encodeApplicationParameters(RequestType::NEW, m_ecdh.getBase64PubKey(), certRequest));
-
-  // sign the Interest packet
-  m_keyChain.sign(*interest, signingByKey(m_key.getName()));
-  return interest;
-}
-
-std::list<std::string>
-ClientModule::onNewRenewRevokeResponse(const Data& reply)
-{
-  if (!security::verifySignature(reply, *m_ca.m_cert)) {
-    _LOG_ERROR("Cannot verify data signature from " << m_ca.m_caPrefix.toUri());
-    return std::list<std::string>();
-  }
-
-  // error handling
-  processIfError(reply);
-
-  auto contentTLV = reply.getContent();
-  contentTLV.parse();
-
-  // ECDH
-  const auto& peerKeyBase64Str = readString(contentTLV.get(tlv_ecdh_pub));
-  const auto& saltStr = readString(contentTLV.get(tlv_salt));
-  uint64_t saltInt = std::stoull(saltStr);
-  m_ecdh.deriveSecret(peerKeyBase64Str);
-
-  // HKDF
-  hkdf(m_ecdh.context->sharedSecret, m_ecdh.context->sharedSecretLen,
-       (uint8_t*)&saltInt, sizeof(saltInt), m_aesKey, sizeof(m_aesKey));
-
-  // update state
-  m_status = static_cast<Status>(readNonNegativeInteger(contentTLV.get(tlv_status)));
-  m_requestId = readString(contentTLV.get(tlv_request_id));
-  m_challengeList.clear();
-  for (auto const& element : contentTLV.elements()) {
-    if (element.type() == tlv_challenge) {
-      m_challengeList.push_back(readString(element));
-    }
-  }
-  return m_challengeList;
-}
-
-shared_ptr<Interest>
-ClientModule::generateRevokeInterest(const security::v2::Certificate& certificate)
-{
-  // Name requestedName = identityName;
-  bool findCa = false;
-  for (const auto& caItem : m_config.m_caItems) {
-    if (caItem.m_caPrefix.isPrefixOf(certificate.getName())) {
-      m_ca = caItem;
-      findCa = true;
-    }
-  }
-  if (!findCa) {  // if cannot find, cannot proceed
-    _LOG_TRACE("Cannot find corresponding CA for the certificate.");
-    return nullptr;
-  }
-
-  // generate Interest packet
-  Name interestName = m_ca.m_caPrefix;
-  interestName.append("CA").append("REVOKE");
-  auto interest = make_shared<Interest>(interestName);
-  interest->setMustBeFresh(true);
-  interest->setCanBePrefix(false);
-  interest->setApplicationParameters(
-      NEW_RENEW_REVOKE::encodeApplicationParameters(RequestType::REVOKE, m_ecdh.getBase64PubKey(), certificate));
-
-  // return the Interest packet
-  return interest;
-}
-
-shared_ptr<Interest>
-ClientModule::generateChallengeInterest(const Block& challengeRequest)
-{
-  challengeRequest.parse();
-  m_challengeType = readString(challengeRequest.get(tlv_selected_challenge));
-
-  Name interestName = m_ca.m_caPrefix;
-  interestName.append("CA").append("CHALLENGE").append(m_requestId);
-  auto interest = make_shared<Interest>(interestName);
-  interest->setMustBeFresh(true);
-  interest->setCanBePrefix(false);
-
-  // encrypt the Interest parameters
-  auto paramBlock = encodeBlockWithAesGcm128(tlv::ApplicationParameters, m_aesKey,
-                                             challengeRequest.value(), challengeRequest.value_size(),
-                                             (const uint8_t*)"test", strlen("test"));
-  interest->setApplicationParameters(paramBlock);
-
-  m_keyChain.sign(*interest, signingByKey(m_key.getName()));
-  return interest;
-}
-
-void
-ClientModule::onChallengeResponse(const Data& reply)
-{
-  if (!security::verifySignature(reply, *m_ca.m_cert)) {
-    _LOG_ERROR("Cannot verify data signature from " << m_ca.m_caPrefix.toUri());
-    return;
-  }
-
-  // error handling
-  processIfError(reply);
-
-  auto result = decodeBlockWithAesGcm128(reply.getContent(), m_aesKey, (const uint8_t*)"test", strlen("test"));
-
-  Block contentTLV = makeBinaryBlock(tlv_encrypted_payload, result.data(), result.size());
-  contentTLV.parse();
-
-  // update state
-  m_status = static_cast<Status>(readNonNegativeInteger(contentTLV.get(tlv_status)));
-  m_challengeStatus = readString(contentTLV.get(tlv_challenge_status));
-  m_remainingTries = readNonNegativeInteger(contentTLV.get(tlv_remaining_tries));
-  m_freshBefore = time::system_clock::now() +
-                  time::seconds(readNonNegativeInteger(contentTLV.get(tlv_remaining_time)));
-
-  if (contentTLV.find(tlv_issued_cert_name) != contentTLV.elements_end()) {
-    Block issuedCertNameBlock = contentTLV.get(tlv_issued_cert_name);
-    issuedCertNameBlock.parse();
-    m_issuedCertName.wireDecode(issuedCertNameBlock.get(tlv::Name));
-  }
-}
-
-shared_ptr<Interest>
-ClientModule::generateDownloadInterest()
-{
-  Name interestName = m_ca.m_caPrefix;
-  interestName.append("CA").append("DOWNLOAD").append(m_requestId);
-  auto interest = make_shared<Interest>(interestName);
-  interest->setMustBeFresh(true);
-  interest->setCanBePrefix(false);
-  return interest;
-}
-
-shared_ptr<Interest>
-ClientModule::generateCertFetchInterest()
-{
-  Name interestName = m_issuedCertName;
-  auto interest = make_shared<Interest>(interestName);
-  interest->setMustBeFresh(true);
-  interest->setCanBePrefix(false);
-  return interest;
-}
-
-void
-ClientModule::onCertFetchResponse(const Data& reply)
-{
-  try {
-    security::v2::Certificate cert(reply.getContent().blockFromValue());
-    m_keyChain.addCertificate(m_key, cert);
-    _LOG_TRACE("Fetched and installed the cert " << cert.getName());
-  }
-  catch (const std::exception& e) {
-    _LOG_ERROR("Cannot add replied certificate into the keychain " << e.what());
-    return;
-  }
-}
-
-void
-ClientModule::endSession()
-{
-  if (getApplicationStatus() == Status::SUCCESS || getApplicationStatus() == Status::ENDED) {
-    return;
-  }
-  if (m_isNewlyCreatedIdentity) {
-    // put the identity into the if scope is because it may cause an error
-    // outside since when endSession is called, identity may not have been created yet.
-    auto identity = m_keyChain.getPib().getIdentity(m_identityName);
-    m_keyChain.deleteIdentity(identity);
-  }
-  else if (m_isNewlyCreatedKey) {
-    auto identity = m_keyChain.getPib().getIdentity(m_identityName);
-    m_keyChain.deleteKey(identity, m_key);
-  }
-  m_status = Status::ENDED;
-}
-
-void
-ClientModule::processIfError(const Data& data)
-{
-  auto contentTLV = data.getContent();
-  if (ErrorTLV::isErrorContent(contentTLV)) {
-  try {
-    auto error = ErrorTLV::decodefromDataContent(contentTLV);
-    _LOG_ERROR("Error data returned for " << data.getName() << ": " << std::endl <<
-               "Code: " << errorCodeToString(std::get<0>(error)) << std::endl <<
-               "Info: " << std::get<1>(error) << std::endl);
-    } catch (const std::exception& e) {
-      _LOG_ERROR("Cannot parse error data content for " << data.getName());
-      return;
-    }
-  }
-}
-
-}  // namespace ndncert
-}  // namespace ndn
diff --git a/src/client-module.hpp b/src/client-module.hpp
deleted file mode 100644
index b9c0f35..0000000
--- a/src/client-module.hpp
+++ /dev/null
@@ -1,151 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2017-2020, Regents of the University of California.
- *
- * This file is part of ndncert, a certificate management system based on NDN.
- *
- * ndncert is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * ndncert is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received copies of the GNU General Public License along with
- * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- *
- * See AUTHORS.md for complete list of ndncert authors and contributors.
- */
-
-#ifndef NDNCERT_CLIENT_MODULE_HPP
-#define NDNCERT_CLIENT_MODULE_HPP
-
-#include "configuration.hpp"
-#include "request-state.hpp"
-#include "crypto-support/crypto-helper.hpp"
-
-namespace ndn {
-namespace ndncert {
-
-// TODO
-// For each CA item in Client.Conf, create a validator instance and initialize it with CA's cert
-// The validator instance should be in CaConfigItem
-
-class ClientModule : noncopyable
-{
-public:
-  /**
-   * @brief Error that can be thrown from ClientModule
-   */
-  class Error : public std::runtime_error
-  {
-  public:
-    using std::runtime_error::runtime_error;
-  };
-
-public:
-  ClientModule(security::v2::KeyChain& keyChain);
-
-  ~ClientModule();
-
-  ClientConfig&
-  getClientConf()
-  {
-    return m_config;
-  }
-
-  Status
-  getApplicationStatus() const
-  {
-    return m_status;
-  }
-
-  std::string
-  getChallengeStatus() const
-  {
-    return m_challengeStatus;
-  }
-
-  shared_ptr<Interest>
-  generateInfoInterest(const Name& caName);
-
-  bool
-  verifyInfoResponse(const Data& reply);
-
-  /**
-   * @brief Process the replied PROBE INFO Data packet
-   * Warning: this function will add a new trust anchor into the application.
-   * Please invoke this function only when reply can be fully trusted or the CA
-   * can be verified in later challenge phase.
-   */
-  void
-  addCaFromInfoResponse(const Data& reply);
-
-  shared_ptr<Interest>
-  generateProbeInterest(const CaConfigItem& ca, std::vector<std::tuple<std::string, std::string>>&& probeInfo);
-
-  void
-  onProbeResponse(const Data& reply);
-
-  shared_ptr<Interest>
-  generateNewInterest(const time::system_clock::TimePoint& notBefore,
-                      const time::system_clock::TimePoint& notAfter,
-                      const Name& identityName = Name());
-
-  shared_ptr<Interest>
-  generateRevokeInterest(const security::v2::Certificate& certificate);
-
-  std::list<std::string>
-  onNewRenewRevokeResponse(const Data& reply);
-
-  shared_ptr<Interest>
-  generateChallengeInterest(const Block& paramTLV);
-
-  void
-  onChallengeResponse(const Data& reply);
-
-  shared_ptr<Interest>
-  generateDownloadInterest();
-
-  shared_ptr<Interest>
-  generateCertFetchInterest();
-
-  void
-  onCertFetchResponse(const Data& reply);
-
-  void
-  endSession();
-
-  static void
-  processIfError(const Data& data);
-
-PUBLIC_WITH_TESTS_ELSE_PRIVATE:
-  ClientConfig m_config;
-  security::v2::KeyChain& m_keyChain;
-
-  CaConfigItem m_ca;
-  security::Key m_key;
-  Name m_identityName;
-
-  std::string m_requestId = "";
-  Status m_status = Status::NOT_STARTED;
-  std::string m_challengeStatus = "";
-  std::string m_challengeType = "";
-  Name m_issuedCertName;
-  std::list<std::string> m_challengeList;
-  bool m_isCertInstalled = false;
-  bool m_isNewlyCreatedIdentity = false;
-  bool m_isNewlyCreatedKey = false;
-
-  int m_remainingTries = 0;
-  time::system_clock::TimePoint m_freshBefore;
-
-  ECDHState m_ecdh;
-  uint8_t m_aesKey[16] = {0};
-};
-
-} // namespace ndncert
-} // namespace ndn
-
-#endif // NDNCERT_CLIENT_MODULE_HPP
diff --git a/src/configuration.cpp b/src/configuration.cpp
index 8d1d397..10fe352 100644
--- a/src/configuration.cpp
+++ b/src/configuration.cpp
@@ -27,7 +27,7 @@
 namespace ndncert {
 
 void
-CaConfigItem::parse(const JsonSection& configJson)
+CaProfile::parse(const JsonSection& configJson)
 {
   // CA prefix
   m_caPrefix = Name(configJson.get(CONFIG_CA_PREFIX, ""));
@@ -79,7 +79,7 @@
 }
 
 JsonSection
-CaConfigItem::toJson() const
+CaProfile::toJson() const
 {
   JsonSection caItem;
   caItem.put(CONFIG_CA_PREFIX, m_caPrefix.toUri());
@@ -150,7 +150,7 @@
 }
 
 void
-ClientConfig::load(const std::string& fileName)
+RequesterCaCache::load(const std::string& fileName)
 {
   JsonSection configJson;
   try {
@@ -166,12 +166,12 @@
 }
 
 void
-ClientConfig::load(const JsonSection& configSection)
+RequesterCaCache::load(const JsonSection& configSection)
 {
   m_caItems.clear();
   auto caList = configSection.get_child("ca-list");
   for (auto item : caList) {
-    CaConfigItem caItem;
+    CaProfile caItem;
     caItem.parse(item.second);
     if (caItem.m_cert == nullptr) {
       BOOST_THROW_EXCEPTION(std::runtime_error("No CA certificate is loaded from JSON configuration."));
@@ -181,7 +181,7 @@
 }
 
 void
-ClientConfig::save(const std::string& fileName) const
+RequesterCaCache::save(const std::string& fileName) const
 {
   JsonSection configJson;
   for (const auto& caItem : m_caItems) {
@@ -196,9 +196,21 @@
 }
 
 void
-ClientConfig::removeCaItem(const Name& caName)
+RequesterCaCache::removeCaProfile(const Name& caName)
 {
-  m_caItems.remove_if([&](const CaConfigItem& item) { return item.m_caPrefix == caName; });
+  m_caItems.remove_if([&](const CaProfile& item) { return item.m_caPrefix == caName; });
+}
+
+void
+RequesterCaCache::addCaProfile(const CaProfile& profile)
+{
+  for (auto& item : m_caItems) {
+    if (item.m_caPrefix == profile.m_caPrefix) {
+      item = profile;
+      return;
+    }
+  }
+  m_caItems.push_back(profile);
 }
 
 }  // namespace ndncert
diff --git a/src/configuration.hpp b/src/configuration.hpp
index 892c056..6b9edeb 100644
--- a/src/configuration.hpp
+++ b/src/configuration.hpp
@@ -26,7 +26,7 @@
 namespace ndn {
 namespace ndncert {
 
-struct CaConfigItem {
+struct CaProfile {
   /**
    * CA Name prefix (without /CA suffix).
    */
@@ -131,7 +131,7 @@
   void
   load(const std::string& fileName);
 
-  CaConfigItem m_caItem;
+  CaProfile m_caItem;
   /**
    * Used for CA redirection as specified in
    * https://github.com/named-data/ndncert/wiki/NDNCERT-Protocol-0.3-PROBE-Extensions#probe-extension-for-redirection
@@ -153,7 +153,7 @@
  * For Client configuration format, please refer to:
  *   https://github.com/named-data/ndncert/wiki/Client-Configuration-Sample
  */
-class ClientConfig {
+class RequesterCaCache {
 public:
   /**
    * @throw std::runtime_error when config file cannot be correctly parsed.
@@ -171,9 +171,15 @@
   save(const std::string& fileName) const;
 
   void
-  removeCaItem(const Name& caName);
+  removeCaProfile(const Name& caName);
 
-  std::list<CaConfigItem> m_caItems;
+  /**
+   * Be cautious. This will add a new trust anchor for requesters.
+   */
+  void
+  addCaProfile(const CaProfile& profile);
+
+  std::list<CaProfile> m_caItems;
 };
 
 }  // namespace ndncert
diff --git a/src/protocol-detail/challenge.cpp b/src/protocol-detail/challenge.cpp
index 61a3519..85875fd 100644
--- a/src/protocol-detail/challenge.cpp
+++ b/src/protocol-detail/challenge.cpp
@@ -19,8 +19,6 @@
  */
 
 #include "challenge.hpp"
-#include "../ndncert-common.hpp"
-#include "../request-state.hpp"
 
 namespace ndn {
 namespace ndncert {
diff --git a/src/protocol-detail/error.cpp b/src/protocol-detail/error.cpp
index b74180b..dabfa6a 100644
--- a/src/protocol-detail/error.cpp
+++ b/src/protocol-detail/error.cpp
@@ -37,16 +37,11 @@
 ErrorTLV::decodefromDataContent(const Block& block)
 {
   block.parse();
+  if (block.find(tlv_error_code) == block.elements_end()) {
+    return std::make_tuple(ErrorCode::NO_ERROR, "");
+  }
   ErrorCode error = static_cast<ErrorCode>(readNonNegativeInteger(block.get(tlv_error_code)));
-  auto description = readString(block.get(tlv_error_info));
-  return std::make_tuple(error, description);
-}
-
-bool
-ErrorTLV::isErrorContent(const Block& block)
-{
-  block.parse();
-  return block.find(tlv_error_code) != block.elements_end();
+  return std::make_tuple(error, readString(block.get(tlv_error_info)));
 }
 
 }  // namespace ndncert
diff --git a/src/protocol-detail/error.hpp b/src/protocol-detail/error.hpp
index d39fe1a..8a2cda0 100644
--- a/src/protocol-detail/error.hpp
+++ b/src/protocol-detail/error.hpp
@@ -39,9 +39,6 @@
    */
   static std::tuple<ErrorCode, std::string>
   decodefromDataContent(const Block& block);
-
-  static bool
-  isErrorContent(const Block& block);
 };
 
 }  // namespace ndncert
diff --git a/src/protocol-detail/info.cpp b/src/protocol-detail/info.cpp
index 06eba0e..7c0e449 100644
--- a/src/protocol-detail/info.cpp
+++ b/src/protocol-detail/info.cpp
@@ -24,7 +24,7 @@
 namespace ndncert {
 
 Block
-INFO::encodeDataContent(const CaConfigItem& caConfig, const security::v2::Certificate& certificate)
+INFO::encodeDataContent(const CaProfile& caConfig, const security::v2::Certificate& certificate)
 {
   auto content = makeEmptyBlock(tlv::Content);
   content.push_back(makeNestedBlock(tlv_ca_prefix, caConfig.m_caPrefix));
@@ -48,10 +48,10 @@
   return content;
 }
 
-CaConfigItem
+CaProfile
 INFO::decodeDataContent(const Block& block)
 {
-  CaConfigItem result;
+  CaProfile result;
   block.parse();
   for (auto const& item : block.elements()) {
     switch (item.type()) {
diff --git a/src/protocol-detail/info.hpp b/src/protocol-detail/info.hpp
index 03f0d61..571d120 100644
--- a/src/protocol-detail/info.hpp
+++ b/src/protocol-detail/info.hpp
@@ -32,12 +32,12 @@
    * Encode CA configuration and its certificate into a TLV block as INFO Data packet content.
    */
   static Block
-  encodeDataContent(const CaConfigItem& caConfig, const security::v2::Certificate& certificate);
+  encodeDataContent(const CaProfile& caConfig, const security::v2::Certificate& certificate);
 
   /**
    * Decode CA configuration from the TLV block of INFO Data packet content.
    */
-  static CaConfigItem
+  static CaProfile
   decodeDataContent(const Block& block);
 };
 
diff --git a/src/requester.cpp b/src/requester.cpp
new file mode 100644
index 0000000..7bf9594
--- /dev/null
+++ b/src/requester.cpp
@@ -0,0 +1,327 @@
+/* -*- 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 "requester.hpp"
+#include "challenge-module.hpp"
+#include "crypto-support/enc-tlv.hpp"
+#include "protocol-detail/challenge.hpp"
+#include "protocol-detail/error.hpp"
+#include "protocol-detail/info.hpp"
+#include "protocol-detail/new-renew-revoke.hpp"
+#include "protocol-detail/probe.hpp"
+#include <ndn-cxx/security/signing-helpers.hpp>
+#include <ndn-cxx/security/transform/base64-encode.hpp>
+#include <ndn-cxx/security/transform/buffer-source.hpp>
+#include <ndn-cxx/security/transform/stream-sink.hpp>
+#include <ndn-cxx/security/verification-helpers.hpp>
+#include <ndn-cxx/util/io.hpp>
+#include <ndn-cxx/util/random.hpp>
+
+namespace ndn {
+namespace ndncert {
+
+_LOG_INIT(ndncert.client);
+
+RequesterState::RequesterState(security::v2::KeyChain& keyChain, const CaProfile& caItem, RequestType requestType)
+  : m_caItem(caItem)
+  , m_keyChain(keyChain)
+  , m_type(requestType)
+{
+}
+
+shared_ptr<Interest>
+Requester::genCaProfileInterest(const Name& caName)
+{
+  Name interestName = caName;
+  if (readString(caName.at(-1)) != "CA")
+    interestName.append("CA");
+  interestName.append("INFO");
+  auto interest = make_shared<Interest>(interestName);
+  interest->setMustBeFresh(true);
+  interest->setCanBePrefix(false);
+  return interest;
+}
+
+boost::optional<CaProfile>
+Requester::onCaProfileResponse(const Data& reply)
+{
+  auto caItem = INFO::decodeDataContent(reply.getContent());
+  if (!security::verifySignature(reply, *caItem.m_cert)) {
+    _LOG_ERROR("Cannot verify replied Data packet signature.");
+    return boost::none;
+  }
+  return caItem;
+}
+
+shared_ptr<Interest>
+Requester::genProbeInterest(const CaProfile& ca, std::vector<std::tuple<std::string, std::string>>&& probeInfo)
+{
+  Name interestName = ca.m_caPrefix;
+  interestName.append("CA").append("PROBE");
+  auto interest = make_shared<Interest>(interestName);
+  interest->setMustBeFresh(true);
+  interest->setCanBePrefix(false);
+  interest->setApplicationParameters(PROBE::encodeApplicationParameters(std::move(probeInfo)));
+  return interest;
+}
+
+void
+Requester::onProbeResponse(const Data& reply, const CaProfile& ca,
+                           std::vector<Name>& identityNames, std::vector<Name>& otherCas)
+{
+  if (!security::verifySignature(reply, *ca.m_cert)) {
+    _LOG_ERROR("Cannot verify replied Data packet signature.");
+    return;
+  }
+  processIfError(reply);
+  PROBE::decodeDataContent(reply.getContent(), identityNames, otherCas);
+}
+
+shared_ptr<Interest>
+Requester::genNewInterest(RequesterState& state, const Name& identityName,
+                          const time::system_clock::TimePoint& notBefore,
+                          const time::system_clock::TimePoint& notAfter)
+{
+  if (!state.m_caItem.m_caPrefix.isPrefixOf(identityName)) {
+    return nullptr;
+  }
+  if (identityName.empty()) {
+    NDN_LOG_TRACE("Randomly create a new name because identityName is empty and the param is empty.");
+    state.m_identityName = state.m_caItem.m_caPrefix;
+    state.m_identityName.append(std::to_string(random::generateSecureWord64()));
+  }
+  else {
+    state.m_identityName = identityName;
+  }
+
+  // generate a newly key pair or use an existing key
+  const auto& pib = state.m_keyChain.getPib();
+  security::pib::Identity identity;
+  try {
+    identity = pib.getIdentity(state.m_identityName);
+  }
+  catch (const security::Pib::Error& e) {
+    identity = state.m_keyChain.createIdentity(state.m_identityName);
+    state.m_isNewlyCreatedIdentity = true;
+    state.m_isNewlyCreatedKey = true;
+  }
+  try {
+    state.m_keyPair = identity.getDefaultKey();
+  }
+  catch (const security::Pib::Error& e) {
+    state.m_keyPair = state.m_keyChain.createKey(identity);
+    state.m_isNewlyCreatedKey = true;
+  }
+  auto& keyName = state.m_keyPair.getName();
+
+  // generate certificate request
+  security::v2::Certificate certRequest;
+  certRequest.setName(Name(keyName).append("cert-request").appendVersion());
+  certRequest.setContentType(tlv::ContentType_Key);
+  certRequest.setContent(state.m_keyPair.getPublicKey().data(), state.m_keyPair.getPublicKey().size());
+  SignatureInfo signatureInfo;
+  signatureInfo.setValidityPeriod(security::ValidityPeriod(notBefore, notAfter));
+  state.m_keyChain.sign(certRequest, signingByKey(keyName).setSignatureInfo(signatureInfo));
+
+  // generate Interest packet
+  Name interestName = state.m_caItem.m_caPrefix;
+  interestName.append("CA").append("NEW");
+  auto interest = make_shared<Interest>(interestName);
+  interest->setMustBeFresh(true);
+  interest->setCanBePrefix(false);
+  interest->setApplicationParameters(
+      NEW_RENEW_REVOKE::encodeApplicationParameters(RequestType::NEW, state.m_ecdh.getBase64PubKey(), certRequest));
+
+  // sign the Interest packet
+  state.m_keyChain.sign(*interest, signingByKey(keyName));
+  return interest;
+}
+
+shared_ptr<Interest>
+Requester::genRevokeInterest(RequesterState& state, const security::v2::Certificate& certificate)
+{
+  if (!state.m_caItem.m_caPrefix.isPrefixOf(certificate.getName())) {
+    return nullptr;
+  }
+  // generate Interest packet
+  Name interestName = state.m_caItem.m_caPrefix;
+  interestName.append("CA").append("REVOKE");
+  auto interest = make_shared<Interest>(interestName);
+  interest->setMustBeFresh(true);
+  interest->setCanBePrefix(false);
+  interest->setApplicationParameters(
+      NEW_RENEW_REVOKE::encodeApplicationParameters(RequestType::REVOKE, state.m_ecdh.getBase64PubKey(), certificate));
+  return interest;
+}
+
+std::list<std::string>
+Requester::onNewRenewRevokeResponse(RequesterState& state, const Data& reply)
+{
+  if (!security::verifySignature(reply, *state.m_caItem.m_cert)) {
+    _LOG_ERROR("Cannot verify replied Data packet signature.");
+    return std::list<std::string>();
+  }
+  processIfError(reply);
+
+  auto contentTLV = reply.getContent();
+  contentTLV.parse();
+
+  // ECDH
+  const auto& peerKeyBase64Str = readString(contentTLV.get(tlv_ecdh_pub));
+  const auto& saltStr = readString(contentTLV.get(tlv_salt));
+  uint64_t saltInt = std::stoull(saltStr);
+  state.m_ecdh.deriveSecret(peerKeyBase64Str);
+
+  // HKDF
+  hkdf(state.m_ecdh.context->sharedSecret, state.m_ecdh.context->sharedSecretLen,
+       (uint8_t*)&saltInt, sizeof(saltInt), state.m_aesKey, sizeof(state.m_aesKey));
+
+  // update state
+  state.m_status = static_cast<Status>(readNonNegativeInteger(contentTLV.get(tlv_status)));
+  state.m_requestId = readString(contentTLV.get(tlv_request_id));
+  std::list<std::string> challengeList;
+  for (auto const& element : contentTLV.elements()) {
+    if (element.type() == tlv_challenge) {
+      challengeList.push_back(readString(element));
+    }
+  }
+  return challengeList;
+}
+
+std::vector<std::tuple<std::string, std::string>>
+Requester::selectOrContinueChallenge(RequesterState& state, const std::string& challengeSelected)
+{
+  auto challenge = ChallengeModule::createChallengeModule(challengeSelected);
+  if (challenge == nullptr) {
+    BOOST_THROW_EXCEPTION(std::runtime_error("The challenge selected is not supported by your current version of NDNCERT."));
+  }
+  state.m_challengeType = challengeSelected;
+  return challenge->getRequestedParameterList(state.m_status, state.m_challengeStatus);
+}
+
+shared_ptr<Interest>
+Requester::genChallengeInterest(const RequesterState& state,
+                                std::vector<std::tuple<std::string, std::string>>&& parameters)
+{
+  if (state.m_challengeType == "") {
+    BOOST_THROW_EXCEPTION(std::runtime_error("The challenge has not been selected."));
+  }
+  auto challenge = ChallengeModule::createChallengeModule(state.m_challengeType);
+  if (challenge == nullptr) {
+    BOOST_THROW_EXCEPTION(std::runtime_error("The challenge selected is not supported by your current version of NDNCERT."));
+  }
+  auto challengeParams = challenge->genChallengeRequestTLV(state.m_status, state.m_challengeStatus, std::move(parameters));
+
+  Name interestName = state.m_caItem.m_caPrefix;
+  interestName.append("CA").append("CHALLENGE").append(state.m_requestId);
+  auto interest = make_shared<Interest>(interestName);
+  interest->setMustBeFresh(true);
+  interest->setCanBePrefix(false);
+
+  // encrypt the Interest parameters
+  auto paramBlock = encodeBlockWithAesGcm128(tlv::ApplicationParameters, state.m_aesKey,
+                                             challengeParams.value(), challengeParams.value_size(),
+                                             (const uint8_t*)"test", strlen("test"));
+  interest->setApplicationParameters(paramBlock);
+  state.m_keyChain.sign(*interest, signingByKey(state.m_keyPair.getName()));
+  return interest;
+}
+
+void
+Requester::onChallengeResponse(RequesterState& state, const Data& reply)
+{
+  if (!security::verifySignature(reply, *state.m_caItem.m_cert)) {
+    _LOG_ERROR("Cannot verify replied Data packet signature.");
+    return;
+  }
+  processIfError(reply);
+  auto result = decodeBlockWithAesGcm128(reply.getContent(), state.m_aesKey, (const uint8_t*)"test", strlen("test"));
+  Block contentTLV = makeBinaryBlock(tlv_encrypted_payload, result.data(), result.size());
+  contentTLV.parse();
+
+  // update state
+  state.m_status = static_cast<Status>(readNonNegativeInteger(contentTLV.get(tlv_status)));
+  state.m_challengeStatus = readString(contentTLV.get(tlv_challenge_status));
+  state.m_remainingTries = readNonNegativeInteger(contentTLV.get(tlv_remaining_tries));
+  state.m_freshBefore = time::system_clock::now() +
+                  time::seconds(readNonNegativeInteger(contentTLV.get(tlv_remaining_time)));
+
+  if (contentTLV.find(tlv_issued_cert_name) != contentTLV.elements_end()) {
+    Block issuedCertNameBlock = contentTLV.get(tlv_issued_cert_name);
+    issuedCertNameBlock.parse();
+    state.m_issuedCertName.wireDecode(issuedCertNameBlock.get(tlv::Name));
+  }
+}
+
+shared_ptr<Interest>
+Requester::genCertFetchInterest(const RequesterState& state)
+{
+  Name interestName = state.m_issuedCertName;
+  auto interest = make_shared<Interest>(interestName);
+  interest->setMustBeFresh(false);
+  interest->setCanBePrefix(false);
+  return interest;
+}
+
+shared_ptr<security::v2::Certificate>
+Requester::onCertFetchResponse(const Data& reply)
+{
+  try {
+    return std::make_shared<security::v2::Certificate>(reply.getContent().blockFromValue());
+  }
+  catch (const std::exception& e) {
+    _LOG_ERROR("Cannot parse replied certificate ");
+    return nullptr;
+  }
+}
+
+void
+Requester::endSession(RequesterState& state)
+{
+  if (state.m_status == Status::SUCCESS || state.m_status == Status::ENDED) {
+    return;
+  }
+  if (state.m_isNewlyCreatedIdentity) {
+    // put the identity into the if scope is because it may cause an error
+    // outside since when endSession is called, identity may not have been created yet.
+    auto identity = state.m_keyChain.getPib().getIdentity(state.m_identityName);
+    state.m_keyChain.deleteIdentity(identity);
+  }
+  else if (state.m_isNewlyCreatedKey) {
+    auto identity = state.m_keyChain.getPib().getIdentity(state.m_identityName);
+    state.m_keyChain.deleteKey(identity, state.m_keyPair);
+  }
+  state.m_status = Status::ENDED;
+}
+
+void
+Requester::processIfError(const Data& data)
+{
+  auto errorInfo = ErrorTLV::decodefromDataContent(data.getContent());
+  if (std::get<0>(errorInfo) == ErrorCode::NO_ERROR) {
+    return;
+  }
+  BOOST_THROW_EXCEPTION(std::runtime_error("Error info replied from the CA with Error code: " +
+                                           errorCodeToString(std::get<0>(errorInfo)) +
+                                           " and Error Info: " + std::get<1>(errorInfo)));
+}
+
+}  // namespace ndncert
+}  // namespace ndn
diff --git a/src/requester.hpp b/src/requester.hpp
new file mode 100644
index 0000000..44dd7ee
--- /dev/null
+++ b/src/requester.hpp
@@ -0,0 +1,123 @@
+/* -*- 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_CLIENT_MODULE_HPP
+#define NDNCERT_CLIENT_MODULE_HPP
+
+#include "configuration.hpp"
+#include "request-state.hpp"
+#include "crypto-support/crypto-helper.hpp"
+
+namespace ndn {
+namespace ndncert {
+
+// TODO
+// For each RequesterState, create a validator instance and initialize it with CA's cert
+// The validator instance should be in CaProfile
+
+struct RequesterState {
+  explicit
+  RequesterState(security::v2::KeyChain& keyChain, const CaProfile& caItem, RequestType requestType);
+
+  CaProfile m_caItem;
+  security::v2::KeyChain& m_keyChain;
+  RequestType m_type;
+
+  Name m_identityName;
+  security::Key m_keyPair;
+  std::string m_requestId;
+  Status m_status = Status::NOT_STARTED;
+  std::string m_challengeType;
+  std::string m_challengeStatus;
+  int m_remainingTries = 0;
+  time::system_clock::TimePoint m_freshBefore;
+  Name m_issuedCertName;
+
+  ECDHState m_ecdh;
+  uint8_t m_aesKey[16] = {0};
+
+  bool m_isCertInstalled = false;
+  bool m_isNewlyCreatedIdentity = false;
+  bool m_isNewlyCreatedKey = false;
+};
+
+class Requester : noncopyable
+{
+public:
+  // INFO related helpers
+  static shared_ptr<Interest>
+  genCaProfileInterest(const Name& caName);
+
+  /**
+   * Will first verify the signature of the packet using the key provided inside the profile.
+   * The application should be cautious whether to add CaProfile into the RequesterCaCache.
+   */
+  static boost::optional<CaProfile>
+  onCaProfileResponse(const Data& reply);
+
+  // PROBE related helpers
+  static shared_ptr<Interest>
+  genProbeInterest(const CaProfile& ca, std::vector<std::tuple<std::string, std::string>>&& probeInfo);
+
+  static void
+  onProbeResponse(const Data& reply, const CaProfile& ca,
+                  std::vector<Name>& identityNames, std::vector<Name>& otherCas);
+
+  // NEW/REVOKE/RENEW related helpers
+  static shared_ptr<Interest>
+  genNewInterest(RequesterState& state, const Name& identityName,
+                      const time::system_clock::TimePoint& notBefore,
+                      const time::system_clock::TimePoint& notAfter);
+
+  static shared_ptr<Interest>
+  genRevokeInterest(RequesterState& state, const security::v2::Certificate& certificate);
+
+  static std::list<std::string>
+  onNewRenewRevokeResponse(RequesterState& state, const Data& reply);
+
+  // CHALLENGE helpers
+  static std::vector<std::tuple<std::string, std::string>>
+  selectOrContinueChallenge(RequesterState& state, const std::string& challengeSelected);
+
+  static shared_ptr<Interest>
+  genChallengeInterest(const RequesterState& state,
+                       std::vector<std::tuple<std::string, std::string>>&& parameters);
+
+  static void
+  onChallengeResponse(RequesterState& state, const Data& reply);
+
+  static shared_ptr<Interest>
+  genCertFetchInterest(const RequesterState& state);
+
+  shared_ptr<security::v2::Certificate>
+  onCertFetchResponse(const Data& reply);
+
+  void
+  endSession(RequesterState& state);
+
+private:
+  static void
+  processIfError(const Data& data);
+};
+
+} // namespace ndncert
+} // namespace ndn
+
+#endif // NDNCERT_CLIENT_MODULE_HPP
diff --git a/tests/unit-tests/bench.t.cpp b/tests/unit-tests/bench.t.cpp
index da7e699..28850a8 100644
--- a/tests/unit-tests/bench.t.cpp
+++ b/tests/unit-tests/bench.t.cpp
@@ -19,7 +19,7 @@
  */
 
 #include "ca-module.hpp"
-#include "client-module.hpp"
+#include "requester.hpp"
 #include "challenge-modules/challenge-pin.hpp"
 #include "protocol-detail/info.hpp"
 #include "test-common.hpp"
@@ -90,18 +90,17 @@
   advanceClocks(time::milliseconds(20), 60);
 
   // generate NEW Interest
-  ClientModule client(m_keyChain);
-  CaConfigItem item;
+  CaProfile item;
   item.m_caPrefix = Name("/ndn");
   item.m_cert = std::make_shared<security::v2::Certificate>(cert);
-  client.getClientConf().m_caItems.push_back(item);
-  auto newInterest = client.generateNewInterest(time::system_clock::now(),
-                                                time::system_clock::now() + time::days(1), Name("/ndn/alice"));
+  RequesterState state(m_keyChain, item, RequestType::NEW);
+  auto newInterest = Requester::genNewInterest(state, Name("/ndn/alice"),
+                                               time::system_clock::now(),
+                                               time::system_clock::now() + time::days(1));
 
   std::cout << "New Interest Size: " << newInterest->wireEncode().size() << std::endl;
 
   // generate CHALLENGE Interest
-  ChallengePin pinChallenge;
   shared_ptr<Interest> challengeInterest = nullptr;
   shared_ptr<Interest> challengeInterest2 = nullptr;
   shared_ptr<Interest> challengeInterest3 = nullptr;
@@ -110,49 +109,41 @@
   face.onSendData.connect([&](const Data& response) {
     if (Name("/ndn/CA/NEW").isPrefixOf(response.getName())) {
       std::cout << "NEW Data Size: " << response.wireEncode().size() << std::endl;
-      client.onNewRenewRevokeResponse(response);
-      auto paramList = pinChallenge.getRequestedParameterList(client.m_status, client.m_challengeStatus);
-      challengeInterest = client.generateChallengeInterest(pinChallenge.genChallengeRequestTLV(client.m_status,
-                                                                                               client.m_challengeStatus,
-                                                                                               std::move(paramList)));
+      auto challengeList = Requester::onNewRenewRevokeResponse(state, response);
+      auto paramList = Requester::selectOrContinueChallenge(state, "pin");
+      challengeInterest = Requester::genChallengeInterest(state, std::move(paramList));
     }
     else if (Name("/ndn/CA/CHALLENGE").isPrefixOf(response.getName()) && count == 0) {
       count++;
       BOOST_CHECK(security::verifySignature(response, cert));
 
-      client.onChallengeResponse(response);
-      BOOST_CHECK(client.m_status == Status::CHALLENGE);
-      BOOST_CHECK_EQUAL(client.m_challengeStatus, ChallengePin::NEED_CODE);
-
-      auto paramList = pinChallenge.getRequestedParameterList(client.m_status, client.m_challengeStatus);
-      challengeInterest2 = client.generateChallengeInterest(pinChallenge.genChallengeRequestTLV(client.m_status,
-                                                                                                client.m_challengeStatus,
-                                                                                                std::move(paramList)));
+      Requester::onChallengeResponse(state, response);
+      BOOST_CHECK(state.m_status == Status::CHALLENGE);
+      BOOST_CHECK_EQUAL(state.m_challengeStatus, ChallengePin::NEED_CODE);
+      auto paramList = Requester::selectOrContinueChallenge(state, "pin");
+      challengeInterest2 = Requester::genChallengeInterest(state, std::move(paramList));
     }
     else if (Name("/ndn/CA/CHALLENGE").isPrefixOf(response.getName()) && count == 1) {
       count++;
       BOOST_CHECK(security::verifySignature(response, cert));
 
-      client.onChallengeResponse(response);
-      BOOST_CHECK(client.m_status == Status::CHALLENGE);
-      BOOST_CHECK_EQUAL(client.m_challengeStatus, ChallengePin::WRONG_CODE);
+      Requester::onChallengeResponse(state, response);
+      BOOST_CHECK(state.m_status == Status::CHALLENGE);
+      BOOST_CHECK_EQUAL(state.m_challengeStatus, ChallengePin::WRONG_CODE);
 
-      auto paramList = pinChallenge.getRequestedParameterList(client.m_status, client.m_challengeStatus);
+      auto paramList = Requester::selectOrContinueChallenge(state, "pin");
       auto request = ca.getCertificateRequest(*challengeInterest2);
       auto secret = request.m_challengeState->m_secrets.get(ChallengePin::PARAMETER_KEY_CODE, "");
       std::get<1>(paramList[0]) = secret;
-      challengeInterest3 = client.generateChallengeInterest(pinChallenge.genChallengeRequestTLV(client.m_status,
-                                                                                                client.m_challengeStatus,
-                                                                                                std::move(paramList)));
+      challengeInterest3 = Requester::genChallengeInterest(state, std::move(paramList));
       std::cout << "CHALLENGE Interest Size: " << challengeInterest3->wireEncode().size() << std::endl;
     }
     else if (Name("/ndn/CA/CHALLENGE").isPrefixOf(response.getName()) && count == 2) {
       std::cout << "CHALLENGE Data Size: " << response.wireEncode().size() << std::endl;
       count++;
       BOOST_CHECK(security::verifySignature(response, cert));
-
-      client.onChallengeResponse(response);
-      BOOST_CHECK(client.m_status == Status::SUCCESS);
+      Requester::onChallengeResponse(state, response);
+      BOOST_CHECK(state.m_status == Status::SUCCESS);
     }
   });
 
diff --git a/tests/unit-tests/ca-module.t.cpp b/tests/unit-tests/ca-module.t.cpp
index d6a2980..ff139c6 100644
--- a/tests/unit-tests/ca-module.t.cpp
+++ b/tests/unit-tests/ca-module.t.cpp
@@ -22,8 +22,8 @@
 #include "challenge-module.hpp"
 #include "challenge-modules/challenge-email.hpp"
 #include "challenge-modules/challenge-pin.hpp"
-#include "client-module.hpp"
 #include "protocol-detail/info.hpp"
+#include "requester.hpp"
 #include "test-common.hpp"
 
 namespace ndn {
@@ -171,15 +171,13 @@
   CaModule ca(face, m_keyChain, "tests/unit-tests/config-files/config-ca-1", "ca-storage-memory");
   advanceClocks(time::milliseconds(20), 60);
 
-  ClientModule client(m_keyChain);
-  CaConfigItem item;
+  CaProfile item;
   item.m_caPrefix = Name("/ndn");
   item.m_cert = std::make_shared<security::v2::Certificate>(cert);
-  client.getClientConf().m_caItems.push_back(item);
-
-  auto interest = client.generateNewInterest(time::system_clock::now(),
-                                             time::system_clock::now() + time::days(1),
-                                             Name("/ndn/zhiyi"));
+  RequesterState state(m_keyChain, item, RequestType::NEW);
+  auto interest = Requester::genNewInterest(state, Name("/ndn/zhiyi"),
+                                            time::system_clock::now(),
+                                            time::system_clock::now() + time::days(1));
 
   int count = 0;
   face.onSendData.connect([&](const Data& response) {
@@ -201,9 +199,9 @@
 
     BOOST_CHECK(challengeBlockCount != 0);
 
-    client.onNewRenewRevokeResponse(response);
+    auto challengeList = Requester::onNewRenewRevokeResponse(state, response);
     auto ca_encryption_key = ca.getCaStorage()->getRequest(readString(contentBlock.get(tlv_request_id))).m_encryptionKey;
-    BOOST_CHECK_EQUAL_COLLECTIONS(client.m_aesKey, client.m_aesKey + sizeof(client.m_aesKey),
+    BOOST_CHECK_EQUAL_COLLECTIONS(state.m_aesKey, state.m_aesKey + sizeof(state.m_aesKey),
                                   ca_encryption_key.value(), ca_encryption_key.value() + ca_encryption_key.value_size());
   });
   face.receive(*interest);
@@ -222,19 +220,14 @@
   CaModule ca(face, m_keyChain, "tests/unit-tests/config-files/config-ca-1");
   advanceClocks(time::milliseconds(20), 60);
 
-  ClientModule client(m_keyChain);
-  CaConfigItem item;
+  CaProfile item;
   item.m_caPrefix = Name("/ndn");
   item.m_cert = std::make_shared<security::v2::Certificate>(cert);
-  client.getClientConf().m_caItems.push_back(item);
+  RequesterState state(m_keyChain, item, RequestType::NEW);
   auto current_tp = time::system_clock::now();
-  auto interest1 = client.generateNewInterest(current_tp, current_tp - time::hours(1),
-                                              Name("/ndn/zhiyi"));
-  auto interest2 = client.generateNewInterest(current_tp, current_tp + time::days(361),
-                                              Name("/ndn/zhiyi"));
-  auto interest3 = client.generateNewInterest(current_tp - time::hours(1),
-                                              current_tp + time::hours(2),
-                                              Name("/ndn/zhiyi"));
+  auto interest1 = Requester::genNewInterest(state, Name("/ndn/zhiyi"), current_tp, current_tp - time::hours(1));
+  auto interest2 = Requester::genNewInterest(state, Name("/ndn/zhiyi"), current_tp, current_tp + time::days(361));
+  auto interest3 = Requester::genNewInterest(state, Name("/ndn/zhiyi"), current_tp - time::hours(1), current_tp + time::hours(2));
   face.onSendData.connect([&](const Data& response) {
     auto contentTlv = response.getContent();
     contentTlv.parse();
@@ -258,21 +251,17 @@
   CaModule ca(face, m_keyChain, "tests/unit-tests/config-files/config-ca-1", "ca-storage-memory");
   advanceClocks(time::milliseconds(20), 60);
 
-  ClientModule client(m_keyChain);
-  CaConfigItem item;
+  CaProfile item;
   item.m_caPrefix = Name("/ndn");
   item.m_cert = std::make_shared<security::v2::Certificate>(cert);
-  client.getClientConf().m_caItems.push_back(item);
+  RequesterState state(m_keyChain, item, RequestType::NEW);
 
-  auto interest1 = client.generateNewInterest(time::system_clock::now(),
-                                              time::system_clock::now() + time::days(1),
-                                              Name("/ndn/a"));
-  auto interest2 = client.generateNewInterest(time::system_clock::now(),
-                                              time::system_clock::now() + time::days(1),
-                                              Name("/ndn/a/b"));
-  auto interest3 = client.generateNewInterest(time::system_clock::now(),
-                                              time::system_clock::now() + time::days(1),
-                                              Name("/ndn/a/b/c/d"));
+  auto interest1 = Requester::genNewInterest(state, Name("/ndn/a"), time::system_clock::now(),
+                                              time::system_clock::now() + time::days(1));
+  auto interest2 = Requester::genNewInterest(state, Name("/ndn/a/b"), time::system_clock::now(),
+                                              time::system_clock::now() + time::days(1));
+  auto interest3 = Requester::genNewInterest(state, Name("/ndn/a/b/c/d"), time::system_clock::now(),
+                                              time::system_clock::now() + time::days(1));
 
   face.onSendData.connect([&](const Data& response) {
     auto contentTlv = response.getContent();
@@ -303,14 +292,14 @@
   CaModule ca(face, m_keyChain, "tests/unit-tests/config-files/config-ca-1");
   advanceClocks(time::milliseconds(20), 60);
 
-  ClientModule client(m_keyChain);
-  CaConfigItem item;
+  CaProfile item;
   item.m_caPrefix = Name("/ndn");
   item.m_cert = std::make_shared<security::v2::Certificate>(cert);
-  client.getClientConf().m_caItems.push_back(item);
+  RequesterState state(m_keyChain, item, RequestType::NEW);
+
   auto current_tp = time::system_clock::now();
-  auto interest1 = client.generateNewInterest(current_tp, current_tp + time::days(1), Name("/ndn"));
-  auto interest2 = client.generateNewInterest(current_tp, current_tp + time::days(1), Name("/ndn/a/b/c/d"));
+  auto interest1 = Requester::genNewInterest(state, Name("/ndn"), current_tp, current_tp + time::days(1));
+  auto interest2 = Requester::genNewInterest(state, Name("/ndn/a/b/c/d"), current_tp, current_tp + time::days(1));
   face.onSendData.connect([&](const Data& response) {
     auto contentTlv = response.getContent();
     contentTlv.parse();
@@ -334,16 +323,15 @@
   advanceClocks(time::milliseconds(20), 60);
 
   // generate NEW Interest
-  ClientModule client(m_keyChain);
-  CaConfigItem item;
+  CaProfile item;
   item.m_caPrefix = Name("/ndn");
   item.m_cert = std::make_shared<security::v2::Certificate>(cert);
-  client.getClientConf().m_caItems.push_back(item);
-  auto newInterest = client.generateNewInterest(time::system_clock::now(),
-                                                time::system_clock::now() + time::days(1), Name("/ndn/zhiyi"));
+  RequesterState state(m_keyChain, item, RequestType::NEW);
+
+  auto newInterest = Requester::genNewInterest(state, Name("/ndn/zhiyi"), time::system_clock::now(),
+                                                time::system_clock::now() + time::days(1));
 
   // generate CHALLENGE Interest
-  ChallengePin pinChallenge;
   shared_ptr<Interest> challengeInterest = nullptr;
   shared_ptr<Interest> challengeInterest2 = nullptr;
   shared_ptr<Interest> challengeInterest3 = nullptr;
@@ -351,47 +339,40 @@
   int count = 0;
   face.onSendData.connect([&](const Data& response) {
     if (Name("/ndn/CA/NEW").isPrefixOf(response.getName())) {
-      client.onNewRenewRevokeResponse(response);
-      auto paramList = pinChallenge.getRequestedParameterList(client.m_status, client.m_challengeStatus);
-      challengeInterest = client.generateChallengeInterest(pinChallenge.genChallengeRequestTLV(client.m_status,
-                                                                                               client.m_challengeStatus,
-                                                                                               std::move(paramList)));
+      auto challengeList = Requester::onNewRenewRevokeResponse(state, response);
+      auto paramList = Requester::selectOrContinueChallenge(state, "pin");
+      challengeInterest = Requester::genChallengeInterest(state, std::move(paramList));
     }
     else if (Name("/ndn/CA/CHALLENGE").isPrefixOf(response.getName()) && count == 0) {
       count++;
       BOOST_CHECK(security::verifySignature(response, cert));
 
-      client.onChallengeResponse(response);
-      BOOST_CHECK(client.m_status == Status::CHALLENGE);
-      BOOST_CHECK_EQUAL(client.m_challengeStatus, ChallengePin::NEED_CODE);
-
-      auto paramList = pinChallenge.getRequestedParameterList(client.m_status, client.m_challengeStatus);
-      challengeInterest2 = client.generateChallengeInterest(pinChallenge.genChallengeRequestTLV(client.m_status,
-                                                                                                client.m_challengeStatus,
-                                                                                                std::move(paramList)));
+      Requester::onChallengeResponse(state, response);
+      BOOST_CHECK(state.m_status == Status::CHALLENGE);
+      BOOST_CHECK_EQUAL(state.m_challengeStatus, ChallengePin::NEED_CODE);
+      auto paramList = Requester::selectOrContinueChallenge(state, "pin");
+      challengeInterest2 = Requester::genChallengeInterest(state, std::move(paramList));
     }
     else if (Name("/ndn/CA/CHALLENGE").isPrefixOf(response.getName()) && count == 1) {
       count++;
       BOOST_CHECK(security::verifySignature(response, cert));
 
-      client.onChallengeResponse(response);
-      BOOST_CHECK(client.m_status == Status::CHALLENGE);
-      BOOST_CHECK_EQUAL(client.m_challengeStatus, ChallengePin::WRONG_CODE);
+      Requester::onChallengeResponse(state, response);
+      BOOST_CHECK(state.m_status == Status::CHALLENGE);
+      BOOST_CHECK_EQUAL(state.m_challengeStatus, ChallengePin::WRONG_CODE);
 
-      auto paramList = pinChallenge.getRequestedParameterList(client.m_status, client.m_challengeStatus);
+      auto paramList = Requester::selectOrContinueChallenge(state, "pin");
       auto request = ca.getCertificateRequest(*challengeInterest2);
       auto secret = request.m_challengeState->m_secrets.get(ChallengePin::PARAMETER_KEY_CODE, "");
       std::get<1>(paramList[0]) = secret;
-      challengeInterest3 = client.generateChallengeInterest(pinChallenge.genChallengeRequestTLV(client.m_status,
-                                                                                                client.m_challengeStatus,
-                                                                                                std::move(paramList)));
+      challengeInterest3 = Requester::genChallengeInterest(state, std::move(paramList));
+      std::cout << "CHALLENGE Interest Size: " << challengeInterest3->wireEncode().size() << std::endl;
     }
     else if (Name("/ndn/CA/CHALLENGE").isPrefixOf(response.getName()) && count == 2) {
       count++;
       BOOST_CHECK(security::verifySignature(response, cert));
-
-      client.onChallengeResponse(response);
-      BOOST_CHECK(client.m_status == Status::SUCCESS);
+      Requester::onChallengeResponse(state, response);
+      BOOST_CHECK(state.m_status == Status::SUCCESS);
     }
   });
 
@@ -431,13 +412,12 @@
   RequestState certRequest(Name("/ndn"), "122", RequestType::NEW, Status::SUCCESS, clientCert, makeEmptyBlock(tlv::ContentType_Key));
   auto issuedCert = ca.issueCertificate(certRequest);
 
-  ClientModule client(m_keyChain);
-  CaConfigItem item;
+  CaProfile item;
   item.m_caPrefix = Name("/ndn");
   item.m_cert = std::make_shared<security::v2::Certificate>(cert);
-  client.getClientConf().m_caItems.push_back(item);
+  RequesterState state(m_keyChain, item, RequestType::REVOKE);
 
-  auto interest = client.generateRevokeInterest(issuedCert);
+  auto interest = Requester::genRevokeInterest(state, issuedCert);
 
   int count = 0;
   face.onSendData.connect([&](const Data& response) {
@@ -459,9 +439,9 @@
 
     BOOST_CHECK(challengeBlockCount != 0);
 
-    client.onNewRenewRevokeResponse(response);
+    auto challengeList = Requester::onNewRenewRevokeResponse(state, response);
     auto ca_encryption_key = ca.getCaStorage()->getRequest(readString(contentBlock.get(tlv_request_id))).m_encryptionKey;
-    BOOST_CHECK_EQUAL_COLLECTIONS(client.m_aesKey, client.m_aesKey + sizeof(client.m_aesKey),
+    BOOST_CHECK_EQUAL_COLLECTIONS(state.m_aesKey, state.m_aesKey + sizeof(state.m_aesKey),
                                   ca_encryption_key.value(), ca_encryption_key.value() + ca_encryption_key.value_size());
   });
   face.receive(*interest);
@@ -493,13 +473,12 @@
                                                            time::system_clock::now() + time::hours(10)));
   m_keyChain.sign(clientCert, signingByKey(clientKey.getName()).setSignatureInfo(signatureInfo));
 
-  ClientModule client(m_keyChain);
-  CaConfigItem item;
+  CaProfile item;
   item.m_caPrefix = Name("/ndn");
   item.m_cert = std::make_shared<security::v2::Certificate>(cert);
-  client.getClientConf().m_caItems.push_back(item);
+  RequesterState state(m_keyChain, item, RequestType::NEW);
 
-  auto interest = client.generateRevokeInterest(clientCert);
+  auto interest = Requester::genRevokeInterest(state, clientCert);
 
   bool receiveData = false;
   face.onSendData.connect([&](const Data& response) {
diff --git a/tests/unit-tests/client-module.t.cpp b/tests/unit-tests/client-module.t.cpp
index eb4cd45..79baee2 100644
--- a/tests/unit-tests/client-module.t.cpp
+++ b/tests/unit-tests/client-module.t.cpp
@@ -19,7 +19,7 @@
  */
 
 #include <protocol-detail/error.hpp>
-#include "client-module.hpp"
+#include "requester.hpp"
 #include "challenge-module.hpp"
 #include "ca-module.hpp"
 #include "test-common.hpp"
@@ -28,43 +28,7 @@
 namespace ndncert {
 namespace tests {
 
-BOOST_FIXTURE_TEST_SUITE(TestClientModule, IdentityManagementTimeFixture)
-
-BOOST_AUTO_TEST_CASE(ClientModuleInitialize)
-{
-  ClientModule client(m_keyChain);
-  client.getClientConf().load("tests/unit-tests/config-files/config-client-1");
-  BOOST_CHECK_EQUAL(client.getClientConf().m_caItems.size(), 2);
-}
-
-BOOST_AUTO_TEST_CASE(Probe)
-{
-  ClientModule client(m_keyChain);
-  client.getClientConf().load("tests/unit-tests/config-files/config-client-1");
-
-  auto identity = addIdentity(Name("/site"));
-  auto key = identity.getDefaultKey();
-  auto cert = key.getDefaultCertificate();
-
-  CaConfigItem item;
-  item.m_probeParameterKeys.push_back("email");
-  item.m_probeParameterKeys.push_back("uid");
-  item.m_probeParameterKeys.push_back("name");
-  item.m_caPrefix = Name("/site");
-  item.m_cert = std::make_shared<security::v2::Certificate>(cert);
-  client.getClientConf().m_caItems.push_back(item);
-
-  std::vector<std::tuple<std::string, std::string>> probeParams;
-  probeParams.push_back(std::make_tuple("email", "zhiyi@cs.ucla.edu"));
-  probeParams.push_back(std::make_tuple("uid", "987654321"));
-  probeParams.push_back(std::make_tuple("name", "Zhiyi Zhang"));
-  auto firstInterest = client.generateProbeInterest(item, std::move(probeParams));
-  BOOST_CHECK(firstInterest->getName().at(-1).isParametersSha256Digest());
-  // ignore the last name component (ParametersSha256Digest)
-  BOOST_CHECK_EQUAL(firstInterest->getName().getPrefix(-1), "/site/CA/PROBE");
-  BOOST_CHECK_EQUAL(readString(firstInterest->getApplicationParameters().get(tlv_parameter_value)),
-                    "zhiyi@cs.ucla.edu");
-}
+BOOST_FIXTURE_TEST_SUITE(TestRequester, IdentityManagementTimeFixture)
 
 BOOST_AUTO_TEST_CASE(ErrorHandling)
 {
@@ -72,47 +36,24 @@
   auto key = identity.getDefaultKey();
   auto cert = key.getDefaultCertificate();
 
-  ClientModule client(m_keyChain);
-  CaConfigItem item;
+  CaProfile item;
   item.m_caPrefix = Name("/site");
   item.m_cert = std::make_shared<security::v2::Certificate>(cert);
-  client.getClientConf().m_caItems.push_back(item);
-
-  client.generateProbeInterest(item,std::vector<std::tuple<std::string, std::string>>());
+  RequesterState state(m_keyChain, item, RequestType::NEW);
 
   Data errorPacket;
   errorPacket.setName(Name("/site/pretend/this/is/error/packet"));
   errorPacket.setFreshnessPeriod(time::seconds(100));
-  errorPacket.setContent(ErrorTLV::encodeDataContent(ErrorCode::NO_ERROR, "This is a test."));
+  errorPacket.setContent(ErrorTLV::encodeDataContent(ErrorCode::INVALID_PARAMETER, "This is a test."));
   m_keyChain.sign(errorPacket, signingByIdentity(identity));
 
-  BOOST_CHECK_THROW(client.onProbeResponse(errorPacket), std::exception);
-  BOOST_CHECK_THROW(client.onNewRenewRevokeResponse(errorPacket), std::exception);
-  BOOST_CHECK_THROW(client.onChallengeResponse(errorPacket), std::exception);
+  std::vector<Name> ids, cas;
+  BOOST_CHECK_THROW(Requester::onProbeResponse(errorPacket, item, ids, cas), std::runtime_error);
+  BOOST_CHECK_THROW(Requester::onNewRenewRevokeResponse(state, errorPacket), std::runtime_error);
+  BOOST_CHECK_THROW(Requester::onChallengeResponse(state, errorPacket), std::runtime_error);
 }
 
-// BOOST_AUTO_TEST_CASE(GenProbeRequestJson)
-// {
-//   ClientModule client(m_keyChain);
-//   client.getClientConf().load("tests/unit-tests/config-files/config-client-1");
-
-//   auto identity = addIdentity(Name("/site"));
-//   auto key = identity.getDefaultKey();
-//   auto cert = key.getDefaultCertificate();
-
-//   CaConfigItem item;
-//   item.m_probe = "email:uid:name";
-//   item.m_caPrefix = Name("/site");
-//   item.m_cert = std::make_shared<security::v2::Certificate>(cert);
-//   client.getClientConf().m_caItems.push_back(item);
-
-//   auto interestPacket = client.genProbeRequestJson(item, "yufeng@ucla.edu:123456789:Yufeng Zhang");
-//   BOOST_CHECK_EQUAL(interestPacket.get("email", ""), "yufeng@ucla.edu");
-//   BOOST_CHECK_EQUAL(interestPacket.get("uid", ""), "123456789");
-//   BOOST_CHECK_EQUAL(interestPacket.get("name", ""), "Yufeng Zhang");
-// }
-
-BOOST_AUTO_TEST_SUITE_END() // TestClientModule
+BOOST_AUTO_TEST_SUITE_END() // TestRequester
 
 } // namespace tests
 } // namespace ndncert
diff --git a/tests/unit-tests/configuration.t.cpp b/tests/unit-tests/configuration.t.cpp
index ec47f9d..d7cb506 100644
--- a/tests/unit-tests/configuration.t.cpp
+++ b/tests/unit-tests/configuration.t.cpp
@@ -67,9 +67,9 @@
   BOOST_CHECK_THROW(config.load("tests/unit-tests/config-files/config-ca-4"), std::runtime_error);
 }
 
-BOOST_AUTO_TEST_CASE(ClientConfigFile)
+BOOST_AUTO_TEST_CASE(RequesterCaCacheFile)
 {
-  ClientConfig config;
+  RequesterCaCache config;
   config.load("tests/unit-tests/config-files/config-client-1");
   BOOST_CHECK_EQUAL(config.m_caItems.size(), 2);
 
@@ -93,9 +93,9 @@
                     "/ndn/site1/KEY/%11%BC%22%F4c%15%FF%17/self/%FD%00%00%01Y%C8%14%D9%A5");
 }
 
-BOOST_AUTO_TEST_CASE(ClientConfigFileWithErrors)
+BOOST_AUTO_TEST_CASE(RequesterCaCacheFileWithErrors)
 {
-  ClientConfig config;
+  RequesterCaCache config;
   // nonexistent file
   BOOST_CHECK_THROW(config.load("tests/unit-tests/config-files/Nonexist"), std::runtime_error);
   // missing certificate
@@ -104,12 +104,12 @@
   BOOST_CHECK_THROW(config.load("tests/unit-tests/config-files/config-client-3"), std::runtime_error);
 }
 
-BOOST_AUTO_TEST_CASE(ClientConfigFileAddAndRemoveCaItem)
+BOOST_AUTO_TEST_CASE(RequesterCaCacheFileAddAndremoveCaProfile)
 {
-  ClientConfig config;
+  RequesterCaCache config;
   config.load("tests/unit-tests/config-files/config-client-1");
 
-  CaConfigItem item;
+  CaProfile item;
   item.m_caPrefix = Name("/test");
   item.m_caInfo = "test";
 
@@ -118,7 +118,7 @@
   auto lastItem = config.m_caItems.back();
   BOOST_CHECK_EQUAL(lastItem.m_caPrefix, "/test");
 
-  config.removeCaItem(Name("/test"));
+  config.removeCaProfile(Name("/test"));
   BOOST_CHECK_EQUAL(config.m_caItems.size(), 2);
   lastItem = config.m_caItems.back();
   BOOST_CHECK_EQUAL(lastItem.m_caPrefix, "/ndn/edu/ucla/zhiyi");
diff --git a/tools/ndncert-client.cpp b/tools/ndncert-client.cpp
index e81d792..5fbea9b 100644
--- a/tools/ndncert-client.cpp
+++ b/tools/ndncert-client.cpp
@@ -19,7 +19,7 @@
  */
 
 #include "challenge-module.hpp"
-#include "client-module.hpp"
+#include "requester.hpp"
 #include "protocol-detail/info.hpp"
 #include <ndn-cxx/security/verification-helpers.hpp>
 #include <boost/asio.hpp>
@@ -40,7 +40,7 @@
 security::v2::KeyChain keyChain;
 std::string challengeType;
 int validityPeriod = -1;
-ClientModule client(keyChain);
+Requester client(keyChain);
 
 static void
 captureParams(std::vector<std::tuple<std::string, std::string>>& requirement)
@@ -257,7 +257,7 @@
               << "Introduction: " << item.m_caInfo << "\n"
               << "***************************************\n";
   }
-  std::vector<CaConfigItem> caVector{std::begin(caList), std::end(caList)};
+  std::vector<CaProfile> caVector{std::begin(caList), std::end(caList)};
   std::cerr << "Step "
             << nStep++ << ": Please type in the CA INDEX that you want to apply"
             << " or type in NONE if your expected CA is not in the list\n";