security: Introduce hierarchical validator

Change-Id: Ie99abc2b8de4ff227c21e34fef19544c01a9f5dd
diff --git a/security/anchor.cert b/security/anchor.cert
index 6eeaef4..32a7523 100644
--- a/security/anchor.cert
+++ b/security/anchor.cert
@@ -1,16 +1,13 @@
-Bv0C2gcvCANuZG4IA0tFWQgRa3NrLTEzOTMzNzgwNTI0MDkIB0lELUNFUlQIB/0B
-RGvNyo0UAxgBAhX9AW0wggFpMCIYDzIwMTQwMjI2MDEyNzMyWhgPMjAxNDAzMDUw
-ODM5MzJaMB8wHQYDVQQpExYvbmRuL2tzay0xMzkzMzc4MDUyNDA5MIIBIDANBgkq
-hkiG9w0BAQEFAAOCAQ0AMIIBCAKCAQEAyfBfMsDEKjn5sEF/2B5uUvojnpUvh8/r
-rF5nDVdG7faJVjWkrdnPYweUQItnNRDfiTPYoAINfFdlSdtsCV3gMQjXvrJa5pFP
-Ha4M7nZ9YVExXAnYTSdHAqwFi2bfNJMNUvME7R0lVhjDcyJAdB4IjMc+W7s80rOr
-1BFo3Jf66HE+hLnQsHWC/L3AxR5IYJ7laubDeQBUCJWfbQ+xbM8cb8y39O7ONyhw
-hbJ4hGqel6wvffsGVNzQcZkkGFvpns8FTMrYdhV3rYbmRKxSy09Xh6qDkkuCvgf7
-zZHDZvJGIibEKlfjHD82EJn+V4Ib7PDFAy9le/rissmyZ3gBI7QcqwIBERYtGwEB
-HCgHJggDbmRuCANLRVkIEWtzay0xMzkzMzc4MDUyNDA5CAdJRC1DRVJUF/0BABlT
-1uvcHSjoMNnaeuK73LeI93OqZwOK3Prtl6KNYG/p6ob8uue0pU6x9u8YIu8qj1Gt
-1uIvFhgOqlkbBMUebCjFeRTwlddXnyCWaWH7oJMYZbEjuCyv3498xgEV+4K5fF/x
-BePjfFVD70Vfh3gMUqR2L3ktXzXcLyemT/PvLMXWJ6iQXX4FNLbpAx2EdMzwfFbE
-uPK9/QUBJIG92ks44e9M9iD7meiW+nLjKi4ebiCWYOVcoIazfEi13ZCiBXwbXTNv
-HM0wp7ODHf0CUz3cKfl9pLl8bC9UNEIxFi2b9Vr5UpCy2i8xiTGaxJpvICSFYGFg
-Z7eA1LjpBYouO/4W7+w=
+Bv0C1QcwCANuZG4IA0tFWQgRa3NrLTEzOTc1MzcyMjg2NDkIB0lELUNFUlQICAAAAUVjtyQUFAMY
+AQIV/QFnMIIBYzAiGA8yMDE0MDQxNDAwMDAwMFoYDzIwMTgxMjMxMjM1OTU5WjAZMBcGA1UEKRMQ
+TkROIFRlc3RiZWQgUm9vdDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAL/XonINE4KB
+IC5G3Aab3uJmz9nf/4nTRa2/tWDPn5Notqs/+h4i/xF7LusKbBXkugLRjYcJsO1bIVf597921U8L
+pCfQdhaxBWfr26M4ZsZ3i2cVanBvxTneZQWYahsg8M0gA+jMFbSkpgVhMsAfVR+UCPhcvPkZ/94V
+HoOq3Nae36RRW64pa0QKCuUluFRVrAhZD9zj+QJHM8faOlOJTLnss9vjggb5MWu/dS8cYEfY9o/O
+oVlpbeCbCoQJdhGc0NWhEmpTx/UZHSUEDKRV6643ETx63yQtT99WIp4+VVBAoxh1Dz8i4ePBjgBs
+DJTW7p3xxreOxeW/ndIKNpUzv/UCAREWLRsBARwoByYIA25kbggDS0VZCBFrc2stMTM5NzUzNzIy
+ODY0OQgHSUQtQ0VSVBf9AQAhyPUjSO3lCt1XDWSAsTiw7M454QlyUIXWvS5WqIXGE9zJgIMxt0OL
+ec6o6rg5LZyH+hh6MSoK9ItUvQDVA/FXyQdTfKZAshGhx7ejEmKwU5yCt3PXfprfwQZwuImjkFo2
+QpgrwN/cToXGkiEOQ/7r5qXCSAEagmIWiLncefixfTopK7mGW+ADQryvChtTeiXawWuq0xPC45wm
+wzkoEyE2cGuEbgdyQoHTB0i/75nJVmQOc6iJbgAfxrdLIC53krKe5QRX8H1GDf5IrELmwC7kKa0b
+b06RJ+hLLGEKa286IOHeAJAFsQu3wnoThWBFvUDegt41zF50awzgosHLrZRD
\ No newline at end of file
diff --git a/src/chat-dialog-backend.cpp b/src/chat-dialog-backend.cpp
index 2644006..f175615 100644
--- a/src/chat-dialog-backend.cpp
+++ b/src/chat-dialog-backend.cpp
@@ -10,8 +10,11 @@
 
 #include "chat-dialog-backend.hpp"
 
