security: Add Validator

Change-Id: Ib97bbb1a95f43684f14f02d0e50b1b6b6f93979b
diff --git a/src/sync-validator.cc b/src/sync-validator.cc
new file mode 100644
index 0000000..03c292b
--- /dev/null
+++ b/src/sync-validator.cc
@@ -0,0 +1,259 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *                     Yingdi Yu
+ *
+ * BSD license, See the LICENSE file for more information
+ *
+ * Author: Yingdi Yu <yingdi@cs.ucla.edu>
+ */
+
+#include "sync-validator.h"
+#include "sync-logging.h"
+#include <ndn-cpp-dev/security/certificate-cache-ttl.hpp>
+#include <queue>
+
+using namespace ndn;
+using namespace std;
+
+INIT_LOGGER ("SyncValidator");
+
+namespace Sync {
+
+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,
+                             shared_ptr<Face> face,
+                             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_dataRule(rule)
+{
+  if(!static_cast<bool>(face))
+    throw Error("Face is not set!");
+
+  if(!static_cast<bool>(m_certificateCache))
+    m_certificateCache = make_shared<CertificateCacheTtl>(m_face->ioService());
+
+  Name certPrefix = prefix;
+  certPrefix.append("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()
+{
+  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.getIntroduceeName()) == 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.getIntroduceeName()] = edgeIt->second.getIntroduceeCert().getPublicKeyInfo();
+                  nodeQueue.push(edgeIt->second.getIntroduceeName());
+                }
+            }
+        }
+      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()))
+    {
+      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
+            {
+              Name interestName = m_prefix;
+              interestName.append("intro-cert").append(keyLocatorName.wireEncode());
+              Interest interest(interestName);
+              interest.setInterestLifetime(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: " + string(e.what()));
+        }
+      catch(KeyLocator::Error& e)
+        {
+          return onValidationFailed(data.shared_from_this(),
+                                    "Key Locator is not a name: " + data.getName().toUri());
+        }
+    }
+
+  if(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
+            return onValidationFailed(data.shared_from_this(), 
+                                      "Signer cannot be trusted: " + keyLocatorName.toUri());
+        }
+      catch(SignatureSha256WithRsa::Error& e)
+        {
+          return onValidationFailed(data.shared_from_this(), 
+                                    "Not SignatureSha256WithRsa signature: " + 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 data 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: " + string(e.what()));
+    }
+}
+
+void
+SyncValidator::onCertificateValidationFailed(const shared_ptr<const Data>& signCertificate, 
+                                             const 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 string& msg)
+{
+  _LOG_DEBUG("SyncValidator::onCertRegisterFailed: " << msg);
+}
+
+} // namespace Sync