blob: 9972511b976edca5b97de834bec98b4bf851f10b [file] [log] [blame]
/* -*- 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 "ndn-cxx/security/v2/certificate-bundle-fetcher.hpp"
#include "ndn-cxx/face.hpp"
#include "ndn-cxx/util/logger.hpp"
namespace ndn {
namespace security {
namespace v2 {
NDN_LOG_INIT(ndn.security.v2.CertificateBundleFetcher);
#define NDN_LOG_DEBUG_DEPTH(x) NDN_LOG_DEBUG(std::string(state->getDepth() + 1, '>') << " " << x)
#define NDN_LOG_TRACE_DEPTH(x) NDN_LOG_TRACE(std::string(state->getDepth() + 1, '>') << " " << x)
CertificateBundleFetcher::CertificateBundleFetcher(unique_ptr<CertificateFetcher> inner,
Face& face)
: m_inner(std::move(inner))
, m_face(face)
, m_bundleInterestLifetime(1000)
{
BOOST_ASSERT(m_inner != nullptr);
}
void
CertificateBundleFetcher::setBundleInterestLifetime(time::milliseconds time)
{
m_bundleInterestLifetime = time;
}
time::milliseconds
CertificateBundleFetcher::getBundleInterestLifetime() const
{
return m_bundleInterestLifetime;
}
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 = 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.setChildSelector(1);
bundleInterest.setInterestLifetime(m_bundleInterestLifetime);
m_face.expressInterest(bundleInterest,
[=] (const Interest& interest, const Data& data) {
dataCallback(data, true, certRequest, state, continueValidation);
},
[=] (const Interest& interest, const lp::Nack& nack) {
nackCallback(nack, certRequest, state, continueValidation, bundleNamePrefix);
},
[=] (const Interest& 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& interest, const Data& data) {
dataCallback(data, false, certRequest, state, continueValidation);
},
[=] (const Interest& interest, const lp::Nack& nack) {
nackCallback(nack, certRequest, state, continueValidation, fullBundleName);
},
[=] (const Interest& 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 from network " << 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::Component lastComponent = name.at(-1);
Name bundleName = name;
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(00);
return bundleName;
}
} // namespace v2
} // namespace security
} // namespace ndn