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/tests/unit-tests/ca-memory.t.cpp b/tests/unit-tests/ca-memory.t.cpp
index 7425a0e..a1bcd28 100644
--- a/tests/unit-tests/ca-memory.t.cpp
+++ b/tests/unit-tests/ca-memory.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2017-2019, Regents of the University of California.
+ * Copyright (c) 2017-2020, Regents of the University of California.
  *
  * This file is part of ndncert, a certificate management system based on NDN.
  *
@@ -82,7 +82,7 @@
   auto cert1 = key1.getDefaultCertificate();
 
   // add operation
-  CertificateRequest request1(Name("/ndn/site1"), "123", STATUS_BEFORE_CHALLENGE, cert1);
+  CertificateRequest request1(Name("/ndn/site1"), "123", REQUEST_TYPE_NEW, STATUS_BEFORE_CHALLENGE, cert1);
   BOOST_CHECK_NO_THROW(storage.addRequest(request1));
 
   // get operation
@@ -95,7 +95,7 @@
   json.put("code", "1234");
 
   // update operation
-  CertificateRequest request2(Name("/ndn/site1"), "123", STATUS_CHALLENGE, CHALLENGE_STATUS_SUCCESS,
+  CertificateRequest request2(Name("/ndn/site1"), "123", REQUEST_TYPE_NEW, STATUS_CHALLENGE, CHALLENGE_STATUS_SUCCESS,
                              "Email", time::toIsoString(time::system_clock::now()), 3600, 3, json, cert1);
   storage.updateRequest(request2);
   result = storage.getRequest("123");
@@ -106,7 +106,7 @@
   auto identity2 = addIdentity(Name("/ndn/site2"));
   auto key2 = identity2.getDefaultKey();
   auto cert2 = key2.getDefaultCertificate();
-  CertificateRequest request3(Name("/ndn/site2"), "456", STATUS_BEFORE_CHALLENGE, cert2);
+  CertificateRequest request3(Name("/ndn/site2"), "456", REQUEST_TYPE_NEW, STATUS_BEFORE_CHALLENGE, cert2);
   storage.addRequest(request3);
 
   // list operation
diff --git a/tests/unit-tests/ca-module.t.cpp b/tests/unit-tests/ca-module.t.cpp
index cb3cfcd..64a5d7f 100644
--- a/tests/unit-tests/ca-module.t.cpp
+++ b/tests/unit-tests/ca-module.t.cpp
@@ -46,7 +46,7 @@
 
   advanceClocks(time::milliseconds(20), 60);
   BOOST_CHECK_EQUAL(ca.m_registeredPrefixHandles.size(), 2);
-  BOOST_CHECK_EQUAL(ca.m_interestFilterHandles.size(), 4);  // onInfo, onProbe, onNew, onChallenge
+  BOOST_CHECK_EQUAL(ca.m_interestFilterHandles.size(), 5); // onInfo, onProbe, onNew, onChallenge, onRevoke
 }
 
 BOOST_AUTO_TEST_CASE(HandleProbe)
@@ -367,7 +367,112 @@
   BOOST_CHECK_EQUAL(count, 3);
 }
 
