improve the robustness of ndncert library

Change-Id: Iaabc4d8f28ca27a7e7f501ebd122c5231ceb3ac0
diff --git a/src/ca-config.cpp b/src/ca-config.cpp
index 3165b2d..3145839 100644
--- a/src/ca-config.cpp
+++ b/src/ca-config.cpp
@@ -19,6 +19,7 @@
  */
 
 #include "ca-config.hpp"
+#include "challenge-module.hpp"
 #include <ndn-cxx/util/io.hpp>
 #include <boost/filesystem.hpp>
 
@@ -32,9 +33,8 @@
   try {
     boost::property_tree::read_json(fileName, configJson);
   }
-  catch (const boost::property_tree::info_parser_error& error) {
-    BOOST_THROW_EXCEPTION(Error("Failed to parse configuration file " + fileName +
-                                " " + error.message() + " line " + std::to_string(error.line())));
+  catch (const std::exception& error) {
+    BOOST_THROW_EXCEPTION(Error("Failed to parse configuration file " + fileName + ", " + error.what()));
   }
 
   if (configJson.begin() == configJson.end()) {
@@ -46,18 +46,21 @@
 void
 CaConfig::parse(const JsonSection& configJson)
 {
-    // essential info
-    m_caName = Name(configJson.get<std::string>("ca-prefix"));
-    m_freshnessPeriod = time::seconds(configJson.get("issuing-freshness", 720));
-    m_validityPeriod = time::days(configJson.get("max-validity-period", 360));
+  // essential info
+  m_caName = Name(configJson.get("ca-prefix", ""));
+  if (m_caName.empty()) {
+    BOOST_THROW_EXCEPTION(Error("Cannot read ca-prefix from the config file"));
+  }
+  m_freshnessPeriod = time::seconds(configJson.get("issuing-freshness", 720));
+  m_validityPeriod = time::days(configJson.get("max-validity-period", 360));
 
-    // optional info
-    m_probe = configJson.get("probe", "");
-    m_caInfo = configJson.get("ca-info", "");
+  // optional info
+  m_probe = configJson.get("probe", "");
+  m_caInfo = configJson.get("ca-info", "");
 
-    // optional supported challenges
-    auto challengeList = configJson.get_child("supported-challenges");
-    m_supportedChallenges = parseChallengeList(challengeList);
+  // optional supported challenges
+  auto challengeList = configJson.get_child("supported-challenges");
+  m_supportedChallenges = parseChallengeList(challengeList);
 }
 
 std::list<std::string>
@@ -66,7 +69,15 @@
   std::list<std::string> result;
   auto it = section.begin();
   for (; it != section.end(); it++) {
-    result.push_back(boost::algorithm::to_lower_copy(it->second.get<std::string>("type")));
+    auto challengeType = it->second.get("type", "");
+    if (challengeType == "") {
+      BOOST_THROW_EXCEPTION(Error("Cannot read type in supported-challenges from the config file"));
+    }
+    challengeType = boost::algorithm::to_lower_copy(challengeType);
+    if (!ChallengeModule::supportChallenge(challengeType)) {
+      BOOST_THROW_EXCEPTION(Error("Does not support challenge read from the config file"));
+    }
+    result.push_back(challengeType);
   }
   return result;
 }
diff --git a/src/ca-config.hpp b/src/ca-config.hpp
index e20b8b4..0186067 100644
--- a/src/ca-config.hpp
+++ b/src/ca-config.hpp
@@ -65,6 +65,12 @@
   };
 
 public:
+  /**
+   * @throw CaConfig::Error when config file does not exist
+   * @throw CaConfig::Error when the JSON text in the file cannot be parsed correctly
+   * @throw CaConfig::Error when the ca-prefix attribute in JSON text is empty
+   * @throw CaConfig::Error when the challenge is not specified or is not supported
+   */
   void
   load(const std::string& fileName);
 
diff --git a/src/ca-module.cpp b/src/ca-module.cpp
index 89bf8a7..7dbb053 100644
--- a/src/ca-module.cpp
+++ b/src/ca-module.cpp
@@ -340,7 +340,7 @@
   }
 
   // load the corresponding challenge module
-  std::string challengeType = paramJson.get<std::string>(JSON_CLIENT_SELECTED_CHALLENGE);
+  std::string challengeType = paramJson.get(JSON_CLIENT_SELECTED_CHALLENGE, "");
   auto challenge = ChallengeModule::createChallengeModule(challengeType);
   JsonSection contentJson;
   if (challenge == nullptr) {
diff --git a/src/ca-module.hpp b/src/ca-module.hpp
index 3926d97..0b857b0 100644
--- a/src/ca-module.hpp
+++ b/src/ca-module.hpp
@@ -121,7 +121,7 @@
   std::list<InterestFilterHandle> m_interestFilterHandles;
 
   ECDHState m_ecdh;
-  uint8_t m_aesKey[32] = {0};
+  uint8_t m_aesKey[16] = {0};
 };
 
 } // namespace ndncert
diff --git a/src/challenge-module.cpp b/src/challenge-module.cpp
index 8c332cb..9288330 100644
--- a/src/challenge-module.cpp
+++ b/src/challenge-module.cpp
@@ -31,11 +31,19 @@
 
 ChallengeModule::~ChallengeModule() = default;
 
