| /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ |
| /* |
| * Copyright (c) 2013-2023 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 "ndn-cxx/security/certificate-bundle-fetcher.hpp" |
| #include "ndn-cxx/security/validation-policy-simple-hierarchy.hpp" |
| #include "ndn-cxx/util/regex/regex-pattern-list-matcher.hpp" |
| |
| #include "tests/test-common.hpp" |
| #include "tests/unit/security/validator-fixture.hpp" |
| |
| #include <boost/mp11/list.hpp> |
| |
| namespace ndn::tests { |
| |
| using namespace ndn::security; |
| |
| BOOST_AUTO_TEST_SUITE(Security) |
| BOOST_AUTO_TEST_SUITE(TestCertificateBundleFetcher) |
| |
| class CertificateBundleFetcherWrapper : public CertificateBundleFetcher |
| { |
| public: |
| CertificateBundleFetcherWrapper(Face& face) |
| : CertificateBundleFetcher(make_unique<CertificateFetcherFromNetwork>(face), face) |
| { |
| } |
| }; |
| |
| struct BundleWithFinalBlockId {}; |
| struct BundleWithoutFinalBlockId {}; |
| struct Timeout {}; |
| struct Nack {}; |
| |
| template<class Response> |
| class CertificateBundleFetcherFixture : public HierarchicalValidatorFixture<ValidationPolicySimpleHierarchy, |
| CertificateBundleFetcherWrapper> |
| { |
| public: |
| CertificateBundleFetcherFixture() |
| : data("/Security/ValidatorFixture/Sub1/Sub3/Data") |
| , bundleRegexMatcher(std::make_shared<RegexPatternListMatcher>("<>*<_BUNDLE><>*", nullptr)) |
| { |
| subSubIdentity = addSubCertificate("/Security/ValidatorFixture/Sub1/Sub3", subIdentity); |
| cache.insert(subSubIdentity.getDefaultKey().getDefaultCertificate()); |
| m_keyChain.sign(data, signingByIdentity(subSubIdentity)); |
| |
| 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) { |
| face.receive(*cert); |
| } |
| } |
| }; |
| } |
| |
| private: |
| void |
| makeResponse(const Interest& interest); |
| |
| shared_ptr<Data> |
| makeBundle(const Interest& interest) const |
| { |
| Block certList(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(std::move(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(std::move(subCert)); |
| certList.push_back(std::move(anchor)); |
| } |
| |
| auto certBundle = make_shared<Data>(); |
| certBundle->setName(bundleName); |
| certBundle->setFreshnessPeriod(100_s); |
| certBundle->setContent(certList); |
| return certBundle; |
| } |
| |
| protected: |
| Data data; |
| Identity subSubIdentity; |
| shared_ptr<RegexPatternListMatcher> bundleRegexMatcher; |
| }; |
| |
| template<> |
| void |
| CertificateBundleFetcherFixture<BundleWithFinalBlockId>::makeResponse(const Interest& interest) |
| { |
| auto certBundle = makeBundle(interest); |
| certBundle->setFinalBlock(name::Component::fromSegment(1)); |
| m_keyChain.sign(*certBundle, signingWithSha256()); |
| face.receive(*certBundle); |
| } |
| |
| template<> |
| void |
| CertificateBundleFetcherFixture<BundleWithoutFinalBlockId>::makeResponse(const Interest& interest) |
| { |
| auto certBundle = makeBundle(interest); |
| m_keyChain.sign(*certBundle, signingWithSha256()); |
| face.receive(*certBundle); |
| } |
| |
| template<> |
| void |
| CertificateBundleFetcherFixture<Timeout>::makeResponse(const Interest&) |
| { |
| this->advanceClocks(200_s); |
| } |
| |
| template<> |
| void |
| CertificateBundleFetcherFixture<Nack>::makeResponse(const Interest& interest) |
| { |
| face.receive(makeNack(interest, lp::NackReason::NO_ROUTE)); |
| } |
| |
| using SuccessWithBundle = boost::mp11::mp_list<BundleWithFinalBlockId, BundleWithoutFinalBlockId>; |
| |
| BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateSuccessWithBundle, T, SuccessWithBundle, |
| CertificateBundleFetcherFixture<T>) |
| { |
| 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::mp11::mp_list<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() // Security |
| |
| } // namespace ndn::tests |