Rename 'tests/unit-tests' directory to 'tests/unit'
Change-Id: I78ea29938259fac288781bed12fb2399ac7eba26
diff --git a/tests/unit/security/v2/additional-description.t.cpp b/tests/unit/security/v2/additional-description.t.cpp
new file mode 100644
index 0000000..7ceee8c
--- /dev/null
+++ b/tests/unit/security/v2/additional-description.t.cpp
@@ -0,0 +1,103 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/additional-description.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(TestAdditionalDescription)
+
+const uint8_t description[] = {
+ 0xfd, 0x01, 0x02, 0x28,
+ 0xfd, 0x02, 0x00, 0x10, // DescriptionEntry
+ 0xfd, 0x02, 0x01, 0x04, // DescriptionKey
+ 0x6b, 0x65, 0x79, 0x31, // "key1"
+ 0xfd, 0x02, 0x02, 0x04, // DescriptionValue
+ 0x76, 0x61, 0x6c, 0x31, // "val1"
+ 0xfd, 0x02, 0x00, 0x10, // DescriptionEntry
+ 0xfd, 0x02, 0x01, 0x04, // DescriptionKey
+ 0x6b, 0x65, 0x79, 0x32, // "key2"
+ 0xfd, 0x02, 0x02, 0x04, // DescriptionValue
+ 0x76, 0x61, 0x6c, 0x32, // "val2"
+};
+
+const std::string text = "((key1:val1), (key2:val2))";
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ AdditionalDescription aDescription;
+
+ aDescription.set("key2", "val2");
+ aDescription.set("key1", "val1");
+
+ BOOST_REQUIRE_NO_THROW(aDescription.get("key1"));
+ BOOST_REQUIRE_NO_THROW(aDescription.get("key2"));
+ BOOST_REQUIRE_THROW(aDescription.get("key3"), AdditionalDescription::Error);
+
+ BOOST_CHECK_EQUAL(aDescription.has("key1"), true);
+ BOOST_CHECK_EQUAL(aDescription.has("key2"), true);
+ BOOST_CHECK_EQUAL(aDescription.has("key3"), false);
+
+ auto val1 = aDescription.get("key1");
+ auto val2 = aDescription.get("key2");
+
+ BOOST_CHECK_EQUAL(val1, "val1");
+ BOOST_CHECK_EQUAL(val2, "val2");
+
+ auto it = aDescription.begin();
+ BOOST_CHECK_EQUAL(it->second, "val1");
+ it++;
+ BOOST_CHECK_EQUAL(it->second, "val2");
+ it++;
+ BOOST_CHECK(it == aDescription.end());
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(aDescription.wireEncode().wire(),
+ aDescription.wireEncode().wire() + aDescription.wireEncode().size(),
+ description,
+ description + sizeof(description));
+
+ BOOST_REQUIRE_NO_THROW(AdditionalDescription(Block(description, sizeof(description))));
+ AdditionalDescription aDescription2(Block(description, sizeof(description)));
+
+ BOOST_CHECK_EQUAL(aDescription2, aDescription);
+
+ AdditionalDescription aDescription3;
+ aDescription3.set("key3", "val3");
+ aDescription3.set("key2", "val2");
+
+ BOOST_CHECK_NE(aDescription2, aDescription3);
+
+ std::ostringstream os;
+ os << aDescription;
+ BOOST_CHECK_EQUAL(os.str(), text);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestAdditionalDescription
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/certificate-bundle-fetcher.t.cpp b/tests/unit/security/v2/certificate-bundle-fetcher.t.cpp
new file mode 100644
index 0000000..d2e0565
--- /dev/null
+++ b/tests/unit/security/v2/certificate-bundle-fetcher.t.cpp
@@ -0,0 +1,191 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/certificate-bundle-fetcher.hpp"
+#include "security/v2/validation-policy-simple-hierarchy.hpp"
+#include "util/regex/regex-pattern-list-matcher.hpp"
+#include "lp/nack.hpp"
+
+#include "boost-test.hpp"
+#include "validator-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(TestCertificateBundleFetcher)
+
+class CertificateBundleFetcherWrapper : public CertificateBundleFetcher
+{
+public:
+ CertificateBundleFetcherWrapper(Face& face)
+ : CertificateBundleFetcher(make_unique<CertificateFetcherFromNetwork>(face), face)
+ {
+ }
+};
+
+class Bundle
+{
+};
+
+class Cert
+{
+};
+
+class Timeout
+{
+};
+
+class Nack
+{
+};
+
+template<class Response>
+class CertificateBundleFetcherFixture : public HierarchicalValidatorFixture<ValidationPolicySimpleHierarchy,
+ CertificateBundleFetcherWrapper>
+{
+public:
+ CertificateBundleFetcherFixture()
+ : data("/Security/V2/ValidatorFixture/Sub1/Sub3/Data")
+ {
+ subSubIdentity = addSubCertificate("/Security/V2/ValidatorFixture/Sub1/Sub3", subIdentity);
+ cache.insert(subSubIdentity.getDefaultKey().getDefaultCertificate());
+
+ m_keyChain.sign(data, signingByIdentity(subSubIdentity));
+ bundleRegexMatcher = make_shared<RegexPatternListMatcher>("<>*<_BUNDLE><>*", nullptr);
+ processInterest = [this] (const Interest& interest) {
+ // check if the interest is for Bundle or individual certificates
+ if (bundleRegexMatcher->match(interest.getName(), 0, interest.getName().size())) {
+ makeResponse(interest);
+ }
+ else {
+ auto cert = cache.find(interest);
+ if (cert == nullptr) {
+ return;
+ }
+ face.receive(*cert);
+ }
+ };
+ }
+
+ void
+ makeResponse(const Interest& interest);
+
+public:
+ Data data;
+ Identity subSubIdentity;
+ shared_ptr<RegexPatternListMatcher> bundleRegexMatcher;
+};
+
+template<>
+void
+CertificateBundleFetcherFixture<Bundle>::makeResponse(const Interest& interest)
+{
+ Block certList = Block(tlv::Content);
+ Name bundleName(interest.getName());
+
+ if (!bundleName.get(-1).isSegment() || bundleName.get(-1).toSegment() == 0) {
+ Block subSubCert = subSubIdentity.getDefaultKey().getDefaultCertificate().wireEncode();
+ certList.push_back(subSubCert);
+
+ if (!bundleName.get(-1).isSegment()) {
+ bundleName
+ .appendVersion()
+ .appendSegment(0);
+ }
+ }
+ else {
+ Block subCert = subIdentity.getDefaultKey().getDefaultCertificate().wireEncode();
+ Block anchor = identity.getDefaultKey().getDefaultCertificate().wireEncode();
+ certList.push_back(subCert);
+ certList.push_back(anchor);
+ }
+
+ shared_ptr<Data> certBundle = make_shared<Data>();
+ certBundle->setName(bundleName);
+ certBundle->setFreshnessPeriod(100_s);
+ certBundle->setContent(certList);
+ certBundle->setFinalBlock(name::Component::fromSegment(1));
+
+ m_keyChain.sign(*certBundle, signingWithSha256());
+
+ face.receive(*certBundle);
+}
+
+template<>
+void
+CertificateBundleFetcherFixture<Timeout>::makeResponse(const Interest& interest)
+{
+ this->advanceClocks(200_s);
+}
+
+template<>
+void
+CertificateBundleFetcherFixture<Nack>::makeResponse(const Interest& interest)
+{
+ lp::Nack nack(interest);
+ nack.setHeader(lp::NackHeader().setReason(lp::NackReason::NO_ROUTE));
+ face.receive(nack);
+}
+
+BOOST_FIXTURE_TEST_CASE(ValidateSuccessWithBundle, CertificateBundleFetcherFixture<Bundle>)
+{
+ VALIDATE_SUCCESS(this->data, "Should get accepted, as interest brings the bundle segments");
+ BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 2); // produced bundle has 2 segments
+
+ for (const auto& sentInterest : this->face.sentInterests) {
+ BOOST_CHECK(this->bundleRegexMatcher->match(sentInterest.getName(), 0, sentInterest.getName().size()));
+ }
+}
+
+using SuccessWithoutBundle = boost::mpl::vector<Nack, Timeout>;
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateSuccessWithoutBundle, T, SuccessWithoutBundle, CertificateBundleFetcherFixture<T>)
+{
+ VALIDATE_SUCCESS(this->data, "Should get accepted, as interest brings the certs");
+ BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 4); // since interest for Bundle fails, each cert is retrieved
+
+ bool toggle = true;
+ for (const auto& sentInterest : this->face.sentInterests) {
+ if (toggle) {
+ // every alternate interest is going to be that of a bundle
+ BOOST_CHECK(this->bundleRegexMatcher->match(sentInterest.getName(), 0, sentInterest.getName().size()));
+ }
+ else {
+ BOOST_CHECK(!this->bundleRegexMatcher->match(sentInterest.getName(), 0, sentInterest.getName().size()));
+ }
+ toggle = !toggle;
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCertificateBundleFetcher
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/certificate-cache.t.cpp b/tests/unit/security/v2/certificate-cache.t.cpp
new file mode 100644
index 0000000..4ae3505
--- /dev/null
+++ b/tests/unit/security/v2/certificate-cache.t.cpp
@@ -0,0 +1,108 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/certificate-cache.hpp"
+
+#include "../../identity-management-time-fixture.hpp"
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+
+class CertificateCacheFixture : public ndn::tests::IdentityManagementTimeFixture
+{
+public:
+ CertificateCacheFixture()
+ : certCache(10_s)
+ {
+ identity = addIdentity("/TestCertificateCache/");
+ cert = identity.getDefaultKey().getDefaultCertificate();
+ }
+
+public:
+ CertificateCache certCache;
+ Identity identity;
+ Certificate cert;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestCertificateCache, CertificateCacheFixture)
+
+BOOST_AUTO_TEST_CASE(RemovalTime)
+{
+ // Cache lifetime is capped to 10 seconds during cache construction
+
+ BOOST_CHECK_NO_THROW(certCache.insert(cert));
+ BOOST_CHECK(certCache.find(cert.getName()) != nullptr);
+
+ advanceClocks(11_s, 1);
+ BOOST_CHECK(certCache.find(cert.getName()) == nullptr);
+
+ BOOST_CHECK_NO_THROW(certCache.insert(cert));
+ BOOST_CHECK(certCache.find(cert.getName()) != nullptr);
+
+ advanceClocks(5_s);
+ BOOST_CHECK(certCache.find(cert.getName()) != nullptr);
+
+ advanceClocks(15_s);
+ BOOST_CHECK(certCache.find(cert.getName()) == nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(FindByInterest)
+{
+ BOOST_CHECK_NO_THROW(certCache.insert(cert));
+
+ // Find by interest
+ BOOST_CHECK(certCache.find(Interest(cert.getIdentity())) != nullptr);
+ BOOST_CHECK(certCache.find(Interest(cert.getKeyName())) != nullptr);
+ BOOST_CHECK(certCache.find(Interest(Name(cert.getName()).appendVersion())) == nullptr);
+
+ advanceClocks(12_s);
+ BOOST_CHECK(certCache.find(Interest(cert.getIdentity())) == nullptr);
+
+ Certificate cert3 = addCertificate(identity.getDefaultKey(), "3");
+ Certificate cert4 = addCertificate(identity.getDefaultKey(), "4");
+ Certificate cert5 = addCertificate(identity.getDefaultKey(), "5");
+
+ certCache.insert(cert3);
+ certCache.insert(cert4);
+ certCache.insert(cert5);
+
+ Interest interest4(cert3.getKeyName());
+ interest4.setExclude(Exclude().excludeOne(cert3.getName().at(Certificate::ISSUER_ID_OFFSET)));
+ BOOST_CHECK(certCache.find(interest4) != nullptr);
+ BOOST_CHECK_NE(certCache.find(interest4)->getName(), cert3.getName());
+
+ // TODO cover more cases with different interests
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCertificateCache
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/certificate-fetcher-direct-fetch.t.cpp b/tests/unit/security/v2/certificate-fetcher-direct-fetch.t.cpp
new file mode 100644
index 0000000..68fe6b1
--- /dev/null
+++ b/tests/unit/security/v2/certificate-fetcher-direct-fetch.t.cpp
@@ -0,0 +1,205 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/certificate-fetcher-direct-fetch.hpp"
+#include "security/v2/validation-policy-simple-hierarchy.hpp"
+#include "lp/nack.hpp"
+#include "lp/tags.hpp"
+
+#include "boost-test.hpp"
+#include "validator-fixture.hpp"
+
+#include <boost/range/adaptor/strided.hpp>
+#include <boost/range/adaptor/sliced.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(TestCertificateFetcherDirectFetch)
+
+class Cert
+{
+};
+
+class Timeout
+{
+};
+
+class Nack
+{
+};
+
+template<class Response>
+class CertificateFetcherDirectFetchFixture : public HierarchicalValidatorFixture<ValidationPolicySimpleHierarchy,
+ CertificateFetcherDirectFetch>
+{
+public:
+ CertificateFetcherDirectFetchFixture()
+ : data("/Security/V2/ValidatorFixture/Sub1/Sub3/Data")
+ , interest("/Security/V2/ValidatorFixture/Sub1/Sub3/Interest")
+ , interestNoTag("/Security/V2/ValidatorFixture/Sub1/Sub3/Interest2")
+ {
+ Identity subSubIdentity = addSubCertificate("/Security/V2/ValidatorFixture/Sub1/Sub3", subIdentity);
+ cache.insert(subSubIdentity.getDefaultKey().getDefaultCertificate());
+
+ m_keyChain.sign(data, signingByIdentity(subSubIdentity));
+ m_keyChain.sign(interest, signingByIdentity(subSubIdentity));
+ m_keyChain.sign(interestNoTag, signingByIdentity(subSubIdentity));
+
+ data.setTag(make_shared<lp::IncomingFaceIdTag>(123));
+ interest.setTag(make_shared<lp::IncomingFaceIdTag>(123));
+
+ processInterest = [this] (const Interest& interest) {
+ auto nextHopFaceIdTag = interest.template getTag<lp::NextHopFaceIdTag>();
+ if (nextHopFaceIdTag == nullptr) {
+ makeResponse(interest); // respond only to the "infrastructure" interest
+ }
+ };
+ }
+
+ void
+ makeResponse(const Interest& interest);
+
+public:
+ Data data;
+ Interest interest;
+ Interest interestNoTag;
+};
+
+template<>
+void
+CertificateFetcherDirectFetchFixture<Cert>::makeResponse(const Interest& interest)
+{
+ auto cert = cache.find(interest);
+ if (cert == nullptr) {
+ return;
+ }
+ face.receive(*cert);
+}
+
+template<>
+void
+CertificateFetcherDirectFetchFixture<Timeout>::makeResponse(const Interest& interest)
+{
+ // do nothing
+}
+
+template<>
+void
+CertificateFetcherDirectFetchFixture<Nack>::makeResponse(const Interest& interest)
+{
+ lp::Nack nack(interest);
+ nack.setHeader(lp::NackHeader().setReason(lp::NackReason::NO_ROUTE));
+ face.receive(nack);
+}
+
+using Failures = boost::mpl::vector<Timeout, Nack>;
+
+BOOST_FIXTURE_TEST_CASE(ValidateSuccessData, CertificateFetcherDirectFetchFixture<Cert>)
+{
+ VALIDATE_SUCCESS(this->data, "Should get accepted, normal and/or direct interests bring certs");
+ BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 4);
+
+ // odd interests
+ for (const auto& sentInterest : this->face.sentInterests | boost::adaptors::strided(2)) {
+ BOOST_CHECK(sentInterest.template getTag<lp::NextHopFaceIdTag>() != nullptr);
+ }
+
+ // even interests
+ for (const auto& sentInterest : this->face.sentInterests |
+ boost::adaptors::sliced(1, this->face.sentInterests.size()) |
+ boost::adaptors::strided(2)) {
+ BOOST_CHECK(sentInterest.template getTag<lp::NextHopFaceIdTag>() == nullptr);
+ }
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateFailureData, T, Failures, CertificateFetcherDirectFetchFixture<T>)
+{
+ VALIDATE_FAILURE(this->data, "Should fail, as all interests either NACKed or timeout");
+ // Direct fetcher sends two interests each time - to network and face
+ // 3 retries on nack or timeout (2 * (1 + 3) = 4)
+ BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 8);
+
+ // odd interests
+ for (const auto& sentInterest : this->face.sentInterests | boost::adaptors::strided(2)) {
+ BOOST_CHECK(sentInterest.template getTag<lp::NextHopFaceIdTag>() != nullptr);
+ }
+
+ // even interests
+ for (const auto& sentInterest : this->face.sentInterests |
+ boost::adaptors::sliced(1, this->face.sentInterests.size()) |
+ boost::adaptors::strided(2)) {
+ BOOST_CHECK(sentInterest.template getTag<lp::NextHopFaceIdTag>() == nullptr);
+ }
+}
+
+BOOST_FIXTURE_TEST_CASE(ValidateSuccessInterest, CertificateFetcherDirectFetchFixture<Cert>)
+{
+ VALIDATE_SUCCESS(this->interest, "Should get accepted, normal and/or direct interests bring certs");
+ BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 4);
+
+ // odd interests
+ for (const auto& sentInterest : this->face.sentInterests | boost::adaptors::strided(2)) {
+ BOOST_CHECK(sentInterest.template getTag<lp::NextHopFaceIdTag>() != nullptr);
+ }
+
+ // even interests
+ for (const auto& sentInterest : this->face.sentInterests |
+ boost::adaptors::sliced(1, this->face.sentInterests.size()) |
+ boost::adaptors::strided(2)) {
+ BOOST_CHECK(sentInterest.template getTag<lp::NextHopFaceIdTag>() == nullptr);
+ }
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateFailureInterest, T, Failures, CertificateFetcherDirectFetchFixture<T>)
+{
+ VALIDATE_FAILURE(this->interest, "Should fail, as all interests either NACKed or timeout");
+ // Direct fetcher sends two interests each time - to network and face
+ // 3 retries on nack or timeout (2 * (1 + 3) = 4)
+ BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 8);
+
+ // odd interests
+ for (const auto& sentInterest : this->face.sentInterests | boost::adaptors::strided(2)) {
+ BOOST_CHECK(sentInterest.template getTag<lp::NextHopFaceIdTag>() != nullptr);
+ }
+
+ // even interests
+ for (const auto& sentInterest : this->face.sentInterests |
+ boost::adaptors::sliced(1, this->face.sentInterests.size()) |
+ boost::adaptors::strided(2)) {
+ BOOST_CHECK(sentInterest.template getTag<lp::NextHopFaceIdTag>() == nullptr);
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCertificateFetcherDirectFetch
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/certificate-fetcher-from-network.t.cpp b/tests/unit/security/v2/certificate-fetcher-from-network.t.cpp
new file mode 100644
index 0000000..5a6b395
--- /dev/null
+++ b/tests/unit/security/v2/certificate-fetcher-from-network.t.cpp
@@ -0,0 +1,140 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/certificate-fetcher-from-network.hpp"
+#include "security/v2/validation-policy-simple-hierarchy.hpp"
+#include "lp/nack.hpp"
+
+#include "boost-test.hpp"
+#include "validator-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(TestCertificateFetcherFromNetwork)
+
+class Cert
+{
+};
+
+class Timeout
+{
+};
+
+class Nack
+{
+};
+
+template<class Response>
+class CertificateFetcherFromNetworkFixture : public HierarchicalValidatorFixture<ValidationPolicySimpleHierarchy,
+ CertificateFetcherFromNetwork>
+{
+public:
+ CertificateFetcherFromNetworkFixture()
+ : data("/Security/V2/ValidatorFixture/Sub1/Sub3/Data")
+ , interest("/Security/V2/ValidatorFixture/Sub1/Sub3/Interest")
+ {
+ Identity subSubIdentity = addSubCertificate("/Security/V2/ValidatorFixture/Sub1/Sub3", subIdentity);
+ cache.insert(subSubIdentity.getDefaultKey().getDefaultCertificate());
+
+ m_keyChain.sign(data, signingByIdentity(subSubIdentity));
+ m_keyChain.sign(interest, signingByIdentity(subSubIdentity));
+
+ processInterest = bind(&CertificateFetcherFromNetworkFixture<Response>::makeResponse, this, _1);
+ }
+
+ void
+ makeResponse(const Interest& interest);
+
+public:
+ Data data;
+ Interest interest;
+};
+
+template<>
+void
+CertificateFetcherFromNetworkFixture<Cert>::makeResponse(const Interest& interest)
+{
+ auto cert = cache.find(interest);
+ if (cert == nullptr) {
+ return;
+ }
+ face.receive(*cert);
+}
+
+template<>
+void
+CertificateFetcherFromNetworkFixture<Timeout>::makeResponse(const Interest& interest)
+{
+ // do nothing
+}
+
+template<>
+void
+CertificateFetcherFromNetworkFixture<Nack>::makeResponse(const Interest& interest)
+{
+ lp::Nack nack(interest);
+ nack.setHeader(lp::NackHeader().setReason(lp::NackReason::NO_ROUTE));
+ face.receive(nack);
+}
+
+using Failures = boost::mpl::vector<Timeout, Nack>;
+
+BOOST_FIXTURE_TEST_CASE(ValidateSuccess, CertificateFetcherFromNetworkFixture<Cert>)
+{
+ VALIDATE_SUCCESS(this->data, "Should get accepted, as normal interests bring cert");
+ BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 2);
+ this->face.sentInterests.clear();
+
+ this->advanceClocks(1_h, 2); // expire validator caches
+
+ VALIDATE_SUCCESS(this->interest, "Should get accepted, as interests bring certs");
+ BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 2);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateFailure, T, Failures, CertificateFetcherFromNetworkFixture<T>)
+{
+ VALIDATE_FAILURE(this->data, "Should fail, as interests don't bring data");
+ // first interest + 3 retries
+ BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 4);
+
+ this->face.sentInterests.clear();
+
+ this->advanceClocks(1_h, 2); // expire validator caches
+
+ VALIDATE_FAILURE(this->interest, "Should fail, as interests don't bring data");
+ BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 4);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCertificateFetcherFromNetwork
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/certificate-fetcher-offline.t.cpp b/tests/unit/security/v2/certificate-fetcher-offline.t.cpp
new file mode 100644
index 0000000..102a308
--- /dev/null
+++ b/tests/unit/security/v2/certificate-fetcher-offline.t.cpp
@@ -0,0 +1,75 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/certificate-fetcher-offline.hpp"
+#include "security/v2/validation-policy-simple-hierarchy.hpp"
+
+#include "boost-test.hpp"
+#include "validator-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+
+class CertificateFetcherOfflineWrapper : public CertificateFetcherOffline
+{
+public:
+ CertificateFetcherOfflineWrapper(Face&)
+ {
+ }
+};
+
+using CertificateFetcherOfflineFixture = HierarchicalValidatorFixture<ValidationPolicySimpleHierarchy,
+ CertificateFetcherOfflineWrapper>;
+
+BOOST_FIXTURE_TEST_SUITE(TestCertificateFetcherOffline, CertificateFetcherOfflineFixture)
+
+typedef boost::mpl::vector<Interest, Data> Packets;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(Validate, Packet, Packets)
+{
+ Packet unsignedPacket("/Security/V2/ValidatorFixture/Sub1/Packet");
+
+ Packet packet = unsignedPacket;
+ m_keyChain.sign(packet, signingByIdentity(subIdentity));
+ VALIDATE_FAILURE(packet, "Should fail, as no cert should be requested");
+ BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 0);
+
+ packet = unsignedPacket;
+ m_keyChain.sign(packet, signingByIdentity(identity));
+ VALIDATE_SUCCESS(packet, "Should succeed, as signed by trust anchor");
+ BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCertificateFetcherOffline
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/certificate.t.cpp b/tests/unit/security/v2/certificate.t.cpp
new file mode 100644
index 0000000..e677aed
--- /dev/null
+++ b/tests/unit/security/v2/certificate.t.cpp
@@ -0,0 +1,291 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ *
+ * @author Zhiyi Zhang <dreamerbarrychang@gmail.com>
+ */
+
+#include "security/v2/certificate.hpp"
+
+#include "boost-test.hpp"
+#include "../../unit-test-time-fixture.hpp"
+
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_FIXTURE_TEST_SUITE(TestCertificate, UnitTestTimeFixture)
+
+const uint8_t PUBLIC_KEY[] = {
+ 0x30, 0x81, 0x9d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x81, 0x8b, 0x00, 0x30, 0x81, 0x87, 0x02, 0x81, 0x81, 0x00, 0x9e,
+ 0x06, 0x3e, 0x47, 0x85, 0xb2, 0x34, 0x37, 0xaa, 0x85, 0x47, 0xac, 0x03, 0x24, 0x83, 0xb5,
+ 0x9c, 0xa8, 0x05, 0x3a, 0x24, 0x1e, 0xeb, 0x89, 0x01, 0xbb, 0xe9, 0x9b, 0xb2, 0xc3, 0x22,
+ 0xac, 0x68, 0xe3, 0xf0, 0x6c, 0x02, 0xce, 0x68, 0xa6, 0xc4, 0xd0, 0xa7, 0x06, 0x90, 0x9c,
+ 0xaa, 0x1b, 0x08, 0x1d, 0x8b, 0x43, 0x9a, 0x33, 0x67, 0x44, 0x6d, 0x21, 0xa3, 0x1b, 0x88,
+ 0x9a, 0x97, 0x5e, 0x59, 0xc4, 0x15, 0x0b, 0xd9, 0x2c, 0xbd, 0x51, 0x07, 0x61, 0x82, 0xad,
+ 0xc1, 0xb8, 0xd7, 0xbf, 0x9b, 0xcf, 0x7d, 0x24, 0xc2, 0x63, 0xf3, 0x97, 0x17, 0xeb, 0xfe,
+ 0x62, 0x25, 0xba, 0x5b, 0x4d, 0x8a, 0xc2, 0x7a, 0xbd, 0x43, 0x8a, 0x8f, 0xb8, 0xf2, 0xf1,
+ 0xc5, 0x6a, 0x30, 0xd3, 0x50, 0x8c, 0xc8, 0x9a, 0xdf, 0xef, 0xed, 0x35, 0xe7, 0x7a, 0x62,
+ 0xea, 0x76, 0x7c, 0xbb, 0x08, 0x26, 0xc7, 0x02, 0x01, 0x11
+};
+
+const uint8_t SIG_INFO[] = {
+ 0x16, 0x55, 0x1B, 0x01, 0x01, 0x1C, 0x26, 0x07, 0x24, 0x08, 0x03, 0x6E, 0x64, 0x6E, 0x08, 0x05,
+ 0x73, 0x69, 0x74, 0x65, 0x31, 0x08, 0x11, 0x6B, 0x73, 0x6B, 0x2D, 0x32, 0x35, 0x31, 0x36, 0x34,
+ 0x32, 0x35, 0x33, 0x37, 0x37, 0x30, 0x39, 0x34, 0x08, 0x03, 0x4B, 0x45, 0x59, 0xFD, 0x00, 0xFD,
+ 0x26, 0xFD, 0x00, 0xFE, 0x0F, 0x32, 0x30, 0x31, 0x35, 0x30, 0x38, 0x31, 0x34, 0x54, 0x32, 0x32,
+ 0x33, 0x37, 0x33, 0x39, 0xFD, 0x00, 0xFF, 0x0F, 0x32, 0x30, 0x31, 0x35, 0x30, 0x38, 0x31, 0x38,
+ 0x54, 0x32, 0x32, 0x33, 0x37, 0x33, 0x38
+};
+
+const uint8_t SIG_VALUE[] = {
+ 0x17, 0x80, // SignatureValue
+ 0x2f, 0xd6, 0xf1, 0x6e, 0x80, 0x6f, 0x10, 0xbe, 0xb1, 0x6f, 0x3e, 0x31, 0xec,
+ 0xe3, 0xb9, 0xea, 0x83, 0x30, 0x40, 0x03, 0xfc, 0xa0, 0x13, 0xd9, 0xb3, 0xc6,
+ 0x25, 0x16, 0x2d, 0xa6, 0x58, 0x41, 0x69, 0x62, 0x56, 0xd8, 0xb3, 0x6a, 0x38,
+ 0x76, 0x56, 0xea, 0x61, 0xb2, 0x32, 0x70, 0x1c, 0xb6, 0x4d, 0x10, 0x1d, 0xdc,
+ 0x92, 0x8e, 0x52, 0xa5, 0x8a, 0x1d, 0xd9, 0x96, 0x5e, 0xc0, 0x62, 0x0b, 0xcf,
+ 0x3a, 0x9d, 0x7f, 0xca, 0xbe, 0xa1, 0x41, 0x71, 0x85, 0x7a, 0x8b, 0x5d, 0xa9,
+ 0x64, 0xd6, 0x66, 0xb4, 0xe9, 0x8d, 0x0c, 0x28, 0x43, 0xee, 0xa6, 0x64, 0xe8,
+ 0x55, 0xf6, 0x1c, 0x19, 0x0b, 0xef, 0x99, 0x25, 0x1e, 0xdc, 0x78, 0xb3, 0xa7,
+ 0xaa, 0x0d, 0x14, 0x58, 0x30, 0xe5, 0x37, 0x6a, 0x6d, 0xdb, 0x56, 0xac, 0xa3,
+ 0xfc, 0x90, 0x7a, 0xb8, 0x66, 0x9c, 0x0e, 0xf6, 0xb7, 0x64, 0xd1
+};
+
+const uint8_t CERT[] = {
+ 0x06, 0xFD, 0x01, 0xBB, // Data
+ 0x07, 0x33, // Name /ndn/site1/KEY/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B
+ 0x08, 0x03, 0x6E, 0x64, 0x6E,
+ 0x08, 0x05, 0x73, 0x69, 0x74, 0x65, 0x31,
+ 0x08, 0x03, 0x4B, 0x45, 0x59,
+ 0x08, 0x11,
+ 0x6B, 0x73, 0x6B, 0x2D, 0x31, 0x34, 0x31, 0x36, 0x34, 0x32, 0x35, 0x33, 0x37, 0x37, 0x30, 0x39,
+ 0x34,
+ 0x08, 0x04, 0x30, 0x31, 0x32, 0x33,
+ 0x08, 0x07, 0xFD, 0x00, 0x00, 0x01, 0x49, 0xC9, 0x8B,
+ 0x14, 0x09, // MetaInfo
+ 0x18, 0x01, 0x02, // ContentType = Key
+ 0x19, 0x04, 0x00, 0x36, 0xEE, 0x80, // FreshnessPeriod = 3600000 ms
+ 0x15, 0xA0, // Content
+ 0x30, 0x81, 0x9D, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x81, 0x8B, 0x00, 0x30, 0x81, 0x87, 0x02, 0x81, 0x81, 0x00, 0x9E, 0x06, 0x3E,
+ 0x47, 0x85, 0xB2, 0x34, 0x37, 0xAA, 0x85, 0x47, 0xAC, 0x03, 0x24, 0x83, 0xB5, 0x9C, 0xA8, 0x05,
+ 0x3A, 0x24, 0x1E, 0xEB, 0x89, 0x01, 0xBB, 0xE9, 0x9B, 0xB2, 0xC3, 0x22, 0xAC, 0x68, 0xE3, 0xF0,
+ 0x6C, 0x02, 0xCE, 0x68, 0xA6, 0xC4, 0xD0, 0xA7, 0x06, 0x90, 0x9C, 0xAA, 0x1B, 0x08, 0x1D, 0x8B,
+ 0x43, 0x9A, 0x33, 0x67, 0x44, 0x6D, 0x21, 0xA3, 0x1B, 0x88, 0x9A, 0x97, 0x5E, 0x59, 0xC4, 0x15,
+ 0x0B, 0xD9, 0x2C, 0xBD, 0x51, 0x07, 0x61, 0x82, 0xAD, 0xC1, 0xB8, 0xD7, 0xBF, 0x9B, 0xCF, 0x7D,
+ 0x24, 0xC2, 0x63, 0xF3, 0x97, 0x17, 0xEB, 0xFE, 0x62, 0x25, 0xBA, 0x5B, 0x4D, 0x8A, 0xC2, 0x7A,
+ 0xBD, 0x43, 0x8A, 0x8F, 0xB8, 0xF2, 0xF1, 0xC5, 0x6A, 0x30, 0xD3, 0x50, 0x8C, 0xC8, 0x9A, 0xDF,
+ 0xEF, 0xED, 0x35, 0xE7, 0x7A, 0x62, 0xEA, 0x76, 0x7C, 0xBB, 0x08, 0x26, 0xC7, 0x02, 0x01, 0x11,
+ 0x16, 0x55, // SignatureInfo
+ 0x1B, 0x01, 0x01, // SignatureType
+ 0x1C, 0x26, // KeyLocator: /ndn/site1/KEY/ksk-2516425377094
+ 0x07, 0x24,
+ 0x08, 0x03, 0x6E, 0x64, 0x6E,
+ 0x08, 0x05, 0x73, 0x69, 0x74, 0x65, 0x31,
+ 0x08, 0x03, 0x4B, 0x45, 0x59,
+ 0x08, 0x11,
+ 0x6B, 0x73, 0x6B, 0x2D, 0x32, 0x35, 0x31, 0x36, 0x34, 0x32, 0x35, 0x33, 0x37, 0x37, 0x30, 0x39,
+ 0x34,
+ 0xFD, 0x00, 0xFD, 0x26, // ValidityPeriod: (20150814T223739, 20150818T223738)
+ 0xFD, 0x00, 0xFE, 0x0F,
+ 0x32, 0x30, 0x31, 0x35, 0x30, 0x38, 0x31, 0x34, 0x54, 0x32, 0x32, 0x33, 0x37, 0x33, 0x39,
+ 0xFD, 0x00, 0xFF, 0x0F,
+ 0x32, 0x30, 0x31, 0x35, 0x30, 0x38, 0x31, 0x38, 0x54, 0x32, 0x32, 0x33, 0x37, 0x33, 0x38,
+ 0x17, 0x80, // SignatureValue
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+Signature
+generateFakeSignature()
+{
+ Block block1(SIG_INFO, sizeof(SIG_INFO));
+ SignatureInfo signatureInfo(block1);
+
+ Name keyLocatorName("/ndn/site1/KEY/ksk-2516425377094");
+ KeyLocator keyLocator(keyLocatorName);
+ signatureInfo.setKeyLocator(keyLocator);
+
+ ValidityPeriod period(time::fromIsoString("20141111T050000"), time::fromIsoString("20141111T060000"));
+ signatureInfo.setValidityPeriod(period);
+
+ Signature signature(signatureInfo);
+ Block block2(SIG_VALUE, sizeof(SIG_VALUE));
+ signature.setValue(block2);
+
+ return signature;
+}
+
+BOOST_AUTO_TEST_CASE(Construction)
+{
+ Block block(CERT, sizeof(CERT));
+ Certificate certificate(block);
+
+ BOOST_CHECK_EQUAL(certificate.getName(), "/ndn/site1/KEY/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B");
+ BOOST_CHECK_EQUAL(certificate.getKeyName(), "/ndn/site1/KEY/ksk-1416425377094");
+ BOOST_CHECK_EQUAL(certificate.getIdentity(), "/ndn/site1");
+ BOOST_CHECK_EQUAL(certificate.getIssuerId(), name::Component("0123"));
+ BOOST_CHECK_EQUAL(certificate.getKeyId(), name::Component("ksk-1416425377094"));
+ BOOST_CHECK_EQUAL(certificate.getSignature().getKeyLocator().getName(), "/ndn/site1/KEY/ksk-2516425377094");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(certificate.getValidityPeriod()), "(20150814T223739, 20150818T223738)");
+
+ BOOST_CHECK_THROW(certificate.getExtension(12345), ndn::SignatureInfo::Error);
+ BOOST_CHECK_NO_THROW(certificate.getPublicKey());
+
+ Data data(block);
+ Certificate certificate2(std::move(data));
+ BOOST_CHECK_EQUAL(certificate, certificate2);
+}
+
+BOOST_AUTO_TEST_CASE(Setters)
+{
+ Certificate certificate;
+ certificate.setName("/ndn/site1/KEY/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B");
+ certificate.setFreshnessPeriod(1_h);
+ certificate.setContent(PUBLIC_KEY, sizeof(PUBLIC_KEY));
+ certificate.setSignature(generateFakeSignature());
+
+ BOOST_CHECK_EQUAL(certificate.getName(), "/ndn/site1/KEY/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B");
+ BOOST_CHECK_EQUAL(certificate.getKeyName(), "/ndn/site1/KEY/ksk-1416425377094");
+ BOOST_CHECK_EQUAL(certificate.getIdentity(), "/ndn/site1");
+ BOOST_CHECK_EQUAL(certificate.getIssuerId(), name::Component("0123"));
+ BOOST_CHECK_EQUAL(certificate.getKeyId(), name::Component("ksk-1416425377094"));
+ BOOST_CHECK_EQUAL(certificate.getSignature().getKeyLocator().getName(), "/ndn/site1/KEY/ksk-2516425377094");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(certificate.getValidityPeriod()), "(20141111T050000, 20141111T060000)");
+
+ BOOST_CHECK_THROW(certificate.getExtension(12345), ndn::SignatureInfo::Error);
+ BOOST_CHECK_NO_THROW(certificate.getPublicKey());
+}
+
+BOOST_AUTO_TEST_CASE(ValidityPeriodChecking)
+{
+ Certificate certificate;
+ certificate.setName("/ndn/site1/KEY/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B");
+ certificate.setFreshnessPeriod(1_h);
+ certificate.setContent(PUBLIC_KEY, sizeof(PUBLIC_KEY));
+ certificate.setSignature(generateFakeSignature());
+
+ BOOST_CHECK_EQUAL(certificate.isValid(), true);
+ BOOST_CHECK_EQUAL(certificate.isValid(time::fromIsoString("20141111T045959")), false);
+ BOOST_CHECK_EQUAL(certificate.isValid(time::fromIsoString("20141111T060001")), false);
+}
+
+// This fixture prepares a well-formed certificate. A test case then modifies one of the
+// fields, and verifies the Certificate class correctly identifies the certificate as
+// malformed.
+class InvalidCertFixture
+{
+public:
+ InvalidCertFixture()
+ {
+ Certificate certBase(Block(CERT, sizeof(CERT)));
+ BOOST_CHECK_NO_THROW((Certificate(certBase)));
+
+ m_certBase = Data(certBase);
+ m_certBase.setSignature(generateFakeSignature());
+
+ BOOST_CHECK_NO_THROW((Certificate(m_certBase)));
+ }
+
+public:
+ Data m_certBase;
+};
+
+BOOST_FIXTURE_TEST_CASE(InvalidName, InvalidCertFixture)
+{
+ Data data(m_certBase);
+ data.setName("/ndn/site1/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B");
+ data.setSignature(generateFakeSignature());
+
+ BOOST_CHECK_THROW((Certificate(data)), Certificate::Error);
+ BOOST_CHECK_THROW((Certificate(std::move(data))), Certificate::Error);
+}
+
+BOOST_FIXTURE_TEST_CASE(InvalidType, InvalidCertFixture)
+{
+ Data data(m_certBase);
+ data.setContentType(tlv::ContentType_Blob);
+ data.setSignature(generateFakeSignature());
+
+ BOOST_CHECK_THROW((Certificate(data)), Certificate::Error);
+ BOOST_CHECK_THROW((Certificate(std::move(data))), Certificate::Error);
+}
+
+BOOST_FIXTURE_TEST_CASE(EmptyContent, InvalidCertFixture)
+{
+ Data data(m_certBase);
+ data.setContent(nullptr, 0);
+ data.setSignature(generateFakeSignature());
+
+ BOOST_CHECK_THROW((Certificate(data)), Certificate::Error);
+ BOOST_CHECK_THROW((Certificate(std::move(data))), Certificate::Error);
+
+ Certificate cert(m_certBase);
+ cert.setContent(nullptr, 0);
+ cert.setSignature(generateFakeSignature());
+ BOOST_CHECK_THROW(cert.getPublicKey(), Certificate::Error);
+}
+
+BOOST_AUTO_TEST_CASE(PrintCertificateInfo)
+{
+ const std::string expectedCertificateInfo = std::string(R"INFO(
+Certificate name:
+ /ndn/site1/KEY/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B
+Validity:
+ NotBefore: 20150814T223739
+ NotAfter: 20150818T223738
+Public key bits:
+ MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCeBj5HhbI0N6qFR6wDJIO1nKgF
+ OiQe64kBu+mbssMirGjj8GwCzmimxNCnBpCcqhsIHYtDmjNnRG0hoxuImpdeWcQV
+ C9ksvVEHYYKtwbjXv5vPfSTCY/OXF+v+YiW6W02Kwnq9Q4qPuPLxxWow01CMyJrf
+ 7+0153pi6nZ8uwgmxwIBEQ==
+Signature Information:
+ Signature Type: SignatureSha256WithRsa
+ Key Locator: Name=/ndn/site1/KEY/ksk-2516425377094
+)INFO").substr(1);
+
+ Certificate certificate(Block(CERT, sizeof(CERT)));
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(certificate), expectedCertificateInfo);
+
+ // @todo Check output formats of other certificates
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCertificate
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/key-chain.t.cpp b/tests/unit/security/v2/key-chain.t.cpp
new file mode 100644
index 0000000..649ef7b
--- /dev/null
+++ b/tests/unit/security/v2/key-chain.t.cpp
@@ -0,0 +1,445 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/key-chain.hpp"
+#include "security/signing-helpers.hpp"
+#include "security/verification-helpers.hpp"
+
+#include "boost-test.hpp"
+#include "identity-management-fixture.hpp"
+#include "../../test-home-env-saver.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_FIXTURE_TEST_SUITE(TestKeyChain, TestHomeEnvSaver)
+
+template<class Path>
+class TestHomeAndPibFixture : public TestHomeFixture<Path>
+{
+public:
+ TestHomeAndPibFixture()
+ {
+ unsetenv("NDN_CLIENT_PIB");
+ unsetenv("NDN_CLIENT_TPM");
+ }
+
+ ~TestHomeAndPibFixture()
+ {
+ try {
+ const_cast<std::string&>(KeyChain::getDefaultPibLocator()).clear();
+ }
+ catch (const KeyChain::Error&) {
+ // ignore
+ }
+
+ try {
+ const_cast<std::string&>(KeyChain::getDefaultTpmLocator()).clear();
+ }
+ catch (const KeyChain::Error&) {
+ // ignore
+ }
+ }
+};
+
+struct PibPathConfigFileHome
+{
+ const std::string PATH = "build/config-file-home/";
+};
+
+BOOST_FIXTURE_TEST_CASE(ConstructorNormalConfig, TestHomeAndPibFixture<PibPathConfigFileHome>)
+{
+ createClientConf({"pib=pib-memory:", "tpm=tpm-memory:"});
+
+ BOOST_REQUIRE_NO_THROW(KeyChain());
+
+ KeyChain keyChain;
+ BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-memory:");
+ BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-memory:");
+ BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-memory:");
+}
+
+struct PibPathConfigFileEmptyHome
+{
+ const std::string PATH = "build/config-file-empty-home/";
+};
+
+BOOST_FIXTURE_TEST_CASE(ConstructorEmptyConfig, TestHomeAndPibFixture<PibPathConfigFileEmptyHome>)
+{
+ createClientConf({"pib=pib-memory:"});
+
+#if defined(NDN_CXX_HAVE_OSX_FRAMEWORKS)
+ std::string oldHOME;
+ if (std::getenv("OLD_HOME"))
+ oldHOME = std::getenv("OLD_HOME");
+
+ std::string HOME;
+ if (std::getenv("HOME"))
+ HOME = std::getenv("HOME");
+
+ if (!oldHOME.empty())
+ setenv("HOME", oldHOME.c_str(), 1);
+ else
+ unsetenv("HOME");
+#endif
+
+ BOOST_REQUIRE_NO_THROW(KeyChain());
+ KeyChain keyChain;
+ BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-memory:");
+
+#if defined(NDN_CXX_HAVE_OSX_FRAMEWORKS)
+ BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-osxkeychain:");
+ BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-osxkeychain:");
+#else
+ BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-file:");
+ BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-file:");
+#endif
+
+#if defined(NDN_CXX_HAVE_OSX_FRAMEWORKS)
+ if (!HOME.empty())
+ setenv("HOME", HOME.c_str(), 1);
+ else
+ unsetenv("HOME");
+
+ if (!oldHOME.empty())
+ setenv("OLD_HOME", oldHOME.c_str(), 1);
+ else
+ unsetenv("OLD_HOME");
+#endif
+}
+
+struct PibPathConfigFileEmpty2Home
+{
+ const std::string PATH = "build/config-file-empty2-home/";
+};
+
+BOOST_FIXTURE_TEST_CASE(ConstructorEmpty2Config, TestHomeAndPibFixture<PibPathConfigFileEmpty2Home>)
+{
+ createClientConf({"tpm=tpm-memory:"});
+
+ BOOST_REQUIRE_NO_THROW(KeyChain());
+
+ KeyChain keyChain;
+ BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-sqlite3:");
+ BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-memory:");
+ BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-memory:");
+}
+
+struct PibPathConfigFileMalformedHome
+{
+ const std::string PATH = "build/config-file-malformed-home/";
+};
+
+BOOST_FIXTURE_TEST_CASE(ConstructorMalConfig, TestHomeAndPibFixture<PibPathConfigFileMalformedHome>)
+{
+ createClientConf({"pib=lord", "tpm=ring"});
+
+ BOOST_REQUIRE_THROW(KeyChain(), KeyChain::Error); // Wrong configuration. Error expected.
+}
+
+struct PibPathConfigFileMalformed2Home
+{
+ const std::string PATH = "build/config-file-malformed2-home/";
+};
+
+BOOST_FIXTURE_TEST_CASE(ConstructorMal2Config, TestHomeAndPibFixture<PibPathConfigFileMalformed2Home>)
+{
+ createClientConf({"pib=pib-sqlite3:%PATH%", "tpm=just-wrong"});
+
+ BOOST_REQUIRE_THROW(KeyChain(), KeyChain::Error); // Wrong configuration. Error expected.
+}
+
+struct PibPathConfigFileNonCanonicalTpm
+{
+ const std::string PATH = "build/config-file-non-canonical-tpm/";
+};
+
+BOOST_FIXTURE_TEST_CASE(ConstructorNonCanonicalTpm, TestHomeAndPibFixture<PibPathConfigFileNonCanonicalTpm>) // Bug 4297
+{
+ createClientConf({"pib=pib-sqlite3:", "tpm=tpm-file"});
+
+ {
+ KeyChain keyChain;
+ keyChain.createIdentity("/test");
+ BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-sqlite3:");
+ BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-file:");
+ }
+
+ {
+ KeyChain keyChain;
+ BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-sqlite3:");
+ BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-file:");
+ BOOST_CHECK(keyChain.getPib().getIdentities().find("/test") != keyChain.getPib().getIdentities().end());
+ }
+}
+
+BOOST_AUTO_TEST_CASE(KeyChainWithCustomTpmAndPib)
+{
+ BOOST_REQUIRE_NO_THROW((KeyChain("pib-memory", "tpm-memory")));
+ BOOST_REQUIRE_NO_THROW((KeyChain("pib-memory:", "tpm-memory:")));
+ BOOST_REQUIRE_NO_THROW((KeyChain("pib-memory:/something", "tpm-memory:/something")));
+
+ KeyChain keyChain("pib-memory", "tpm-memory");
+ BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-memory:");
+ BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-memory:");
+ BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-memory:");
+}
+
+BOOST_FIXTURE_TEST_CASE(Management, IdentityManagementFixture)
+{
+ Name identityName("/test/id");
+ Name identity2Name("/test/id2");
+
+ BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 0);
+ BOOST_REQUIRE_THROW(m_keyChain.getPib().getDefaultIdentity(), Pib::Error);
+
+ // Create identity
+ Identity id = m_keyChain.createIdentity(identityName);
+ BOOST_CHECK(id);
+ BOOST_CHECK(m_keyChain.getPib().getIdentities().find(identityName) != m_keyChain.getPib().getIdentities().end());
+ // The first added identity becomes the default identity
+ BOOST_REQUIRE_NO_THROW(m_keyChain.getPib().getDefaultIdentity());
+ // The default key of the added identity must exist
+ Key key;
+ BOOST_REQUIRE_NO_THROW(key = id.getDefaultKey());
+ // The default certificate of the default key must exist
+ BOOST_REQUIRE_NO_THROW(key.getDefaultCertificate());
+
+ // Delete key
+ Name key1Name = key.getName();
+ BOOST_CHECK_NO_THROW(id.getKey(key1Name));
+ BOOST_CHECK_EQUAL(id.getKeys().size(), 1);
+ m_keyChain.deleteKey(id, key);
+ // The key instance should not be valid any more
+ BOOST_CHECK(!key);
+ BOOST_CHECK_THROW(id.getKey(key1Name), Pib::Error);
+ BOOST_CHECK_EQUAL(id.getKeys().size(), 0);
+
+ // Create another key
+ m_keyChain.createKey(id);
+ // The added key becomes the default key.
+ BOOST_REQUIRE_NO_THROW(id.getDefaultKey());
+ Key key2 = id.getDefaultKey();
+ BOOST_REQUIRE(key2);
+ BOOST_CHECK_NE(key2.getName(), key1Name);
+ BOOST_CHECK_EQUAL(id.getKeys().size(), 1);
+ BOOST_REQUIRE_NO_THROW(key2.getDefaultCertificate());
+
+ // Create the third key
+ Key key3 = m_keyChain.createKey(id);
+ BOOST_CHECK_NE(key3.getName(), key2.getName());
+ // The added key will not be the default key, because the default key already exists
+ BOOST_CHECK_EQUAL(id.getDefaultKey().getName(), key2.getName());
+ BOOST_CHECK_EQUAL(id.getKeys().size(), 2);
+ BOOST_REQUIRE_NO_THROW(key3.getDefaultCertificate());
+
+ // Delete cert
+ BOOST_CHECK_EQUAL(key3.getCertificates().size(), 1);
+ Certificate key3Cert1 = *key3.getCertificates().begin();
+ Name key3CertName = key3Cert1.getName();
+ m_keyChain.deleteCertificate(key3, key3CertName);
+ BOOST_CHECK_EQUAL(key3.getCertificates().size(), 0);
+ BOOST_REQUIRE_THROW(key3.getDefaultCertificate(), Pib::Error);
+
+ // Add cert
+ m_keyChain.addCertificate(key3, key3Cert1);
+ BOOST_CHECK_EQUAL(key3.getCertificates().size(), 1);
+ BOOST_REQUIRE_NO_THROW(key3.getDefaultCertificate());
+ m_keyChain.addCertificate(key3, key3Cert1); // overwriting the cert should work
+ BOOST_CHECK_EQUAL(key3.getCertificates().size(), 1);
+ // Add another cert
+ Certificate key3Cert2 = key3Cert1;
+ Name key3Cert2Name = key3.getName();
+ key3Cert2Name.append("Self");
+ key3Cert2Name.appendVersion();
+ key3Cert2.setName(key3Cert2Name);
+ m_keyChain.addCertificate(key3, key3Cert2);
+ BOOST_CHECK_EQUAL(key3.getCertificates().size(), 2);
+
+ // Default certificate setting
+ BOOST_CHECK_EQUAL(key3.getDefaultCertificate().getName(), key3CertName);
+ m_keyChain.setDefaultCertificate(key3, key3Cert2);
+ BOOST_CHECK_EQUAL(key3.getDefaultCertificate().getName(), key3Cert2Name);
+
+ // Default key setting
+ BOOST_CHECK_EQUAL(id.getDefaultKey().getName(), key2.getName());
+ m_keyChain.setDefaultKey(id, key3);
+ BOOST_CHECK_EQUAL(id.getDefaultKey().getName(), key3.getName());
+
+ // Default identity setting
+ Identity id2 = m_keyChain.createIdentity(identity2Name);
+ BOOST_CHECK_EQUAL(m_keyChain.getPib().getDefaultIdentity().getName(), id.getName());
+ m_keyChain.setDefaultIdentity(id2);
+ BOOST_CHECK_EQUAL(m_keyChain.getPib().getDefaultIdentity().getName(), id2.getName());
+
+ // Delete identity
+ m_keyChain.deleteIdentity(id);
+ // The identity instance should not be valid any more
+ BOOST_CHECK(!id);
+ BOOST_REQUIRE_THROW(m_keyChain.getPib().getIdentity(identityName), Pib::Error);
+ BOOST_CHECK(m_keyChain.getPib().getIdentities().find(identityName) == m_keyChain.getPib().getIdentities().end());
+}
+
+BOOST_FIXTURE_TEST_CASE(GeneralSigningInterface, IdentityManagementFixture)
+{
+ Identity id = addIdentity("/id");
+ Key key = id.getDefaultKey();
+ Certificate cert = key.getDefaultCertificate();
+
+ std::list<SigningInfo> signingInfos = {
+ SigningInfo(),
+
+ SigningInfo(SigningInfo::SIGNER_TYPE_ID, id.getName()),
+ signingByIdentity(id.getName()),
+
+ SigningInfo(id),
+ signingByIdentity(id),
+
+ SigningInfo(SigningInfo::SIGNER_TYPE_KEY, key.getName()),
+ signingByKey(key.getName()),
+
+ SigningInfo(key),
+ signingByKey(key),
+
+ SigningInfo(SigningInfo::SIGNER_TYPE_CERT, cert.getName()),
+ signingByCertificate(cert.getName()),
+ signingByCertificate(cert),
+
+ SigningInfo(SigningInfo::SIGNER_TYPE_SHA256),
+ signingWithSha256()
+ };
+
+ for (const auto& signingInfo : signingInfos) {
+ BOOST_TEST_MESSAGE("SigningInfo: " << signingInfo);
+ Data data("/data");
+ Interest interest("/interest");
+
+ if (signingInfo.getSignerType() == SigningInfo::SIGNER_TYPE_NULL) {
+ m_keyChain.sign(data);
+ m_keyChain.sign(interest);
+ }
+ else {
+ m_keyChain.sign(data, signingInfo);
+ m_keyChain.sign(interest, signingInfo);
+ }
+
+ Signature interestSignature(interest.getName()[-2].blockFromValue(), interest.getName()[-1].blockFromValue());
+
+ if (signingInfo.getSignerType() == SigningInfo::SIGNER_TYPE_SHA256) {
+ BOOST_CHECK_EQUAL(data.getSignature().getType(), tlv::DigestSha256);
+ BOOST_CHECK_EQUAL(interestSignature.getType(), tlv::DigestSha256);
+
+ BOOST_CHECK(verifyDigest(data, DigestAlgorithm::SHA256));
+ BOOST_CHECK(verifyDigest(interest, DigestAlgorithm::SHA256));
+ }
+ else {
+ BOOST_CHECK_EQUAL(data.getSignature().getType(), tlv::SignatureSha256WithEcdsa);
+ BOOST_CHECK_EQUAL(interestSignature.getType(), tlv::SignatureSha256WithEcdsa);
+
+ BOOST_CHECK_EQUAL(data.getSignature().getKeyLocator().getName(), cert.getName().getPrefix(-2));
+ BOOST_CHECK_EQUAL(interestSignature.getKeyLocator().getName(), cert.getName().getPrefix(-2));
+
+ BOOST_CHECK(verifySignature(data, key));
+ BOOST_CHECK(verifySignature(interest, key));
+ }
+ }
+}
+
+BOOST_FIXTURE_TEST_CASE(PublicKeySigningDefaults, IdentityManagementFixture)
+{
+ Data data("/test/data");
+
+ // Identity will be created with generated key and self-signed cert with default parameters
+ BOOST_CHECK_THROW(m_keyChain.sign(data, signingByIdentity("/non-existing/identity")), KeyChain::InvalidSigningInfoError);
+
+ // Create identity with EC key and the corresponding self-signed certificate
+ Identity id = addIdentity("/ndn/test/ec", EcKeyParams());
+ BOOST_CHECK_NO_THROW(m_keyChain.sign(data, signingByIdentity(id.getName())));
+ BOOST_CHECK_EQUAL(data.getSignature().getType(),
+ KeyChain::getSignatureType(EcKeyParams().getKeyType(), DigestAlgorithm::SHA256));
+ BOOST_CHECK(id.getName().isPrefixOf(data.getSignature().getKeyLocator().getName()));
+
+ // Create identity with RSA key and the corresponding self-signed certificate
+ id = addIdentity("/ndn/test/rsa", RsaKeyParams());
+ BOOST_CHECK_NO_THROW(m_keyChain.sign(data, signingByIdentity(id.getName())));
+ BOOST_CHECK_EQUAL(data.getSignature().getType(),
+ KeyChain::getSignatureType(RsaKeyParams().getKeyType(), DigestAlgorithm::SHA256));
+ BOOST_CHECK(id.getName().isPrefixOf(data.getSignature().getKeyLocator().getName()));
+}
+
+BOOST_FIXTURE_TEST_CASE(ExportImport, IdentityManagementFixture)
+{
+ Identity id = addIdentity("/TestKeyChain/ExportIdentity/");
+ Certificate cert = id.getDefaultKey().getDefaultCertificate();
+
+ shared_ptr<SafeBag> exported = m_keyChain.exportSafeBag(cert, "1234", 4);
+ Block block = exported->wireEncode();
+
+ m_keyChain.deleteIdentity(id);
+ BOOST_CHECK_THROW(m_keyChain.exportSafeBag(cert, "1234", 4), KeyChain::Error);
+
+ BOOST_CHECK_EQUAL(m_keyChain.getTpm().hasKey(cert.getKeyName()), false);
+ BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 0);
+
+ SafeBag imported;
+ imported.wireDecode(block);
+ m_keyChain.importSafeBag(imported, "1234", 4);
+ BOOST_CHECK_THROW(m_keyChain.importSafeBag(imported, "1234", 4), KeyChain::Error);
+
+ BOOST_CHECK_EQUAL(m_keyChain.getTpm().hasKey(cert.getKeyName()), true);
+ BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 1);
+ BOOST_REQUIRE_NO_THROW(m_keyChain.getPib().getIdentity(cert.getIdentity()));
+ Identity newId = m_keyChain.getPib().getIdentity(cert.getIdentity());
+ BOOST_CHECK_EQUAL(newId.getKeys().size(), 1);
+ BOOST_REQUIRE_NO_THROW(newId.getKey(cert.getKeyName()));
+ Key newKey = newId.getKey(cert.getKeyName());
+ BOOST_CHECK_EQUAL(newKey.getCertificates().size(), 1);
+ BOOST_REQUIRE_NO_THROW(newKey.getCertificate(cert.getName()));
+
+ m_keyChain.deleteIdentity(newId);
+ BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 0);
+ BOOST_CHECK_EQUAL(m_keyChain.getTpm().hasKey(cert.getKeyName()), false);
+}
+
+BOOST_FIXTURE_TEST_CASE(SelfSignedCertValidity, IdentityManagementFixture)
+{
+ Certificate cert = addIdentity("/Security/V2/TestKeyChain/SelfSignedCertValidity")
+ .getDefaultKey()
+ .getDefaultCertificate();
+ BOOST_CHECK(cert.isValid());
+ BOOST_CHECK(cert.isValid(time::system_clock::now() + 10 * 365_days));
+ BOOST_CHECK_GT(cert.getValidityPeriod().getPeriod().second, time::system_clock::now() + 10 * 365_days);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestKeyChain
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/trust-anchor-container.t.cpp b/tests/unit/security/v2/trust-anchor-container.t.cpp
new file mode 100644
index 0000000..efd6ca3
--- /dev/null
+++ b/tests/unit/security/v2/trust-anchor-container.t.cpp
@@ -0,0 +1,198 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/trust-anchor-container.hpp"
+#include "util/io.hpp"
+
+#include "../../identity-management-time-fixture.hpp"
+#include "boost-test.hpp"
+
+#include <boost/filesystem.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+
+/**
+ * This fixture creates a directory and prepares two certificates.
+ * cert1 is written to a file under the directory, while cert2 is not.
+ */
+class AnchorContainerTestFixture : public IdentityManagementTimeFixture
+{
+public:
+ AnchorContainerTestFixture()
+ {
+ namespace fs = boost::filesystem;
+
+ fs::create_directory(fs::path(UNIT_TEST_CONFIG_PATH));
+
+ certDirPath = fs::path(UNIT_TEST_CONFIG_PATH) / "test-cert-dir";
+ fs::create_directory(certDirPath);
+
+ certPath1 = fs::path(UNIT_TEST_CONFIG_PATH) / "test-cert-dir" / "trust-anchor-1.cert";
+ certPath2 = fs::path(UNIT_TEST_CONFIG_PATH) / "test-cert-dir" / "trust-anchor-2.cert";
+
+ identity1 = addIdentity("/TestAnchorContainer/First");
+ cert1 = identity1.getDefaultKey().getDefaultCertificate();
+ saveCertToFile(cert1, certPath1.string());
+
+ identity2 = addIdentity("/TestAnchorContainer/Second");
+ cert2 = identity2.getDefaultKey().getDefaultCertificate();
+ saveCertToFile(cert2, certPath2.string());
+ }
+
+ ~AnchorContainerTestFixture()
+ {
+ boost::filesystem::remove_all(UNIT_TEST_CONFIG_PATH);
+ }
+
+public:
+ TrustAnchorContainer anchorContainer;
+
+ boost::filesystem::path certDirPath;
+ boost::filesystem::path certPath1;
+ boost::filesystem::path certPath2;
+
+ Identity identity1;
+ Identity identity2;
+
+ Certificate cert1;
+ Certificate cert2;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestTrustAnchorContainer, AnchorContainerTestFixture)
+
+// one static group and one dynamic group created from file
+BOOST_AUTO_TEST_CASE(Insert)
+{
+ // Static
+ anchorContainer.insert("group1", Certificate(cert1));
+ BOOST_CHECK(anchorContainer.find(cert1.getName()) != nullptr);
+ BOOST_CHECK(anchorContainer.find(identity1.getName()) != nullptr);
+ const Certificate* cert = anchorContainer.find(cert1.getName());
+ BOOST_CHECK_NO_THROW(anchorContainer.insert("group1", Certificate(cert1)));
+ BOOST_CHECK_EQUAL(cert, anchorContainer.find(cert1.getName())); // still the same instance of the certificate
+ // cannot add dynamic group when static already exists
+ BOOST_CHECK_THROW(anchorContainer.insert("group1", certPath1.string(), 1_s), TrustAnchorContainer::Error);
+ BOOST_CHECK_EQUAL(anchorContainer.getGroup("group1").size(), 1);
+ BOOST_CHECK_EQUAL(anchorContainer.size(), 1);
+
+ // From file
+ anchorContainer.insert("group2", certPath2.string(), 1_s);
+ BOOST_CHECK(anchorContainer.find(cert2.getName()) != nullptr);
+ BOOST_CHECK(anchorContainer.find(identity2.getName()) != nullptr);
+ BOOST_CHECK_THROW(anchorContainer.insert("group2", Certificate(cert2)), TrustAnchorContainer::Error);
+ BOOST_CHECK_THROW(anchorContainer.insert("group2", certPath2.string(), 1_s), TrustAnchorContainer::Error);
+ BOOST_CHECK_EQUAL(anchorContainer.getGroup("group2").size(), 1);
+ BOOST_CHECK_EQUAL(anchorContainer.size(), 2);
+
+ boost::filesystem::remove(certPath2);
+ advanceClocks(1_s, 11);
+
+ BOOST_CHECK(anchorContainer.find(identity2.getName()) == nullptr);
+ BOOST_CHECK(anchorContainer.find(cert2.getName()) == nullptr);
+ BOOST_CHECK_EQUAL(anchorContainer.getGroup("group2").size(), 0);
+ BOOST_CHECK_EQUAL(anchorContainer.size(), 1);
+
+ TrustAnchorGroup& group = anchorContainer.getGroup("group1");
+ auto staticGroup = dynamic_cast<StaticTrustAnchorGroup*>(&group);
+ BOOST_REQUIRE(staticGroup != nullptr);
+ BOOST_CHECK_EQUAL(staticGroup->size(), 1);
+ staticGroup->remove(cert1.getName());
+ BOOST_CHECK_EQUAL(staticGroup->size(), 0);
+ BOOST_CHECK_EQUAL(anchorContainer.size(), 0);
+
+ BOOST_CHECK_THROW(anchorContainer.getGroup("non-existing-group"), TrustAnchorContainer::Error);
+}
+
+BOOST_AUTO_TEST_CASE(DynamicAnchorFromDir)
+{
+ boost::filesystem::remove(certPath2);
+
+ anchorContainer.insert("group", certDirPath.string(), 1_s, true /* isDir */);
+
+ BOOST_CHECK(anchorContainer.find(identity1.getName()) != nullptr);
+ BOOST_CHECK(anchorContainer.find(identity2.getName()) == nullptr);
+ BOOST_CHECK_EQUAL(anchorContainer.getGroup("group").size(), 1);
+
+ saveCertToFile(cert2, certPath2.string());
+
+ advanceClocks(100_ms, 11);
+
+ BOOST_CHECK(anchorContainer.find(identity1.getName()) != nullptr);
+ BOOST_CHECK(anchorContainer.find(identity2.getName()) != nullptr);
+ BOOST_CHECK_EQUAL(anchorContainer.getGroup("group").size(), 2);
+
+ boost::filesystem::remove_all(certDirPath);
+
+ advanceClocks(100_ms, 11);
+
+ BOOST_CHECK(anchorContainer.find(identity1.getName()) == nullptr);
+ BOOST_CHECK(anchorContainer.find(identity2.getName()) == nullptr);
+ BOOST_CHECK_EQUAL(anchorContainer.getGroup("group").size(), 0);
+}
+
+BOOST_FIXTURE_TEST_CASE(FindByInterest, AnchorContainerTestFixture)
+{
+ anchorContainer.insert("group1", certPath1.string(), 1_s);
+ Interest interest(identity1.getName());
+ BOOST_CHECK(anchorContainer.find(interest) != nullptr);
+ Interest interest1(identity1.getName().getPrefix(-1));
+ BOOST_CHECK(anchorContainer.find(interest1) != nullptr);
+ Interest interest2(Name(identity1.getName()).appendVersion());
+ BOOST_CHECK(anchorContainer.find(interest2) == nullptr);
+
+ Certificate cert3 = addCertificate(identity1.getDefaultKey(), "3");
+ Certificate cert4 = addCertificate(identity1.getDefaultKey(), "4");
+ Certificate cert5 = addCertificate(identity1.getDefaultKey(), "5");
+
+ Certificate cert3Copy = cert3;
+ anchorContainer.insert("group2", std::move(cert3Copy));
+ anchorContainer.insert("group3", std::move(cert4));
+ anchorContainer.insert("group4", std::move(cert5));
+
+ Interest interest3(cert3.getKeyName());
+ const Certificate* foundCert = anchorContainer.find(interest3);
+ BOOST_REQUIRE(foundCert != nullptr);
+ BOOST_CHECK(interest3.getName().isPrefixOf(foundCert->getName()));
+ BOOST_CHECK_EQUAL(foundCert->getName(), cert3.getName());
+
+ interest3.setExclude(Exclude().excludeOne(cert3.getName().at(Certificate::ISSUER_ID_OFFSET)));
+ foundCert = anchorContainer.find(interest3);
+ BOOST_REQUIRE(foundCert != nullptr);
+ BOOST_CHECK(interest3.getName().isPrefixOf(foundCert->getName()));
+ BOOST_CHECK_NE(foundCert->getName(), cert3.getName());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestTrustAnchorContainer
+BOOST_AUTO_TEST_SUITE_END() // Detail
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validation-error.t.cpp b/tests/unit/security/v2/validation-error.t.cpp
new file mode 100644
index 0000000..1db3aac
--- /dev/null
+++ b/tests/unit/security/v2/validation-error.t.cpp
@@ -0,0 +1,61 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/validation-error.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(TestValidationError)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ ValidationError e1{ValidationError::Code::INVALID_SIGNATURE};
+ BOOST_CHECK_EQUAL(e1.getCode(), 1);
+ BOOST_CHECK_EQUAL(e1.getInfo(), "");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(e1), "Invalid signature");
+
+ ValidationError e2{ValidationError::Code::NO_SIGNATURE, "message"};
+ BOOST_CHECK_EQUAL(e2.getCode(), 2);
+ BOOST_CHECK_EQUAL(e2.getInfo(), "message");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(e2), "Missing signature (message)");
+
+ ValidationError e3{65535, "other message"};
+ BOOST_CHECK_EQUAL(e3.getCode(), 65535);
+ BOOST_CHECK_EQUAL(e3.getInfo(), "other message");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(e3), "Custom error code 65535 (other message)");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestValidationError
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validation-policy-accept-all.t.cpp b/tests/unit/security/v2/validation-policy-accept-all.t.cpp
new file mode 100644
index 0000000..61466d6
--- /dev/null
+++ b/tests/unit/security/v2/validation-policy-accept-all.t.cpp
@@ -0,0 +1,79 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/validation-policy-accept-all.hpp"
+
+#include "boost-test.hpp"
+#include "validator-fixture.hpp"
+
+#include <boost/mpl/vector.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+
+class ValidationPolicyAcceptAllFixture : public ValidatorFixture<ValidationPolicyAcceptAll>
+{
+public:
+ ValidationPolicyAcceptAllFixture()
+ {
+ identity = addIdentity("/Security/V2/TestValidationPolicyAcceptAll");
+ // don't add trust anchors
+ }
+
+public:
+ Identity identity;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestValidationPolicyAcceptAll, ValidationPolicyAcceptAllFixture)
+
+typedef boost::mpl::vector<Interest, Data> Packets;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(Validate, Packet, Packets)
+{
+ Packet unsignedPacket("/Security/V2/TestValidationPolicyAcceptAll/Sub/Packet");
+
+ Packet packet = unsignedPacket;
+ VALIDATE_SUCCESS(packet, "Should accept unsigned");
+
+ packet = unsignedPacket;
+ m_keyChain.sign(packet, signingWithSha256());
+ VALIDATE_SUCCESS(packet, "Should accept Sha256Digest signature");
+
+ packet = unsignedPacket;
+ m_keyChain.sign(packet, signingByIdentity(identity));
+ VALIDATE_SUCCESS(packet, "Should accept signature while no trust anchors configured");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestValidationPolicyAcceptAll
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validation-policy-command-interest.t.cpp b/tests/unit/security/v2/validation-policy-command-interest.t.cpp
new file mode 100644
index 0000000..7bd42ea
--- /dev/null
+++ b/tests/unit/security/v2/validation-policy-command-interest.t.cpp
@@ -0,0 +1,485 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/validation-policy-command-interest.hpp"
+#include "security/v2/validation-policy-simple-hierarchy.hpp"
+#include "security/v2/validation-policy-accept-all.hpp"
+#include "security/command-interest-signer.hpp"
+#include "security/signing-helpers.hpp"
+
+#include "boost-test.hpp"
+#include "validator-fixture.hpp"
+#include "make-interest-data.hpp"
+
+#include <boost/lexical_cast.hpp>
+#include <boost/mpl/vector.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+
+class DefaultOptions
+{
+public:
+ static ValidationPolicyCommandInterest::Options
+ getOptions()
+ {
+ return {};
+ }
+};
+
+template<class T, class InnerPolicy>
+class CommandInterestPolicyWrapper : public ValidationPolicyCommandInterest
+{
+public:
+ CommandInterestPolicyWrapper()
+ : ValidationPolicyCommandInterest(make_unique<InnerPolicy>(), T::getOptions())
+ {
+ }
+};
+
+template<class T, class InnerPolicy = ValidationPolicySimpleHierarchy>
+class ValidationPolicyCommandInterestFixture : public HierarchicalValidatorFixture<CommandInterestPolicyWrapper<T, InnerPolicy>>
+{
+public:
+ ValidationPolicyCommandInterestFixture()
+ : m_signer(this->m_keyChain)
+ {
+ }
+
+ Interest
+ makeCommandInterest(const Identity& identity)
+ {
+ return m_signer.makeCommandInterest(Name(identity.getName()).append("CMD"),
+ signingByIdentity(identity));
+ }
+
+public:
+ CommandInterestSigner m_signer;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestValidationPolicyCommandInterest, ValidationPolicyCommandInterestFixture<DefaultOptions>)
+
+BOOST_AUTO_TEST_SUITE(Accepts)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ auto i1 = makeCommandInterest(identity);
+ VALIDATE_SUCCESS(i1, "Should succeed (within grace period)");
+ VALIDATE_FAILURE(i1, "Should fail (replay attack)");
+
+ advanceClocks(5_ms);
+ auto i2 = makeCommandInterest(identity);
+ VALIDATE_SUCCESS(i2, "Should succeed (timestamp larger than previous)");
+
+ auto i3 = m_signer.makeCommandInterest(Name(identity.getName()).append("CMD"), signingWithSha256());
+ VALIDATE_FAILURE(i3, "Should fail (Sha256 signature violates policy)");
+}
+
+BOOST_AUTO_TEST_CASE(DataPassthru)
+{
+ Data d1("/Security/V2/ValidatorFixture/Sub1");
+ m_keyChain.sign(d1);
+ VALIDATE_SUCCESS(d1, "Should succeed (fallback on inner validation policy for data)");
+}
+
+using ValidationPolicyAcceptAllCommands = ValidationPolicyCommandInterestFixture<DefaultOptions,
+ ValidationPolicyAcceptAll>;
+
+BOOST_FIXTURE_TEST_CASE(SignedWithSha256, ValidationPolicyAcceptAllCommands) // Bug 4635
+{
+ auto i1 = m_signer.makeCommandInterest("/hello/world/CMD", signingWithSha256());
+ VALIDATE_SUCCESS(i1, "Should succeed (within grace period)");
+ VALIDATE_FAILURE(i1, "Should fail (replay attack)");
+
+ advanceClocks(5_ms);
+ auto i2 = m_signer.makeCommandInterest("/hello/world/CMD", signingWithSha256());
+ VALIDATE_SUCCESS(i2, "Should succeed (timestamp larger than previous)");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Accepts
+
+BOOST_AUTO_TEST_SUITE(Rejects)
+
+BOOST_AUTO_TEST_CASE(NameTooShort)
+{
+ auto i1 = makeInterest("/name/too/short");
+ VALIDATE_FAILURE(*i1, "Should fail (name is too short)");
+}
+
+BOOST_AUTO_TEST_CASE(BadTimestamp)
+{
+ auto i1 = makeCommandInterest(identity);
+ setNameComponent(i1, command_interest::POS_TIMESTAMP, "not-timestamp");
+ VALIDATE_FAILURE(i1, "Should fail (timestamp is missing)");
+}
+
+BOOST_AUTO_TEST_CASE(BadSigInfo)
+{
+ auto i1 = makeCommandInterest(identity);
+ setNameComponent(i1, command_interest::POS_SIG_INFO, "not-SignatureInfo");
+ VALIDATE_FAILURE(i1, "Should fail (signature info is missing)");
+}
+
+BOOST_AUTO_TEST_CASE(MissingKeyLocator)
+{
+ auto i1 = makeCommandInterest(identity);
+ SignatureInfo sigInfo(tlv::SignatureSha256WithRsa);
+ setNameComponent(i1, command_interest::POS_SIG_INFO,
+ sigInfo.wireEncode().begin(), sigInfo.wireEncode().end());
+ VALIDATE_FAILURE(i1, "Should fail (missing KeyLocator)");
+}
+
+BOOST_AUTO_TEST_CASE(BadKeyLocatorType)
+{
+ auto i1 = makeCommandInterest(identity);
+ KeyLocator kl;
+ kl.setKeyDigest(makeBinaryBlock(tlv::KeyDigest, "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD", 8));
+ SignatureInfo sigInfo(tlv::SignatureSha256WithRsa);
+ sigInfo.setKeyLocator(kl);
+ setNameComponent(i1, command_interest::POS_SIG_INFO,
+ sigInfo.wireEncode().begin(), sigInfo.wireEncode().end());
+ VALIDATE_FAILURE(i1, "Should fail (bad KeyLocator type)");
+}
+
+BOOST_AUTO_TEST_CASE(BadCertName)
+{
+ auto i1 = makeCommandInterest(identity);
+ KeyLocator kl;
+ kl.setName("/bad/cert/name");
+ SignatureInfo sigInfo(tlv::SignatureSha256WithRsa);
+ sigInfo.setKeyLocator(kl);
+ setNameComponent(i1, command_interest::POS_SIG_INFO,
+ sigInfo.wireEncode().begin(), sigInfo.wireEncode().end());
+ VALIDATE_FAILURE(i1, "Should fail (bad certificate name)");
+}
+
+BOOST_AUTO_TEST_CASE(InnerPolicyReject)
+{
+ auto i1 = makeCommandInterest(otherIdentity);
+ VALIDATE_FAILURE(i1, "Should fail (inner policy should reject)");
+}
+
+class GracePeriod15Sec
+{
+public:
+ static ValidationPolicyCommandInterest::Options
+ getOptions()
+ {
+ ValidationPolicyCommandInterest::Options options;
+ options.gracePeriod = 15_s;
+ return options;
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(TimestampOutOfGracePositive, ValidationPolicyCommandInterestFixture<GracePeriod15Sec>)
+{
+ auto i1 = makeCommandInterest(identity); // signed at 0s
+ advanceClocks(16_s); // verifying at +16s
+ VALIDATE_FAILURE(i1, "Should fail (timestamp outside the grace period)");
+ rewindClockAfterValidation();
+
+ auto i2 = makeCommandInterest(identity); // signed at +16s
+ VALIDATE_SUCCESS(i2, "Should succeed");
+}
+
+BOOST_FIXTURE_TEST_CASE(TimestampOutOfGraceNegative, ValidationPolicyCommandInterestFixture<GracePeriod15Sec>)
+{
+ auto i1 = makeCommandInterest(identity); // signed at 0s
+ advanceClocks(1_s);
+ auto i2 = makeCommandInterest(identity); // signed at +1s
+ advanceClocks(1_s);
+ auto i3 = makeCommandInterest(identity); // signed at +2s
+
+ systemClock->advance(-18_s); // verifying at -16s
+ VALIDATE_FAILURE(i1, "Should fail (timestamp outside the grace period)");
+ rewindClockAfterValidation();
+
+ // CommandInterestValidator should not remember i1's timestamp
+ VALIDATE_FAILURE(i2, "Should fail (timestamp outside the grace period)");
+ rewindClockAfterValidation();
+
+ // CommandInterestValidator should not remember i2's timestamp, and should treat i3 as initial
+ advanceClocks(18_s); // verifying at +2s
+ VALIDATE_SUCCESS(i3, "Should succeed");
+}
+
+BOOST_AUTO_TEST_CASE(TimestampReorderEqual)
+{
+ auto i1 = makeCommandInterest(identity); // signed at 0s
+ VALIDATE_SUCCESS(i1, "Should succeed");
+
+ auto i2 = makeCommandInterest(identity); // signed at 0s
+ setNameComponent(i2, command_interest::POS_TIMESTAMP,
+ i1.getName()[command_interest::POS_TIMESTAMP]);
+ VALIDATE_FAILURE(i2, "Should fail (timestamp reordered)");
+
+ advanceClocks(2_s);
+ auto i3 = makeCommandInterest(identity); // signed at +2s
+ VALIDATE_SUCCESS(i3, "Should succeed");
+}
+
+BOOST_AUTO_TEST_CASE(TimestampReorderNegative)
+{
+ auto i2 = makeCommandInterest(identity); // signed at 0ms
+ advanceClocks(200_ms);
+ auto i3 = makeCommandInterest(identity); // signed at +200ms
+ advanceClocks(900_ms);
+ auto i1 = makeCommandInterest(identity); // signed at +1100ms
+ advanceClocks(300_ms);
+ auto i4 = makeCommandInterest(identity); // signed at +1400ms
+
+ systemClock->advance(-300_ms); // verifying at +1100ms
+ VALIDATE_SUCCESS(i1, "Should succeed");
+ rewindClockAfterValidation();
+
+ systemClock->advance(-1100_ms); // verifying at 0ms
+ VALIDATE_FAILURE(i2, "Should fail (timestamp reordered)");
+ rewindClockAfterValidation();
+
+ // CommandInterestValidator should not remember i2's timestamp
+ advanceClocks(200_ms); // verifying at +200ms
+ VALIDATE_FAILURE(i3, "Should fail (timestamp reordered)");
+ rewindClockAfterValidation();
+
+ advanceClocks(1200_ms); // verifying at 1400ms
+ VALIDATE_SUCCESS(i4, "Should succeed");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Rejects
+
+BOOST_AUTO_TEST_SUITE(Options)
+
+template<class T>
+class GracePeriod
+{
+public:
+ static ValidationPolicyCommandInterest::Options
+ getOptions()
+ {
+ ValidationPolicyCommandInterest::Options options;
+ options.gracePeriod = time::seconds(T::value);
+ return options;
+ }
+};
+
+typedef boost::mpl::vector<
+ GracePeriod<boost::mpl::int_<0>>,
+ GracePeriod<boost::mpl::int_<-1>>
+> GraceNonPositiveValues;
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(GraceNonPositive, GracePeriod, GraceNonPositiveValues,
+ ValidationPolicyCommandInterestFixture<GracePeriod>)
+{
+ auto i1 = this->makeCommandInterest(this->identity); // signed at 0ms
+ auto i2 = this->makeCommandInterest(this->subIdentity); // signed at 0ms
+ for (auto interest : {&i1, &i2}) {
+ setNameComponent(*interest, command_interest::POS_TIMESTAMP,
+ name::Component::fromNumber(time::toUnixTimestamp(time::system_clock::now()).count()));
+ } // ensure timestamps are exactly 0ms
+
+ VALIDATE_SUCCESS(i1, "Should succeed when validating at 0ms");
+ this->rewindClockAfterValidation();
+
+ this->advanceClocks(1_ms);
+ VALIDATE_FAILURE(i2, "Should fail when validating at 1ms");
+}
+
+class LimitedRecordsOptions
+{
+public:
+ static ValidationPolicyCommandInterest::Options
+ getOptions()
+ {
+ ValidationPolicyCommandInterest::Options options;
+ options.gracePeriod = 15_s;
+ options.maxRecords = 3;
+ return options;
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(LimitedRecords, ValidationPolicyCommandInterestFixture<LimitedRecordsOptions>)
+{
+ Identity id1 = this->addSubCertificate("/Security/V2/ValidatorFixture/Sub1", identity);
+ this->cache.insert(id1.getDefaultKey().getDefaultCertificate());
+ Identity id2 = this->addSubCertificate("/Security/V2/ValidatorFixture/Sub2", identity);
+ this->cache.insert(id2.getDefaultKey().getDefaultCertificate());
+ Identity id3 = this->addSubCertificate("/Security/V2/ValidatorFixture/Sub3", identity);
+ this->cache.insert(id3.getDefaultKey().getDefaultCertificate());
+ Identity id4 = this->addSubCertificate("/Security/V2/ValidatorFixture/Sub4", identity);
+ this->cache.insert(id4.getDefaultKey().getDefaultCertificate());
+
+ auto i1 = makeCommandInterest(id2);
+ auto i2 = makeCommandInterest(id3);
+ auto i3 = makeCommandInterest(id4);
+ auto i00 = makeCommandInterest(id1); // signed at 0s
+ advanceClocks(1_s);
+ auto i01 = makeCommandInterest(id1); // signed at 1s
+ advanceClocks(1_s);
+ auto i02 = makeCommandInterest(id1); // signed at 2s
+
+ VALIDATE_SUCCESS(i00, "Should succeed");
+ rewindClockAfterValidation();
+
+ VALIDATE_SUCCESS(i02, "Should succeed");
+ rewindClockAfterValidation();
+
+ VALIDATE_SUCCESS(i1, "Should succeed");
+ rewindClockAfterValidation();
+
+ VALIDATE_SUCCESS(i2, "Should succeed");
+ rewindClockAfterValidation();
+
+ VALIDATE_SUCCESS(i3, "Should succeed, forgets identity id1");
+ rewindClockAfterValidation();
+
+ VALIDATE_SUCCESS(i01, "Should succeed despite timestamp is reordered, because record has been evicted");
+}
+
+class UnlimitedRecordsOptions
+{
+public:
+ static ValidationPolicyCommandInterest::Options
+ getOptions()
+ {
+ ValidationPolicyCommandInterest::Options options;
+ options.gracePeriod = 15_s;
+ options.maxRecords = -1;
+ return options;
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(UnlimitedRecords, ValidationPolicyCommandInterestFixture<UnlimitedRecordsOptions>)
+{
+ std::vector<Identity> identities;
+ for (int i = 0; i < 20; ++i) {
+ Identity id = this->addSubCertificate("/Security/V2/ValidatorFixture/Sub" + to_string(i), identity);
+ this->cache.insert(id.getDefaultKey().getDefaultCertificate());
+ identities.push_back(id);
+ }
+
+ auto i1 = makeCommandInterest(identities.at(0)); // signed at 0s
+ advanceClocks(1_s);
+ for (int i = 0; i < 20; ++i) {
+ auto i2 = makeCommandInterest(identities.at(i)); // signed at +1s
+
+ VALIDATE_SUCCESS(i2, "Should succeed");
+ rewindClockAfterValidation();
+ }
+ VALIDATE_FAILURE(i1, "Should fail (timestamp reorder)");
+}
+
+class ZeroRecordsOptions
+{
+public:
+ static ValidationPolicyCommandInterest::Options
+ getOptions()
+ {
+ ValidationPolicyCommandInterest::Options options;
+ options.gracePeriod = 15_s;
+ options.maxRecords = 0;
+ return options;
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(ZeroRecords, ValidationPolicyCommandInterestFixture<ZeroRecordsOptions>)
+{
+ auto i1 = makeCommandInterest(identity); // signed at 0s
+ advanceClocks(1_s);
+ auto i2 = makeCommandInterest(identity); // signed at +1s
+ VALIDATE_SUCCESS(i2, "Should succeed");
+ rewindClockAfterValidation();
+
+ VALIDATE_SUCCESS(i1, "Should succeed despite timestamp is reordered, because record isn't kept");
+}
+
+class LimitedRecordLifetimeOptions
+{
+public:
+ static ValidationPolicyCommandInterest::Options
+ getOptions()
+ {
+ ValidationPolicyCommandInterest::Options options;
+ options.gracePeriod = 400_s;
+ options.recordLifetime = 300_s;
+ return options;
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(LimitedRecordLifetime, ValidationPolicyCommandInterestFixture<LimitedRecordLifetimeOptions>)
+{
+ auto i1 = makeCommandInterest(identity); // signed at 0s
+ advanceClocks(240_s);
+ auto i2 = makeCommandInterest(identity); // signed at +240s
+ advanceClocks(120_s);
+ auto i3 = makeCommandInterest(identity); // signed at +360s
+
+ systemClock->advance(-360_s); // rewind system clock to 0s
+ VALIDATE_SUCCESS(i1, "Should succeed");
+ rewindClockAfterValidation();
+
+ VALIDATE_SUCCESS(i3, "Should succeed");
+ rewindClockAfterValidation();
+
+ advanceClocks(30_s, 301_s); // advance steady clock by 301s, and system clock to +301s
+ VALIDATE_SUCCESS(i2, "Should succeed despite timestamp is reordered, because record has been expired");
+}
+
+class ZeroRecordLifetimeOptions
+{
+public:
+ static ValidationPolicyCommandInterest::Options
+ getOptions()
+ {
+ ValidationPolicyCommandInterest::Options options;
+ options.gracePeriod = 15_s;
+ options.recordLifetime = time::seconds::zero();
+ return options;
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(ZeroRecordLifetime, ValidationPolicyCommandInterestFixture<ZeroRecordLifetimeOptions>)
+{
+ auto i1 = makeCommandInterest(identity); // signed at 0s
+ advanceClocks(1_s);
+ auto i2 = makeCommandInterest(identity); // signed at +1s
+ VALIDATE_SUCCESS(i2, "Should succeed");
+ rewindClockAfterValidation();
+
+ VALIDATE_SUCCESS(i1, "Should succeed despite timestamp is reordered, because record has been expired");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Options
+
+BOOST_AUTO_TEST_SUITE_END() // TestValidationPolicyCommandInterest
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validation-policy-config.t.cpp b/tests/unit/security/v2/validation-policy-config.t.cpp
new file mode 100644
index 0000000..cca3e7a
--- /dev/null
+++ b/tests/unit/security/v2/validation-policy-config.t.cpp
@@ -0,0 +1,539 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/validation-policy-config.hpp"
+#include "security/transform/base64-encode.hpp"
+#include "security/transform/buffer-source.hpp"
+#include "security/transform/stream-sink.hpp"
+#include "util/logger.hpp"
+#include "util/io.hpp"
+
+#include "boost-test.hpp"
+#include "validator-config/common.hpp"
+#include "validator-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+namespace tests {
+
+using namespace ndn::tests;
+using namespace ndn::security::v2::tests;
+
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(TestValidationPolicyConfig)
+
+template<typename Packet>
+class PacketName;
+
+template<>
+class PacketName<Interest>
+{
+public:
+ static std::string
+ getName()
+ {
+ return "interest";
+ }
+};
+
+template<>
+class PacketName<Data>
+{
+public:
+ static std::string
+ getName()
+ {
+ return "data";
+ }
+};
+
+template<typename PacketType>
+class ValidationPolicyConfigFixture : public HierarchicalValidatorFixture<ValidationPolicyConfig>
+{
+public:
+ ValidationPolicyConfigFixture()
+ : path(boost::filesystem::path(UNIT_TEST_CONFIG_PATH) / "security" / "v2" / "validation-policy-config")
+ {
+ boost::filesystem::create_directories(path);
+ baseConfig = R"CONF(
+ rule
+ {
+ id test-rule-id
+ for )CONF" + PacketName<Packet>::getName() + R"CONF(
+ filter
+ {
+ type name
+ name )CONF" + identity.getName().toUri() + R"CONF(
+ relation is-prefix-of
+ }
+ checker
+ {
+ type hierarchical
+ sig-type rsa-sha256
+ }
+ }
+ )CONF";
+ }
+
+ ~ValidationPolicyConfigFixture()
+ {
+ boost::filesystem::remove_all(path);
+ }
+
+public:
+ using Packet = PacketType;
+
+ const boost::filesystem::path path;
+ std::string baseConfig;
+};
+
+template<typename PacketType>
+class LoadStringWithFileAnchor : public ValidationPolicyConfigFixture<PacketType>
+{
+public:
+ LoadStringWithFileAnchor()
+ {
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
+
+ this->saveCertificate(this->identity, (this->path / "identity.ndncert").string());
+ this->policy.load(this->baseConfig + R"CONF(
+ trust-anchor
+ {
+ type file
+ file-name "trust-anchor.ndncert"
+ }
+ )CONF", (this->path / "test-config").string());
+
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
+ BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
+ }
+};
+
+template<typename PacketType>
+class LoadFileWithFileAnchor : public ValidationPolicyConfigFixture<PacketType>
+{
+public:
+ LoadFileWithFileAnchor()
+ {
+ std::string configFile = (this->path / "config.conf").string();
+ {
+ std::ofstream config(configFile.c_str());
+ config << this->baseConfig << R"CONF(
+ trust-anchor
+ {
+ type file
+ file-name "trust-anchor.ndncert"
+ }
+ )CONF";
+ }
+ this->saveCertificate(this->identity, (this->path / "identity.ndncert").string());
+
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
+
+ this->policy.load(configFile);
+
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
+ BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
+ }
+};
+
+template<typename PacketType>
+class LoadSectionWithFileAnchor : public ValidationPolicyConfigFixture<PacketType>
+{
+public:
+ LoadSectionWithFileAnchor()
+ {
+ auto section = makeSection(this->baseConfig + R"CONF(
+ trust-anchor
+ {
+ type file
+ file-name "trust-anchor.ndncert"
+ }
+ )CONF");
+
+ this->saveCertificate(this->identity, (this->path / "identity.ndncert").string());
+
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
+
+ this->policy.load(section, (this->path / "test-config").string());
+
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
+ BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
+ }
+};
+
+template<typename PacketType>
+class LoadStringWithBase64Anchor : public ValidationPolicyConfigFixture<PacketType>
+{
+public:
+ LoadStringWithBase64Anchor()
+ {
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
+
+ std::ostringstream os;
+ using namespace ndn::security::transform;
+ const auto& cert = this->identity.getDefaultKey().getDefaultCertificate();
+ bufferSource(cert.wireEncode().wire(), cert.wireEncode().size()) >> base64Encode(false) >> streamSink(os);
+
+ this->policy.load(this->baseConfig + R"CONF(
+ trust-anchor
+ {
+ type base64
+ base64-string ")CONF" + os.str() + R"CONF("
+ }
+ )CONF", (this->path / "test-config").string());
+
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
+ BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
+ }
+};
+
+class NoRefresh
+{
+public:
+ static std::string
+ getRefreshString()
+ {
+ return "";
+ }
+};
+
+class Refresh1h
+{
+public:
+ static std::string
+ getRefreshString()
+ {
+ return "refresh 1h";
+ }
+
+ static time::milliseconds
+ getRefreshTime()
+ {
+ return 1_h;
+ }
+};
+
+class Refresh1m
+{
+public:
+ static std::string
+ getRefreshString()
+ {
+ return "refresh 1m";
+ }
+
+ static time::milliseconds
+ getRefreshTime()
+ {
+ return 1_min;
+ }
+};
+
+class Refresh1s
+{
+public:
+ static std::string
+ getRefreshString()
+ {
+ return "refresh 1s";
+ }
+
+ static time::milliseconds
+ getRefreshTime()
+ {
+ return 1_s;
+ }
+};
+
+template<typename PacketType, typename Refresh = NoRefresh>
+class LoadStringWithDirAnchor : public ValidationPolicyConfigFixture<PacketType>
+{
+public:
+ LoadStringWithDirAnchor()
+ {
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
+
+ boost::filesystem::create_directories(this->path / "keys");
+ this->saveCertificate(this->identity, (this->path / "keys" / "identity.ndncert").string());
+
+ this->policy.load(this->baseConfig + R"CONF(
+ trust-anchor
+ {
+ type dir
+ dir keys
+ )CONF" + Refresh::getRefreshString() + R"CONF(
+ }
+ )CONF", (this->path / "test-config").string());
+
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
+ BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
+ }
+};
+
+using DataPolicies = boost::mpl::vector<LoadStringWithFileAnchor<Data>,
+ LoadFileWithFileAnchor<Data>,
+ LoadSectionWithFileAnchor<Data>,
+ LoadStringWithBase64Anchor<Data>,
+ LoadStringWithDirAnchor<Data>,
+ LoadStringWithDirAnchor<Data, Refresh1h>,
+ LoadStringWithDirAnchor<Data, Refresh1m>,
+ LoadStringWithDirAnchor<Data, Refresh1s>
+ >;
+
+using InterestPolicies = boost::mpl::vector<LoadStringWithFileAnchor<Interest>,
+ LoadFileWithFileAnchor<Interest>,
+ LoadSectionWithFileAnchor<Interest>,
+ LoadStringWithBase64Anchor<Interest>,
+ LoadStringWithDirAnchor<Interest>,
+ LoadStringWithDirAnchor<Interest, Refresh1h>,
+ LoadStringWithDirAnchor<Interest, Refresh1m>,
+ LoadStringWithDirAnchor<Interest, Refresh1s>
+ >;
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateData, Policy, DataPolicies, Policy)
+{
+ BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 1);
+ BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 0);
+
+ using Packet = typename Policy::Packet;
+ Packet unsignedPacket("/Security/V2/ValidatorFixture/Sub1/Sub2/Packet");
+
+ Packet packet = unsignedPacket;
+ VALIDATE_FAILURE(packet, "Unsigned");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingWithSha256());
+ VALIDATE_FAILURE(packet, "Policy doesn't accept Sha256Digest signature");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->identity));
+ VALIDATE_SUCCESS(packet, "Should get accepted, as signed by the anchor");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
+ VALIDATE_SUCCESS(packet, "Should get accepted, as signed by the policy-compliant cert");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->otherIdentity));
+ VALIDATE_FAILURE(packet, "Should fail, as signed by the policy-violating cert");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->subSelfSignedIdentity));
+ VALIDATE_FAILURE(packet, "Should fail, because subSelfSignedIdentity is not a trust anchor");
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateInterest, Policy, InterestPolicies, Policy)
+{
+ BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 0);
+ BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 1);
+
+ using Packet = typename Policy::Packet;
+ Packet unsignedPacket("/Security/V2/ValidatorFixture/Sub1/Sub2/Packet");
+
+ Packet packet = unsignedPacket;
+ VALIDATE_FAILURE(packet, "Unsigned");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingWithSha256());
+ VALIDATE_FAILURE(packet, "Policy doesn't accept Sha256Digest signature");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->identity));
+ VALIDATE_SUCCESS(packet, "Should get accepted, as signed by the anchor");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
+ VALIDATE_FAILURE(packet, "Should fail, as there is no matching rule for data");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->otherIdentity));
+ VALIDATE_FAILURE(packet, "Should fail, as signed by the policy-violating cert");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->subSelfSignedIdentity));
+ VALIDATE_FAILURE(packet, "Should fail, because subSelfSignedIdentity is not a trust anchor");
+}
+
+BOOST_FIXTURE_TEST_CASE(Reload, HierarchicalValidatorFixture<ValidationPolicyConfig>)
+{
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
+ this->policy.load(R"CONF(
+ rule
+ {
+ id test-rule-data-id
+ for data
+ filter
+ {
+ type name
+ name /foo/bar
+ relation is-prefix-of
+ }
+ checker
+ {
+ type hierarchical
+ sig-type rsa-sha256
+ }
+ }
+ rule
+ {
+ id test-rule-interest-id
+ for interest
+ filter
+ {
+ type name
+ name /foo/bar
+ relation is-prefix-of
+ }
+ checker
+ {
+ type hierarchical
+ sig-type rsa-sha256
+ }
+ }
+ trust-anchor
+ {
+ type dir
+ dir keys
+ refresh 1h
+ }
+ )CONF", "test-config");
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
+ BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
+ BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 1);
+ BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 1);
+
+ this->policy.load(R"CONF(
+ trust-anchor
+ {
+ type any
+ }
+ )CONF", "test-config");
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
+ BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, true);
+ BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 0);
+ BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 0);
+}
+
+using Packets = boost::mpl::vector<Interest, Data>;
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(TrustAnchorWildcard, Packet, Packets, ValidationPolicyConfigFixture<Packet>)
+{
+ this->policy.load(R"CONF(
+ trust-anchor
+ {
+ type any
+ }
+ )CONF", "test-config");
+
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
+ BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, true);
+ BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 0);
+ BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 0);
+
+ Packet unsignedPacket("/Security/V2/ValidatorFixture/Sub1/Sub2/Packet");
+
+ Packet packet = unsignedPacket;
+ VALIDATE_SUCCESS(packet, "Policy should accept everything");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingWithSha256());
+ VALIDATE_SUCCESS(packet, "Policy should accept everything");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->identity));
+ VALIDATE_SUCCESS(packet, "Policy should accept everything");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
+ VALIDATE_SUCCESS(packet, "Policy should accept everything");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->otherIdentity));
+ VALIDATE_SUCCESS(packet, "Policy should accept everything");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->subSelfSignedIdentity));
+ VALIDATE_SUCCESS(packet, "Policy should accept everything");
+}
+
+using RefreshPolicies = boost::mpl::vector<Refresh1h, Refresh1m, Refresh1s>;
+
+// Somehow, didn't work without this wrapper
+template<typename RefreshPolicy>
+class RefreshPolicyFixture : public LoadStringWithDirAnchor<Data, RefreshPolicy>
+{
+public:
+};
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateRefresh, Refresh, RefreshPolicies, RefreshPolicyFixture<Refresh>)
+{
+ using Packet = Data;
+ Packet unsignedPacket("/Security/V2/ValidatorFixture/Sub1/Sub2/Packet");
+
+ boost::filesystem::remove(this->path / "keys" / "identity.ndncert");
+ this->advanceClocks(Refresh::getRefreshTime(), 3);
+
+ Packet packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->identity));
+ VALIDATE_FAILURE(packet, "Should fail, as the trust anchor should no longer exist");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
+ VALIDATE_FAILURE(packet, "Should fail, as the trust anchor should no longer exist");
+}
+
+BOOST_FIXTURE_TEST_CASE(OrphanedPolicyLoad, HierarchicalValidatorFixture<ValidationPolicyConfig>) // Bug #4758
+{
+ ValidationPolicyConfig policy1;
+ BOOST_CHECK_THROW(policy1.load("trust-anchor { type any }", "test-config"), Error);
+
+ // Reloading would have triggered a segfault
+ BOOST_CHECK_THROW(policy1.load("trust-anchor { type any }", "test-config"), Error);
+
+ ValidationPolicyConfig policy2;
+
+ std::string config = R"CONF(
+ trust-anchor
+ {
+ type dir
+ dir keys
+ refresh 1h
+ }
+ )CONF";
+
+ // Inserting trust anchor would have triggered a segfault
+ BOOST_CHECK_THROW(policy2.load(config, "test-config"), Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestValidationPolicyConfig
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validation-policy-simple-hierarchy.t.cpp b/tests/unit/security/v2/validation-policy-simple-hierarchy.t.cpp
new file mode 100644
index 0000000..8661ac5
--- /dev/null
+++ b/tests/unit/security/v2/validation-policy-simple-hierarchy.t.cpp
@@ -0,0 +1,80 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/validation-policy-simple-hierarchy.hpp"
+
+#include "boost-test.hpp"
+#include "validator-fixture.hpp"
+
+#include <boost/mpl/vector.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_FIXTURE_TEST_SUITE(TestValidationPolicySimpleHierarchy,
+ HierarchicalValidatorFixture<ValidationPolicySimpleHierarchy>)
+
+typedef boost::mpl::vector<Interest, Data> Packets;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(Validate, Packet, Packets)
+{
+ Packet unsignedPacket("/Security/V2/ValidatorFixture/Sub1/Sub2/Packet");
+
+ Packet packet = unsignedPacket;
+ VALIDATE_FAILURE(packet, "Unsigned");
+
+ packet = unsignedPacket;
+ m_keyChain.sign(packet, signingWithSha256());
+ VALIDATE_FAILURE(packet, "Policy doesn't accept Sha256Digest signature");
+
+ packet = unsignedPacket;
+ m_keyChain.sign(packet, signingByIdentity(identity));
+ VALIDATE_SUCCESS(packet, "Should get accepted, as signed by the anchor");
+
+ packet = unsignedPacket;
+ m_keyChain.sign(packet, signingByIdentity(subIdentity));
+ VALIDATE_SUCCESS(packet, "Should get accepted, as signed by the policy-compliant cert");
+
+ packet = unsignedPacket;
+ m_keyChain.sign(packet, signingByIdentity(otherIdentity));
+ VALIDATE_FAILURE(packet, "Should fail, as signed by the policy-violating cert");
+
+ packet = unsignedPacket;
+ m_keyChain.sign(packet, signingByIdentity(subSelfSignedIdentity));
+ VALIDATE_FAILURE(packet, "Should fail, because subSelfSignedIdentity is not a trust anchor");
+
+ // TODO add checks with malformed packets
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestValidator
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validator-config/checker.t.cpp b/tests/unit/security/v2/validator-config/checker.t.cpp
new file mode 100644
index 0000000..2170908
--- /dev/null
+++ b/tests/unit/security/v2/validator-config/checker.t.cpp
@@ -0,0 +1,374 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/validator-config/checker.hpp"
+#include "security/command-interest-signer.hpp"
+#include "security/v2/validation-policy.hpp"
+#include "security/v2/validation-state.hpp"
+
+#include "boost-test.hpp"
+#include "common.hpp"
+#include "identity-management-fixture.hpp"
+#include "../validator-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+namespace tests {
+
+using namespace ndn::tests;
+using namespace ndn::security::v2::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(ValidatorConfig)
+
+class CheckerFixture : public IdentityManagementFixture
+{
+public:
+ CheckerFixture()
+ {
+ names.push_back("/foo/bar");
+ names.push_back("/foo/bar/bar");
+ names.push_back("/foo");
+ names.push_back("/other/prefix");
+ }
+
+ Name
+ makeSignedInterestName(const Name& name)
+ {
+ return Name(name).append("SignatureInfo").append("SignatureValue");
+ }
+
+ Name
+ makeKeyLocatorName(const Name& name)
+ {
+ return Name(name).append("KEY").append("v=1");
+ }
+
+public:
+ std::vector<Name> names;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestChecker, CheckerFixture)
+
+class NameRelationEqual : public CheckerFixture
+{
+public:
+ NameRelationEqual()
+ : checker("/foo/bar", NameRelation::EQUAL)
+ {
+ }
+
+public:
+ NameRelationChecker checker;
+ std::vector<std::vector<bool>> outcomes = {{true, false, false, false},
+ {true, false, false, false},
+ {true, false, false, false},
+ {true, false, false, false}};
+};
+
+class NameRelationIsPrefixOf : public CheckerFixture
+{
+public:
+ NameRelationIsPrefixOf()
+ : checker("/foo/bar", NameRelation::IS_PREFIX_OF)
+ {
+ }
+
+public:
+ NameRelationChecker checker;
+ std::vector<std::vector<bool>> outcomes = {{true, true, false, false},
+ {true, true, false, false},
+ {true, true, false, false},
+ {true, true, false, false}};
+};
+
+class NameRelationIsStrictPrefixOf : public CheckerFixture
+{
+public:
+ NameRelationIsStrictPrefixOf()
+ : checker("/foo/bar", NameRelation::IS_STRICT_PREFIX_OF)
+ {
+ }
+
+public:
+ NameRelationChecker checker;
+ std::vector<std::vector<bool>> outcomes = {{false, true, false, false},
+ {false, true, false, false},
+ {false, true, false, false},
+ {false, true, false, false}};
+};
+
+class RegexEqual : public CheckerFixture
+{
+public:
+ RegexEqual()
+ : checker(Regex("^<foo><bar><KEY><>$"))
+ {
+ }
+
+public:
+ RegexChecker checker;
+ std::vector<std::vector<bool>> outcomes = {{true, false, false, false},
+ {true, false, false, false},
+ {true, false, false, false},
+ {true, false, false, false}};
+};
+
+class RegexIsPrefixOf : public CheckerFixture
+{
+public:
+ RegexIsPrefixOf()
+ : checker(Regex("^<foo><bar><>*<KEY><>$"))
+ {
+ }
+
+public:
+ RegexChecker checker;
+ std::vector<std::vector<bool>> outcomes = {{true, true, false, false},
+ {true, true, false, false},
+ {true, true, false, false},
+ {true, true, false, false}};
+};
+
+class RegexIsStrictPrefixOf : public CheckerFixture
+{
+public:
+ RegexIsStrictPrefixOf()
+ : checker(Regex("^<foo><bar><>+<KEY><>$"))
+ {
+ }
+
+public:
+ RegexChecker checker;
+ std::vector<std::vector<bool>> outcomes = {{false, true, false, false},
+ {false, true, false, false},
+ {false, true, false, false},
+ {false, true, false, false}};
+};
+
+class HyperRelationEqual : public CheckerFixture
+{
+public:
+ HyperRelationEqual()
+ : checker("^(<>+)$", "\\1", "^(<>+)<KEY><>$", "\\1", NameRelation::EQUAL)
+ {
+ }
+
+public:
+ HyperRelationChecker checker;
+ std::vector<std::vector<bool>> outcomes = {{true, false, false, false},
+ {false, true, false, false},
+ {false, false, true, false},
+ {false, false, false, true}};
+};
+
+class HyperRelationIsPrefixOf : public CheckerFixture
+{
+public:
+ HyperRelationIsPrefixOf()
+ : checker("^(<>+)$", "\\1", "^(<>+)<KEY><>$", "\\1", NameRelation::IS_PREFIX_OF)
+ {
+ }
+
+public:
+ HyperRelationChecker checker;
+ std::vector<std::vector<bool>> outcomes = {{true, false, true, false},
+ {true, true, true, false},
+ {false, false, true, false},
+ {false, false, false, true}};
+};
+
+class HyperRelationIsStrictPrefixOf : public CheckerFixture
+{
+public:
+ HyperRelationIsStrictPrefixOf()
+ : checker("^(<>+)$", "\\1", "^(<>+)<KEY><>$", "\\1", NameRelation::IS_STRICT_PREFIX_OF)
+ {
+ }
+
+public:
+ HyperRelationChecker checker;
+ std::vector<std::vector<bool>> outcomes = {{false, false, true, false},
+ {true, false, true, false},
+ {false, false, false, false},
+ {false, false, false, false}};
+};
+
+class Hierarchical : public CheckerFixture
+{
+public:
+ Hierarchical()
+ : checkerPtr(Checker::create(makeSection(R"CONF(
+ type hierarchical
+ sig-type rsa-sha256
+ )CONF"), "test-config"))
+ , checker(*checkerPtr)
+ {
+ }
+
+public:
+ std::unique_ptr<Checker> checkerPtr;
+ Checker& checker;
+
+ std::vector<std::vector<bool>> outcomes = {{true, false, true, false},
+ {true, true, true, false},
+ {false, false, true, false},
+ {false, false, false, true}};
+};
+
+class CustomizedNameRelation : public CheckerFixture
+{
+public:
+ CustomizedNameRelation()
+ : checkerPtr(Checker::create(makeSection(R"CONF(
+ type customized
+ sig-type rsa-sha256
+ key-locator
+ {
+ type name
+ name /foo/bar
+ relation equal
+ }
+ )CONF"), "test-config"))
+ , checker(*checkerPtr)
+ {
+ }
+
+public:
+ std::unique_ptr<Checker> checkerPtr;
+ Checker& checker;
+
+ std::vector<std::vector<bool>> outcomes = {{true, false, false, false},
+ {true, false, false, false},
+ {true, false, false, false},
+ {true, false, false, false}};
+};
+
+class CustomizedRegex : public CheckerFixture
+{
+public:
+ CustomizedRegex()
+ : checkerPtr(Checker::create(makeSection(R"CONF(
+ type customized
+ sig-type rsa-sha256
+ key-locator
+ {
+ type name
+ regex ^<foo><bar><KEY><>$
+ }
+ )CONF"), "test-config"))
+ , checker(*checkerPtr)
+ {
+ }
+
+public:
+ std::unique_ptr<Checker> checkerPtr;
+ Checker& checker;
+
+ std::vector<std::vector<bool>> outcomes = {{true, false, false, false},
+ {true, false, false, false},
+ {true, false, false, false},
+ {true, false, false, false}};
+};
+
+class CustomizedHyperRelation : public CheckerFixture
+{
+public:
+ CustomizedHyperRelation()
+ : checkerPtr(Checker::create(makeSection(R"CONF(
+ type customized
+ sig-type rsa-sha256
+ key-locator
+ {
+ type name
+ hyper-relation
+ {
+ k-regex ^(<>+)<KEY><>$
+ k-expand \\1
+ h-relation is-prefix-of
+ p-regex ^(<>+)$
+ p-expand \\1
+ }
+ }
+ )CONF"), "test-config"))
+ , checker(*checkerPtr)
+ {
+ }
+
+public:
+ std::unique_ptr<Checker> checkerPtr;
+ Checker& checker;
+
+ std::vector<std::vector<bool>> outcomes = {{true, false, true, false},
+ {true, true, true, false},
+ {false, false, true, false},
+ {false, false, false, true}};
+};
+
+
+using Tests = boost::mpl::vector<NameRelationEqual, NameRelationIsPrefixOf, NameRelationIsStrictPrefixOf,
+ RegexEqual, RegexIsPrefixOf, RegexIsStrictPrefixOf,
+ HyperRelationEqual, HyperRelationIsPrefixOf, HyperRelationIsStrictPrefixOf,
+ Hierarchical,
+ CustomizedNameRelation, CustomizedRegex, CustomizedHyperRelation>;
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(Checks, T, Tests, T)
+{
+ BOOST_ASSERT(this->outcomes.size() == this->names.size());
+ for (size_t i = 0; i < this->names.size(); ++i) {
+ BOOST_ASSERT(this->outcomes[i].size() == this->names.size());
+ for (size_t j = 0; j < this->names.size(); ++j) {
+ const Name& pktName = this->names[i];
+ Name klName = this->makeKeyLocatorName(this->names[j]);
+ bool expectedOutcome = this->outcomes[i][j];
+
+ auto dataState = make_shared<DummyValidationState>();
+ BOOST_CHECK_EQUAL(this->checker.check(tlv::Data, pktName, klName, dataState), expectedOutcome);
+ BOOST_CHECK_EQUAL(boost::logic::indeterminate(dataState->getOutcome()), expectedOutcome);
+ if (boost::logic::indeterminate(dataState->getOutcome()) == !expectedOutcome) {
+ BOOST_CHECK_EQUAL(dataState->getOutcome(), !expectedOutcome);
+ }
+
+ auto interestState = make_shared<DummyValidationState>();
+ BOOST_CHECK_EQUAL(this->checker.check(tlv::Interest, this->makeSignedInterestName(pktName),
+ klName, interestState), expectedOutcome);
+ BOOST_CHECK_EQUAL(boost::logic::indeterminate(interestState->getOutcome()), expectedOutcome);
+ if (boost::logic::indeterminate(interestState->getOutcome()) == !expectedOutcome) {
+ BOOST_CHECK_EQUAL(interestState->getOutcome(), !expectedOutcome);
+ }
+ }
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestChecker
+BOOST_AUTO_TEST_SUITE_END() // ValidatorConfig
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validator-config/common.hpp b/tests/unit/security/v2/validator-config/common.hpp
new file mode 100644
index 0000000..231e8eb
--- /dev/null
+++ b/tests/unit/security/v2/validator-config/common.hpp
@@ -0,0 +1,48 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_TESTS_SECURITY_V2_VALIDATOR_CONFIG_COMMON_HPP
+#define NDN_TESTS_SECURITY_V2_VALIDATOR_CONFIG_COMMON_HPP
+
+#include <boost/property_tree/info_parser.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+namespace tests {
+
+inline ConfigSection
+makeSection(const std::string& config)
+{
+ std::istringstream inputStream(config);
+ ConfigSection section;
+ boost::property_tree::read_info(inputStream, section);
+ return section;
+}
+
+} // namespace tests
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_TESTS_SECURITY_V2_VALIDATOR_CONFIG_COMMON_HPP
diff --git a/tests/unit/security/v2/validator-config/filter.t.cpp b/tests/unit/security/v2/validator-config/filter.t.cpp
new file mode 100644
index 0000000..fbd1264
--- /dev/null
+++ b/tests/unit/security/v2/validator-config/filter.t.cpp
@@ -0,0 +1,201 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/validator-config/filter.hpp"
+#include "security/command-interest-signer.hpp"
+
+#include "boost-test.hpp"
+#include "common.hpp"
+#include "identity-management-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(ValidatorConfig)
+
+class FilterFixture : public IdentityManagementFixture
+{
+public:
+ Interest
+ makeSignedInterest(const Name& name)
+ {
+ Interest interest(name);
+ m_keyChain.sign(interest);
+ return interest;
+ }
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestFilter, FilterFixture)
+
+#define CHECK_FOR_MATCHES(filter, same, longer, shorter, different) \
+ BOOST_CHECK_EQUAL(filter.match(tlv::Interest, makeSignedInterest("/foo/bar").getName()), same); \
+ BOOST_CHECK_EQUAL(filter.match(tlv::Data, Data("/foo/bar").getName()), same); \
+ BOOST_CHECK_EQUAL(filter.match(tlv::Interest, makeSignedInterest("/foo/bar/bar").getName()), longer); \
+ BOOST_CHECK_EQUAL(filter.match(tlv::Data, Data("/foo/bar/bar").getName()), longer); \
+ BOOST_CHECK_EQUAL(filter.match(tlv::Interest, makeSignedInterest("/foo").getName()), shorter); \
+ BOOST_CHECK_EQUAL(filter.match(tlv::Data, Data("/foo").getName()), shorter); \
+ BOOST_CHECK_EQUAL(filter.match(tlv::Interest, makeSignedInterest("/other/prefix").getName()), different); \
+ BOOST_CHECK_EQUAL(filter.match(tlv::Data, Data("/other/prefix").getName()), different);
+
+BOOST_AUTO_TEST_CASE(RelationName)
+{
+ RelationNameFilter f1("/foo/bar", NameRelation::EQUAL);
+ CHECK_FOR_MATCHES(f1, true, false, false, false);
+
+ RelationNameFilter f2("/foo/bar", NameRelation::IS_PREFIX_OF);
+ CHECK_FOR_MATCHES(f2, true, true, false, false);
+
+ RelationNameFilter f3("/foo/bar", NameRelation::IS_STRICT_PREFIX_OF);
+ CHECK_FOR_MATCHES(f3, false, true, false, false);
+}
+
+BOOST_AUTO_TEST_CASE(RegexName)
+{
+ RegexNameFilter f1(Regex("^<foo><bar>$"));
+ CHECK_FOR_MATCHES(f1, true, false, false, false);
+
+ RegexNameFilter f2(Regex("^<foo><bar><>*$"));
+ CHECK_FOR_MATCHES(f2, true, true, false, false);
+
+ RegexNameFilter f3(Regex("^<foo><bar><>+$"));
+ CHECK_FOR_MATCHES(f3, false, true, false, false);
+}
+
+BOOST_FIXTURE_TEST_SUITE(Create, FilterFixture)
+
+BOOST_AUTO_TEST_CASE(Errors)
+{
+ BOOST_CHECK_THROW(Filter::create(makeSection(""), "test-config"), Error);
+ BOOST_CHECK_THROW(Filter::create(makeSection("type unknown"), "test-config"), Error);
+ BOOST_CHECK_THROW(Filter::create(makeSection("type name"), "test-config"), Error);
+
+ std::string config = R"CONF(
+ type name
+ not-name-or-regex stuff
+ )CONF";
+ BOOST_CHECK_THROW(Filter::create(makeSection(config), "test-config"), Error);
+
+ config = R"CONF(
+ type name
+ name /foo/bar
+ )CONF";
+ BOOST_CHECK_THROW(Filter::create(makeSection(config), "test-config"), Error);
+
+ config = R"CONF(
+ type name
+ name /foo/bar
+ not-relation stuff
+ )CONF";
+ BOOST_CHECK_THROW(Filter::create(makeSection(config), "test-config"), Error);
+
+ config = R"CONF(
+ type name
+ name /foo/bar
+ relation equal
+ not-end stuff
+ )CONF";
+ BOOST_CHECK_THROW(Filter::create(makeSection(config), "test-config"), Error);
+
+ config = R"CONF(
+ type name
+ regex ^<foo><bar>$
+ not-end stuff
+ )CONF";
+ BOOST_CHECK_THROW(Filter::create(makeSection(config), "test-config"), Error);
+}
+
+BOOST_AUTO_TEST_CASE(NameFilter)
+{
+ std::string config = R"CONF(
+ type name
+ name /foo/bar
+ relation equal
+ )CONF";
+ auto f1 = Filter::create(makeSection(config), "test-config");
+ CHECK_FOR_MATCHES((*f1), true, false, false, false);
+
+ config = R"CONF(
+ type name
+ name /foo/bar
+ relation is-prefix-of
+ )CONF";
+ auto f2 = Filter::create(makeSection(config), "test-config");
+ CHECK_FOR_MATCHES((*f2), true, true, false, false);
+
+ config = R"CONF(
+ type name
+ name /foo/bar
+ relation is-strict-prefix-of
+ )CONF";
+ auto f3 = Filter::create(makeSection(config), "test-config");
+ CHECK_FOR_MATCHES((*f3), false, true, false, false);
+}
+
+BOOST_AUTO_TEST_CASE(RegexFilter)
+{
+ std::string config = R"CONF(
+ type name
+ regex ^<foo><bar>$
+ )CONF";
+ auto f1 = Filter::create(makeSection(config), "test-config");
+ CHECK_FOR_MATCHES((*f1), true, false, false, false);
+
+ config = R"CONF(
+ type name
+ regex ^<foo><bar><>*$
+ )CONF";
+ auto f2 = Filter::create(makeSection(config), "test-config");
+ CHECK_FOR_MATCHES((*f2), true, true, false, false);
+
+ config = R"CONF(
+ type name
+ regex ^<foo><bar><>+$
+ )CONF";
+ auto f3 = Filter::create(makeSection(config), "test-config");
+ CHECK_FOR_MATCHES((*f3), false, true, false, false);
+
+ config = R"CONF(
+ type name
+ regex ^<>*$
+ )CONF";
+ auto f4 = Filter::create(makeSection(config), "test-config");
+ CHECK_FOR_MATCHES((*f4), true, true, true, true);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Create
+
+BOOST_AUTO_TEST_SUITE_END() // TestFilter
+BOOST_AUTO_TEST_SUITE_END() // ValidatorConfig
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validator-config/name-relation.t.cpp b/tests/unit/security/v2/validator-config/name-relation.t.cpp
new file mode 100644
index 0000000..157b55a
--- /dev/null
+++ b/tests/unit/security/v2/validator-config/name-relation.t.cpp
@@ -0,0 +1,79 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/validator-config/name-relation.hpp"
+
+#include "boost-test.hpp"
+
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(ValidatorConfig)
+BOOST_AUTO_TEST_SUITE(TestNameRelation)
+
+BOOST_AUTO_TEST_CASE(ToString)
+{
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(NameRelation::EQUAL), "equal");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(NameRelation::IS_PREFIX_OF),
+ "is-prefix-of");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(NameRelation::IS_STRICT_PREFIX_OF),
+ "is-strict-prefix-of");
+}
+
+BOOST_AUTO_TEST_CASE(FromString)
+{
+ BOOST_CHECK_EQUAL(getNameRelationFromString("equal"), NameRelation::EQUAL);
+ BOOST_CHECK_EQUAL(getNameRelationFromString("is-prefix-of"), NameRelation::IS_PREFIX_OF);
+ BOOST_CHECK_EQUAL(getNameRelationFromString("is-strict-prefix-of"), NameRelation::IS_STRICT_PREFIX_OF);
+ BOOST_CHECK_THROW(getNameRelationFromString("unknown"), validator_config::Error);
+}
+
+BOOST_AUTO_TEST_CASE(CheckRelation)
+{
+ BOOST_CHECK(checkNameRelation(NameRelation::EQUAL, "/prefix", "/prefix"));
+ BOOST_CHECK(!checkNameRelation(NameRelation::EQUAL, "/prefix", "/prefix/other"));
+
+ BOOST_CHECK(checkNameRelation(NameRelation::IS_PREFIX_OF, "/prefix", "/prefix"));
+ BOOST_CHECK(checkNameRelation(NameRelation::IS_PREFIX_OF, "/prefix", "/prefix/other"));
+ BOOST_CHECK(!checkNameRelation(NameRelation::IS_PREFIX_OF, "/prefix/other", "/prefix"));
+
+ BOOST_CHECK(!checkNameRelation(NameRelation::IS_STRICT_PREFIX_OF, "/prefix", "/prefix"));
+ BOOST_CHECK(checkNameRelation(NameRelation::IS_STRICT_PREFIX_OF, "/prefix", "/prefix/other"));
+ BOOST_CHECK(!checkNameRelation(NameRelation::IS_STRICT_PREFIX_OF, "/prefix/other", "/prefix"));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestNameRelation
+BOOST_AUTO_TEST_SUITE_END() // ValidatorConfig
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validator-config/rule.t.cpp b/tests/unit/security/v2/validator-config/rule.t.cpp
new file mode 100644
index 0000000..bb78dfe
--- /dev/null
+++ b/tests/unit/security/v2/validator-config/rule.t.cpp
@@ -0,0 +1,209 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/validator-config/rule.hpp"
+
+#include "boost-test.hpp"
+#include "common.hpp"
+#include "identity-management-fixture.hpp"
+#include "../validator-fixture.hpp"
+
+#include <boost/mpl/vector_c.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+namespace tests {
+
+using namespace ndn::tests;
+using namespace ndn::security::v2::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(ValidatorConfig)
+
+template<uint32_t PktType>
+class RuleFixture : public IdentityManagementFixture
+{
+public:
+ RuleFixture()
+ : rule(ruleId, PktType)
+ , pktName("/foo/bar")
+ {
+ if (PktType == tlv::Interest) {
+ pktName = Name("/foo/bar/SigInfo/SigValue");
+ }
+ }
+
+public:
+ const std::string ruleId = "rule-id";
+ Rule rule;
+ Name pktName;
+};
+
+using PktTypes = boost::mpl::vector_c<uint32_t, tlv::Data, tlv::Interest>;
+
+BOOST_AUTO_TEST_SUITE(TestRule)
+
+BOOST_FIXTURE_TEST_CASE(Errors, RuleFixture<tlv::Data>)
+{
+ BOOST_CHECK_THROW(rule.match(tlv::Interest, this->pktName), Error);
+
+ auto state = make_shared<DummyValidationState>();
+ BOOST_CHECK_THROW(rule.check(tlv::Interest, this->pktName, "/foo/bar", state), Error);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(Constructor, PktType, PktTypes, RuleFixture<PktType::value>)
+{
+ BOOST_CHECK_EQUAL(this->rule.getId(), this->ruleId);
+ BOOST_CHECK_EQUAL(this->rule.getPktType(), PktType::value);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(EmptyRule, PktType, PktTypes, RuleFixture<PktType::value>)
+{
+ BOOST_CHECK_EQUAL(this->rule.match(PktType::value, this->pktName), true);
+
+ auto state = make_shared<DummyValidationState>();
+ BOOST_CHECK_EQUAL(this->rule.check(PktType::value, this->pktName, "/foo/bar", state), false);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(Filters, PktType, PktTypes, RuleFixture<PktType::value>)
+{
+ this->rule.addFilter(make_unique<RegexNameFilter>(Regex("^<foo><bar>$")));
+
+ BOOST_CHECK_EQUAL(this->rule.match(PktType::value, this->pktName), true);
+ BOOST_CHECK_EQUAL(this->rule.match(PktType::value, "/not" + this->pktName.toUri()), false);
+
+ this->rule.addFilter(make_unique<RegexNameFilter>(Regex("^<not><foo><bar>$")));
+
+ BOOST_CHECK_EQUAL(this->rule.match(PktType::value, this->pktName), true);
+ BOOST_CHECK_EQUAL(this->rule.match(PktType::value, "/not" + this->pktName.toUri()), true);
+
+ auto state = make_shared<DummyValidationState>();
+ BOOST_CHECK_EQUAL(this->rule.check(PktType::value, this->pktName, "/foo/bar", state), false);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(Checkers, PktType, PktTypes, RuleFixture<PktType::value>)
+{
+ this->rule.addChecker(make_unique<HyperRelationChecker>("^(<>+)$", "\\1",
+ "^<not>?(<>+)$", "\\1",
+ NameRelation::EQUAL));
+
+ auto state = make_shared<DummyValidationState>();
+ BOOST_CHECK_EQUAL(this->rule.check(PktType::value, this->pktName, "/foo/bar", state), true);
+
+ state = make_shared<DummyValidationState>();
+ BOOST_CHECK_EQUAL(this->rule.check(PktType::value, this->pktName, "/not/foo/bar", state), true);
+
+ this->rule.addChecker(make_unique<HyperRelationChecker>("^(<>+)$", "\\1",
+ "^(<>+)$", "\\1",
+ NameRelation::EQUAL));
+ state = make_shared<DummyValidationState>();
+ BOOST_CHECK_EQUAL(this->rule.check(PktType::value, this->pktName, "/foo/bar", state), true);
+
+ state = make_shared<DummyValidationState>();
+ BOOST_CHECK_EQUAL(this->rule.check(PktType::value, this->pktName, "/not/foo/bar", state), false);
+}
+
+BOOST_AUTO_TEST_SUITE(Create)
+
+BOOST_AUTO_TEST_CASE(Errors)
+{
+ BOOST_CHECK_THROW(Rule::create(makeSection(""), "test-config"), Error);
+
+ std::string config = R"CONF(
+ id rule-id
+ for something
+ )CONF";
+ BOOST_CHECK_THROW(Rule::create(makeSection(config), "test-config"), Error);
+
+ config = R"CONF(
+ id rule-id
+ for data
+ )CONF";
+ BOOST_CHECK_THROW(Rule::create(makeSection(config), "test-config"), Error); // at least one checker required
+
+ config = R"CONF(
+ id rule-id
+ for data
+ checker
+ {
+ type hierarchical
+ sig-type rsa-sha256
+ }
+ other stuff
+ )CONF";
+ BOOST_CHECK_THROW(Rule::create(makeSection(config), "test-config"), Error);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(FilterAndChecker, PktType, PktTypes, RuleFixture<PktType::value>)
+{
+ std::string config = R"CONF(
+ id rule-id
+ for )CONF" + (PktType::value == tlv::Data ? "data"s : "interest"s) + R"CONF(
+ filter
+ {
+ type name
+ regex ^<foo><bar>$
+ }
+ checker
+ {
+ type customized
+ sig-type rsa-sha256
+ key-locator
+ {
+ type name
+ hyper-relation
+ {
+ k-regex ^(<>+)$
+ k-expand \\1
+ h-relation equal
+ p-regex ^(<>+)$
+ p-expand \\1
+ }
+ }
+ }
+ )CONF";
+ auto rule = Rule::create(makeSection(config), "test-config");
+
+ BOOST_CHECK_EQUAL(rule->match(PktType::value, this->pktName), true);
+ BOOST_CHECK_EQUAL(rule->match(PktType::value, "/not" + this->pktName.toUri()), false);
+
+ auto state = make_shared<DummyValidationState>();
+ BOOST_CHECK_EQUAL(rule->check(PktType::value, this->pktName, "/foo/bar", state), true);
+
+ state = make_shared<DummyValidationState>();
+ BOOST_CHECK_EQUAL(rule->check(PktType::value, this->pktName, "/not/foo/bar", state), false);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Create
+
+BOOST_AUTO_TEST_SUITE_END() // TestRule
+BOOST_AUTO_TEST_SUITE_END() // ValidatorConfig
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validator-fixture.hpp b/tests/unit/security/v2/validator-fixture.hpp
new file mode 100644
index 0000000..c9a5763
--- /dev/null
+++ b/tests/unit/security/v2/validator-fixture.hpp
@@ -0,0 +1,183 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_TESTS_SECURITY_V2_VALIDATOR_FIXTURE_HPP
+#define NDN_TESTS_SECURITY_V2_VALIDATOR_FIXTURE_HPP
+
+#include "security/v2/validator.hpp"
+#include "security/v2/certificate-fetcher-from-network.hpp"
+#include "util/dummy-client-face.hpp"
+
+#include "../../identity-management-time-fixture.hpp"
+
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+template<class ValidationPolicy, class CertificateFetcher = CertificateFetcherFromNetwork>
+class ValidatorFixture : public ndn::tests::IdentityManagementTimeFixture
+{
+public:
+ ValidatorFixture()
+ : face(io, {true, true})
+ , validator(make_unique<ValidationPolicy>(), make_unique<CertificateFetcher>(face))
+ , policy(static_cast<ValidationPolicy&>(validator.getPolicy()))
+ , cache(100_days)
+ {
+ processInterest = [this] (const Interest& interest) {
+ auto cert = cache.find(interest);
+ if (cert != nullptr) {
+ face.receive(*cert);
+ }
+ };
+ }
+
+ virtual
+ ~ValidatorFixture() = default;
+
+ template<class Packet>
+ void
+ validate(const Packet& packet, const std::string& msg, bool expectSuccess, int line)
+ {
+ std::string detailedInfo = msg + " on line " + to_string(line);
+ size_t nCallbacks = 0;
+ this->validator.validate(packet,
+ [&] (const Packet&) {
+ ++nCallbacks;
+ BOOST_CHECK_MESSAGE(expectSuccess,
+ (expectSuccess ? "OK: " : "FAILED: ") + detailedInfo);
+ },
+ [&] (const Packet&, const ValidationError& error) {
+ ++nCallbacks;
+ BOOST_CHECK_MESSAGE(!expectSuccess,
+ (!expectSuccess ? "OK: " : "FAILED: ") + detailedInfo +
+ (expectSuccess ? " (" + boost::lexical_cast<std::string>(error) + ")" : ""));
+ });
+
+ mockNetworkOperations();
+ BOOST_CHECK_EQUAL(nCallbacks, 1);
+ }
+
+ void
+ mockNetworkOperations()
+ {
+ util::signal::ScopedConnection connection = face.onSendInterest.connect([this] (const Interest& interest) {
+ if (processInterest != nullptr) {
+ io.post(bind(processInterest, interest));
+ }
+ });
+ advanceClocks(time::milliseconds(s_mockPeriod), s_mockTimes);
+ }
+
+ /** \brief undo clock advancement of mockNetworkOperations
+ */
+ void
+ rewindClockAfterValidation()
+ {
+ this->systemClock->advance(time::milliseconds(s_mockPeriod * s_mockTimes * -1));
+ }
+
+public:
+ util::DummyClientFace face;
+ std::function<void(const Interest& interest)> processInterest;
+ Validator validator;
+ ValidationPolicy& policy;
+
+ CertificateCache cache;
+
+private:
+ const static int s_mockPeriod;
+ const static int s_mockTimes;
+};
+
+template<class ValidationPolicy, class CertificateFetcher>
+const int ValidatorFixture<ValidationPolicy, CertificateFetcher>::s_mockPeriod = 250;
+
+template<class ValidationPolicy, class CertificateFetcher>
+const int ValidatorFixture<ValidationPolicy, CertificateFetcher>::s_mockTimes = 200;
+
+template<class ValidationPolicy, class CertificateFetcher = CertificateFetcherFromNetwork>
+class HierarchicalValidatorFixture : public ValidatorFixture<ValidationPolicy, CertificateFetcher>
+{
+public:
+ HierarchicalValidatorFixture()
+ {
+ identity = this->addIdentity("/Security/V2/ValidatorFixture");
+ subIdentity = this->addSubCertificate("/Security/V2/ValidatorFixture/Sub1", identity);
+ subSelfSignedIdentity = this->addIdentity("/Security/V2/ValidatorFixture/Sub1/Sub2");
+ otherIdentity = this->addIdentity("/Security/V2/OtherIdentity");
+
+ this->validator.loadAnchor("", Certificate(identity.getDefaultKey().getDefaultCertificate()));
+
+ this->cache.insert(identity.getDefaultKey().getDefaultCertificate());
+ this->cache.insert(subIdentity.getDefaultKey().getDefaultCertificate());
+ this->cache.insert(subSelfSignedIdentity.getDefaultKey().getDefaultCertificate());
+ this->cache.insert(otherIdentity.getDefaultKey().getDefaultCertificate());
+ }
+
+public:
+ Identity identity;
+ Identity subIdentity;
+ Identity subSelfSignedIdentity;
+ Identity otherIdentity;
+};
+
+#define VALIDATE_SUCCESS(packet, message) this->template validate(packet, message, true, __LINE__)
+#define VALIDATE_FAILURE(packet, message) this->template validate(packet, message, false, __LINE__)
+
+class DummyValidationState : public ValidationState
+{
+public:
+ ~DummyValidationState()
+ {
+ m_outcome = false;
+ }
+
+ void
+ fail(const ValidationError& error) override
+ {
+ // BOOST_TEST_MESSAGE(error);
+ m_outcome = false;
+ }
+
+private:
+ void
+ verifyOriginalPacket(const Certificate& trustedCert) override
+ {
+ // do nothing
+ }
+
+ void
+ bypassValidation() override
+ {
+ // do nothing
+ }
+};
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_TESTS_SECURITY_V2_VALIDATOR_FIXTURE_HPP
diff --git a/tests/unit/security/v2/validator.t.cpp b/tests/unit/security/v2/validator.t.cpp
new file mode 100644
index 0000000..ff33984
--- /dev/null
+++ b/tests/unit/security/v2/validator.t.cpp
@@ -0,0 +1,344 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/validator.hpp"
+#include "security/v2/validation-policy-simple-hierarchy.hpp"
+
+#include "boost-test.hpp"
+#include "validator-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_FIXTURE_TEST_SUITE(TestValidator, HierarchicalValidatorFixture<ValidationPolicySimpleHierarchy>)
+
+BOOST_AUTO_TEST_CASE(ConstructorSetValidator)
+{
+ auto middlePolicy = make_unique<ValidationPolicySimpleHierarchy>();
+ auto innerPolicy = make_unique<ValidationPolicySimpleHierarchy>();
+
+ validator.getPolicy().setInnerPolicy(std::move(middlePolicy));
+ validator.getPolicy().setInnerPolicy(std::move(innerPolicy));
+
+ BOOST_CHECK(validator.getPolicy().m_validator != nullptr);
+ BOOST_CHECK(validator.getPolicy().getInnerPolicy().m_validator != nullptr);
+ BOOST_CHECK(validator.getPolicy().getInnerPolicy().getInnerPolicy().m_validator != nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(Timeouts)
+{
+ processInterest = nullptr; // no response for all interests
+
+ Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+ m_keyChain.sign(data, signingByIdentity(subIdentity));
+
+ VALIDATE_FAILURE(data, "Should fail to retrieve certificate");
+ BOOST_CHECK_GT(face.sentInterests.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(NackedInterests)
+{
+ processInterest = [this] (const Interest& interest) {
+ lp::Nack nack(interest);
+ nack.setReason(lp::NackReason::NO_ROUTE);
+ face.receive(nack);
+ };
+
+ Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+ m_keyChain.sign(data, signingByIdentity(subIdentity));
+
+ VALIDATE_FAILURE(data, "All interests should get NACKed");
+ // 1 for the first interest, 3 for the retries on nack
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 4);
+}
+
+BOOST_AUTO_TEST_CASE(MalformedCert)
+{
+ Data malformedCert = subIdentity.getDefaultKey().getDefaultCertificate();
+ malformedCert.setContentType(tlv::ContentType_Blob);
+ m_keyChain.sign(malformedCert, signingByIdentity(identity));
+ // wrong content type & missing ValidityPeriod
+ BOOST_REQUIRE_THROW(Certificate(malformedCert.wireEncode()), tlv::Error);
+
+ auto originalProcessInterest = processInterest;
+ processInterest = [this, &originalProcessInterest, &malformedCert] (const Interest& interest) {
+ if (interest.getName().isPrefixOf(malformedCert.getName())) {
+ face.receive(malformedCert);
+ }
+ else {
+ originalProcessInterest(interest);
+ }
+ };
+
+ Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+ m_keyChain.sign(data, signingByIdentity(subIdentity));
+
+ VALIDATE_FAILURE(data, "Signed by a malformed certificate");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(ExpiredCert)
+{
+ Data expiredCert = subIdentity.getDefaultKey().getDefaultCertificate();
+ SignatureInfo info;
+ info.setValidityPeriod(ValidityPeriod(time::system_clock::now() - 2_h,
+ time::system_clock::now() - 1_h));
+ m_keyChain.sign(expiredCert, signingByIdentity(identity).setSignatureInfo(info));
+ BOOST_REQUIRE_NO_THROW(Certificate(expiredCert.wireEncode()));
+
+ auto originalProcessInterest = processInterest;
+ processInterest = [this, &originalProcessInterest, &expiredCert] (const Interest& interest) {
+ if (interest.getName().isPrefixOf(expiredCert.getName())) {
+ face.receive(expiredCert);
+ }
+ else {
+ originalProcessInterest(interest);
+ }
+ };
+
+ Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+ m_keyChain.sign(data, signingByIdentity(subIdentity));
+
+ VALIDATE_FAILURE(data, "Signed by an expired certificate");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(ResetAnchors)
+{
+ validator.resetAnchors();
+
+ Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+ m_keyChain.sign(data, signingByIdentity(subIdentity));
+ VALIDATE_FAILURE(data, "Should fail, as no anchors configured");
+}
+
+BOOST_AUTO_TEST_CASE(TrustedCertCaching)
+{
+ Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+ m_keyChain.sign(data, signingByIdentity(subIdentity));
+
+ VALIDATE_SUCCESS(data, "Should get accepted, as signed by the policy-compliant cert");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
+ face.sentInterests.clear();
+
+ processInterest = nullptr; // disable data responses from mocked network
+
+ VALIDATE_SUCCESS(data, "Should get accepted, based on the cached trusted cert");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
+ face.sentInterests.clear();
+
+ advanceClocks(1_h, 2); // expire trusted cache
+
+ VALIDATE_FAILURE(data, "Should try and fail to retrieve certs");
+ BOOST_CHECK_GT(face.sentInterests.size(), 1);
+ face.sentInterests.clear();
+}
+
+BOOST_AUTO_TEST_CASE(ResetVerifiedCertificates)
+{
+ Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+ m_keyChain.sign(data, signingByIdentity(subIdentity));
+ VALIDATE_SUCCESS(data, "Should get accepted, as signed by the policy-compliant cert");
+
+ // reset anchors
+ validator.resetAnchors();
+ VALIDATE_SUCCESS(data, "Should get accepted, as signed by the cert in trusted cache");
+
+ // reset trusted cache
+ validator.resetVerifiedCertificates();
+ VALIDATE_FAILURE(data, "Should fail, as no trusted cache or anchors");
+}
+
+BOOST_AUTO_TEST_CASE(UntrustedCertCaching)
+{
+ Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+ m_keyChain.sign(data, signingByIdentity(subSelfSignedIdentity));
+
+ VALIDATE_FAILURE(data, "Should fail, as signed by the policy-violating cert");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
+ face.sentInterests.clear();
+
+ processInterest = nullptr; // disable data responses from mocked network
+
+ VALIDATE_FAILURE(data, "Should fail again, but no network operations expected");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
+ face.sentInterests.clear();
+
+ advanceClocks(10_min, 2); // expire untrusted cache
+
+ VALIDATE_FAILURE(data, "Should try and fail to retrieve certs");
+ BOOST_CHECK_GT(face.sentInterests.size(), 1);
+ face.sentInterests.clear();
+}
+
+class ValidationPolicySimpleHierarchyForInterestOnly : public ValidationPolicySimpleHierarchy
+{
+public:
+ void
+ checkPolicy(const Data& data, const shared_ptr<ValidationState>& state,
+ const ValidationContinuation& continueValidation) override
+ {
+ continueValidation(nullptr, state);
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(ValidateInterestsButBypassForData,
+ HierarchicalValidatorFixture<ValidationPolicySimpleHierarchyForInterestOnly>)
+{
+ Interest interest("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest");
+ Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest");
+
+ VALIDATE_FAILURE(interest, "Unsigned");
+ VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
+ face.sentInterests.clear();
+
+ interest = Interest("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest");
+ m_keyChain.sign(interest, signingWithSha256());
+ m_keyChain.sign(data, signingWithSha256());
+ VALIDATE_FAILURE(interest, "Required KeyLocator/Name missing (not passed to policy)");
+ VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
+ face.sentInterests.clear();
+
+ m_keyChain.sign(interest, signingByIdentity(identity));
+ m_keyChain.sign(data, signingByIdentity(identity));
+ VALIDATE_SUCCESS(interest, "Should get accepted, as signed by the anchor");
+ VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
+ face.sentInterests.clear();
+
+ m_keyChain.sign(interest, signingByIdentity(subIdentity));
+ m_keyChain.sign(data, signingByIdentity(subIdentity));
+ VALIDATE_FAILURE(interest, "Should fail, as policy is not allowed to create new trust anchors");
+ VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
+ face.sentInterests.clear();
+
+ m_keyChain.sign(interest, signingByIdentity(otherIdentity));
+ m_keyChain.sign(data, signingByIdentity(otherIdentity));
+ VALIDATE_FAILURE(interest, "Should fail, as signed by the policy-violating cert");
+ VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
+ // no network operations expected, as certificate is not validated by the policy
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
+ face.sentInterests.clear();
+
+ advanceClocks(1_h, 2); // expire trusted cache
+
+ m_keyChain.sign(interest, signingByIdentity(subSelfSignedIdentity));
+ m_keyChain.sign(data, signingByIdentity(subSelfSignedIdentity));
+ VALIDATE_FAILURE(interest, "Should fail, as policy is not allowed to create new trust anchors");
+ VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
+ face.sentInterests.clear();
+}
+
+BOOST_AUTO_TEST_CASE(InfiniteCertChain)
+{
+ processInterest = [this] (const Interest& interest) {
+ // create another key for the same identity and sign it properly
+ Key parentKey = m_keyChain.createKey(subIdentity);
+ Key requestedKey = subIdentity.getKey(interest.getName());
+
+ Name certificateName = requestedKey.getName();
+ certificateName
+ .append("looper")
+ .appendVersion();
+ v2::Certificate certificate;
+ certificate.setName(certificateName);
+
+ // set metainfo
+ certificate.setContentType(tlv::ContentType_Key);
+ certificate.setFreshnessPeriod(1_h);
+
+ // set content
+ certificate.setContent(requestedKey.getPublicKey().data(), requestedKey.getPublicKey().size());
+
+ // set signature-info
+ SignatureInfo info;
+ info.setValidityPeriod(security::ValidityPeriod(time::system_clock::now() - 10_days,
+ time::system_clock::now() + 10_days));
+
+ m_keyChain.sign(certificate, signingByKey(parentKey).setSignatureInfo(info));
+ face.receive(certificate);
+ };
+
+ Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+ m_keyChain.sign(data, signingByIdentity(subIdentity));
+
+ validator.setMaxDepth(40);
+ BOOST_CHECK_EQUAL(validator.getMaxDepth(), 40);
+ VALIDATE_FAILURE(data, "Should fail, as certificate should be looped");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 40);
+ face.sentInterests.clear();
+
+ advanceClocks(1_h, 5); // expire caches
+
+ validator.setMaxDepth(30);
+ BOOST_CHECK_EQUAL(validator.getMaxDepth(), 30);
+ 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() - 100_days,
+ time::system_clock::now() + 100_days});
+ 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
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn