security: Enable validator fetch cert directly from interest sender

Change-Id: I8fab50145a9a053c85c1b2c6be752ba71b0120ef
Refs: #2237
diff --git a/AUTHORS.md b/AUTHORS.md
index 48457ef..59e7e77 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -39,4 +39,4 @@
 * Marcin Juszkiewicz    <http://marcin.juszkiewicz.com.pl/>
 * Susmit Shannigrahi    <https://www.linkedin.com/in/susmit-shannigrahi-90433b8>
 * José Quevedo          <http://atnog.av.it.pt/members/jquevedo>
-* Zhiyi Zhang           <zhangzhiyi1919@cs.ucla.edu>
+* Zhiyi Zhang           <http://irl.cs.ucla.edu/~zhiyi/>
diff --git a/src/security/validation-request.hpp b/src/security/validation-request.hpp
index 000f61b..a48e365 100644
--- a/src/security/validation-request.hpp
+++ b/src/security/validation-request.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -17,8 +17,6 @@
  * <http://www.gnu.org/licenses/>.
  *
  * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
- *
- * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>
  */
 
 #ifndef NDN_SECURITY_VALIDATION_REQUEST_HPP
@@ -58,17 +56,14 @@
   ValidationRequest(const Interest& interest,
                     const OnDataValidated& onDataValidated,
                     const OnDataValidationFailed& onDataValidationFailed,
-                    int nRetries, int nSteps)
+                    int nRetries, int nSteps,
+                    uint64_t requesterFaceId = 0)
     : m_interest(interest)
     , m_onDataValidated(onDataValidated)
     , m_onDataValidationFailed(onDataValidationFailed)
     , m_nRetries(nRetries)
     , m_nSteps(nSteps)
-  {
-  }
-
-  virtual
-  ~ValidationRequest()
+    , m_requesterFaceId(requesterFaceId)
   {
   }
 
@@ -82,6 +77,8 @@
   int m_nRetries;
   /// @brief the number of validation steps that have been performed.
   int m_nSteps;
+  /// @brief the incoming face id of the origin packet.
+  uint64_t m_requesterFaceId;
 };
 
 } // namespace security
diff --git a/src/security/validator-config.cpp b/src/security/validator-config.cpp
index 13bcc73..64c0187 100644
--- a/src/security/validator-config.cpp
+++ b/src/security/validator-config.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -17,14 +17,12 @@
  * <http://www.gnu.org/licenses/>.
  *
  * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
- *
- * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>
- * @author Zhiyi Zhang <zhangzhiyi1919@gmail.com>
  */
 
 #include "validator-config.hpp"
 #include "certificate-cache-ttl.hpp"
 #include "../util/io.hpp"
+#include "../lp/tags.hpp"
 
 #include <boost/filesystem.hpp>
 #include <boost/property_tree/info_parser.hpp>
@@ -722,10 +720,16 @@
 
     Interest certInterest(keyLocatorName);
 
+    uint64_t incomingFaceId = 0;
+    auto incomingFaceIdTag = packet.template getTag<lp::IncomingFaceIdTag>();
+    if (incomingFaceIdTag != nullptr) {
+      incomingFaceId = incomingFaceIdTag->get();
+    }
     auto nextStep = make_shared<ValidationRequest>(certInterest,
                                                    onCertValidated,
                                                    onCertValidationFailed,
-                                                   1, nSteps + 1);
+                                                   1, nSteps + 1,
+                                                   incomingFaceId);
 
     nextSteps.push_back(nextStep);
     return;
diff --git a/src/security/validator-config.hpp b/src/security/validator-config.hpp
index 2a5fac9..47d536e 100644
--- a/src/security/validator-config.hpp
+++ b/src/security/validator-config.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -17,9 +17,6 @@
  * <http://www.gnu.org/licenses/>.
  *
  * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
- *
- * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>
- * @author Zhiyi Zhang <zhangzhiyi1919@gmail.com>
  */
 
 #ifndef NDN_SECURITY_VALIDATOR_CONFIG_HPP
diff --git a/src/security/validator.cpp b/src/security/validator.cpp
index 84aaa0f..d3af387 100644
--- a/src/security/validator.cpp
+++ b/src/security/validator.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -17,13 +17,11 @@
  * <http://www.gnu.org/licenses/>.
  *
  * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
- *
- * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>
- * @author Jeff Thompson <jefft0@remap.ucla.edu>
  */
 
 #include "validator.hpp"
 #include "../util/crypto.hpp"
+#include "../lp/tags.hpp"
 
 #include "v1/cryptopp.hpp"
 
@@ -35,11 +33,12 @@
 
 Validator::Validator(Face* face)
   : m_face(face)
+  , m_wantDirectCertFetch(false)
 {
 }
 
 Validator::Validator(Face& face)
