blob: 2089e2aa1ac22aad0edce0f0b29586462932e1ae [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2017, Regents of the University of California
*
* This file is part of NDN DeLorean, An Authentication System for Data Archives in
* Named Data Networking. See AUTHORS.md for complete list of NDN DeLorean authors
* and contributors.
*
* NDN DeLorean 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.
*
* NDN DeLorean 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 NDN
* DeLorean, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#include "logger.hpp"
#include "tlv.hpp"
#include "conf/config-file.hpp"
namespace nsl {
const int Logger::N_DATA_FETCHING_RETRIAL = 2;
Logger::Logger(ndn::Face& face, const std::string& configFile)
: m_face(face)
, m_merkleTree(m_db)
, m_validator(m_face)
{
conf::ConfigFile conf(configFile);
conf.parse();
m_loggerName = conf.getLoggerName();
m_treePrefix = m_loggerName;
m_treePrefix.append("tree");
m_leafPrefix = m_loggerName;
m_leafPrefix.append("leaf");
m_logPrefix = m_loggerName;
m_logPrefix.append("log");
m_merkleTree.setLoggerName(m_treePrefix);
m_merkleTree.loadPendingSubTrees();
m_db.open(conf.getDbDir());
// initialize security environment: keychain
initializeKeys();
// load policy checker
m_policyChecker.loadPolicy(conf.getPolicy());
// load validator rules
m_validator.load(conf.getValidatorRule(), conf.getConfFileName());
// register subtree prefix
m_face.setInterestFilter(m_treePrefix,
bind(&Logger::onSubTreeInterest, this, _1, _2),
[] (const Name&) {},
[] (const Name&, const std::string&) {});
// register leaf prefix
m_face.setInterestFilter(m_leafPrefix,
bind(&Logger::onLeafInterest, this, _1, _2),
[] (const Name&) {},
[] (const Name&, const std::string&) {});
// register log prefix
m_face.setInterestFilter(m_logPrefix,
bind(&Logger::onLogRequestInterest, this, _1, _2),
[] (const Name&) {},
[] (const Name&, const std::string&) {});
}
NonNegativeInteger
Logger::addSelfSignedCert(ndn::IdentityCertificate& cert, const Timestamp& timestamp)
{
if (!ndn::Validator::verifySignature(cert, cert.getPublicKeyInfo()))
throw Error("Not self-signed cert");
NonNegativeInteger dataSeqNo = m_merkleTree.getNextLeafSeqNo();
Leaf leaf(cert.getFullName(), timestamp, dataSeqNo, dataSeqNo, m_leafPrefix);
if (m_merkleTree.addLeaf(dataSeqNo, leaf.getHash())) {
bool result = m_db.insertLeafData(leaf, cert);
BOOST_ASSERT(result);
m_db.getLeaf(dataSeqNo);
}
else
throw Error("Cannot add cert");
return dataSeqNo;
}
void
Logger::initializeKeys()
{
Name certName = m_keyChain.createIdentity(m_loggerName);
Name dskKeyName = m_keyChain.generateEcdsaKeyPair(m_loggerName);
std::vector<ndn::CertificateSubjectDescription> subjectDescription;
auto dskCert =
m_keyChain.prepareUnsignedIdentityCertificate(dskKeyName, m_loggerName,
time::system_clock::now(),
time::system_clock::now() + time::days(1),
subjectDescription);
m_keyChain.sign(*dskCert, certName);
m_keyChain.addCertificate(*dskCert);
m_dskCert = dskCert;
}
void
Logger::onSubTreeInterest(const ndn::InterestFilter& interestFilter, const Interest& interest)
{
Name interestName = interest.getName();
size_t levelOffset = m_treePrefix.size();
size_t seqNoOffset = m_treePrefix.size() + 1;
if (interestName.size() < seqNoOffset + 1)
return; // interest is too short to answer
NonNegativeInteger level;
NonNegativeInteger seqNo;
try {
seqNo = interestName.get(seqNoOffset).toNumber();
level = interestName.get(levelOffset).toNumber();
}
catch (tlv::Error&) {
return;
}
Node::Index peakIndex = SubTreeBinary::toSubTreePeakIndex(Node::Index(seqNo, level));
shared_ptr<Data> data;
data = m_merkleTree.getPendingSubTreeData(peakIndex.level);
if (data != nullptr && interestName.isPrefixOf(data->getName())) {
m_face.put(*data);
return;
}
data = m_db.getSubTreeData(peakIndex.level, peakIndex.seqNo);
if (data != nullptr && interestName.isPrefixOf(data->getName())) {
m_face.put(*data);
return;
}
}
void
Logger::onLeafInterest(const ndn::InterestFilter& interestFilter, const Interest& interest)
{
Name interestName = interest.getName();
size_t seqNoOffset = m_leafPrefix.size();
size_t hashOffset = m_leafPrefix.size() + 1;
if (interestName.size() < seqNoOffset + 1)
return; // interest is too short to answer
NonNegativeInteger seqNo;
try {
seqNo = interestName.get(seqNoOffset).toNumber();
}
catch (tlv::Error&) {
return;
}
auto result = m_db.getLeaf(seqNo);
if (result.first != nullptr) {
if (interestName.size() >= hashOffset + 1) {
ndn::ConstBufferPtr leafHash;
try {
leafHash = make_shared<ndn::Buffer>(interestName.get(hashOffset).value(),
interestName.get(hashOffset).value_size());
ndn::ConstBufferPtr hash = result.first->getHash();
if (*hash != *leafHash)
return;
}
catch (tlv::Error&) {
return;
}
}
result.first->setLoggerName(m_leafPrefix);
m_face.put(*result.first->encode());
}
}
void
Logger::onLogRequestInterest(const ndn::InterestFilter& interestFilter, const Interest& interest)
{
m_validator.validate(interest,
bind(&Logger::requestValidatedCallback, this, _1),
[] (const shared_ptr<const Interest>&, const std::string&) {});
}
void
Logger::requestValidatedCallback(const shared_ptr<const Interest>& interest)
{
BOOST_ASSERT(interest->getName().size() == (m_logPrefix.size() + 6));
Name request = interest->getName().getPrefix(-4); // TODO: remove sig-related components
size_t dataOffset = m_logPrefix.size();
size_t signerOffset = m_logPrefix.size() + 1;
if (request.size() < signerOffset + 1)
return; // request is too short to answer
Name dataName;
NonNegativeInteger signerSeqNo;
try {
dataName.wireDecode(request.get(dataOffset).blockFromValue());
signerSeqNo = request.get(signerOffset).toNumber();
}
catch (tlv::Error&) {
return;
}
auto result = m_db.getLeaf(signerSeqNo);
if (result.first == nullptr || result.second == nullptr)
return;
Interest dataInterest(dataName);
m_face.expressInterest(dataInterest,
bind(&Logger::dataReceivedCallback, this, _1, _2,
signerSeqNo, *interest),
bind(&Logger::dataTimeoutCallback, this, _1,
N_DATA_FETCHING_RETRIAL, signerSeqNo, *interest));
}
void
Logger::dataReceivedCallback(const Interest& interest, Data& data,
const NonNegativeInteger& signerSeqNo,
const Interest& reqInterest)
{
auto result = m_db.getLeaf(signerSeqNo);
BOOST_ASSERT(result.first != nullptr);
BOOST_ASSERT(result.second != nullptr);
Timestamp dataTimestamp = time::toUnixTimestamp(time::system_clock::now()).count() / 1000;
try {
ndn::IdentityCertificate cert(*result.second);
if (m_policyChecker.check(dataTimestamp, data, result.first->getTimestamp(), cert)) {
NonNegativeInteger dataSeqNo = m_merkleTree.getNextLeafSeqNo();
Leaf leaf(data.getFullName(), dataTimestamp, dataSeqNo, signerSeqNo, m_leafPrefix);
if (m_merkleTree.addLeaf(dataSeqNo, leaf.getHash())) {
if (data.getContentType() == ndn::tlv::ContentType_Key)
m_db.insertLeafData(leaf, data);
else
m_db.insertLeafData(leaf);
makeLogResponse(reqInterest, LoggerResponse(dataSeqNo));
}
else
makeLogResponse(reqInterest,
LoggerResponse(tlv::LogResponse_Error_Tree, "cannot add leaf"));
}
else
makeLogResponse(reqInterest,
LoggerResponse(tlv::LogResponse_Error_Policy, "cannot pass policy checking"));
}
catch (tlv::Error&) {
makeLogResponse(reqInterest,
LoggerResponse(tlv::LogResponse_Error_Signer, "signer is wrong"));
}
}
void
Logger::dataTimeoutCallback(const Interest& interest, int nRetrials,
const NonNegativeInteger& signerSeqNo,
const Interest& reqInterest)
{
if (nRetrials > 0) {
m_face.expressInterest(interest,
bind(&Logger::dataReceivedCallback, this, _1, _2,
signerSeqNo, reqInterest),
bind(&Logger::dataTimeoutCallback, this, _1,
nRetrials - 1, signerSeqNo, reqInterest));
}
}
void
Logger::makeLogResponse(const Interest& reqInterest, const LoggerResponse& response)
{
auto data = make_shared<Data>(reqInterest.getName());
data->setContent(response.wireEncode());
BOOST_ASSERT(m_dskCert != nullptr);
m_keyChain.sign(*data, m_dskCert->getName());
m_face.put(*data);
}
} // namespace nsl