do some file move and rename

Change-Id: I034ea026c52c588966d2c66fcb3d3deea2e40102
diff --git a/src/identity-challenge/challenge-credential.cpp b/src/identity-challenge/challenge-credential.cpp
new file mode 100644
index 0000000..f6e26c2
--- /dev/null
+++ b/src/identity-challenge/challenge-credential.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2017-2020, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#include "challenge-credential.hpp"
+#include <iostream>
+#include <ndn-cxx/security/verification-helpers.hpp>
+#include <ndn-cxx/security/signing-helpers.hpp>
+#include <ndn-cxx/security/transform/public-key.hpp>
+#include <ndn-cxx/util/io.hpp>
+
+namespace ndn {
+namespace ndncert {
+
+_LOG_INIT(ndncert.challenge.credential);
+NDNCERT_REGISTER_CHALLENGE(ChallengeCredential, "Credential");
+
+const std::string ChallengeCredential::PARAMETER_KEY_CREDENTIAL_CERT = "issued-cert";
+const std::string ChallengeCredential::PARAMETER_KEY_PROOF_OF_PRIVATE_KEY = "proof-of-private-key";
+
+ChallengeCredential::ChallengeCredential(const std::string& configPath)
+    : ChallengeModule("Credential", 1, time::seconds(1))
+{
+  if (configPath.empty()) {
+    m_configFile = std::string(SYSCONFDIR) + "/ndncert/challenge-credential.conf";
+  }
+  else {
+    m_configFile = configPath;
+  }
+}
+
+void
+ChallengeCredential::parseConfigFile()
+{
+  JsonSection config;
+  try {
+    boost::property_tree::read_json(m_configFile, config);
+  }
+  catch (const boost::property_tree::info_parser_error& error) {
+    BOOST_THROW_EXCEPTION(std::runtime_error("Failed to parse configuration file " + m_configFile +
+                                             " " + error.message() + " line " + std::to_string(error.line())));
+  }
+
+  if (config.begin() == config.end()) {
+    BOOST_THROW_EXCEPTION(std::runtime_error("Error processing configuration file: " + m_configFile + " no data"));
+  }
+
+  m_trustAnchors.clear();
+  auto anchorList = config.get_child("anchor-list");
+  auto it = anchorList.begin();
+  for (; it != anchorList.end(); it++) {
+    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);
+  }
+}
+
+// For CA
+std::tuple<ErrorCode, std::string>
+ChallengeCredential::handleChallengeRequest(const Block& params, CaState& request)
+{
+  params.parse();
+  if (m_trustAnchors.empty()) {
+    parseConfigFile();
+  }
+  security::v2::Certificate credential;
+  const uint8_t* signature;
+  size_t signatureLen;
+  const auto& elements = params.elements();
+  for (size_t i = 0; i < elements.size(); i++) {
+    if (elements[i].type() == tlv_parameter_key) {
+      if (readString(elements[i]) == PARAMETER_KEY_CREDENTIAL_CERT) {
+        try {
+          credential.wireDecode(elements[i + 1].blockFromValue());
+        }
+        catch (const std::exception& e) {
+          _LOG_ERROR("Cannot load challenge parameter: credential " << e.what());
+          return returnWithError(request, ErrorCode::INVALID_PARAMETER, "Cannot challenge credential: credential." + std::string(e.what()));
+        }
+      }
+      else if (readString(elements[i]) == PARAMETER_KEY_PROOF_OF_PRIVATE_KEY) {
+        signature = elements[i + 1].value();
+        signatureLen = elements[i + 1].value_size();
+      }
+    }
+  }
+
+  // verify the credential and the self-signed cert
+  Name signingKeyName = credential.getSignature().getKeyLocator().getName();
+  security::transform::PublicKey key;
+  const auto& pubKeyBuffer = credential.getPublicKey();
+  key.loadPkcs8(pubKeyBuffer.data(), pubKeyBuffer.size());
+  for (auto anchor : m_trustAnchors) {
+    if (anchor.getKeyName() == signingKeyName) {
+      if (security::verifySignature(credential, anchor) &&
+          security::verifySignature((uint8_t*)request.m_requestId.c_str(), request.m_requestId.size(), signature, signatureLen, key)) {
+        return returnWithSuccess(request);
+      }
+    }
+  }
+
+  _LOG_TRACE("Cannot verify the proof of private key against credential");
+  return returnWithError(request, ErrorCode::INVALID_PARAMETER, "Cannot verify the proof of private key against credential.");
+}
+
+// For Client
+std::vector<std::tuple<std::string, std::string>>
+ChallengeCredential::getRequestedParameterList(Status status, const std::string& challengeStatus)
+{
+  std::vector<std::tuple<std::string, std::string>> result;
+  if (status == Status::BEFORE_CHALLENGE) {
+    result.push_back(std::make_tuple(PARAMETER_KEY_CREDENTIAL_CERT, "Please provide the certificate issued by a trusted CA."));
+    result.push_back(std::make_tuple(PARAMETER_KEY_PROOF_OF_PRIVATE_KEY, "Please sign a Data packet with request ID as the content."));
+    return result;
+  }
+  BOOST_THROW_EXCEPTION(std::runtime_error("Unexpected status or challenge status."));
+}
+
+Block
+ChallengeCredential::genChallengeRequestTLV(Status status, const std::string& challengeStatus,
+                                            std::vector<std::tuple<std::string, std::string>>&& params)
+{
+  Block request = makeEmptyBlock(tlv_encrypted_payload);
+  if (status == Status::BEFORE_CHALLENGE) {
+    if (params.size() != 2) {
+      BOOST_THROW_EXCEPTION(std::runtime_error("Wrong parameter provided."));
+    }
+    request.push_back(makeStringBlock(tlv_selected_challenge, CHALLENGE_TYPE));
+    for (const auto& item : params) {
+      if (std::get<0>(item) == PARAMETER_KEY_CREDENTIAL_CERT) {
+        request.push_back(makeStringBlock(tlv_parameter_key, PARAMETER_KEY_CREDENTIAL_CERT));
+        Block valueBlock = makeEmptyBlock(tlv_parameter_value);
+        auto& certTlvStr = std::get<1>(item);
+        valueBlock.push_back(Block((uint8_t*)certTlvStr.c_str(), certTlvStr.size()));
+        request.push_back(valueBlock);
+      }
+      else if (std::get<0>(item) == PARAMETER_KEY_PROOF_OF_PRIVATE_KEY) {
+        request.push_back(makeStringBlock(tlv_parameter_key, PARAMETER_KEY_PROOF_OF_PRIVATE_KEY));
+        auto& sigTlvStr = std::get<1>(item);
+        Block valueBlock = makeBinaryBlock(tlv_parameter_value, (uint8_t*)sigTlvStr.c_str(), sigTlvStr.size());
+        request.push_back(valueBlock);
+      }
+      else {
+        BOOST_THROW_EXCEPTION(std::runtime_error("Wrong parameter provided."));
+      }
+    }
+  }
+  else {
+    BOOST_THROW_EXCEPTION(std::runtime_error("Unexpected status or challenge status."));
+  }
+  request.encode();
+  return request;
+}
+
+void
+ChallengeCredential::fulfillParameters(std::vector<std::tuple<std::string, std::string>>& params,
+                                       KeyChain& keyChain, const Name& issuedCertName, const std::string& requestId)
+{
+  auto& pib = keyChain.getPib();
+  auto id = pib.getIdentity(security::v2::extractIdentityFromCertName(issuedCertName));
+  auto issuedCert = id.getKey(security::v2::extractKeyNameFromCertName(issuedCertName)).getCertificate(issuedCertName);
+  auto issuedCertTlv = issuedCert.wireEncode();
+  auto signatureTlv = keyChain.sign((uint8_t*)requestId.c_str(), requestId.length(), security::signingByCertificate(issuedCertName));
+  for (auto& item : params) {
+    if (std::get<0>(item) == PARAMETER_KEY_CREDENTIAL_CERT) {
+      std::get<1>(item) = std::string((char*)issuedCertTlv.wire(), issuedCertTlv.size());
+    }
+    else if (std::get<0>(item) == PARAMETER_KEY_PROOF_OF_PRIVATE_KEY) {
+      std::get<1>(item) = std::string((char*)signatureTlv.value(), signatureTlv.value_size());
+    }
+  }
+  return;
+}
+
+}  // namespace ndncert
+}  // namespace ndn
diff --git a/src/identity-challenge/challenge-credential.hpp b/src/identity-challenge/challenge-credential.hpp
new file mode 100644
index 0000000..0ecfdb2
--- /dev/null
+++ b/src/identity-challenge/challenge-credential.hpp
@@ -0,0 +1,86 @@
+/**
+ * Copyright (c) 2017-2019, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#ifndef NDNCERT_CHALLENGE_CREDENTIAL_HPP
+#define NDNCERT_CHALLENGE_CREDENTIAL_HPP
+
+#include "challenge-module.hpp"
+
+namespace ndn {
+namespace ndncert {
+
+/**
+ * @brief Provide Credential based challenge
+ *
+ * Credential here means the certificate issued by a trust anchor. Once the requester
+ * could proof his/her possession of an existing certificate from other certificate issuer,
+ * the requester could finish the challenge.
+ *
+ * The requester needs to provide the proof of the possession of a certificate issued by
+ * a trust anchor. The challenge require the requester to pass the BASE64 certificate and
+ * a BASE64 Data packet signed by the credential pub key and whose content is the request id.
+ *
+ * The main process of this challenge module is:
+ *   1. Requester provides a certificate signed by that trusted certificate as credential.
+ *   2. The challenge module will verify the signature of the credential.
+ *   3. The content of the signed Data is the request id
+ *
+ * Failure info when application fails:
+ *   FAILURE_INVALID_CREDENTIAL: When the cert issued from trust anchor or self-signed cert
+ *     cannot be validated.
+ *   FAILURE_INVALID_FORMAT: When the credential format is wrong.
+ */
+class ChallengeCredential : public ChallengeModule
+{
+public:
+  ChallengeCredential(const std::string& configPath = "");
+
+  // For CA
+  std::tuple<ErrorCode, std::string>
+  handleChallengeRequest(const Block& params, CaState& request) override;
+
+  // For Client
+  std::vector<std::tuple<std::string, std::string>>
+  getRequestedParameterList(Status status, const std::string& challengeStatus) override;
+
+  Block
+  genChallengeRequestTLV(Status status, const std::string& challengeStatus,
+                         std::vector<std::tuple<std::string, std::string>>&& params) override;
+
+  static void
+  fulfillParameters(std::vector<std::tuple<std::string, std::string>>& params,
+                    KeyChain& keyChain, const Name& issuedCertName, const std::string& requestId);
+
+  // challenge parameters
+  static const std::string PARAMETER_KEY_CREDENTIAL_CERT;
+  static const std::string PARAMETER_KEY_PROOF_OF_PRIVATE_KEY;
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  void
+  parseConfigFile();
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  std::list<security::v2::Certificate> m_trustAnchors;
+  std::string m_configFile;
+};
+
+} // namespace ndncert
+} // namespace ndn
+
+#endif // NDNCERT_CHALLENGE_CREDENTIAL_HPP
diff --git a/src/identity-challenge/challenge-email.cpp b/src/identity-challenge/challenge-email.cpp
new file mode 100644
index 0000000..2775d9e
--- /dev/null
+++ b/src/identity-challenge/challenge-email.cpp
@@ -0,0 +1,174 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2017-2019, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#include "challenge-email.hpp"
+#include <regex>
+
+namespace ndn {
+namespace ndncert {
+
+_LOG_INIT(ndncert.challenge.email);
+NDNCERT_REGISTER_CHALLENGE(ChallengeEmail, "email");
+
+const std::string ChallengeEmail::NEED_CODE = "need-code";
+const std::string ChallengeEmail::WRONG_CODE = "wrong-code";
+const std::string ChallengeEmail::INVALID_EMAIL = "invalid-email";
+const std::string ChallengeEmail::PARAMETER_KEY_EMAIL = "email";
+const std::string ChallengeEmail::PARAMETER_KEY_CODE = "code";
+
+ChallengeEmail::ChallengeEmail(const std::string& scriptPath,
+                               const size_t& maxAttemptTimes,
+                               const time::seconds secretLifetime)
+    : ChallengeModule("email", maxAttemptTimes, secretLifetime)
+    , m_sendEmailScript(scriptPath)
+{
+}
+
+// For CA
+std::tuple<ErrorCode, std::string>
+ChallengeEmail::handleChallengeRequest(const Block& params, CaState& request)
+{
+  params.parse();
+  auto currentTime = time::system_clock::now();
+  if (request.m_status == Status::BEFORE_CHALLENGE) {
+    // for the first time, init the challenge
+    std::string emailAddress = readString(params.get(tlv_parameter_value));
+    if (!isValidEmailAddress(emailAddress)) {
+      return returnWithNewChallengeStatus(request, INVALID_EMAIL, JsonSection(), m_maxAttemptTimes - 1, m_secretLifetime);
+    }
+    auto lastComponentRequested = readString(request.m_cert.getIdentity().get(-1));
+    if (lastComponentRequested != emailAddress) {
+      _LOG_TRACE("Email and requested name do not match. Email " << emailAddress << "requested last component " << lastComponentRequested);
+    }
+    std::string emailCode = generateSecretCode();
+    JsonSection secretJson;
+    secretJson.add(PARAMETER_KEY_CODE, emailCode);
+    // send out the email
+    sendEmail(emailAddress, emailCode, request);
+    _LOG_TRACE("Secret for request " << request.m_requestId << " : " << emailCode);
+    return returnWithNewChallengeStatus(request, NEED_CODE, std::move(secretJson), m_maxAttemptTimes, m_secretLifetime);
+  }
+  if (request.m_challengeState) {
+    if (request.m_challengeState->m_challengeStatus == NEED_CODE ||
+        request.m_challengeState->m_challengeStatus == WRONG_CODE) {
+      _LOG_TRACE("Challenge Interest arrives. Challenge Status: " << request.m_challengeState->m_challengeStatus);
+      // the incoming interest should bring the pin code
+      std::string givenCode = readString(params.get(tlv_parameter_value));
+      auto secret = request.m_challengeState->m_secrets;
+      // check if run out of time
+      if (currentTime - request.m_challengeState->m_timestamp >= m_secretLifetime) {
+        return returnWithError(request, ErrorCode::OUT_OF_TIME, "Secret expired.");
+      }
+      // check if provided secret is correct
+      if (givenCode == secret.get<std::string>(PARAMETER_KEY_CODE)) {
+        // the code is correct
+        _LOG_TRACE("Correct secret code. Challenge succeeded.");
+        return returnWithSuccess(request);
+      }
+      // otherwise, check remaining attempt times
+      if (request.m_challengeState->m_remainingTries > 1) {
+        auto remainTime = m_secretLifetime - (currentTime - request.m_challengeState->m_timestamp);
+        _LOG_TRACE("Wrong secret code provided. Remaining Tries - 1.");
+        return returnWithNewChallengeStatus(request, WRONG_CODE, std::move(secret),
+                                            request.m_challengeState->m_remainingTries - 1,
+                                            time::duration_cast<time::seconds>(remainTime));
+      }
+      else {
+        // run out times
+        _LOG_TRACE("Wrong secret code provided. Ran out tires. Challenge failed.");
+        return returnWithError(request, ErrorCode::OUT_OF_TRIES, "Ran out tires.");
+      }
+    }
+  }
+  return returnWithError(request, ErrorCode::INVALID_PARAMETER, "Unexpected status or challenge status");
+}
+
+// For Client
+std::vector<std::tuple<std::string, std::string>>
+ChallengeEmail::getRequestedParameterList(Status status, const std::string& challengeStatus)
+{
+  std::vector<std::tuple<std::string, std::string>> result;
+  if (status == Status::BEFORE_CHALLENGE && challengeStatus == "") {
+    result.push_back(std::make_tuple(PARAMETER_KEY_EMAIL, "Please input your email address"));
+  }
+  else if (status == Status::CHALLENGE && challengeStatus == NEED_CODE) {
+    result.push_back(std::make_tuple(PARAMETER_KEY_CODE, "Please input your verification code"));
+  }
+  else if (status == Status::CHALLENGE && challengeStatus == WRONG_CODE) {
+    result.push_back(std::make_tuple(PARAMETER_KEY_CODE, "Incorrect code, please try again"));
+  }
+  else {
+    BOOST_THROW_EXCEPTION(std::runtime_error("Unexpected status or challenge status."));
+  }
+  return result;
+}
+
+Block
+ChallengeEmail::genChallengeRequestTLV(Status status, const std::string& challengeStatus, std::vector<std::tuple<std::string, std::string>>&& params)
+{
+  Block request = makeEmptyBlock(tlv_encrypted_payload);
+  if (status == Status::BEFORE_CHALLENGE) {
+    if (params.size() != 1 || std::get<0>(params[0]) != PARAMETER_KEY_EMAIL) {
+      BOOST_THROW_EXCEPTION(std::runtime_error("Wrong parameter provided."));
+    }
+    request.push_back(makeStringBlock(tlv_selected_challenge, CHALLENGE_TYPE));
+    request.push_back(makeStringBlock(tlv_parameter_key, PARAMETER_KEY_EMAIL));
+    request.push_back(makeStringBlock(tlv_parameter_value, std::get<1>(params[0])));
+  }
+  else if (status == Status::CHALLENGE && (challengeStatus == NEED_CODE || challengeStatus == WRONG_CODE)) {
+    if (params.size() != 1 || std::get<0>(params[0]) != PARAMETER_KEY_CODE) {
+      BOOST_THROW_EXCEPTION(std::runtime_error("Wrong parameter provided."));
+    }
+    request.push_back(makeStringBlock(tlv_selected_challenge, CHALLENGE_TYPE));
+    request.push_back(makeStringBlock(tlv_parameter_key, PARAMETER_KEY_CODE));
+    request.push_back(makeStringBlock(tlv_parameter_value, std::get<1>(params[0])));
+  }
+  else {
+    BOOST_THROW_EXCEPTION(std::runtime_error("Unexpected status or challenge status."));
+  }
+  request.encode();
+  return request;
+}
+
+bool
+ChallengeEmail::isValidEmailAddress(const std::string& emailAddress)
+{
+  const std::string pattern = R"_REGEX_((^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9\-\.]+$))_REGEX_";
+  static const std::regex emailPattern(pattern);
+  return std::regex_match(emailAddress, emailPattern);
+}
+
+void
+ChallengeEmail::sendEmail(const std::string& emailAddress, const std::string& secret,
+                          const CaState& request) const
+{
+  std::string command = m_sendEmailScript;
+  command += " \"" + emailAddress + "\" \"" + secret + "\" \"" + request.m_caPrefix.toUri() + "\" \"" + request.m_cert.getName().toUri() + "\"";
+  int result = system(command.c_str());
+  if (result == -1) {
+    _LOG_TRACE("EmailSending Script " + m_sendEmailScript + " fails.");
+  }
+  _LOG_TRACE("EmailSending Script " + m_sendEmailScript +
+             " was executed successfully with return value" + std::to_string(result) + ".");
+  return;
+}
+
+}  // namespace ndncert
+}  // namespace ndn
diff --git a/src/identity-challenge/challenge-email.hpp b/src/identity-challenge/challenge-email.hpp
new file mode 100644
index 0000000..5cab3cb
--- /dev/null
+++ b/src/identity-challenge/challenge-email.hpp
@@ -0,0 +1,93 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2017-2019, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#ifndef NDNCERT_CHALLENGE_EMAIL_HPP
+#define NDNCERT_CHALLENGE_EMAIL_HPP
+
+#include "challenge-module.hpp"
+
+namespace ndn {
+namespace ndncert {
+
+/**
+ * @brief Provide Email based challenge
+ *
+ * For challenge design
+ * @sa https://github.com/named-data/ndncert/wiki/NDN-Certificate-Management-Protocol
+ * For deployment instructions:
+ * @sa https://github.com/named-data/ndncert/wiki/Deploy-Email-Challenge
+ *
+ * The main process of this challenge module is:
+ *   1. Requester provides its email address.
+ *   2. The challenge module will send a verification code to this email address.
+ *   3. Requester provides the verification code to challenge module.
+ *
+ * There are several challenge status in EMAIL challenge:
+ *   NEED_CODE: When email address is provided and the verification code has been sent out.
+ *   WRONG_CODE: Wrong code but still within secret lifetime and within max try times.
+ *
+ * Failure info when application fails:
+ *   FAILURE_MAXRETRY: When run out retry times.
+ *   FAILURE_INVALID_EMAIL: When the email is invalid.
+ *   FAILURE_TIMEOUT: When the secret lifetime expires.
+ */
+class ChallengeEmail : public ChallengeModule
+{
+public:
+  ChallengeEmail(const std::string& scriptPath = "ndncert-send-email-challenge",
+                 const size_t& maxAttemptTimes = 3,
+                 const time::seconds secretLifetime = time::seconds(300));
+
+  // For CA
+  std::tuple<ErrorCode, std::string>
+  handleChallengeRequest(const Block& params, CaState& request) override;
+
+  // For Client
+  std::vector<std::tuple<std::string, std::string>>
+  getRequestedParameterList(Status status, const std::string& challengeStatus) override;
+
+  Block
+  genChallengeRequestTLV(Status status, const std::string& challengeStatus,
+                         std::vector<std::tuple<std::string, std::string>>&& params) override;
+
+  // challenge status
+  static const std::string NEED_CODE;
+  static const std::string WRONG_CODE;
+  static const std::string INVALID_EMAIL;
+  // challenge parameters
+  static const std::string PARAMETER_KEY_EMAIL;
+  static const std::string PARAMETER_KEY_CODE;
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  static bool
+  isValidEmailAddress(const std::string& emailAddress);
+
+  void
+  sendEmail(const std::string& emailAddress, const std::string& secret,
+            const CaState& request) const;
+
+private:
+  std::string m_sendEmailScript;
+};
+
+} // namespace ndncert
+} // namespace ndn
+
+#endif // NDNCERT_CHALLENGE_EMAIL_HPP
diff --git a/src/identity-challenge/challenge-module.cpp b/src/identity-challenge/challenge-module.cpp
new file mode 100644
index 0000000..812a85d
--- /dev/null
+++ b/src/identity-challenge/challenge-module.cpp
@@ -0,0 +1,104 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2017-2019, 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 "identity-challenge/challenge-module.hpp"
+#include <ndn-cxx/util/random.hpp>
+
+namespace ndn {
+namespace ndncert {
+
+ChallengeModule::ChallengeModule(const std::string& challengeType,
+                                 size_t maxAttemptTimes,
+                                 time::seconds secretLifetime)
+  : CHALLENGE_TYPE(challengeType)
+  , m_maxAttemptTimes(maxAttemptTimes)
+  , m_secretLifetime(secretLifetime)
+{
+}
+
+bool
+ChallengeModule::isChallengeSupported(const std::string& challengeType)
+{
+  ChallengeFactory& factory = getFactory();
+  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();
+}
+
+ChallengeModule::ChallengeFactory&
+ChallengeModule::getFactory()
+{
+  static ChallengeModule::ChallengeFactory factory;
+  return factory;
+}
+
+std::string
+ChallengeModule::generateSecretCode()
+{
+  uint32_t securityCode = 0;
+  do {
+    securityCode = random::generateSecureWord32();
+  }
+  while (securityCode >= 4294000000);
+  securityCode /= 4294;
+  std::string result = std::to_string(securityCode);
+  while (result.length() < 6) {
+    result = "0" + result;
+  }
+  return result;
+}
+
+std::tuple<ErrorCode, std::string>
+ChallengeModule::returnWithError(CaState& request, ErrorCode errorCode, std::string&& errorInfo)
+{
+  request.m_status = Status::FAILURE;
+  request.m_challengeType = "";
+  request.m_challengeState = boost::none;
+  return std::make_tuple(errorCode, std::move(errorInfo));
+}
+
+std::tuple<ErrorCode, std::string>
+ChallengeModule::returnWithNewChallengeStatus(CaState& request, const std::string& challengeStatus,
+                                              JsonSection&& challengeSecret, size_t remainingTries, time::seconds remainingTime)
+{
+  request.m_status = Status::CHALLENGE;
+  request.m_challengeType = CHALLENGE_TYPE;
+  request.m_challengeState = ChallengeState(challengeStatus, time::system_clock::now(), remainingTries, remainingTime, std::move(challengeSecret));
+  return std::make_tuple(ErrorCode::NO_ERROR, "");
+}
+
+std::tuple<ErrorCode, std::string>
+ChallengeModule::returnWithSuccess(CaState& request)
+{
+  request.m_status = Status::PENDING;
+  request.m_challengeType = CHALLENGE_TYPE;
+  request.m_challengeState = boost::none;
+  return std::make_tuple(ErrorCode::NO_ERROR, "");
+}
+
+} // namespace ndncert
+} // namespace ndn
diff --git a/src/identity-challenge/challenge-module.hpp b/src/identity-challenge/challenge-module.hpp
new file mode 100644
index 0000000..4349762
--- /dev/null
+++ b/src/identity-challenge/challenge-module.hpp
@@ -0,0 +1,104 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2017-2019, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#ifndef NDNCERT_CHALLENGE_MODULE_HPP
+#define NDNCERT_CHALLENGE_MODULE_HPP
+
+#include "ca-state.hpp"
+
+namespace ndn {
+namespace ndncert {
+
+class ChallengeModule : noncopyable {
+public:
+  explicit
+  ChallengeModule(const std::string& challengeType, size_t maxAttemptTimes, time::seconds secretLifetime);
+
+  virtual ~ChallengeModule() = default;
+
+  template <class ChallengeType>
+  static void
+  registerChallengeModule(const std::string& typeName)
+  {
+    ChallengeFactory& factory = getFactory();
+    BOOST_ASSERT(factory.count(typeName) == 0);
+    factory[typeName] = [] { return make_unique<ChallengeType>(); };
+  }
+
+  static bool
+  isChallengeSupported(const std::string& challengeType);
+
+  static unique_ptr<ChallengeModule>
+  createChallengeModule(const std::string& challengeType);
+
+  // For CA
+  virtual std::tuple<ErrorCode, std::string>
+  handleChallengeRequest(const Block& params, CaState& request) = 0;
+
+  // For Client
+  virtual std::vector<std::tuple<std::string, std::string>>
+  getRequestedParameterList(Status status, const std::string& challengeStatus) = 0;
+
+  virtual Block
+  genChallengeRequestTLV(Status status, const std::string& challengeStatus,
+                         std::vector<std::tuple<std::string, std::string>>&& params) = 0;
+
+  // helpers
+  static std::string
+  generateSecretCode();
+
+protected:
+  // used by challenge modules
+  std::tuple<ErrorCode, std::string>
+  returnWithError(CaState& request, ErrorCode errorCode, std::string&& errorInfo);
+
+  std::tuple<ErrorCode, std::string>
+  returnWithNewChallengeStatus(CaState& request, const std::string& challengeStatus,
+                               JsonSection&& challengeSecret, size_t remainingTries, time::seconds remainingTime);
+
+  std::tuple<ErrorCode, std::string>
+  returnWithSuccess(CaState& request);
+
+public:
+  const std::string CHALLENGE_TYPE;
+  const size_t m_maxAttemptTimes;
+  const time::seconds m_secretLifetime;
+
+private:
+  typedef function<unique_ptr<ChallengeModule>()> ChallengeCreateFunc;
+  typedef std::map<std::string, ChallengeCreateFunc> ChallengeFactory;
+
+  static ChallengeFactory&
+  getFactory();
+};
+
+#define NDNCERT_REGISTER_CHALLENGE(C, T)                              \
+  static class NdnCert##C##ChallengeRegistrationClass {               \
+  public:                                                             \
+    NdnCert##C##ChallengeRegistrationClass()                          \
+    {                                                                 \
+      ::ndn::ndncert::ChallengeModule::registerChallengeModule<C>(T); \
+    }                                                                 \
+  } g_NdnCert##C##ChallengeRegistrationVariable
+
+}  // namespace ndncert
+}  // namespace ndn
+
+#endif  // NDNCERT_CHALLENGE_MODULE_HPP
diff --git a/src/identity-challenge/challenge-pin.cpp b/src/identity-challenge/challenge-pin.cpp
new file mode 100644
index 0000000..a5d5580
--- /dev/null
+++ b/src/identity-challenge/challenge-pin.cpp
@@ -0,0 +1,128 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2017-2019, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#include "challenge-pin.hpp"
+#include <ndn-cxx/util/random.hpp>
+
+namespace ndn {
+namespace ndncert {
+
+_LOG_INIT(ndncert.challenge.pin);
+NDNCERT_REGISTER_CHALLENGE(ChallengePin, "pin");
+
+const std::string ChallengePin::NEED_CODE = "need-code";
+const std::string ChallengePin::WRONG_CODE = "wrong-code";
+const std::string ChallengePin::PARAMETER_KEY_CODE = "code";
+
+ChallengePin::ChallengePin(const size_t& maxAttemptTimes, const time::seconds& secretLifetime)
+    : ChallengeModule("pin", maxAttemptTimes, secretLifetime)
+{
+}
+
+// For CA
+std::tuple<ErrorCode, std::string>
+ChallengePin::handleChallengeRequest(const Block& params, CaState& request)
+{
+  params.parse();
+  auto currentTime = time::system_clock::now();
+  if (request.m_status == Status::BEFORE_CHALLENGE) {
+    _LOG_TRACE("Challenge Interest arrives. Init the challenge");
+    // for the first time, init the challenge
+    std::string secretCode = generateSecretCode();
+    JsonSection secretJson;
+    secretJson.add(PARAMETER_KEY_CODE, secretCode);
+    _LOG_TRACE("Secret for request " << request.m_requestId << " : " << secretCode);
+    return returnWithNewChallengeStatus(request, NEED_CODE, std::move(secretJson), m_maxAttemptTimes, m_secretLifetime);
+  }
+  if (request.m_challengeState) {
+    if (request.m_challengeState->m_challengeStatus == NEED_CODE ||
+        request.m_challengeState->m_challengeStatus == WRONG_CODE) {
+      _LOG_TRACE("Challenge Interest arrives. Challenge Status: " << request.m_challengeState->m_challengeStatus);
+      // the incoming interest should bring the pin code
+      std::string givenCode = readString(params.get(tlv_parameter_value));
+      auto secret = request.m_challengeState->m_secrets;
+      if (currentTime - request.m_challengeState->m_timestamp >= m_secretLifetime) {
+        return returnWithError(request, ErrorCode::OUT_OF_TIME, "Secret expired.");
+      }
+      if (givenCode == secret.get<std::string>(PARAMETER_KEY_CODE)) {
+        _LOG_TRACE("Correct PIN code. Challenge succeeded.");
+        return returnWithSuccess(request);
+      }
+      // check rest attempt times
+      if (request.m_challengeState->m_remainingTries > 1) {
+        auto remainTime = m_secretLifetime - (currentTime - request.m_challengeState->m_timestamp);
+        _LOG_TRACE("Wrong PIN code provided. Remaining Tries - 1.");
+        return returnWithNewChallengeStatus(request, WRONG_CODE, std::move(secret),
+                                            request.m_challengeState->m_remainingTries - 1,
+                                            time::duration_cast<time::seconds>(remainTime));
+      }
+      else {
+        // run out times
+        _LOG_TRACE("Wrong PIN code provided. Ran out tires. Challenge failed.");
+        return returnWithError(request, ErrorCode::OUT_OF_TRIES, "Ran out tires.");
+      }
+    }
+  }
+  return returnWithError(request, ErrorCode::INVALID_PARAMETER, "Unexpected status or challenge status");
+}
+
+// For Client
+std::vector<std::tuple<std::string, std::string>>
+ChallengePin::getRequestedParameterList(Status status, const std::string& challengeStatus)
+{
+  std::vector<std::tuple<std::string, std::string>> result;
+  if (status == Status::BEFORE_CHALLENGE) {
+    // do nothing
+  }
+  else if (status == Status::CHALLENGE && challengeStatus == NEED_CODE) {
+    result.push_back(std::make_tuple(PARAMETER_KEY_CODE, "Please input your PIN code"));
+  }
+  else if (status == Status::CHALLENGE && challengeStatus == WRONG_CODE) {
+    result.push_back(std::make_tuple(PARAMETER_KEY_CODE, "Incorrect PIN code, please try again"));
+  }
+  else {
+    BOOST_THROW_EXCEPTION(std::runtime_error("Unexpected status or challenge status."));
+  }
+  return result;
+}
+
+Block
+ChallengePin::genChallengeRequestTLV(Status status, const std::string& challengeStatus, std::vector<std::tuple<std::string, std::string>>&& params)
+{
+  Block request = makeEmptyBlock(tlv_encrypted_payload);
+  if (status == Status::BEFORE_CHALLENGE) {
+    request.push_back(makeStringBlock(tlv_selected_challenge, CHALLENGE_TYPE));
+  }
+  else if (status == Status::CHALLENGE && (challengeStatus == NEED_CODE || challengeStatus == WRONG_CODE)) {
+    if (params.size() != 1 || std::get<0>(params[0]) != PARAMETER_KEY_CODE) {
+      BOOST_THROW_EXCEPTION(std::runtime_error("Wrong parameter provided."));
+    }
+    request.push_back(makeStringBlock(tlv_selected_challenge, CHALLENGE_TYPE));
+    request.push_back(makeStringBlock(tlv_parameter_key, PARAMETER_KEY_CODE));
+    request.push_back(makeStringBlock(tlv_parameter_value, std::get<1>(params[0])));
+  }
+  else {
+    BOOST_THROW_EXCEPTION(std::runtime_error("Unexpected status or challenge status."));
+  }
+  request.encode();
+  return request;
+}
+}  // namespace ndncert
+}  // namespace ndn
diff --git a/src/identity-challenge/challenge-pin.hpp b/src/identity-challenge/challenge-pin.hpp
new file mode 100644
index 0000000..53f7a10
--- /dev/null
+++ b/src/identity-challenge/challenge-pin.hpp
@@ -0,0 +1,75 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2017-2019, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#ifndef NDNCERT_CHALLENGE_PIN_HPP
+#define NDNCERT_CHALLENGE_PIN_HPP
+
+#include "challenge-module.hpp"
+
+namespace ndn {
+namespace ndncert {
+
+/**
+ * @brief Provide PIN code based challenge
+ *
+ * @sa https://github.com/named-data/ndncert/wiki/NDN-Certificate-Management-Protocol
+ *
+ * The main process of this challenge module is:
+ *   1. End entity provides empty string. The first POLL is only for selection.
+ *   2. The challenge module will generate a PIN code in ChallengeDefinedField.
+ *   3. End entity provides the verification code from some way to challenge module.
+ *
+ * There are four specific status defined in this challenge:
+ *   NEED_CODE: When selection is made.
+ *   WRONG_CODE: Get wrong verification code but still with secret lifetime and max retry times.
+ *
+ * Failure info when application fails:
+ *   FAILURE_TIMEOUT: When secret is out-dated.
+ *   FAILURE_MAXRETRY: When requester tries too many times.
+ */
+class ChallengePin : public ChallengeModule
+{
+public:
+  ChallengePin(const size_t& maxAttemptTimes = 3,
+               const time::seconds& secretLifetime = time::seconds(3600));
+
+  // For CA
+  std::tuple<ErrorCode, std::string>
+  handleChallengeRequest(const Block& params, CaState& request) override;
+
+  // For Client
+  std::vector<std::tuple<std::string, std::string>>
+  getRequestedParameterList(Status status, const std::string& challengeStatus) override;
+
+  Block
+  genChallengeRequestTLV(Status status, const std::string& challengeStatus,
+                         std::vector<std::tuple<std::string, std::string>>&& params) override;
+
+  // challenge status
+  static const std::string NEED_CODE;
+  static const std::string WRONG_CODE;
+  // parameters
+  static const std::string PARAMETER_KEY_CODE;
+};
+
+} // namespace ndncert
+} // namespace ndn
+
+#endif // NDNCERT_CHALLENGE_PIN_HPP