-  : m_face(&face)
+  : Validator(&face)
 {
 }
 
@@ -311,6 +310,12 @@
   }
 
   for (shared_ptr<ValidationRequest> step : nextSteps) {
+    if (m_wantDirectCertFetch && step->m_requesterFaceId != 0) {
+      Interest directFetchInterest(step->m_interest);
+      directFetchInterest.refreshNonce();
+      directFetchInterest.setTag(make_shared<lp::NextHopFaceIdTag>(step->m_requesterFaceId));
+      m_face->expressInterest(directFetchInterest, nullptr, nullptr, nullptr);
+    }
     m_face->expressInterest(step->m_interest,
                             bind(&Validator::onData, this, _1, _2, step),
                             bind(&Validator::onNack, this, _1, _2,
@@ -322,5 +327,11 @@
   }
 }
 
+void
+Validator::setDirectCertFetchEnabled(bool isEnabled)
+{
+  m_wantDirectCertFetch = isEnabled;
+}
+
 } // namespace security
 } // namespace ndn
diff --git a/src/security/validator.hpp b/src/security/validator.hpp
index edc0365..bb401b9 100644
--- a/src/security/validator.hpp
+++ b/src/security/validator.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -17,9 +17,6 @@
  * <http://www.gnu.org/licenses/>.
  *
  * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
- *
- * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>
- * @author Jeff Thompson <jefft0@remap.ucla.edu>
  */
 
 #ifndef NDN_SECURITY_VALIDATOR_HPP
@@ -100,6 +97,27 @@
     validate(interest, onValidated, onValidationFailed, 0);
   }
 
+  /**
+   * @brief Enable or disable the direct certificate fetch feature.
+   *
+   * When enabled, the validator will attempt to fetch the certificate that signs an Interest from
+   * the sender of that Interest, as identified by IncomingFaceId field, in addition to fetching
+   * from the infrastructure.
+   *
+   * Prior to enabling this feature, the application must enable NextHopFaceId privilege on the face
+   * used by this validator.
+   *
+   * @note Current implementation can only fetch the Interest signer certificate from the
+   * Interest sender; the issuer certificate of that certificate is only fetched from the
+   * infrastructure.
+   *
+   * @note Currently, this feature can only be used with ValidatorConfig.
+   *
+   * @param isEnabled Set true to enable the feature or false to disable.
+   */
+  void
+  setDirectCertFetchEnabled(bool isEnabled);
+
   /*****************************************
    *      verifySignature method set       *
    *****************************************/
@@ -329,6 +347,7 @@
 
 protected:
   Face* m_face;
+  bool m_wantDirectCertFetch;
 };
 
 } // namespace security
diff --git a/tests/unit-tests/security/validator-config.t.cpp b/tests/unit-tests/security/validator-config.t.cpp
index e3f2f99..2419e8d 100644
--- a/tests/unit-tests/security/validator-config.t.cpp
+++ b/tests/unit-tests/security/validator-config.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2016 Regents of the University of California.
+ * Copyright (c) 2013-2017 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -22,12 +22,14 @@
 #include "security/validator-config.hpp"
 
 #include "security/key-chain.hpp"
-#include "security/signing-helpers.hpp"
 #include "util/io.hpp"
 #include "util/scheduler.hpp"
 #include "util/dummy-client-face.hpp"
+#include "lp/tags.hpp"
+#include "lp/nack.hpp"
 
 #include <boost/asio.hpp>
+#include <boost/logic/tribool.hpp>
 
 #include "identity-management-fixture.hpp"
 #include "../identity-management-time-fixture.hpp"
@@ -1224,7 +1226,6 @@
   Name root("/TestValidatorConfig");
   BOOST_REQUIRE(saveIdentityCertificate(root, "trust-anchor-8.cert", true));
 
-
   Name sld("/TestValidatorConfig/Nrd-1");
   BOOST_REQUIRE(addIdentity(sld));
   advanceClocks(time::milliseconds(100));
@@ -1486,6 +1487,152 @@
   advanceClocks(time::milliseconds(10), 20);
 }
 
