add logger
Change-Id: If41d6d94086ec9fffb954a52e1529ccfad7f42fe
diff --git a/core/logger.cpp b/core/logger.cpp
new file mode 100644
index 0000000..6a1e67c
--- /dev/null
+++ b/core/logger.cpp
@@ -0,0 +1,306 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014, Regents of the University of California
+ *
+ * This file is part of NSL (NDN Signature Logger).
+ * See AUTHORS.md for complete list of NSL authors and contributors.
+ *
+ * NSL 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.
+ *
+ * NSL 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
+ * NSL, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of nsl authors and contributors.
+ */
+
+#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