ccnx: Added verifier and a few small changes

Change-Id: Idbdc0597bd9afb3cfa7c7bbc05175ca8aa1c6b1b
diff --git a/ccnx/ccnx-key.cc b/ccnx/ccnx-key.cc
index 19dc84d..c3fdf10 100644
--- a/ccnx/ccnx-key.cc
+++ b/ccnx/ccnx-key.cc
@@ -58,8 +58,8 @@
       TiXmlElement *root = doc.RootElement();
       for (TiXmlElement *child = root->FirstChildElement(); child; child = child->NextSiblingElement())
       {
-        const char *elemName = child->Value();
-        const char *text = child->GetText();
+        string elemName = child->Value();
+        string text = child->GetText();
         if (elemName == "Name")
         {
           m_meta.realworldID = text;
@@ -70,7 +70,7 @@
         }
         else if (elemName == "Valid_to")
         {
-          m_meta.validTo = boost::lexical_cast<time_t>(std::string(text));
+          m_meta.validTo = boost::lexical_cast<time_t>(text);
         }
         else if (elemName == "Valid_from")
         {
@@ -109,7 +109,7 @@
     return EXPIRED;
   }
 
-  return VALID;
+  return WITHIN_VALID_TIME_SPAN;
 }
 
 } // Ccnx
diff --git a/ccnx/ccnx-key.h b/ccnx/ccnx-key.h
index 695afda..3d14afc 100644
--- a/ccnx/ccnx-key.h
+++ b/ccnx/ccnx-key.h
@@ -36,7 +36,7 @@
   enum VALIDITY
   {
     NOT_YET_VALID,
-    VALID,
+    WITHIN_VALID_TIME_SPAN,
     EXPIRED,
     OTHER
   };
diff --git a/ccnx/ccnx-pco.cpp b/ccnx/ccnx-pco.cpp
index 591f1d3..4ca46c7 100644
--- a/ccnx/ccnx-pco.cpp
+++ b/ccnx/ccnx-pco.cpp
@@ -37,22 +37,25 @@
 
 }
 
-ParsedContentObject::ParsedContentObject(const unsigned char *data, size_t len, bool verified)
+ParsedContentObject::ParsedContentObject(const unsigned char *data, size_t len, bool integrityChecked, bool verified)
             : m_comps(NULL)
+            , m_integrityChecked(integrityChecked)
             , m_verified(verified)
 {
   init(data, len);
 }
 
-ParsedContentObject::ParsedContentObject(const Bytes &bytes, bool verified)
+ParsedContentObject::ParsedContentObject(const Bytes &bytes, bool integrityChecked, bool verified)
             : m_comps(NULL)
+            , m_integrityChecked(integrityChecked)
             , m_verified(verified)
 {
   init(head(bytes), bytes.size());
 }
 
-ParsedContentObject::ParsedContentObject(const ParsedContentObject &other, bool verified)
+ParsedContentObject::ParsedContentObject(const ParsedContentObject &other, bool integrityChecked, bool verified)
             : m_comps(NULL)
+            , m_integrityChecked(integrityChecked)
             , m_verified(verified)
 {
   init(head(other.m_bytes), other.m_bytes.size());
@@ -100,4 +103,36 @@
   return Name(head(m_bytes), m_comps);
 }
 
+Name
+ParsedContentObject::keyName() const
+{
+  CcnxCharbufPtr ptr = boost::make_shared<CcnxCharbuf>();
+  ccn_charbuf_append(ptr->getBuf(), head(m_bytes) + m_pco.offset[CCN_PCO_B_KeyName_Name], m_pco.offset[CCN_PCO_E_KeyName_Name] - m_pco.offset[CCN_PCO_B_KeyName_Name]);
+
+  return Name(*ptr);
+
+}
+
+HashPtr
+ParsedContentObject::publisherPublicKeyDigest() const
+{
+  const unsigned char *buf = NULL;
+  size_t size = 0;
+  ccn_ref_tagged_BLOB(CCN_DTAG_PublisherPublicKeyDigest, head(m_bytes), m_pco.offset[CCN_PCO_B_PublisherPublicKeyDigest], m_pco.offset[CCN_PCO_E_PublisherPublicKeyDigest], &buf, &size);
+
+  return boost::make_shared<Hash>(buf, size);
+}
+
+ParsedContentObject::Type
+ParsedContentObject::type() const
+{
+  switch (m_pco.type)
+  {
+  case CCN_CONTENT_DATA: return DATA;
+  case CCN_CONTENT_KEY: return KEY;
+  default: break;
+  }
+  return OTHER;
+}
+
 }
