security: Fix certificate loop detection in v2::Validator

Change-Id: I0e55cd6ee9744df07fa13dcd8ee337909c49430f
diff --git a/src/security/v2/validator.cpp b/src/security/v2/validator.cpp
index 4928c67..6dc1f91 100644
--- a/src/security/v2/validator.cpp
+++ b/src/security/v2/validator.cpp
@@ -133,6 +133,12 @@
     return;
   }
 
+  if (state->hasSeenCertificateName(certRequest->m_interest.getName())) {
+    state->fail({ValidationError::Code::LOOP_DETECTED,
+                 "Validation loop detected for certificate `" + certRequest->m_interest.getName().toUri() + "`"});
+    return;
+  }
+
   NDN_LOG_DEBUG_DEPTH("Retrieving " << certRequest->m_interest.getName());
 
   auto cert = findTrustedCert(certRequest->m_interest);
diff --git a/tests/unit-tests/security/v2/validator.t.cpp b/tests/unit-tests/security/v2/validator.t.cpp
index 51ff7fa..73bd23d 100644
--- a/tests/unit-tests/security/v2/validator.t.cpp
+++ b/tests/unit-tests/security/v2/validator.t.cpp
@@ -220,7 +220,7 @@
   face.sentInterests.clear();
 }
 
-BOOST_AUTO_TEST_CASE(LoopingCert)
+BOOST_AUTO_TEST_CASE(InfiniteCertChain)
 {
   processInterest = [this] (const Interest& interest) {
     // create another key for the same identity and sign it properly
@@ -263,10 +263,40 @@
 
   validator.setMaxDepth(30);
   BOOST_CHECK_EQUAL(validator.getMaxDepth(), 30);
-  VALIDATE_FAILURE(data, "Should fail, as certificate should be looped");
+  VALIDATE_FAILURE(data, "Should fail, as certificate chain is infinite");
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 30);
 }
 
+BOOST_AUTO_TEST_CASE(LoopedCertChain)
+{
+  auto s1 = addIdentity("/loop");
+  auto k1 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key1")));
+  auto k2 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key2")));
+  auto k3 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key3")));
+
+  auto makeCert = [this] (Key& key, const Key& signer) {
+    v2::Certificate request = key.getDefaultCertificate();
+    request.setName(Name(key.getName()).append("looper").appendVersion());
+
+    SignatureInfo info;
+    info.setValidityPeriod({time::system_clock::now() - time::days(100),
+                            time::system_clock::now() + time::days(100)});
+    m_keyChain.sign(request, signingByKey(signer).setSignatureInfo(info));
+    m_keyChain.addCertificate(key, request);
+
+    cache.insert(request);
+  };
+
+  makeCert(k1, k2);
+  makeCert(k2, k3);
+  makeCert(k3, k1);
+
+  Data data("/loop/Data");
+  m_keyChain.sign(data, signingByKey(k1));
+  VALIDATE_FAILURE(data, "Should fail, as certificate chain loops");
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 3);
+}
+
 BOOST_AUTO_TEST_SUITE_END() // TestValidator
 BOOST_AUTO_TEST_SUITE_END() // V2
 BOOST_AUTO_TEST_SUITE_END() // Security