blob: 37321e3567bc38fa143996b0670ddc7de30b9146 [file] [log] [blame]
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
/*
* Copyright (c) 2012-2014 University of California, Los Angeles
*
* This file is part of ChronoSync, synchronization library for distributed realtime
* applications for NDN.
*
* ChronoSync is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* ChronoSync 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* ChronoSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*
* @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/web/index.html>
*/
#include "sync-validator.h"
#include "sync-logging.h"
#include <ndn-cxx/security/certificate-cache-ttl.hpp>
#include <queue>
using namespace ndn;
INIT_LOGGER ("SyncValidator");
namespace Sync {
using ndn::shared_ptr;
const shared_ptr<CertificateCache> SyncValidator::DefaultCertificateCache = shared_ptr<CertificateCache>();
const shared_ptr<SecRuleRelative> SyncValidator::DefaultDataRule = shared_ptr<SecRuleRelative>();
SyncValidator::SyncValidator(const Name& prefix,
const IdentityCertificate& anchor,
Face& face,
const PublishCertCallback& publishCertCallback,
shared_ptr<SecRuleRelative> rule,
shared_ptr<CertificateCache> certificateCache,
const int stepLimit)
: Validator(face)
, m_prefix(prefix)
, m_anchor(anchor)
, m_stepLimit(stepLimit)
, m_certificateCache(certificateCache)
, m_publishCertCallback(publishCertCallback)
, m_dataRule(rule)
{
if(!static_cast<bool>(m_certificateCache))
m_certificateCache = make_shared<CertificateCacheTtl>(boost::ref(m_face.getIoService()));
Name certPrefix = prefix;
certPrefix.append("CHRONOS-INTRO-CERT");
m_prefixId = m_face.setInterestFilter(certPrefix,
bind(&SyncValidator::onCertInterest, this, _1, _2),
bind(&SyncValidator::onCertRegisterFailed, this, _1, _2));
setAnchor(m_anchor);
}
void
SyncValidator::deriveTrustNodes()
{
std::queue<Name> nodeQueue;
// Clear existing trust nodes.
m_trustedNodes.clear();
// Add the trust anchor.
IntroNode origin(m_anchor);
m_trustedNodes[origin.name()] = m_anchor.getPublicKeyInfo();
nodeQueue.push(origin.name());
// BFS trusted nodes.
while(!nodeQueue.empty())
{
// Get next trusted node to process.
Nodes::const_iterator it = m_introNodes.find(nodeQueue.front());
const PublicKey& publicKey = m_trustedNodes[nodeQueue.front()];
if(it != m_introNodes.end())
{
// If the trusted node exists in the graph.
IntroNode::const_iterator eeIt = it->second.introduceeBegin();
IntroNode::const_iterator eeEnd = it->second.introduceeEnd();
for(; eeIt != eeEnd; eeIt++)
{
// Check the nodes introduced by the trusted node.
Edges::const_iterator edgeIt = m_introCerts.find(*eeIt);
if(edgeIt != m_introCerts.end()
&& m_trustedNodes.find(edgeIt->second.getIntroduceeCertName()) == m_trustedNodes.end()
&& verifySignature(edgeIt->second, publicKey))
{
// If the introduced node can be validated, add it into trusted node set and the node queue.
m_trustedNodes[edgeIt->second.getIntroduceeCertName()] = edgeIt->second.getIntroduceeCert().getPublicKeyInfo();
nodeQueue.push(edgeIt->second.getIntroduceeCertName());
}
}
}
nodeQueue.pop();
}
}
void
SyncValidator::checkPolicy (const Data& data,
int stepCount,
const OnDataValidated& onValidated,
const OnDataValidationFailed& onValidationFailed,
std::vector<shared_ptr<ValidationRequest> >& nextSteps)
{
if(m_stepLimit == stepCount)
return onValidationFailed(data.shared_from_this(),
"Maximum steps of validation reached: " + data.getName().toUri());
if(m_prefix.isPrefixOf(data.getName()) || (static_cast<bool>(m_dataRule) && m_dataRule->satisfy(data)))
{
try
{
SignatureSha256WithRsa sig(data.getSignature());
Name keyLocatorName = sig.getKeyLocator().getName();
TrustNodes::const_iterator it = m_trustedNodes.find(keyLocatorName);
if(m_trustedNodes.end() != it)
{
if(verifySignature(data, sig, it->second))
return onValidated(data.shared_from_this());
else
return onValidationFailed(data.shared_from_this(),
"Cannot verify signature: " + data.getName().toUri());
}
else
{
_LOG_DEBUG("I am: " << m_anchor.getName().get(0).toUri() << " for " << data.getName());
Name interestName = m_prefix;
interestName.append("CHRONOS-INTRO-CERT").append(keyLocatorName.wireEncode());
Interest interest(interestName);
interest.setInterestLifetime(time::milliseconds(500));
OnDataValidated onKeyValidated = bind(&SyncValidator::onCertificateValidated, this,
_1, data.shared_from_this(), onValidated, onValidationFailed);
OnDataValidationFailed onKeyValidationFailed = bind(&SyncValidator::onCertificateValidationFailed, this,
_1, _2, data.shared_from_this(), onValidationFailed);
shared_ptr<ValidationRequest> nextStep = make_shared<ValidationRequest>(interest,
onKeyValidated,
onKeyValidationFailed,
1,
stepCount + 1);
nextSteps.push_back(nextStep);
return;
}
}
catch(SignatureSha256WithRsa::Error& e)
{
return onValidationFailed(data.shared_from_this(),
"Not SignatureSha256WithRsa signature: " + std::string(e.what()));
}
catch(KeyLocator::Error& e)
{
return onValidationFailed(data.shared_from_this(),
"Key Locator is not a name: " + data.getName().toUri());
}
}
else
return onValidationFailed(data.shared_from_this(),
"No rule or rule is not satisfied: " + data.getName().toUri());
}
void
SyncValidator::checkPolicy (const Interest& interest,
int stepCount,
const OnInterestValidated& onValidated,
const OnInterestValidationFailed& onValidationFailed,
std::vector<shared_ptr<ValidationRequest> >& nextSteps)
{
onValidationFailed(interest.shared_from_this(), "No policy for signed interest checking");
}
void
SyncValidator::onCertificateValidated(const shared_ptr<const Data>& signCertificate,
const shared_ptr<const Data>& data,
const OnDataValidated& onValidated,
const OnDataValidationFailed& onValidationFailed)
{
try
{
IntroCertificate introCert(*signCertificate);
addParticipant(introCert);
if(verifySignature(*data, introCert.getIntroduceeCert().getPublicKeyInfo()))
return onValidated(data);
else
return onValidationFailed(data,
"Cannot verify signature: " + data->getName().toUri());
}
catch(IntroCertificate::Error& e)
{
return onValidationFailed(data,
"Intro cert decoding error: " + std::string(e.what()));
}
}
void
SyncValidator::onCertificateValidationFailed(const shared_ptr<const Data>& signCertificate,
const std::string& failureInfo,
const shared_ptr<const Data>& data,
const OnDataValidationFailed& onValidationFailed)
{
onValidationFailed(data, failureInfo);
}
void
SyncValidator::onCertInterest(const Name& prefix, const Interest& interest)
{
Name name = interest.getName();
Edges::const_iterator it = m_introCerts.begin();
for(; it != m_introCerts.end(); it++)
{
if(name.isPrefixOf(it->first))
{
m_face.put(it->second);
return;
}
}
}
void
SyncValidator::onCertRegisterFailed(const Name& prefix, const std::string& msg)
{
_LOG_DEBUG("SyncValidator::onCertRegisterFailed: " << msg);
}
} // namespace Sync