+#include <QFile>
+
 #ifndef Q_MOC_RUN
 #include <ndn-cxx/util/io.hpp>
+#include <ndn-cxx/security/validator-regex.hpp>
 #include "logging.h"
 #endif
 
@@ -29,6 +32,7 @@
                                      const Name& routingPrefix,
                                      const std::string& chatroomName,
                                      const std::string& nick,
+                                     const Name& signingId,
                                      QObject* parent)
   : QThread(parent)
   , m_localRoutingPrefix(routingPrefix)
@@ -36,6 +40,7 @@
   , m_userChatPrefix(userChatPrefix)
   , m_chatroomName(chatroomName)
   , m_nick(nick)
+  , m_signingId(signingId)
 {
   updatePrefixes();
 }
@@ -74,14 +79,38 @@
 {
   BOOST_ASSERT(m_sock == nullptr);
 
-  m_face = unique_ptr<ndn::Face>(new ndn::Face);
+  m_face = make_shared<ndn::Face>();
   m_scheduler = unique_ptr<ndn::Scheduler>(new ndn::Scheduler(m_face->getIoService()));
 
+  // initialize validator
+  shared_ptr<ndn::IdentityCertificate> anchor = loadTrustAnchor();
+
+  if (static_cast<bool>(anchor)) {
+    shared_ptr<ndn::ValidatorRegex> validator =
+      make_shared<ndn::ValidatorRegex>(m_face.get()); // TODO: Change to Face*
+    validator->addDataVerificationRule(
+      make_shared<ndn::SecRuleRelative>("^<>*<%F0.>(<>*)$",
+                                        "^([^<KEY>]*)<KEY>(<>*)<ksk-.*><ID-CERT>$",
+                                        ">", "\\1", "\\1\\2", true));
+    validator->addDataVerificationRule(
+      make_shared<ndn::SecRuleRelative>("(<>*)$",
+                                        "^([^<KEY>]*)<KEY>(<>*)<ksk-.*><ID-CERT>$",
+                                        ">", "\\1", "\\1\\2", true));
+    validator->addTrustAnchor(anchor);
+
+    m_validator = validator;
+  }
+  else
+    m_validator = shared_ptr<ndn::Validator>();
+
+
   // create a new SyncSocket
   m_sock = make_shared<chronosync::Socket>(m_chatroomPrefix,
                                            m_routableUserChatPrefix,
                                            ref(*m_face),
-                                           bind(&ChatDialogBackend::processSyncUpdate, this, _1));
+                                           bind(&ChatDialogBackend::processSyncUpdate, this, _1),
+                                           m_signingId,
+                                           m_validator);
 
   // schedule a new join event
   m_scheduler->scheduleEvent(time::milliseconds(600),
@@ -94,6 +123,40 @@
   }
 }
 