-BOOST_AUTO_TEST_SUITE_END()  // TestCaModule
+BOOST_AUTO_TEST_CASE(HandleRevoke)
+{
+  auto identity = addIdentity(Name("/ndn"));
+  auto key = identity.getDefaultKey();
+  auto cert = key.getDefaultCertificate();
+
+  util::DummyClientFace face(io, { true, true });
+  CaModule ca(face, m_keyChain, "tests/unit-tests/ca.conf.test", "ca-storage-memory");
+  advanceClocks(time::milliseconds(20), 60);
+
+  //generate a certificate
+  auto clientIdentity = m_keyChain.createIdentity("/ndn/qwerty");
+  auto clientKey = clientIdentity.getDefaultKey();
+  security::v2::Certificate clientCert;
+  clientCert.setName(Name(clientKey.getName()).append("cert-request").appendVersion());
+  clientCert.setContentType(tlv::ContentType_Key);
+  clientCert.setFreshnessPeriod(time::hours(24));
+  clientCert.setContent(clientKey.getPublicKey().data(), clientKey.getPublicKey().size());
+  SignatureInfo signatureInfo;
+  signatureInfo.setValidityPeriod(security::ValidityPeriod(time::system_clock::now(),
+          time::system_clock::now() + time::hours(10)));
+  m_keyChain.sign(clientCert, signingByKey(clientKey.getName()).setSignatureInfo(signatureInfo));
+  CertificateRequest certRequest(Name("/ndn"), "122", REQUEST_TYPE_NEW, STATUS_SUCCESS, clientCert);
+  auto issuedCert = ca.issueCertificate(certRequest);
+
+  ClientModule client(m_keyChain);
+  ClientCaItem item;
+  item.m_caName = Name("/ndn");
+  item.m_anchor = cert;
+  client.getClientConf().m_caItems.push_back(item);
+
+  auto interest = client.generateRevokeInterest(issuedCert);
+
+  int count = 0;
+  face.onSendData.connect([&] (const Data& response) {
+    count++;
+    BOOST_CHECK(security::verifySignature(response, cert));
+    auto contentBlock = response.getContent();
+    contentBlock.parse();
+
+    BOOST_CHECK(readString(contentBlock.get(tlv_ecdh_pub)) != "");
+    BOOST_CHECK(readString(contentBlock.get(tlv_salt)) != "");
+    BOOST_CHECK(readString(contentBlock.get(tlv_request_id)) != "");
+
+    auto challengeBlockCount = 0;
+    for (auto const& element : contentBlock.elements()) {
+        if (element.type() == tlv_challenge) {
+            challengeBlockCount++;
+        }
+    }
+
+    BOOST_CHECK(challengeBlockCount != 0);
+
+    client.onRevokeResponse(response);
+    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);
+
+  advanceClocks(time::milliseconds(20), 60);
+  BOOST_CHECK_EQUAL(count, 1);
+}
+
+BOOST_AUTO_TEST_CASE(HandleRevokeWithBadCert)
+{
+  auto identity = addIdentity(Name("/ndn"));
+  auto key = identity.getDefaultKey();
+  auto cert = key.getDefaultCertificate();
+
+  util::DummyClientFace face(io, { true, true });
+  CaModule ca(face, m_keyChain, "tests/unit-tests/ca.conf.test", "ca-storage-memory");
+  advanceClocks(time::milliseconds(20), 60);
+
+  //generate a certificate
+  auto clientIdentity = m_keyChain.createIdentity("/ndn/qwerty");
+  auto clientKey = clientIdentity.getDefaultKey();
+  security::v2::Certificate clientCert;
+  clientCert.setName(Name(clientKey.getName()).append("NDNCERT").append(std::to_string(1473283247810732701)));
+  clientCert.setContentType(tlv::ContentType_Key);
+  clientCert.setFreshnessPeriod(time::hours(24));
+  clientCert.setContent(clientKey.getPublicKey().data(), clientKey.getPublicKey().size());
+  SignatureInfo signatureInfo;
+  signatureInfo.setValidityPeriod(security::ValidityPeriod(time::system_clock::now(),
+          time::system_clock::now() + time::hours(10)));
+  m_keyChain.sign(clientCert, signingByKey(clientKey.getName()).setSignatureInfo(signatureInfo));
+
+  ClientModule client(m_keyChain);
+  ClientCaItem item;
+  item.m_caName = Name("/ndn");
+  item.m_anchor = cert;
+  client.getClientConf().m_caItems.push_back(item);
+
+  auto interest = client.generateRevokeInterest(clientCert);
+
+  int count = 0;
+  face.onSendData.connect([&] (const Data& response) {
+    count++;
+  });
+  face.receive(*interest);
+
+  advanceClocks(time::milliseconds(20), 60);
+  BOOST_CHECK_EQUAL(count, 0);
+}
+
+
+BOOST_AUTO_TEST_SUITE_END() // TestCaModule
 
 }  // namespace tests
 }  // namespace ndncert
diff --git a/tests/unit-tests/ca-sqlite.t.cpp b/tests/unit-tests/ca-sqlite.t.cpp
index 07782a6..aa70f3a 100644
--- a/tests/unit-tests/ca-sqlite.t.cpp
+++ b/tests/unit-tests/ca-sqlite.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2017-2019, Regents of the University of California.
+ * Copyright (c) 2017-2020, Regents of the University of California.
  *
  * This file is part of ndncert, a certificate management system based on NDN.
  *