-unique_ptr<ChallengeModule>
-ChallengeModule::createChallengeModule(const std::string& canonicalName)
+bool
+ChallengeModule::supportChallenge(const std::string& challengeType)
 {
   ChallengeFactory& factory = getFactory();
-  auto i = factory.find(canonicalName);
+  auto i = factory.find(challengeType);
+  return i == factory.end() ? false : true;
+}
+
+unique_ptr<ChallengeModule>
+ChallengeModule::createChallengeModule(const std::string& challengeType)
+{
+  ChallengeFactory& factory = getFactory();
+  auto i = factory.find(challengeType);
   return i == factory.end() ? nullptr : i->second();
 }
 
diff --git a/src/challenge-module.hpp b/src/challenge-module.hpp
index ab15be8..80670f4 100644
--- a/src/challenge-module.hpp
+++ b/src/challenge-module.hpp
@@ -58,8 +58,11 @@
     factory[typeName] = [] { return make_unique<ChallengeType>(); };
   }
 
+  static bool
+  supportChallenge(const std::string& challengeType);
+
   static unique_ptr<ChallengeModule>
-  createChallengeModule(const std::string& ChallengeType);
+  createChallengeModule(const std::string& challengeType);
 
   // For CA
   virtual void
diff --git a/src/challenge-module/challenge-credential.cpp b/src/challenge-module/challenge-credential.cpp
index 839ccae..f5d7434 100644
--- a/src/challenge-module/challenge-credential.cpp
+++ b/src/challenge-module/challenge-credential.cpp
@@ -41,8 +41,9 @@
   if (configPath == "") {
     m_configFile = std::string(SYSCONFDIR) + "/ndncert/challenge-credential.conf";
   }
-  else
+  else {
     m_configFile = configPath;
+  }
 }
 
 void
@@ -65,9 +66,13 @@
   auto anchorList = config.get_child("anchor-list");
   auto it = anchorList.begin();
   for (; it != anchorList.end(); it++) {
-    std::istringstream ss(it->second.get<std::string>("certificate"));
-    security::v2::Certificate cert = *(io::load<security::v2::Certificate>(ss));
-    m_trustAnchors.push_back(cert);
+    std::istringstream ss(it->second.get("certificate", ""));
+    auto cert = io::load<security::v2::Certificate>(ss);
+    if (cert == nullptr) {
+      _LOG_ERROR("Cannot load the certificate from config file");
+      continue;
+    }
+    m_trustAnchors.push_back(*cert);
   }
 }
 
@@ -79,13 +84,10 @@
     parseConfigFile();
   }
   // load credential parameter