+class IoDeviceSource
+{
+public:
+  typedef char char_type;
+  typedef boost::iostreams::source_tag category;
+
+  explicit
+  IoDeviceSource(QIODevice& source)
+    : m_source(source)
+  {
+  }
+
+  std::streamsize
+  read(char* buffer, std::streamsize n)
+  {
+    return m_source.read(buffer, n);
+  }
+private:
+  QIODevice& m_source;
+};
+
+shared_ptr<ndn::IdentityCertificate>
+ChatDialogBackend::loadTrustAnchor()
+{
+  QFile anchorFile(":/security/anchor.cert");
+
+  if (!anchorFile.open(QIODevice::ReadOnly)) {
+    return {};
+  }
+
+  boost::iostreams::stream<IoDeviceSource> anchorFileStream(anchorFile);
+  return ndn::io::load<ndn::IdentityCertificate>(anchorFileStream);
+}
+
 void
 ChatDialogBackend::close()
 {
@@ -105,6 +168,7 @@
   m_scheduler->cancelAllEvents();
   m_helloEventId.reset();
   m_roster.clear();
+  m_validator.reset();
   m_sock.reset();
 }
 
@@ -131,7 +195,13 @@
     if (updates[i].high - updates[i].low < 3) {
       for (chronosync::SeqNo seq = updates[i].low; seq <= updates[i].high; ++seq) {
         m_sock->fetchData(updates[i].session, seq,
-                          bind(&ChatDialogBackend::processChatData, this, _1, true),
+                          [this] (const shared_ptr<const ndn::Data>& data) {
+                            this->processChatData(data, true, true);
+                          },
+                          [this] (const shared_ptr<const ndn::Data>& data, const std::string& msg) {
+                            this->processChatData(data, true, false);
+                          },
+                          ndn::OnTimeout(),
                           2);
         _LOG_DEBUG("<<< Fetching " << updates[i].session << "/" << seq);
       }
@@ -139,7 +209,13 @@
     else {
       // There are too many msgs to fetch, let's just fetch the latest one
       m_sock->fetchData(updates[i].session, updates[i].high,
-                        bind(&ChatDialogBackend::processChatData, this, _1, false),
+                        [this] (const shared_ptr<const ndn::Data>& data) {
+                          this->processChatData(data, false, true);
+                        },
+                        [this] (const shared_ptr<const ndn::Data>& data, const std::string& msg) {
+                          this->processChatData(data, false, false);
+                        },
+                        ndn::OnTimeout(),
                         2);
     }
 
@@ -156,7 +232,9 @@
 }
 
 void
-ChatDialogBackend::processChatData(const ndn::shared_ptr<const ndn::Data>& data, bool needDisplay)
+ChatDialogBackend::processChatData(const ndn::shared_ptr<const ndn::Data>& data,
+                                   bool needDisplay,
+                                   bool isValidated)
 {
   SyncDemo::ChatMessage msg;
 
@@ -223,10 +301,16 @@
                                       this, remoteSessionPrefix));
 
     // If chat message, notify the frontend
-    if (msg.type() == SyncDemo::ChatMessage::CHAT)
-      emit chatMessageReceived(QString::fromStdString(msg.from()),
-                               QString::fromStdString(msg.data()),
-                               msg.timestamp());
+    if (msg.type() == SyncDemo::ChatMessage::CHAT) {
+      if (isValidated)
+        emit chatMessageReceived(QString::fromStdString(msg.from()),
+                                 QString::fromStdString(msg.data()),
+                                 msg.timestamp());
+      else
+        emit chatMessageReceived(QString::fromStdString(msg.from() + " (Unverified)"),
+                                 QString::fromStdString(msg.data()),
+                                 msg.timestamp());
+    }
 
     // Notify frontend to plot notification on DigestTree.
     emit messageReceived(QString::fromStdString(remoteSessionPrefix.toUri()));