diff --git a/ccnx/ccnx-pco.h b/ccnx/ccnx-pco.h
index 28b039d..d75a7ea 100644
--- a/ccnx/ccnx-pco.h
+++ b/ccnx/ccnx-pco.h
@@ -25,18 +25,28 @@
 #include "ccnx-wrapper.h"
 #include "ccnx-common.h"
 #include "ccnx-name.h"
+#include "hash-helper.h"
 
 namespace Ccnx {
 
 struct MisformedContentObjectException : virtual boost::exception, virtual std::exception { };
 
+class Key;
+typedef boost::shared_ptr<Key> KeyPtr;
+
 class ParsedContentObject
 {
 public:
-  ParsedContentObject(const unsigned char *data, size_t len, bool verified = false);
-  ParsedContentObject(const unsigned char *data, const ccn_parsed_ContentObject &pco, bool verified = false);
-  ParsedContentObject(const Bytes &bytes, bool verified = false);
-  ParsedContentObject(const ParsedContentObject &other, bool verified = false);
+  enum Type
+  {
+    DATA,
+    KEY,
+    OTHER
+  };
+  ParsedContentObject(const unsigned char *data, size_t len, bool integrityChecked = false, bool verified = false);
+  ParsedContentObject(const unsigned char *data, const ccn_parsed_ContentObject &pco, bool integrityChecked = false, bool verified = false);
+  ParsedContentObject(const Bytes &bytes, bool integrityChecked = false, bool verified = false);
+  ParsedContentObject(const ParsedContentObject &other, bool integrityChecked = false, bool verified = false);
   virtual ~ParsedContentObject();
 
   Bytes
@@ -48,6 +58,15 @@
   Name
   name() const;
 
+  Name
+  keyName() const;
+
+  HashPtr
+  publisherPublicKeyDigest() const;
+
+  Type
+  type() const;
+
   inline const Bytes &
   buf () const;
 
@@ -57,6 +76,12 @@
   void
   setVerified(bool verified) { m_verified = verified; }
 
+  bool
+  integrityChecked() const { return m_integrityChecked; }
+
+  void
+  setIntegrityChecked(bool checked) { m_integrityChecked = checked; }
+
   const unsigned char *
   msg() const { return head(m_bytes); }
 
@@ -72,6 +97,7 @@
   ccn_indexbuf *m_comps;
   Bytes m_bytes;
   bool m_verified;
+  bool m_integrityChecked;
 };
 
 const Bytes &
diff --git a/ccnx/ccnx-verifier.cpp b/ccnx/ccnx-verifier.cpp
new file mode 100644
index 0000000..e2832ac
--- /dev/null
+++ b/ccnx/ccnx-verifier.cpp
@@ -0,0 +1,143 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013 University of California, Los Angeles
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Zhenkai Zhu <zhenkai@cs.ucla.edu>
+ *         Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+#include "ccnx-verifier.h"
+
+namespace Ccnx {
+
+static const size_t ROOT_KEY_DIGEST_LEN = 32;  // SHA-256
+static const unsigned char ROOT_KEY_DIGEST[ROOT_KEY_DIGEST_LEN] = {0xa7, 0xd9, 0x8b, 0x81, 0xde, 0x13, 0xfc,
+0x56, 0xc5, 0xa6, 0x92, 0xb4, 0x44, 0x93, 0x6e, 0x56, 0x70, 0x9d, 0x52, 0x6f, 0x70,
+0xed, 0x39, 0xef, 0xb5, 0xe2, 0x3, 0x29, 0xa5, 0x53, 0x3e, 0x68};
+
+Verifier::Verifier(CcnxWrapper *ccnx)
+         : m_ccnx(ccnx)
+         , m_rootKeyDigest(ROOT_KEY_DIGEST, ROOT_KEY_DIGEST_LEN)
+{
+}
+
+Verifier::~Verifier()
+{
+}
+
+bool
+Verifier::verify(const PcoPtr &pco)
+{
+  if (pco->integrityChecked())
+  {
+    return false;
+  }
+
+  HashPtr publisherPublicKeyDigest = pco->publisherPublicKeyDigest();
+  KeyCache::iterator it = m_keyCache.find(*publisherPublicKeyDigest);
+  if (it != m_keyCache.end())
+  {
+    KeyPtr key = it->second;
+    if (key->validity() == Key::WITHIN_VALID_TIME_SPAN)
+    {
+      // integrity checked, and the key is trustworthy
+      pco->setVerified(true);
+      return true;
+    }
+    else
+    {
+      // delete the invalid key cache
+      m_keyCache.erase(it);
+    }
+  }
+
+  // keyName is the name specified in key locator, i.e. without version and segment
+  Name keyName = pco->keyName();
+  int keyNameSize = keyName.size();
+
+  // for keys, we have to make sure key name is strictly prefix of the content name
+  if (pco->type() == ParsedContentObject::KEY)
+  {
+    Name contentName = pco->name();
+    if (keyNameSize >= contentName.size() || contentName.getPartialName(0, keyNameSize) != keyName)
+    {
+      return false;
+    }
+  }
+  else
+  {
+    // for now, user can assign any data using his key
+  }
+
+  Name metaName = keyName.getPartialName(0, keyNameSize - 1) + Name("/info") + keyName.getPartialName(keyNameSize - 1);
+
+  Selectors selectors;
+
+  selectors.childSelector(Selectors::RIGHT)
+           .interestLifetime(1.0);
+
+  PcoPtr keyObject = m_ccnx->get(keyName, selectors);
+  PcoPtr metaObject = m_ccnx->get(metaName, selectors);
+  if (!keyObject || !metaObject || !keyObject->integrityChecked() || !metaObject->integrityChecked())
+  {
+    return false;
+  }
+
+  HashPtr publisherKeyHashInKeyObject = keyObject->publisherPublicKeyDigest();
+  HashPtr publisherKeyHashInMetaObject = metaObject->publisherPublicKeyDigest();
+
+  // make sure key and meta are signed using the same key
+  if (publisherKeyHashInKeyObject->IsZero() || ! (*publisherKeyHashInKeyObject == *publisherKeyHashInMetaObject))
+  {
+    return false;
+  }
+
+  KeyPtr key = boost::make_shared<Key>(keyObject, metaObject);
+  if (key->validity() != Key::WITHIN_VALID_TIME_SPAN)
+  {
+    return false;
+  }
+
+  // check pco is actually signed by this key (maybe redundant)
+  if (! (*pco->publisherPublicKeyDigest() == key->hash()))
+  {
+    return false;
+  }
+
+  // now we only need to make sure the keyObject is trustworthy
+  if (key->hash() == m_rootKeyDigest)
+  {
+    // the key is the root key
+    // do nothing now
+  }
+  else
+  {
+    // can not verify key
+    if (!verify(keyObject))
+    {
+      return false;
+    }
+  }
+
+  // ok, keyObject verified, because metaObject is signed by the same parent key and integrity checked
+  // so metaObject is also verified
+  m_keyCache.insert(std::make_pair(key->hash(), key));
+
+  pco->setVerified(true);
+  return true;
+}
+
+} // Ccnx
diff --git a/ccnx/ccnx-verifier.h b/ccnx/ccnx-verifier.h
new file mode 100644
index 0000000..2ee6def
--- /dev/null
+++ b/ccnx/ccnx-verifier.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013 University of California, Los Angeles
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Zhenkai Zhu <zhenkai@cs.ucla.edu>
+ *         Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+#ifndef CCNX_VERIFIER_H
+#define CCNX_VERIFIER_H
+
+#include "ccnx-common.h"
+#include "ccnx-wrapper.h"
+#include "ccnx-name.h"
+#include "ccnx-key.h"
+#include "ccnx-pco.h"
+#include <map>
+
+namespace Ccnx {
+
+class CcnxWrapper;
+
+// not thread-safe, don't want to add a mutex for KeyCache
+// which increases the possibility of dead-locking
+// ccnx-wrapper would take care of thread-safety issue
+class Verifier
+{
+public:
+  Verifier(CcnxWrapper *ccnx);
+  ~Verifier();
+
+  bool verify(const PcoPtr &pco);
+
+private:
+
+private:
+  CcnxWrapper *m_ccnx;
+  Hash m_rootKeyDigest;
+  typedef std::map<Hash, KeyPtr> KeyCache;
+  KeyCache m_keyCache;
+};
+
+} // Ccnx
+
+#endif // CCNX_VERIFIER_H
diff --git a/ccnx/ccnx-wrapper.cpp b/ccnx/ccnx-wrapper.cpp
index cc87710..58cebcd 100644
--- a/ccnx/ccnx-wrapper.cpp
+++ b/ccnx/ccnx-wrapper.cpp
@@ -455,7 +455,7 @@
   tuple<Closure *, ExecutorPtr, Selectors> *realData = reinterpret_cast< tuple<Closure*, ExecutorPtr, Selectors>* > (selfp->data);
   tie (cp, executor, selectors) = *realData;
 
-  bool verified = false;
+  bool checked = false;
 
   switch (kind)
     {
@@ -468,7 +468,7 @@
       return CCN_UPCALL_RESULT_OK;
 
     case CCN_UPCALL_CONTENT:
-      verified = true;
+      checked = true;
       _LOG_TRACE (">> incomingData content upcall: " << Name (info->content_ccnb, info->content_comps));
       break;
 
@@ -495,7 +495,7 @@
       return CCN_UPCALL_RESULT_OK;
     }
 
-  PcoPtr pco = make_shared<ParsedContentObject> (info->content_ccnb, info->pco->offset[CCN_PCO_E], verified);
+  PcoPtr pco = make_shared<ParsedContentObject> (info->content_ccnb, info->pco->offset[CCN_PCO_E], checked);
 
   // this will be run in executor
   executor->execute (bind (&Closure::runDataCallback, cp, pco->name (), pco));
@@ -684,11 +684,11 @@
 }
 
 bool