@@ -81,7 +81,7 @@
   auto cert1 = key1.getDefaultCertificate();
 
   // add operation
-  CertificateRequest request1(Name("/ndn/site1"), "123", STATUS_BEFORE_CHALLENGE, cert1);
+  CertificateRequest request1(Name("/ndn/site1"), "123", REQUEST_TYPE_NEW, STATUS_BEFORE_CHALLENGE, cert1);
   BOOST_CHECK_NO_THROW(storage.addRequest(request1));
 
   // get operation
@@ -93,7 +93,7 @@
   // update operation
   JsonSection json;
   json.put("test", "4567");
-  CertificateRequest request2(Name("/ndn/site1"), "123", STATUS_CHALLENGE, CHALLENGE_STATUS_SUCCESS,
+  CertificateRequest request2(Name("/ndn/site1"), "123", REQUEST_TYPE_NEW, STATUS_CHALLENGE, CHALLENGE_STATUS_SUCCESS,
                              "Email", time::toIsoString(time::system_clock::now()), 3600, 3, json, cert1);
   storage.updateRequest(request2);
   result = storage.getRequest("123");
@@ -104,7 +104,7 @@
   auto identity2 = addIdentity(Name("/ndn/site2"));
   auto key2 = identity2.getDefaultKey();
   auto cert2 = key2.getDefaultCertificate();
-  CertificateRequest request3(Name("/ndn/site2"), "456", STATUS_BEFORE_CHALLENGE, cert2);
+  CertificateRequest request3(Name("/ndn/site2"), "456", REQUEST_TYPE_NEW, STATUS_BEFORE_CHALLENGE, cert2);
   storage.addRequest(request3);
 
   // list operation
diff --git a/tests/unit-tests/challenge-credential.t.cpp b/tests/unit-tests/challenge-credential.t.cpp
index 2b8e9b2..3472687 100644
--- a/tests/unit-tests/challenge-credential.t.cpp
+++ b/tests/unit-tests/challenge-credential.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2017-2019, Regents of the University of California.
+ * Copyright (c) 2017-2020, Regents of the University of California.
  *
  * This file is part of ndncert, a certificate management system based on NDN.
  *
@@ -55,7 +55,7 @@
   auto identityA = addIdentity(Name("/example"));
   auto keyA = identityA.getDefaultKey();
   auto certA = key.getDefaultCertificate();
-  CertificateRequest request(Name("/example"), "123", STATUS_BEFORE_CHALLENGE, certA);
+  CertificateRequest request(Name("/example"), "123", REQUEST_TYPE_NEW, STATUS_BEFORE_CHALLENGE, certA);
 
   // create requester's existing cert
   auto identityB = addIdentity(Name("/trust/cert"));
diff --git a/tests/unit-tests/challenge-email.t.cpp b/tests/unit-tests/challenge-email.t.cpp
index 8ff47c4..50e30c5 100644
--- a/tests/unit-tests/challenge-email.t.cpp
+++ b/tests/unit-tests/challenge-email.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2017-2019, Regents of the University of California.
+ * Copyright (c) 2017-2020, Regents of the University of California.
  *
  * This file is part of ndncert, a certificate management system based on NDN.
  *
@@ -45,7 +45,7 @@
   auto identity = addIdentity(Name("/ndn/site1"));
   auto key = identity.getDefaultKey();
   auto cert = key.getDefaultCertificate();
-  CertificateRequest request(Name("/ndn/site1"), "123", STATUS_BEFORE_CHALLENGE, cert);
+  CertificateRequest request(Name("/ndn/site1"), "123", REQUEST_TYPE_NEW, STATUS_BEFORE_CHALLENGE, cert);
 
   Block paramTLV = makeEmptyBlock(tlv_encrypted_payload);
   paramTLV.push_back(makeStringBlock(tlv_parameter_key, ChallengeEmail::JSON_EMAIL));
@@ -95,7 +95,7 @@
   auto identity = addIdentity(Name("/ndn/site1"));
   auto key = identity.getDefaultKey();
   auto cert = key.getDefaultCertificate();
-  CertificateRequest request(Name("/ndn/site1"), "123", STATUS_BEFORE_CHALLENGE, cert);
+  CertificateRequest request(Name("/ndn/site1"), "123", REQUEST_TYPE_NEW, STATUS_BEFORE_CHALLENGE, cert);
 
   Block paramTLV = makeEmptyBlock(tlv_encrypted_payload);
   paramTLV.push_back(makeStringBlock(tlv_parameter_key, ChallengeEmail::JSON_EMAIL));
@@ -115,7 +115,7 @@
   auto cert = key.getDefaultCertificate();
   JsonSection json;
   json.put(ChallengeEmail::JSON_CODE, "4567");
-  CertificateRequest request(Name("/ndn/site1"), "123", STATUS_CHALLENGE, ChallengeEmail::NEED_CODE,
+  CertificateRequest request(Name("/ndn/site1"), "123", REQUEST_TYPE_NEW, STATUS_CHALLENGE, ChallengeEmail::NEED_CODE,
                              "Email", time::toIsoString(time::system_clock::now()), 3600, 3, json, cert);
 
   Block paramTLV = makeEmptyBlock(tlv_encrypted_payload);
@@ -137,7 +137,7 @@
   auto cert = key.getDefaultCertificate();
   JsonSection json;
   json.put(ChallengeEmail::JSON_CODE, "4567");
-  CertificateRequest request(Name("/ndn/site1"), "123", STATUS_CHALLENGE, ChallengeEmail::NEED_CODE,
+  CertificateRequest request(Name("/ndn/site1"), "123", REQUEST_TYPE_NEW, STATUS_CHALLENGE, ChallengeEmail::NEED_CODE,
                              "email", time::toIsoString(time::system_clock::now()), 3600, 3, json, cert);
 
   Block paramTLV = makeEmptyBlock(tlv_encrypted_payload);
diff --git a/tests/unit-tests/challenge-pin.t.cpp b/tests/unit-tests/challenge-pin.t.cpp
index a1fea0d..9cabf38 100644
--- a/tests/unit-tests/challenge-pin.t.cpp
+++ b/tests/unit-tests/challenge-pin.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2017-2019, Regents of the University of California.
+ * Copyright (c) 2017-2020, Regents of the University of California.
  *
  * This file is part of ndncert, a certificate management system based on NDN.
  *
@@ -38,7 +38,7 @@
   auto identity = addIdentity(Name("/ndn/site1"));
   auto key = identity.getDefaultKey();
   auto cert = key.getDefaultCertificate();
-  CertificateRequest request(Name("/ndn/site1"), "123", STATUS_BEFORE_CHALLENGE, cert);
+  CertificateRequest request(Name("/ndn/site1"), "123", REQUEST_TYPE_NEW, STATUS_BEFORE_CHALLENGE, cert);
 
   ChallengePin challenge;
   challenge.handleChallengeRequest(makeEmptyBlock(tlv_encrypted_payload), request);
@@ -55,7 +55,7 @@
   auto cert = key.getDefaultCertificate();
   JsonSection secret;
   secret.add(ChallengePin::JSON_PIN_CODE, "12345");
-  CertificateRequest request(Name("/ndn/site1"), "123", STATUS_CHALLENGE, ChallengePin::NEED_CODE, "pin",
+  CertificateRequest request(Name("/ndn/site1"), "123", REQUEST_TYPE_NEW, STATUS_CHALLENGE, ChallengePin::NEED_CODE, "pin",
                              time::toIsoString(time::system_clock::now()), 3600, 3, secret, cert);
 
   Block paramTLV = makeEmptyBlock(tlv_encrypted_payload);
@@ -77,7 +77,7 @@
   auto cert = key.getDefaultCertificate();
   JsonSection secret;
   secret.add(ChallengePin::JSON_PIN_CODE, "12345");
-  CertificateRequest request(Name("/ndn/site1"), "123", STATUS_CHALLENGE, ChallengePin::NEED_CODE, "pin",
+  CertificateRequest request(Name("/ndn/site1"), "123", REQUEST_TYPE_NEW, STATUS_CHALLENGE, ChallengePin::NEED_CODE, "pin",
                              time::toIsoString(time::system_clock::now()), 3600, 3, secret, cert);
 
   Block paramTLV = makeEmptyBlock(tlv_encrypted_payload);