diff --git a/src/chat-dialog-backend.hpp b/src/chat-dialog-backend.hpp
index 051a35e..f01c015 100644
--- a/src/chat-dialog-backend.hpp
+++ b/src/chat-dialog-backend.hpp
@@ -46,6 +46,7 @@
                     const Name& routingPrefix,
                     const std::string& chatroomName,
                     const std::string& nick,
+                    const Name& signingId = Name(),
                     QObject* parent = 0);
 
   ~ChatDialogBackend();
@@ -58,6 +59,9 @@
   void
   initializeSync();
 
+  shared_ptr<ndn::IdentityCertificate>
+  loadTrustAnchor();
+
   void
   close();
 
@@ -65,7 +69,9 @@
   processSyncUpdate(const std::vector<chronosync::MissingDataInfo>& updates);
 
   void
-  processChatData(const ndn::shared_ptr<const ndn::Data>& data, bool needDisplay);
+  processChatData(const ndn::shared_ptr<const ndn::Data>& data,
+                  bool needDisplay,
+                  bool isValidated);
 
   void
   remoteSessionTimeout(const Name& sessionPrefix);
@@ -132,7 +138,7 @@
 private:
   typedef std::map<ndn::Name, UserInfo> BackendRoster;
 
-  unique_ptr<ndn::Face> m_face;
+  shared_ptr<ndn::Face> m_face;
 
   Name m_localRoutingPrefix;             // routable local prefix
   Name m_chatroomPrefix;                 // chatroom sync prefix
@@ -142,6 +148,8 @@
   std::string m_chatroomName;            // chatroom name
   std::string m_nick;                    // user nick
 
+  Name m_signingId;                      // signing identity
+  shared_ptr<ndn::Validator> m_validator;// validator
   shared_ptr<chronosync::Socket> m_sock; // SyncSocket
 
   unique_ptr<ndn::Scheduler> m_scheduler;// scheduler
diff --git a/src/chat-dialog.cpp b/src/chat-dialog.cpp
index 9c74715..776e076 100644
--- a/src/chat-dialog.cpp
+++ b/src/chat-dialog.cpp
@@ -32,10 +32,11 @@
                        const std::string& chatroomName,
                        const std::string& nick,
                        bool isSecured,
+                       const Name& signingId,
                        QWidget* parent)
   : QDialog(parent)
   , ui(new Ui::ChatDialog)
-  , m_backend(chatroomPrefix, userChatPrefix, routingPrefix, chatroomName, nick)
+  , m_backend(chatroomPrefix, userChatPrefix, routingPrefix, chatroomName, nick, signingId)
   , m_chatroomName(chatroomName)
   , m_nick(nick.c_str())
   , m_isSecured(isSecured)
@@ -186,7 +187,7 @@
   }
 
   if (m_isSecured)
-    chatroomInfo->setTrustModel(ChatroomInfo::TRUST_MODEL_WEBOFTRUST);
+    chatroomInfo->setTrustModel(ChatroomInfo::TRUST_MODEL_HIERARCHICAL);
   else
     chatroomInfo->setTrustModel(ChatroomInfo::TRUST_MODEL_NONE);
   return chatroomInfo;
diff --git a/src/chat-dialog.hpp b/src/chat-dialog.hpp
index 3568750..74dd76d 100644
--- a/src/chat-dialog.hpp
+++ b/src/chat-dialog.hpp
@@ -48,6 +48,7 @@
              const std::string& chatroomName,
              const std::string& nick,
              bool isSecured = false,
+             const Name& signingId = Name(),
              QWidget* parent = 0);
 
   ~ChatDialog();
diff --git a/src/controller.cpp b/src/controller.cpp
index a21a621..f716ada 100644
--- a/src/controller.cpp
+++ b/src/controller.cpp
@@ -630,7 +630,9 @@
                      chatPrefix,
                      m_localPrefix,
                      chatroomName.toStdString(),
-                     m_nick);
+                     m_nick,
+                     true,
+                     m_identity);
 
   addChatDialog(chatroomName, chatDialog);
   chatDialog->show();