-  std::istringstream ss1(params.get<std::string>(JSON_CREDENTIAL_CERT));
-  security::v2::Certificate cert;
-  try {
-    cert = *(io::load<security::v2::Certificate>(ss1));
-  }
-  catch (const std::exception& e) {
-    _LOG_ERROR("Cannot load credential parameter: cert" << e.what());
+  std::istringstream ss1(params.get(JSON_CREDENTIAL_CERT, ""));
+  auto cert = io::load<security::v2::Certificate>(ss1);
+  if (cert == nullptr) {
+    _LOG_ERROR("Cannot load credential parameter: cert");
     request.m_status = STATUS_FAILURE;
     request.m_challengeStatus = FAILURE_INVALID_FORMAT_CREDENTIAL;
     updateRequestOnChallengeEnd(request);
@@ -94,13 +96,10 @@
   ss1.str("");
   ss1.clear();
   // load self-signed data
-  std::istringstream ss2(params.get<std::string>(JSON_CREDENTIAL_SELF));
-  Data self;
-  try {
-    self = *(io::load<Data>(ss2));
-  }
-  catch (const std::exception& e) {
-    _LOG_TRACE("Cannot load credential parameter: self-signed cert" << e.what());
+  std::istringstream ss2(params.get(JSON_CREDENTIAL_SELF, ""));
+  auto self = io::load<Data>(ss2);
+  if (self == nullptr) {
+    _LOG_TRACE("Cannot load credential parameter: self-signed cert");
     request.m_status = STATUS_FAILURE;
     request.m_challengeStatus = FAILURE_INVALID_FORMAT_SELF_SIGNED;
     updateRequestOnChallengeEnd(request);
@@ -110,11 +109,11 @@
   ss2.clear();
 
   // verify the credential and the self-signed cert
-  Name signingKeyName = cert.getSignature().getKeyLocator().getName();
+  Name signingKeyName = cert->getSignature().getKeyLocator().getName();
   for (auto anchor : m_trustAnchors) {
     if (anchor.getKeyName() == signingKeyName) {
-      if (security::verifySignature(cert, anchor) && security::verifySignature(self, cert)
-          && readString(self.getContent()) == request.m_requestId) {
+      if (security::verifySignature(*cert, anchor) && security::verifySignature(*self, *cert)
+          && readString(self->getContent()) == request.m_requestId) {
         request.m_status = STATUS_PENDING;
         request.m_challengeStatus = CHALLENGE_STATUS_SUCCESS;
         updateRequestOnChallengeEnd(request);
@@ -150,8 +149,8 @@
 {
   JsonSection result;
   if (status == STATUS_BEFORE_CHALLENGE && challengeStatus == "") {
-    result.put(JSON_CREDENTIAL_CERT, params.get<std::string>(JSON_CREDENTIAL_CERT, ""));
-    result.put(JSON_CREDENTIAL_SELF, params.get<std::string>(JSON_CREDENTIAL_SELF, ""));
+    result.put(JSON_CREDENTIAL_CERT, params.get(JSON_CREDENTIAL_CERT, ""));
+    result.put(JSON_CREDENTIAL_SELF, params.get(JSON_CREDENTIAL_SELF, ""));
   }
   else {
     _LOG_ERROR("Client's status and challenge status are wrong");
diff --git a/src/challenge-module/challenge-email.cpp b/src/challenge-module/challenge-email.cpp
index b22ec93..b9ca344 100644
--- a/src/challenge-module/challenge-email.cpp
+++ b/src/challenge-module/challenge-email.cpp
@@ -50,9 +50,10 @@
 void
 ChallengeEmail::handleChallengeRequest(const JsonSection& params, CertificateRequest& request)
 {
+  auto currentTime = time::system_clock::now();
   if (request.m_challengeStatus == "") {
     // for the first time, init the challenge
-    std::string emailAddress = params.get<std::string>(JSON_EMAIL);
+    std::string emailAddress = params.get(JSON_EMAIL, "");
     if (!isValidEmailAddress(emailAddress)) {
       request.m_status = STATUS_FAILURE;
       request.m_challengeStatus = FAILURE_INVALID_EMAIL;
@@ -79,7 +80,7 @@
     JsonSection secretJson;
     secretJson.add(JSON_CODE, emailCode);
     request.m_challengeSecrets = secretJson;
-    request.m_challengeTp = time::toIsoString(time::system_clock::now());
+    request.m_challengeTp = time::toIsoString(currentTime);
     request.m_remainingTime = m_secretLifetime.count();
     request.m_remainingTries = m_maxAttemptTimes;
     // send out the email
@@ -90,9 +91,9 @@
   else if (request.m_challengeStatus == NEED_CODE || request.m_challengeStatus == WRONG_CODE) {
     _LOG_TRACE("Challenge Interest arrives. Challenge Status: " << request.m_challengeStatus);
     // the incoming interest should bring the pin code
-    std::string givenCode = params.get<std::string>(JSON_CODE);
+    std::string givenCode = params.get(JSON_CODE, "");
     const auto realCode = request.m_challengeSecrets.get<std::string>(JSON_CODE);
-    if (time::system_clock::now() - time::fromIsoString(request.m_challengeTp) >= m_secretLifetime) {
+    if (currentTime - time::fromIsoString(request.m_challengeTp) >= m_secretLifetime) {
       // secret expires
       request.m_status = STATUS_FAILURE;
       request.m_challengeStatus = CHALLENGE_STATUS_FAILURE_TIMEOUT;
@@ -113,7 +114,7 @@
       if (request.m_remainingTries > 1) {
         request.m_challengeStatus = WRONG_CODE;
         request.m_remainingTries = request.m_remainingTries - 1;
-        auto remainTime = m_secretLifetime - (time::system_clock::now() - time::fromIsoString(request.m_challengeTp));
+        auto remainTime = m_secretLifetime - (currentTime - time::fromIsoString(request.m_challengeTp));
         request.m_remainingTime = remainTime.count();
         _LOG_TRACE("Secret code didn't match. Remaining Tries - 1.");
         return;
@@ -161,15 +162,15 @@
   JsonSection result;
   if (status == STATUS_BEFORE_CHALLENGE && challengeStatus == "") {
     result.put(JSON_CLIENT_SELECTED_CHALLENGE, CHALLENGE_TYPE);
-    result.put(JSON_EMAIL, params.get<std::string>(JSON_EMAIL, ""));
+    result.put(JSON_EMAIL, params.get(JSON_EMAIL, ""));
   }
   else if (status == STATUS_CHALLENGE && challengeStatus == NEED_CODE) {
     result.put(JSON_CLIENT_SELECTED_CHALLENGE, CHALLENGE_TYPE);
-    result.put(JSON_CODE, params.get<std::string>(JSON_CODE, ""));
+    result.put(JSON_CODE, params.get(JSON_CODE, ""));
   }
   else if (status == STATUS_CHALLENGE && challengeStatus == WRONG_CODE) {
     result.put(JSON_CLIENT_SELECTED_CHALLENGE, CHALLENGE_TYPE);
-    result.put(JSON_CODE, params.get<std::string>(JSON_CODE, ""));
+    result.put(JSON_CODE, params.get(JSON_CODE, ""));
   }
   else {
     _LOG_ERROR("Client's status and challenge status are wrong");
diff --git a/src/challenge-module/challenge-pin.cpp b/src/challenge-module/challenge-pin.cpp
index 9534b21..87f36cc 100644
--- a/src/challenge-module/challenge-pin.cpp
+++ b/src/challenge-module/challenge-pin.cpp
@@ -44,6 +44,7 @@
 void
 ChallengePin::handleChallengeRequest(const JsonSection& params, CertificateRequest& request)
 {
+  auto currentTime = time::system_clock::now();
   if (request.m_challengeStatus == "") {
     _LOG_TRACE("Challenge Interest arrives. Init the challenge");
     // for the first time, init the challenge
@@ -54,7 +55,7 @@
     JsonSection secretJson;
     secretJson.add(JSON_PIN_CODE, secretCode);
     request.m_challengeSecrets = secretJson;
-    request.m_challengeTp = time::toIsoString(time::system_clock::now());
+    request.m_challengeTp = time::toIsoString(currentTime);
     request.m_remainingTime = m_secretLifetime.count();
     request.m_remainingTries = m_maxAttemptTimes;
     _LOG_TRACE("Secret for request " << request.m_requestId << " : " << secretCode);
@@ -63,9 +64,9 @@
   else if (request.m_challengeStatus == NEED_CODE || request.m_challengeStatus == WRONG_CODE) {
     _LOG_TRACE("Challenge Interest arrives. Challenge Status: " << request.m_challengeStatus);
     // the incoming interest should bring the pin code
-    std::string givenCode = params.get<std::string>(JSON_PIN_CODE);
+    std::string givenCode = params.get(JSON_PIN_CODE, "");
     const auto realCode = request.m_challengeSecrets.get<std::string>(JSON_PIN_CODE);
-    if (time::system_clock::now() - time::fromIsoString(request.m_challengeTp) >= m_secretLifetime) {
+    if (currentTime - time::fromIsoString(request.m_challengeTp) >= m_secretLifetime) {
       // secret expires
       request.m_status = STATUS_FAILURE;
       request.m_challengeStatus = CHALLENGE_STATUS_FAILURE_TIMEOUT;
@@ -86,7 +87,7 @@
       if (request.m_remainingTries > 1) {
         request.m_challengeStatus = WRONG_CODE;
         request.m_remainingTries = request.m_remainingTries - 1;
-        auto remainTime = m_secretLifetime - (time::system_clock::now() - time::fromIsoString(request.m_challengeTp));
+        auto remainTime = m_secretLifetime - (currentTime - time::fromIsoString(request.m_challengeTp));
         request.m_remainingTime = remainTime.count();
         _LOG_TRACE("PIN code didn't match. Remaining Tries - 1.");
         return;
@@ -138,11 +139,11 @@
   }
   else if (status == STATUS_CHALLENGE && challengeStatus == NEED_CODE) {
     result.put(JSON_CLIENT_SELECTED_CHALLENGE, CHALLENGE_TYPE);
-    result.put(JSON_PIN_CODE, params.get<std::string>(JSON_PIN_CODE, ""));
+    result.put(JSON_PIN_CODE, params.get(JSON_PIN_CODE, ""));
   }
   else if (status == STATUS_CHALLENGE && challengeStatus == WRONG_CODE) {
     result.put(JSON_CLIENT_SELECTED_CHALLENGE, CHALLENGE_TYPE);
-    result.put(JSON_PIN_CODE, params.get<std::string>(JSON_PIN_CODE, ""));
+    result.put(JSON_PIN_CODE, params.get(JSON_PIN_CODE, ""));
   }
   else {
     _LOG_ERROR("Client's status and challenge status are wrong");
diff --git a/src/client-config.cpp b/src/client-config.cpp
index 19f8a89..1cd2618 100644
--- a/src/client-config.cpp
+++ b/src/client-config.cpp
@@ -32,13 +32,12 @@
   try {
     boost::property_tree::read_json(fileName, config);
   }
-  catch (const boost::property_tree::info_parser_error& error) {
-    BOOST_THROW_EXCEPTION(Error("Failed to parse configuration file " + fileName +
-                                " " + error.message() + " line " + std::to_string(error.line())));
+  catch (const std::exception& error) {
+    BOOST_THROW_EXCEPTION(Error("Failed to parse configuration file " + fileName + ", " + error.what()));
   }
 
   if (config.begin() == config.end()) {
-    BOOST_THROW_EXCEPTION(Error("Error processing configuration file: " + fileName + " no data"));
+    BOOST_THROW_EXCEPTION(Error("Error processing configuration file: " + fileName + ", no data"));
   }
 
   load(config);
@@ -86,11 +85,18 @@
 ClientConfig::extractCaItem(const JsonSection& configSection)
 {
   ClientCaItem item;
-  item.m_caName = Name(configSection.get<std::string>("ca-prefix"));
-  item.m_caInfo = configSection.get<std::string>("ca-info");
-  item.m_probe = configSection.get<std::string>("probe");
-  std::istringstream ss(configSection.get<std::string>("certificate"));
-  item.m_anchor = *(io::load<security::v2::Certificate>(ss));
+  item.m_caName = Name(configSection.get("ca-prefix", ""));
+  if (item.m_caName.empty()) {
+    BOOST_THROW_EXCEPTION(Error("Cannot read ca-prefix from the config file"));
+  }
+  item.m_caInfo = configSection.get("ca-info", "");
+  item.m_probe = configSection.get("probe", "");
+  std::istringstream ss(configSection.get("certificate", ""));
+  auto anchor = io::load<security::v2::Certificate>(ss);
+  if (anchor == nullptr) {
+    BOOST_THROW_EXCEPTION(Error("Cannot load the certificate from config file"));
+  }
+  item.m_anchor = *anchor;
   return item;
 }
 
diff --git a/src/client-config.hpp b/src/client-config.hpp
index ba02efa..ba63e82 100644
--- a/src/client-config.hpp
+++ b/src/client-config.hpp
@@ -61,6 +61,12 @@
   };
 
 public:
+  /**
+   * @throw ClientConfig::Error when config file does not exist
+   * @throw ClientConfig::Error when the JSON text in the file cannot be parsed correctly
+   * @throw ClientConfig::Error when the ca-prefix attribute in JSON text is empty
+   * @throw ClientConfig::Error when the certificate in JSON text cannot be parsed correctly
+   */
   void
   load(const std::string& fileName);
 
diff --git a/src/client-module.cpp b/src/client-module.cpp
index 24947f4..9ae71c0 100644
--- a/src/client-module.cpp
+++ b/src/client-module.cpp
@@ -116,7 +116,7 @@
   auto contentJson = getJsonFromData(reply);
 
   // read the available name and put it into the state
-  auto nameUri = contentJson.get<std::string>(JSON_CA_NAME, "");
+  auto nameUri = contentJson.get(JSON_CA_NAME, "");
   if (nameUri != "") {
     m_identityName = Name(nameUri);
   }
@@ -200,8 +200,8 @@
   auto contentJson = getJsonFromData(reply);
 
   // ECDH
-  const auto& peerKeyBase64Str = contentJson.get<std::string>(JSON_CA_ECDH, "");
-  const auto& saltStr = contentJson.get<std::string>(JSON_CA_SALT, "");
+  const auto& peerKeyBase64Str = contentJson.get(JSON_CA_ECDH, "");
+  const auto& saltStr = contentJson.get(JSON_CA_SALT, "");
   uint64_t saltInt = std::stoull(saltStr);
   m_ecdh.deriveSecret(peerKeyBase64Str);
 
@@ -210,13 +210,13 @@
        (uint8_t*)&saltInt, sizeof(saltInt), m_aesKey, sizeof(m_aesKey));
 
   // update state
-  m_status = contentJson.get<int>(JSON_CA_STATUS);
-  m_requestId = contentJson.get<std::string>(JSON_CA_REQUEST_ID, "");
+  m_status = contentJson.get(JSON_CA_STATUS, 0);
+  m_requestId = contentJson.get(JSON_CA_REQUEST_ID, "");
 
   auto challengesJson = contentJson.get_child(JSON_CA_CHALLENGES);
   m_challengeList.clear();
   for (const auto& challengeJson : challengesJson) {
-    m_challengeList.push_back(challengeJson.second.get<std::string>(JSON_CA_CHALLENGE_ID, ""));
+    m_challengeList.push_back(challengeJson.second.get(JSON_CA_CHALLENGE_ID, ""));
   }
   return m_challengeList;
 }
@@ -224,7 +224,7 @@
 shared_ptr<Interest>
 ClientModule::generateChallengeInterest(const JsonSection& paramJson)
 {
-  m_challengeType = paramJson.get<std::string>(JSON_CLIENT_SELECTED_CHALLENGE);
+  m_challengeType = paramJson.get(JSON_CLIENT_SELECTED_CHALLENGE, "");
 
   Name interestName = m_ca.m_caName;
   interestName.append("CA").append("_CHALLENGE").append(m_requestId);
@@ -258,10 +258,10 @@
   boost::property_tree::json_parser::read_json(ss, contentJson);
 
   // update state
-  m_status = contentJson.get<int>(JSON_CA_STATUS);
-  m_challengeStatus = contentJson.get<std::string>(JSON_CHALLENGE_STATUS);
-  m_remainingTries = contentJson.get<int>(JSON_CHALLENGE_REMAINING_TRIES);
-  m_freshBefore = time::system_clock::now() + time::seconds(contentJson.get<int>(JSON_CHALLENGE_REMAINING_TIME));
+  m_status = contentJson.get(JSON_CA_STATUS, 0);
+  m_challengeStatus = contentJson.get(JSON_CHALLENGE_STATUS, "");
+  m_remainingTries = contentJson.get(JSON_CHALLENGE_REMAINING_TRIES, 0);
+  m_freshBefore = time::system_clock::now() + time::seconds(contentJson.get(JSON_CHALLENGE_REMAINING_TIME, 0));
 }
 
 shared_ptr<Interest>
@@ -355,7 +355,7 @@
   std::stringstream ss;
   try {
     security::transform::bufferSource(certRequest.wireEncode().wire(), certRequest.wireEncode().size())
-    >> security::transform::base64Encode(true)
+    >> security::transform::base64Encode(false)
     >> security::transform::streamSink(ss);
   }
   catch (const security::transform::Error& e) {
@@ -371,7 +371,7 @@
     // transform the probe data into a base64 string
     try {
       security::transform::bufferSource(probeToken->wireEncode().wire(), probeToken->wireEncode().size())
-      >> security::transform::base64Encode(true)
+      >> security::transform::base64Encode(false)
       >> security::transform::streamSink(ss);
     }
     catch (const security::transform::Error& e) {
diff --git a/src/client-module.hpp b/src/client-module.hpp
index 806c5bc..b3655ef 100644
--- a/src/client-module.hpp
+++ b/src/client-module.hpp
@@ -153,7 +153,7 @@
   time::system_clock::TimePoint m_freshBefore;
 
   ECDHState m_ecdh;
-  uint8_t m_aesKey[32] = {0};
+  uint8_t m_aesKey[16] = {0};
 };
 
 } // namespace ndncert
diff --git a/src/crypto-support/crypto-helper.cpp b/src/crypto-support/crypto-helper.cpp
index ea18dba..0792bfc 100644
--- a/src/crypto-support/crypto-helper.cpp
+++ b/src/crypto-support/crypto-helper.cpp
@@ -148,7 +148,7 @@
 
   std::ostringstream os;
   t::bufferSource(context->publicKey, context->publicKeyLen)
-    >> t::base64Encode()
+    >> t::base64Encode(false)
     >> t::streamSink(os);
   return os.str();
 }
@@ -159,19 +159,24 @@
   auto privECKey = EVP_PKEY_get1_EC_KEY(context->privkey);
 
   if (privECKey == nullptr) {
-    handleErrors("Could not get referenced key when calling EVP_PKEY_get1_EC_KEY().");
+    handleErrors("Could not get referenced key when calling EVP_PKEY_get1_EC_KEY()");
     return nullptr;
   }
 
   auto group = EC_KEY_get0_group(privECKey);
   auto peerPoint = EC_POINT_new(group);
-  EC_POINT_oct2point(group, peerPoint, peerkey, peerKeySize, nullptr);
-
-  if (0 == (context->sharedSecretLen = ECDH_compute_key(context->sharedSecret, 256,
-                                                        peerPoint, privECKey, nullptr))) {
+  int result = EC_POINT_oct2point(group, peerPoint, peerkey, peerKeySize, nullptr);
+  if (result == 0) {
     EC_POINT_free(peerPoint);
     EC_KEY_free(privECKey);
-    handleErrors("Cannot generate ECDH secret with ECDH_compute_key");
+    handleErrors("Cannot convert peer's key into a EC point when calling EC_POINT_oct2point()");
+  }
+
+  if (-1 == (context->sharedSecretLen = ECDH_compute_key(context->sharedSecret, 256,
+                                                         peerPoint, privECKey, nullptr))) {
+    EC_POINT_free(peerPoint);
+    EC_KEY_free(privECKey);
+    handleErrors("Cannot generate ECDH secret when calling ECDH_compute_key()");
   }
   EC_POINT_free(peerPoint);
   EC_KEY_free(privECKey);
@@ -184,7 +189,7 @@
   namespace t = ndn::security::transform;
 
   OBufferStream os;
-  t::bufferSource(peerKeyStr) >> t::base64Decode() >> t::streamSink(os);
+  t::bufferSource(peerKeyStr) >> t::base64Decode(false) >> t::streamSink(os);
   auto result = os.buf();
 
   return this->deriveSecret(result->data(), result->size());
diff --git a/tests/unit-tests/ca-config.t.cpp b/tests/unit-tests/ca-config.t.cpp
index 0cebb26..5b47505 100644
--- a/tests/unit-tests/ca-config.t.cpp
+++ b/tests/unit-tests/ca-config.t.cpp
@@ -32,7 +32,7 @@
 
 BOOST_FIXTURE_TEST_SUITE(TestCaConfig, IdentityManagementFixture)
 
-BOOST_AUTO_TEST_CASE(ReadConfigFileWithFileAnchor)
+BOOST_AUTO_TEST_CASE(ReadConfigFile)
 {
   CaConfig config;
   config.load("tests/unit-tests/ca.conf.test");
@@ -43,6 +43,24 @@
   BOOST_CHECK_EQUAL(config.m_caInfo, "ndn testbed ca");
 }
 
+BOOST_AUTO_TEST_CASE(ReadNonexistConfigFile)
+{
+  CaConfig config;
+  BOOST_CHECK_THROW(config.load("tests/unit-tests/Nonexist"), CaConfig::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ReadConfigFileWithoutCaPrefix)
+{
+  CaConfig config;
+  BOOST_CHECK_THROW(config.load("tests/unit-tests/ca.conf.test2"), CaConfig::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ReadConfigFileWithChallengeNotSupported)
+{
+  CaConfig config;
+  BOOST_CHECK_THROW(config.load("tests/unit-tests/ca.conf.test3"), CaConfig::Error);
+}
+
 BOOST_AUTO_TEST_SUITE_END() // TestCaConfig
 
 } // namespace tests
diff --git a/tests/unit-tests/ca-module.t.cpp b/tests/unit-tests/ca-module.t.cpp
index 374c847..a72a3d8 100644
--- a/tests/unit-tests/ca-module.t.cpp
+++ b/tests/unit-tests/ca-module.t.cpp
@@ -179,7 +179,8 @@
       BOOST_CHECK(challengesJson.size() != 0);
 
       client.onNewResponse(response);
-      BOOST_CHECK_EQUAL_COLLECTIONS(client.m_aesKey, client.m_aesKey + 32, ca.m_aesKey, ca.m_aesKey + 32);
+      BOOST_CHECK_EQUAL_COLLECTIONS(client.m_aesKey, client.m_aesKey + sizeof(client.m_aesKey),
+                                    ca.m_aesKey, ca.m_aesKey + sizeof(ca.m_aesKey));
     });
   face.receive(*interest);
 
diff --git a/tests/unit-tests/ca.conf.test2 b/tests/unit-tests/ca.conf.test2
new file mode 100644
index 0000000..17f058b
--- /dev/null
+++ b/tests/unit-tests/ca.conf.test2
@@ -0,0 +1,9 @@
+{
+  "issuing-freshness": "720",
+  "validity-period": "360",
+  "ca-info": "ndn testbed ca",
+  "supported-challenges":
+  [
+      { "type": "PIN" }
+  ]
+}
\ No newline at end of file
diff --git a/tests/unit-tests/ca.conf.test3 b/tests/unit-tests/ca.conf.test3
new file mode 100644
index 0000000..f12eabb
--- /dev/null
+++ b/tests/unit-tests/ca.conf.test3
@@ -0,0 +1,10 @@
+{
+  "ca-prefix": "/ndn",
+  "issuing-freshness": "720",
+  "validity-period": "360",
+  "ca-info": "ndn testbed ca",
+  "supported-challenges":
+  [
+      { "type": "PINN" }
+  ]
+}
\ No newline at end of file
diff --git a/tests/unit-tests/client-config.t.cpp b/tests/unit-tests/client-config.t.cpp
index 5d662ff..0f8aa95 100644
--- a/tests/unit-tests/client-config.t.cpp
+++ b/tests/unit-tests/client-config.t.cpp
@@ -44,6 +44,24 @@
   BOOST_CHECK_EQUAL(config.m_localNdncertAnchor, "/usr/local/etc/ndncert/anchor.key");
 }
 
+BOOST_AUTO_TEST_CASE(ReadNonexistConfigFile)
+{
+  ClientConfig config;
+  BOOST_CHECK_THROW(config.load("tests/unit-tests/nonexist"), ClientConfig::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ReadConfigFileWithInvalidCert)
+{
+  ClientConfig config;
+  BOOST_CHECK_THROW(config.load("tests/unit-tests/client.conf.test2"), ClientConfig::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ReadConfigFileWithoutCaPrefix)
+{
+  ClientConfig config;
+  BOOST_CHECK_THROW(config.load("tests/unit-tests/client.conf.test3"), ClientConfig::Error);
+}
+
 BOOST_AUTO_TEST_CASE(AddAndRemoveCaItem)
 {
   ClientConfig config;
diff --git a/tests/unit-tests/client.conf.test2 b/tests/unit-tests/client.conf.test2
new file mode 100644
index 0000000..f3d5630
--- /dev/null
+++ b/tests/unit-tests/client.conf.test2
@@ -0,0 +1,18 @@
+{
+  "ca-list":
+  [
+    {
+        "ca-prefix": "/ndn/edu/ucla",
+        "ca-info": "UCLA's ceritificate authority, located in BH4805.",
+        "probe": "email",
+        "certificate": "ANuZG4IBXNpdGUxCANLRVkICBG8IvRjFf8XCARzZWxmCAn9AAABWcgU2aUUCRgBAhkEADbugBX9AU8wggFLMIIBAwYHKoZIzj0CATCB9wIBATAsBgcqhkjOPQEBAiEA/////wAAAAEAAAAAAAAAAAAAAAD///////////////8wWwQg/////wAAAAEAAAAAAAAAAAAAAAD///////////////wEIFrGNdiqOpPns+u9VXaYhrxlHQawzFOw9jvOPD4n0mBLAxUAxJ02CIbnBJNqZnjhE50mt4GffpAEQQRrF9Hy4SxCR/i85uVjpEDydwN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQEDQgAES9Cb9iANUNYmwt5bjwNW1mZgjzIkDJb6FTCdiYWnkMMIVxh2YDllphoWDEAPS6kqJczzCuhnGYpZCp9tTaYKGxZMGwEDHB0HGwgDbmRuCAVzaXRlMQgDS0VZCAgRvCL0YxX/F/0A/Sb9AP4PMTk3MDAxMDFUMDAwMDAw/QD/DzIwMzcwMTE3VDIxMjg0NhdIMEYCIQDXkR1hF3GiP7yLXq+0JBJfi9QC+hhAu/1Bykx+MWz6RAIhANwelBTxxZr2C5bD15mjfhWudK4I1tOb4b/9xWCHyM7F"
+    },
+    {
+        "ca-prefix": "/ndn/edu/ucla/zhiyi",
+        "ca-info": "Zhiyi's own ceritificate authority",
+        "probe": "email",
+        "certificate": "Bv0CJAcsCANuZG4IBXNpdGUxCANLRVkICBG8IvRjFf8XCARzZWxmCAn9AAABWcgU2aUUCRgBAhkEADbugBX9AU8wggFLMIIBAwYHKoZIzj0CATCB9wIBATAsBgcqhkjOPQEBAiEA/////wAAAAEAAAAAAAAAAAAAAAD///////////////8wWwQg/////wAAAAEAAAAAAAAAAAAAAAD///////////////wEIFrGNdiqOpPns+u9VXaYhrxlHQawzFOw9jvOPD4n0mBLAxUAxJ02CIbnBJNqZnjhE50mt4GffpAEQQRrF9Hy4SxCR/i85uVjpEDydwN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQEDQgAES9Cb9iANUNYmwt5bjwNW1mZgjzIkDJb6FTCdiYWnkMMIVxh2YDllphoWDEAPS6kqJczzCuhnGYpZCp9tTaYKGxZMGwEDHB0HGwgDbmRuCAVzaXRlMQgDS0VZCAgRvCL0YxX/F/0A/Sb9AP4PMTk3MDAxMDFUMDAwMDAw/QD/DzIwMzcwMTE3VDIxMjg0NhdIMEYCIQDXkR1hF3GiP7yLXq+0JBJfi9QC+hhAu/1Bykx+MWz6RAIhANwelBTxxZr2C5bD15mjfhWudK4I1tOb4b/9xWCHyM7F"
+    }
+  ],
+  "local-ndncert-anchor": "/usr/local/etc/ndncert/anchor.key"
+}
\ No newline at end of file
diff --git a/tests/unit-tests/client.conf.test3 b/tests/unit-tests/client.conf.test3
new file mode 100644
index 0000000..257850a
--- /dev/null
+++ b/tests/unit-tests/client.conf.test3
@@ -0,0 +1,17 @@
+{
+  "ca-list":
+  [
+    {
+        "ca-info": "UCLA's ceritificate authority, located in BH4805.",
+        "probe": "email",
+        "certificate": "ANuZG4IBXNpdGUxCANLRVkICBG8IvRjFf8XCARzZWxmCAn9AAABWcgU2aUUCRgBAhkEADbugBX9AU8wggFLMIIBAwYHKoZIzj0CATCB9wIBATAsBgcqhkjOPQEBAiEA/////wAAAAEAAAAAAAAAAAAAAAD///////////////8wWwQg/////wAAAAEAAAAAAAAAAAAAAAD///////////////wEIFrGNdiqOpPns+u9VXaYhrxlHQawzFOw9jvOPD4n0mBLAxUAxJ02CIbnBJNqZnjhE50mt4GffpAEQQRrF9Hy4SxCR/i85uVjpEDydwN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQEDQgAES9Cb9iANUNYmwt5bjwNW1mZgjzIkDJb6FTCdiYWnkMMIVxh2YDllphoWDEAPS6kqJczzCuhnGYpZCp9tTaYKGxZMGwEDHB0HGwgDbmRuCAVzaXRlMQgDS0VZCAgRvCL0YxX/F/0A/Sb9AP4PMTk3MDAxMDFUMDAwMDAw/QD/DzIwMzcwMTE3VDIxMjg0NhdIMEYCIQDXkR1hF3GiP7yLXq+0JBJfi9QC+hhAu/1Bykx+MWz6RAIhANwelBTxxZr2C5bD15mjfhWudK4I1tOb4b/9xWCHyM7F"
+    },
+    {
+        "ca-prefix": "/ndn/edu/ucla/zhiyi",
+        "ca-info": "Zhiyi's own ceritificate authority",
+        "probe": "email",
+        "certificate": "Bv0CJAcsCANuZG4IBXNpdGUxCANLRVkICBG8IvRjFf8XCARzZWxmCAn9AAABWcgU2aUUCRgBAhkEADbugBX9AU8wggFLMIIBAwYHKoZIzj0CATCB9wIBATAsBgcqhkjOPQEBAiEA/////wAAAAEAAAAAAAAAAAAAAAD///////////////8wWwQg/////wAAAAEAAAAAAAAAAAAAAAD///////////////wEIFrGNdiqOpPns+u9VXaYhrxlHQawzFOw9jvOPD4n0mBLAxUAxJ02CIbnBJNqZnjhE50mt4GffpAEQQRrF9Hy4SxCR/i85uVjpEDydwN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQEDQgAES9Cb9iANUNYmwt5bjwNW1mZgjzIkDJb6FTCdiYWnkMMIVxh2YDllphoWDEAPS6kqJczzCuhnGYpZCp9tTaYKGxZMGwEDHB0HGwgDbmRuCAVzaXRlMQgDS0VZCAgRvCL0YxX/F/0A/Sb9AP4PMTk3MDAxMDFUMDAwMDAw/QD/DzIwMzcwMTE3VDIxMjg0NhdIMEYCIQDXkR1hF3GiP7yLXq+0JBJfi9QC+hhAu/1Bykx+MWz6RAIhANwelBTxxZr2C5bD15mjfhWudK4I1tOb4b/9xWCHyM7F"
+    }
+  ],
+  "local-ndncert-anchor": "/usr/local/etc/ndncert/anchor.key"
+}
\ No newline at end of file
diff --git a/tests/unit-tests/crypto-helper.t.cpp b/tests/unit-tests/crypto-helper.t.cpp
index eda32d1..b6f8bd3 100644
--- a/tests/unit-tests/crypto-helper.t.cpp
+++ b/tests/unit-tests/crypto-helper.t.cpp
@@ -49,6 +49,16 @@
                                 bobResult, bobResult + 32);
 }
 
+BOOST_AUTO_TEST_CASE(EcdhWithRawKeyWrongInput)
+{
+  ECDHState aliceState;
+  auto alicePub = aliceState.getRawSelfPubKey();
+  BOOST_CHECK(alicePub != nullptr);
+  BOOST_CHECK(aliceState.context->publicKeyLen != 0);
+  uint8_t fakePub[] = {0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b};
+  BOOST_CHECK_THROW(aliceState.deriveSecret(fakePub, sizeof(fakePub)), CryptoError);
+}
+
 BOOST_AUTO_TEST_CASE(EcdhWithBase64Key)
 {
   ECDHState aliceState;