Certificate revocation

Adds the handling of certificate revocation according to ndncert protocol v0.2.
Includes handing on CA module, client module as well as the test cases for these funcationalities.

Currently no internal database are being updated from the revocation.
This actual update and propagation of revocation information needs to relies on a certificate log, which can be attached to the CA module using status update callback.

Change-Id: I21f912285161ce781e17d222e640c8f0c57b50f7
diff --git a/src/client-module.cpp b/src/client-module.cpp
index 697ff8f..1e01dc0 100644
--- a/src/client-module.cpp
+++ b/src/client-module.cpp
@@ -26,6 +26,7 @@
 #include "protocol-detail/probe.hpp"
 #include "protocol-detail/new.hpp"
 #include "protocol-detail/challenge.hpp"
+#include "protocol-detail/revoke.hpp"
 #include <ndn-cxx/util/io.hpp>
 #include <ndn-cxx/security/signing-helpers.hpp>
 #include <ndn-cxx/security/verification-helpers.hpp>
@@ -243,6 +244,68 @@
 }
 
 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_caName.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(
+            REVOKE::encodeApplicationParameters(m_ecdh.getBase64PubKey(), certificate)
+    );
+
+    // return the Interest packet
+    return interest;
+}
+
+std::list<std::string>
+ClientModule::onRevokeResponse(const Data& reply)
+{
+    if (!security::verifySignature(reply, m_ca.m_anchor)) {
+        _LOG_ERROR("Cannot verify data signature from " << m_ca.m_caName.toUri());
+        return std::list<std::string>();
+    }
+    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 = 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::generateChallengeInterest(const Block& challengeRequest)
 {
   challengeRequest.parse();