-CcnxWrapper::verifyPco(PcoPtr &pco)
+CcnxWrapper::checkPcoIntegrity(PcoPtr &pco)
 {
-  bool verified = (ccn_verify_content(m_handle, pco->msg(), (ccn_parsed_ContentObject *)pco->pco()) == 0);
-  pco->setVerified(verified);
-  return verified;
+  bool checked = (ccn_verify_content(m_handle, pco->msg(), (ccn_parsed_ContentObject *)pco->pco()) == 0);
+  pco->setIntegrityChecked(checked);
+  return checked;
 }
 
 // This is needed just for get function implementation
diff --git a/ccnx/ccnx-wrapper.h b/ccnx/ccnx-wrapper.h
index 418dde5..f3438c9 100644
--- a/ccnx/ccnx-wrapper.h
+++ b/ccnx/ccnx-wrapper.h
@@ -93,7 +93,7 @@
   putToCcnd (const Bytes &contentObject);
 
   bool
-  verifyPco(PcoPtr &pco);
+  checkPcoIntegrity(PcoPtr &pco);
 
   PcoPtr
   get (const Name &interest, const Selectors &selector = Selectors(), double maxWait = 4.0/*seconds*/);
diff --git a/src/fetcher.cc b/src/fetcher.cc
index d59f457..44ab9fc 100644
--- a/src/fetcher.cc
+++ b/src/fetcher.cc
@@ -132,8 +132,8 @@
 
   if (m_forwardingHint == Name ())
   {
-    // check whether data is verified in this case; if verified invoke callback
-    if (data->verified())
+    // check whether data integrity is checked in this case
+    if (data->integrityChecked())
     {
       if (!m_segmentCallback.empty ())
       {
@@ -149,12 +149,12 @@
   }
   else
     {
-      // in this case we don't care whether "data" is verified, in fact, we expect it is unverified
+      // in this case we don't care whether "data" is verified,  in fact, we expect it is unverified
       try {
         PcoPtr pco = make_shared<ParsedContentObject> (*data->contentPtr ());
 
         // we need to verify this pco and apply callback only when verified
-        if (m_ccnx->verifyPco(pco))
+        if (m_ccnx->checkPcoIntegrity(pco))
         {
           if (!m_segmentCallback.empty ())
             {
diff --git a/src/hash-helper.h b/src/hash-helper.h
index d289bb1..64a625e 100644
--- a/src/hash-helper.h
+++ b/src/hash-helper.h
@@ -41,8 +41,8 @@
   static HashPtr Origin;
 
   Hash ()
-    : m_length(0)
-    , m_buf(0)
+    : m_buf(0)
+    , m_length(0)
   {
   }
 
diff --git a/test/test-ccnx-wrapper.cc b/test/test-ccnx-wrapper.cc
index 5389702..2089bc2 100644
--- a/test/test-ccnx-wrapper.cc
+++ b/test/test-ccnx-wrapper.cc
@@ -68,7 +68,7 @@
   PcoPtr npco = make_shared<ParsedContentObject> (*(pco->contentPtr()));
   g_dataCallback_counter ++;
   BOOST_CHECK(npco);
-  BOOST_CHECK(c1->verifyPco(npco));
+  BOOST_CHECK(c1->checkPcoIntegrity(npco));
 }
 
 void