blob: 55c1f71ed8367bdc3011a899e29c7ae375e321ff [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2013-2024 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/certificate-request.hpp"
#include "ndn-cxx/security/certificate-storage.hpp"
#include "ndn-cxx/security/validation-state.hpp"
#include "ndn-cxx/tag.hpp"
#include "ndn-cxx/util/logger.hpp"
#define NDN_LOG_DEBUG_DEPTH(x) NDN_LOG_DEBUG(std::string(state->getDepth() + 1, '>') << ' ' << x)
namespace ndn::security {
NDN_LOG_INIT(ndn.security.CertificateBundleFetcher);
using BundleNameTag = SimpleTag<Name, 1000>;
using FinalBlockIdTag = SimpleTag<name::Component, 1001>;
CertificateBundleFetcher::CertificateBundleFetcher(unique_ptr<CertificateFetcher> inner, Face& face)
: m_inner(std::move(inner))
, m_face(face)
{
BOOST_ASSERT(m_inner != nullptr);
}
void
CertificateBundleFetcher::setCertificateStorage(CertificateStorage& certStorage)
{
m_certStorage = &certStorage;
m_inner->setCertificateStorage(certStorage);
}
void
CertificateBundleFetcher::doFetch(const shared_ptr<CertificateRequest>& certRequest,
const shared_ptr<ValidationState>& state,
const ValidationContinuation& continueValidation)
{
auto dataValidationState = std::dynamic_pointer_cast<DataValidationState>(state);
if (dataValidationState == nullptr) {
return m_inner->fetch(certRequest, state, continueValidation);
}
// check if a bundle segment was fetched before
shared_ptr<BundleNameTag> bundleNameTag = state->getTag<BundleNameTag>();
if (bundleNameTag == nullptr) {
const Name& originalDataName = dataValidationState->getOriginalData().getName();
if (originalDataName.empty()) {
return m_inner->fetch(certRequest, state, continueValidation);
}
// derive certificate bundle name from original data name
Name bundleNamePrefix = deriveBundleName(originalDataName);
fetchFirstBundleSegment(bundleNamePrefix, certRequest, state, continueValidation);
}
else {
Name fullBundleName = bundleNameTag->get();
fetchNextBundleSegment(fullBundleName, fullBundleName.get(-1).getSuccessor(),
certRequest, state, continueValidation);
}
}
void
CertificateBundleFetcher::fetchFirstBundleSegment(const Name& bundleNamePrefix,
const shared_ptr<CertificateRequest>& certRequest,
const shared_ptr<ValidationState>& state,
const ValidationContinuation& continueValidation)
{
Interest bundleInterest = Interest(bundleNamePrefix);
bundleInterest.setCanBePrefix(true);
bundleInterest.setMustBeFresh(true);
bundleInterest.setInterestLifetime(m_bundleInterestLifetime);
m_face.expressInterest(bundleInterest,
[=] (const Interest&, const Data& data) {
dataCallback(data, true, certRequest, state, continueValidation);
},
[=] (const Interest&, const lp::Nack& nack) {
nackCallback(nack, certRequest, state, continueValidation, bundleNamePrefix);
},
[=] (const Interest&) {
timeoutCallback(certRequest, state, continueValidation, bundleNamePrefix);
});
}
void
CertificateBundleFetcher::fetchNextBundleSegment(const Name& fullBundleName, const name::Component& segmentNo,
const shared_ptr<CertificateRequest>& certRequest,
const shared_ptr<ValidationState>& state,
const ValidationContinuation& continueValidation)
{
shared_ptr<FinalBlockIdTag> finalBlockId = state->getTag<FinalBlockIdTag>();
if (finalBlockId != nullptr && segmentNo > finalBlockId->get()) {
return m_inner->fetch(certRequest, state, continueValidation);
}
Interest bundleInterest(fullBundleName.getPrefix(-1).append(segmentNo));
bundleInterest.setCanBePrefix(false);
bundleInterest.setMustBeFresh(false);
bundleInterest.setInterestLifetime(m_bundleInterestLifetime);
m_face.expressInterest(bundleInterest,
[=] (const Interest&, const Data& data) {
dataCallback(data, false, certRequest, state, continueValidation);
},
[=] (const Interest&, const lp::Nack& nack) {
nackCallback(nack, certRequest, state, continueValidation, fullBundleName);
},
[=] (const Interest&) {
timeoutCallback(certRequest, state, continueValidation, fullBundleName);
});
}
void
CertificateBundleFetcher::dataCallback(const Data& bundleData,
bool isSegmentZeroExpected,
const shared_ptr<CertificateRequest>& certRequest,
const shared_ptr<ValidationState>& state,
const ValidationContinuation& continueValidation)
{
NDN_LOG_DEBUG_DEPTH("Fetched certificate bundle " << bundleData.getName());
name::Component currentSegment = bundleData.getName().get(-1);
if (!currentSegment.isSegment()) {
return m_inner->fetch(certRequest, state, continueValidation);
}
if (isSegmentZeroExpected && currentSegment.toSegment() != 0) {
// fetch segment zero
fetchNextBundleSegment(bundleData.getName(), name::Component::fromSegment(0),
certRequest, state, continueValidation);
}
else {
state->setTag(make_shared<BundleNameTag>(bundleData.getName()));
const auto& finalBlockId = bundleData.getFinalBlock();
if (finalBlockId) {
state->setTag(make_shared<FinalBlockIdTag>(*finalBlockId));
}
Block bundleContent = bundleData.getContent();
bundleContent.parse();
// store all the certificates in unverified cache
for (const auto& block : bundleContent.elements()) {
m_certStorage->cacheUnverifiedCert(Certificate(block));
}
auto cert = m_certStorage->getUnverifiedCertCache().find(certRequest->interest);
continueValidation(*cert, state);
}
}
void
CertificateBundleFetcher::nackCallback(const lp::Nack& nack,
const shared_ptr<CertificateRequest>& certRequest,
const shared_ptr<ValidationState>& state,
const ValidationContinuation& continueValidation,
const Name& bundleName)
{
NDN_LOG_DEBUG_DEPTH("NACK (" << nack.getReason() << ") while fetching certificate bundle "
<< bundleName);
m_inner->fetch(certRequest, state, continueValidation);
}
void
CertificateBundleFetcher::timeoutCallback(const shared_ptr<CertificateRequest>& certRequest,
const shared_ptr<ValidationState>& state,
const ValidationContinuation& continueValidation,
const Name& bundleName)
{
NDN_LOG_DEBUG_DEPTH("Timeout while fetching certificate bundle " << bundleName);
m_inner->fetch(certRequest, state, continueValidation);
}
Name
CertificateBundleFetcher::deriveBundleName(const Name& name)
{
Name bundleName = name;
const auto& lastComponent = name.at(-1);
if (lastComponent.isImplicitSha256Digest()) {
if (name.size() >= 2 && name.get(-2).isSegment()) {
bundleName = name.getPrefix(-2);
}
else {
bundleName = name.getPrefix(-1);
}
}
else if (lastComponent.isSegment()) {
bundleName = name.getPrefix(-1);
}
bundleName.append("_BUNDLE");
bundleName.appendNumber(0);
return bundleName;
}
} // namespace ndn::security