+class DirectCertFetchFixture : public IdentityManagementTimeFixture
+{
+public:
+  DirectCertFetchFixture()
+    : clientFace(io, m_keyChain, {true, true})
+    , validationResult(boost::logic::indeterminate)
+  {
+    BOOST_REQUIRE(addIdentity(ca));
+    BOOST_REQUIRE(saveIdentityCertificate(ca, "trust-anchor-1.cert", true));
+    BOOST_REQUIRE(addSubCertificate(user, ca));
+
+    userCertName = m_keyChain.getDefaultCertificateNameForIdentity(user);
+    userCert = m_keyChain.getCertificate(userCertName);
+  }
+
+protected:
+  void
+  runTest(const std::function<void(const Interest&, const Interest&)> respond)
+  {
+    optional<Interest> directInterest;
+    optional<Interest> infrastructureInterest;
+    clientFace.onSendInterest.connect([&] (const Interest& interest) {
+      const Name& interestName = interest.getName();
+      if (interestName == userCert->getName().getPrefix(-1)) {
+        auto nextHopFaceIdTag = interest.getTag<lp::NextHopFaceIdTag>();
+        if (nextHopFaceIdTag != nullptr) {
+          BOOST_CHECK(!directInterest);
+          directInterest = interest;
+        }
+        else {
+          BOOST_CHECK(!infrastructureInterest);
+          infrastructureInterest = interest;
+        }
+        if (static_cast<bool>(directInterest) && static_cast<bool>(infrastructureInterest)) {
+          io.post([directInterest, infrastructureInterest, respond] {
+              respond(directInterest.value(), infrastructureInterest.value());
+            });
+          directInterest = nullopt;
+          infrastructureInterest = nullopt;
+        }
+      }
+    });
+
+    const boost::filesystem::path CONFIG_PATH =
+      (boost::filesystem::current_path() / std::string("unit-test-direct.conf"));
+    ValidatorConfig validator(&clientFace);
+    validator.load(CONFIG, CONFIG_PATH.c_str());
+    validator.setDirectCertFetchEnabled(true);
+
+    shared_ptr<Interest> interest = make_shared<Interest>(interestName);
+    interest->setTag(make_shared<lp::IncomingFaceIdTag>(123));
+    BOOST_CHECK_NO_THROW(m_keyChain.sign(*interest, security::signingByIdentity(user)));
+
+    validator.validate(*interest,
+                       [&] (const shared_ptr<const Interest>&) {
+                         BOOST_CHECK(boost::logic::indeterminate(validationResult));
+                         validationResult = true;
+                       },
+                       [&] (const shared_ptr<const Interest>&, const std::string& s) {
+                         BOOST_CHECK(boost::logic::indeterminate(validationResult));
+                         validationResult = false;
+                       });
+
+    advanceClocks(time::milliseconds(200), 60);
+
+    BOOST_CHECK(!boost::logic::indeterminate(validationResult));
+  }
+
+public:
+  util::DummyClientFace clientFace;
+  const Name ca = Name("/DirectCertFetch");
+  const Name user = Name("/DirectCertFetch/user");
+  const Name interestName = Name("/DirectCertFetch/user/tag-interest");
+  const std::string CONFIG = R"_TEXT_(
+      rule
+      {
+        id "RuleForInterest"
+        for interest
+        checker
+        {
+          type hierarchical
+          sig-type rsa-sha256
+        }
+      }
+      rule
+      {
+        id "RuleForData"
+        for data
+        filter
+        {
+          type name
+          regex ^<>*$
+        }
+        checker
+        {
+          type customized
+          sig-type rsa-sha256
+          key-locator
+        {
+          type name
+           regex ^<>*$
+          }
+        }
+      }
+      trust-anchor
+      {
+        type file
+        file-name "trust-anchor-1.cert"
+      }
+      )_TEXT_";
+  boost::logic::tribool validationResult;
+  Name userCertName;
+  shared_ptr<v1::IdentityCertificate> userCert;
+};
+
+BOOST_FIXTURE_TEST_SUITE(DirectCertFetch, DirectCertFetchFixture)
+
+BOOST_AUTO_TEST_CASE(CertFetchSuccess)
+{
+  runTest([this] (const Interest& directInterest, const Interest& infrastructureInterest) {
+      this->clientFace.receive(*userCert);
+    });
+
+  BOOST_CHECK_EQUAL(validationResult, true);
+}
+
+BOOST_AUTO_TEST_CASE(CertFetchTimeout)
+{
+  // In this test case, certificate request would time out
+  runTest([this] (const Interest& directInterest, const Interest& infrastructureInterest) { });
+
+  BOOST_CHECK_EQUAL(validationResult, false);
+}
+
+BOOST_AUTO_TEST_CASE(CertFetchNack)
+{
+  runTest([this] (const Interest& directInterest, const Interest& infrastructureInterest) {
+      lp::Nack nackInfrastructureInterest(infrastructureInterest);
+      nackInfrastructureInterest.setHeader(lp::NackHeader().setReason(lp::NackReason::NO_ROUTE));
+      this->clientFace.receive(nackInfrastructureInterest);
+    });
+
+  BOOST_CHECK_EQUAL(validationResult, false);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // DirectCertFetch
 BOOST_AUTO_TEST_SUITE_END() // TestValidatorConfig
 BOOST_AUTO_TEST_SUITE_END() // Security