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