Add proof of private key challenge

Change-Id: Ic48b65c550603656c962e3c251f4dac1759cb4a1
diff --git a/src/challenge-module/challenge-credential.cpp b/src/challenge-module/challenge-credential.cpp
index 15d8721..08131c1 100644
--- a/src/challenge-module/challenge-credential.cpp
+++ b/src/challenge-module/challenge-credential.cpp
@@ -36,7 +36,7 @@
 const std::string ChallengeCredential::FAILURE_INVALID_FORMAT_SELF_SIGNED = "failure-cannot-parse-self-signed";
 const std::string ChallengeCredential::FAILURE_INVALID_CREDENTIAL = "failure-invalid-credential";
 const std::string ChallengeCredential::JSON_CREDENTIAL_CERT = "issued-cert";
-const std::string ChallengeCredential::JSON_CREDENTIAL_SELF = "self-signed";
+const std::string ChallengeCredential::JSON_PROOF_OF_PRIVATE_KEY = "proof-of-private-key";
 
 ChallengeCredential::ChallengeCredential(const std::string& configPath)
     : ChallengeModule("Credential")
@@ -102,7 +102,7 @@
           return;
         }
       }
-      else if (readString(elements[i]) == JSON_CREDENTIAL_SELF) {
+      else if (readString(elements[i]) == JSON_PROOF_OF_PRIVATE_KEY) {
         std::istringstream ss(readString(params.elements()[i + 1]));
         selfSigned = io::load<security::v2::Certificate>(ss);
         if (selfSigned == nullptr) {
@@ -148,7 +148,7 @@
   JsonSection result;
   if (status == STATUS_BEFORE_CHALLENGE && challengeStatus == "") {
     result.put(JSON_CREDENTIAL_CERT, "Please_copy_anchor_signed_cert_here");
-    result.put(JSON_CREDENTIAL_SELF, "Please_copy_key_signed_request_id_data_here");
+    result.put(JSON_PROOF_OF_PRIVATE_KEY, "Please_copy_key_signed_request_id_data_here");
   }
   else {
     _LOG_ERROR("Client's status and challenge status are wrong");
@@ -162,7 +162,7 @@
   JsonSection result;
   if (status == STATUS_BEFORE_CHALLENGE && challengeStatus == "") {
     result.put(JSON_CREDENTIAL_CERT, params.get(JSON_CREDENTIAL_CERT, ""));
-    result.put(JSON_CREDENTIAL_SELF, params.get(JSON_CREDENTIAL_SELF, ""));
+    result.put(JSON_PROOF_OF_PRIVATE_KEY, params.get(JSON_PROOF_OF_PRIVATE_KEY, ""));
   }
   else {
     _LOG_ERROR("Client's status and challenge status are wrong");
@@ -178,8 +178,8 @@
     request.push_back(makeStringBlock(tlv_selected_challenge, CHALLENGE_TYPE));
     request.push_back(makeStringBlock(tlv_parameter_key, JSON_CREDENTIAL_CERT));
     request.push_back(makeStringBlock(tlv_parameter_value, params.get(JSON_CREDENTIAL_CERT, "")));
-    request.push_back(makeStringBlock(tlv_parameter_key, JSON_CREDENTIAL_SELF));
-    request.push_back(makeStringBlock(tlv_parameter_value, params.get(JSON_CREDENTIAL_SELF, "")));
+    request.push_back(makeStringBlock(tlv_parameter_key, JSON_PROOF_OF_PRIVATE_KEY));
+    request.push_back(makeStringBlock(tlv_parameter_value, params.get(JSON_PROOF_OF_PRIVATE_KEY, "")));
   }
   else {
     _LOG_ERROR("Client's status and challenge status are wrong");
diff --git a/src/challenge-module/challenge-credential.hpp b/src/challenge-module/challenge-credential.hpp
index bf53175..ad2b197 100644
--- a/src/challenge-module/challenge-credential.hpp
+++ b/src/challenge-module/challenge-credential.hpp
@@ -74,7 +74,7 @@
   static const std::string FAILURE_INVALID_FORMAT_CREDENTIAL;
   static const std::string FAILURE_INVALID_FORMAT_SELF_SIGNED;
   static const std::string JSON_CREDENTIAL_CERT;
-  static const std::string JSON_CREDENTIAL_SELF;
+  static const std::string JSON_PROOF_OF_PRIVATE_KEY;
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   std::list<security::v2::Certificate> m_trustAnchors;
diff --git a/src/challenge-module/challenge-private-key.cpp b/src/challenge-module/challenge-private-key.cpp
new file mode 100644
index 0000000..0b77af9
--- /dev/null
+++ b/src/challenge-module/challenge-private-key.cpp
@@ -0,0 +1,135 @@
+/*
+ * 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-private-key.hpp"
+
+#include <iostream>
+#include <ndn-cxx/security/verification-helpers.hpp>
+#include <ndn-cxx/util/io.hpp>
+
+#include "../logging.hpp"
+
+namespace ndn {
+namespace ndncert {
+
+_LOG_INIT(ndncert.ChallengePrivateKey);
+
+NDNCERT_REGISTER_CHALLENGE(ChallengePrivateKey, "Private");
+
+const std::string ChallengePrivateKey::FAILURE_INVALID_REQUEST_TYPE = "failure-invalid-request-type";
+const std::string ChallengePrivateKey::FAILURE_INVALID_FORMAT_SELF_SIGNED = "failure-cannot-parse-self-signed";
+const std::string ChallengePrivateKey::FAILURE_INVALID_CREDENTIAL = "failure-invalid-credential";
+const std::string ChallengePrivateKey::JSON_PROOF_OF_PRIVATE_KEY = "proof-of-private-key";
+
+ChallengePrivateKey::ChallengePrivateKey()
+    : ChallengeModule("PrivateKey")
+{
+}
+
+// For CA
+void
+ChallengePrivateKey::handleChallengeRequest(const Block& params, CertificateRequest& request)
+{
+  if (request.m_requestType == REQUEST_TYPE_NEW) {
+      _LOG_TRACE("Cannot use this private key challenge for new certificate request");
+      request.m_status = STATUS_FAILURE;
+      request.m_challengeStatus = FAILURE_INVALID_REQUEST_TYPE;
+      updateRequestOnChallengeEnd(request);
+  }
+  params.parse();
+  shared_ptr<security::v2::Certificate> selfSigned;
+  auto& elements = params.elements();
+  for (size_t i = 0; i < elements.size(); i++) {
+    if (elements[i].type() == tlv_parameter_key) {
+      if (readString(elements[i]) == JSON_PROOF_OF_PRIVATE_KEY) {
+        std::istringstream ss(readString(params.elements()[i + 1]));
+        selfSigned = io::load<security::v2::Certificate>(ss);
+        if (selfSigned == nullptr) {
+          _LOG_ERROR("Cannot load credential parameter: cert");
+          request.m_status = STATUS_FAILURE;
+          request.m_challengeStatus = FAILURE_INVALID_FORMAT_SELF_SIGNED;
+          updateRequestOnChallengeEnd(request);
+          return;
+        }
+      }
+      else {
+        continue;
+      }
+    }
+  }
+
+  // verify the credential and the self-signed cert
+  if (security::verifySignature(*selfSigned, request.m_cert) &&
+    readString(selfSigned->getContent()) == request.m_requestId) {
+    request.m_status = STATUS_PENDING;
+    request.m_challengeStatus = CHALLENGE_STATUS_SUCCESS;
+    updateRequestOnChallengeEnd(request);
+    return;
+  }
+
+  _LOG_TRACE("Cannot verify the credential + self-signed Data + data content");
+  request.m_status = STATUS_FAILURE;
+  request.m_challengeStatus = FAILURE_INVALID_CREDENTIAL;
+  updateRequestOnChallengeEnd(request);
+}
+
+// For Client
+JsonSection
+ChallengePrivateKey::getRequirementForChallenge(int status, const std::string& challengeStatus)
+{
+  JsonSection result;
+  if (status == STATUS_BEFORE_CHALLENGE && challengeStatus == "") {
+    result.put(JSON_PROOF_OF_PRIVATE_KEY, "Please_copy_key_signed_request_id_data_here");
+  }
+  else {
+    _LOG_ERROR("Client's status and challenge status are wrong");
+  }
+  return result;
+}
+
+JsonSection
+ChallengePrivateKey::genChallengeRequestJson(int status, const std::string& challengeStatus, const JsonSection& params)
+{
+  JsonSection result;
+  if (status == STATUS_BEFORE_CHALLENGE && challengeStatus == "") {
+    result.put(JSON_PROOF_OF_PRIVATE_KEY, params.get(JSON_PROOF_OF_PRIVATE_KEY, ""));
+  }
+  else {
+    _LOG_ERROR("Client's status and challenge status are wrong");
+  }
+  return result;
+}
+
+Block
+ChallengePrivateKey::genChallengeRequestTLV(int status, const std::string& challengeStatus, const JsonSection& params)
+{
+  Block request = makeEmptyBlock(tlv_encrypted_payload);
+  if (status == STATUS_BEFORE_CHALLENGE && challengeStatus == "") {
+    request.push_back(makeStringBlock(tlv_selected_challenge, CHALLENGE_TYPE));
+    request.push_back(makeStringBlock(tlv_parameter_key, JSON_PROOF_OF_PRIVATE_KEY));
+    request.push_back(makeStringBlock(tlv_parameter_value, params.get(JSON_PROOF_OF_PRIVATE_KEY, "")));
+  }
+  else {
+    _LOG_ERROR("Client's status and challenge status are wrong");
+  }
+  request.encode();
+  return request;
+}
+}  // namespace ndncert
+}  // namespace ndn
diff --git a/src/challenge-module/challenge-private-key.hpp b/src/challenge-module/challenge-private-key.hpp
new file mode 100644
index 0000000..8e78d5a
--- /dev/null
+++ b/src/challenge-module/challenge-private-key.hpp
@@ -0,0 +1,75 @@
+/**
+ * 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_CHALLENGE_PRIVATE_KEY_HPP
+#define NDNCERT_CHALLENGE_PRIVATE_KEY_HPP
+
+#include "../challenge-module.hpp"
+
+namespace ndn {
+namespace ndncert {
+
+/**
+ * @brief Private Key based challenge (for renewal and revocation)
+ *
+ * Once the requester could proof his/her possession of the private key corresponds to
+ * the current CA's previous issued certificate, the requester could finish the challenge.
+ *
+ * The requester needs to provide the proof of the possession the private for the certificate
+ * for the previous cerificate. The challenge require the requester to 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. The requester sign a Data packet which content is the request id.
+ *   2. The challenge module will verify the signature of the credential.
+ *
+ * Failure info when application fails:
+ *   FAILURE_INVALID_CREDENTIAL: When the signature cannot be validated.
+ *   FAILURE_INVALID_FORMAT: When the credential format is wrong.
+ */
+class ChallengePrivateKey : public ChallengeModule
+{
+public:
+  ChallengePrivateKey();
+
+  // For CA
+  void
+  handleChallengeRequest(const Block& params, CertificateRequest& request) override;
+
+  // For Client
+  JsonSection
+  getRequirementForChallenge(int status, const std::string& challengeStatus) override;
+
+  JsonSection
+  genChallengeRequestJson(int status, const std::string& challengeStatus, const JsonSection& params) override;
+
+  Block
+  genChallengeRequestTLV(int status, const std::string& challengeStatus, const JsonSection& params) override;
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  static const std::string FAILURE_INVALID_REQUEST_TYPE;
+  static const std::string FAILURE_INVALID_CREDENTIAL;
+  static const std::string FAILURE_INVALID_FORMAT_SELF_SIGNED;
+  static const std::string JSON_PROOF_OF_PRIVATE_KEY;
+};
+
+} // namespace ndncert
+} // namespace ndn
+
+#endif // NDNCERT_CHALLENGE_PRIVATE_KEY_HPP