data: Implementing Data::getFullName() method to get Data packet name with implicit digest

Note that getFullName() method will throw a Data::Error if Data packet
does not have wire encoding, i.e., it has not been constructed from wire
and is not yet signed.

Change-Id: I7a9b8a6c9e4c6eced9bc907bfc81adb5d4f0e4b3
Refs: #1298
diff --git a/src/data.cpp b/src/data.cpp
index 8ce8f4c..596e20b 100644
--- a/src/data.cpp
+++ b/src/data.cpp
@@ -116,6 +116,7 @@
 void
 Data::wireDecode(const Block& wire)
 {
+  m_fullName.clear();
   m_wire = wire;
   m_wire.parse();
 
@@ -156,6 +157,21 @@
   return *this;
 }
 
+const Name&
+Data::getFullName() const
+{
+  if (m_fullName.empty()) {
+    if (!m_wire.hasWire()) {
+      throw Error("Full name requested, but Data packet does not have wire format "
+                  "(e.g., not signed)");
+    }
+    m_fullName = m_name;
+    m_fullName.append(name::Component(crypto::sha256(m_wire.wire(), m_wire.size())));
+  }
+
+  return m_fullName;
+}
+
 Data&
 Data::setMetaInfo(const MetaInfo& metaInfo)
 {
@@ -275,6 +291,7 @@
   // the application to do proper re-signing if necessary
 
   m_wire.reset();
+  m_fullName.clear();
 }
 
 bool
diff --git a/src/data.hpp b/src/data.hpp
index 9338258..70d1a72 100644
--- a/src/data.hpp
+++ b/src/data.hpp
@@ -157,6 +157,15 @@
   //
 
   /**
+   * @brief Get full name of Data packet, including the implicit digest
+   *
+   * @throws Error if Data packet doesn't have a full name yet (wire encoding has not been
+   *         yet created)
+   */
+  const Name&
+  getFullName() const;
+
+  /**
    * @brief Get MetaInfo block from Data packet
    */
   const MetaInfo&
@@ -306,6 +315,7 @@
   Signature m_signature;
 
   mutable Block m_wire;
+  mutable Name m_fullName;
 
   nfd::LocalControlHeader m_localControlHeader;
   friend class nfd::LocalControlHeader;
diff --git a/src/interest.cpp b/src/interest.cpp
index 590d527..986a5ec 100644
--- a/src/interest.cpp
+++ b/src/interest.cpp
@@ -68,13 +68,13 @@
     return false;
 
   if (getMinSuffixComponents() >= 0 &&
-    // Add 1 for the implicit digest.
-      !(name.size() + 1 - m_name.size() >= static_cast<size_t>(getMinSuffixComponents())))
+      // name must include implicit digest
+      !(name.size() - m_name.size() >= static_cast<size_t>(getMinSuffixComponents())))
     return false;
 
   if (getMaxSuffixComponents() >= 0 &&
-    // Add 1 for the implicit digest.
-      !(name.size() + 1 - m_name.size() <= static_cast<size_t>(getMaxSuffixComponents())))
+      // name must include implicit digest
+      !(name.size() - m_name.size() <= static_cast<size_t>(getMaxSuffixComponents())))
     return false;
 
   if (!getExclude().empty() &&
@@ -88,7 +88,7 @@
 bool
 Interest::matchesData(const Data& data) const
 {
-  if (!this->matchesName(data.getName())) {
+  if (!this->matchesName(data.getFullName())) {
     return false;
   }
 
diff --git a/tests/unit-tests/test-data.cpp b/tests/unit-tests/test-data.cpp
index 7e52376..f86c2bc 100644
--- a/tests/unit-tests/test-data.cpp
+++ b/tests/unit-tests/test-data.cpp
@@ -350,6 +350,70 @@
                                   dataBlock.begin(), dataBlock.end());
 }
 
+class DataIdentityFixture
+{
+public:
+  DataIdentityFixture()
+    : identity("/TestData")
+  {
+    identity.appendVersion();
+
+    BOOST_REQUIRE_NO_THROW(certName = keyChain.createIdentity(identity));
+  }
+
+  ~DataIdentityFixture()
+  {
+    BOOST_CHECK_NO_THROW(keyChain.deleteIdentity(identity));
+  }
+
+public:
+  KeyChain keyChain;
+  Name identity;
+  Name certName;
+};
+
+BOOST_FIXTURE_TEST_CASE(FullName, DataIdentityFixture)
+{
+  // Encoding pipeline
+
+  ndn::Data d(ndn::Name("/local/ndn/prefix"));
+  d.setContentType(MetaInfo::TYPE_DEFAULT);
+  d.setFreshnessPeriod(time::seconds(10));
+
+  d.setContent(Content1, sizeof(Content1));
+
+  BOOST_CHECK_THROW(d.getFullName(), Data::Error);
+
+  keyChain.sign(d, certName);
+
+  Name fullName;
+  BOOST_REQUIRE_NO_THROW(fullName = d.getFullName());
+
+  BOOST_CHECK_EQUAL(d.getName().hasWire(), true);
+  BOOST_CHECK_EQUAL(fullName.hasWire(), false);
+
+  // check if name was properly cached
+  BOOST_CHECK_EQUAL(fullName.get(-1).value(), d.getFullName().get(-1).value());
+
+  // check FullName content
+  BOOST_REQUIRE_EQUAL(d.getName().size() + 1, fullName.size());
+  BOOST_CHECK_EQUAL_COLLECTIONS(d.getName().begin(), d.getName().end(),
+                                fullName.begin(), fullName.end() - 1);
+  BOOST_CHECK_EQUAL(fullName.get(-1).value_size(), 32);
+
+  // FullName should be reset after the next line
+  d.setFreshnessPeriod(time::seconds(100));
+  BOOST_CHECK_THROW(d.getFullName(), Data::Error);
+
+  // Decoding pipeline
+  d.wireDecode(Block(Data1, sizeof(Data1)));
+  BOOST_REQUIRE_NO_THROW(fullName = d.getFullName());
+
+  BOOST_CHECK_EQUAL(fullName.toUri(),
+    "/local/ndn/prefix/"
+    "%28%BA%D4%B5%27%5B%D3%92%DB%B6p%C7%5C%F0%B6o%13%F7%94+%21%E8%0FU%C0%E8k7GS%A5H");
+}
+
 BOOST_AUTO_TEST_CASE(EncodeMetaInfo)
 {
   MetaInfo meta;
diff --git a/tests/unit-tests/test-interest.cpp b/tests/unit-tests/test-interest.cpp
index 25f79fa..5bb0a85 100644
--- a/tests/unit-tests/test-interest.cpp
+++ b/tests/unit-tests/test-interest.cpp
@@ -459,36 +459,78 @@
   SignatureSha256WithRsa signature;
   signature.setKeyLocator(KeyLocator("ndn:/B"));
   data.setSignature(signature);
+  data.wireEncode();
   BOOST_CHECK_EQUAL(interest.matchesData(data), true);
 
   Data data1 = data;
-  data1.setName("ndn:/A");// violates MinSuffixComponents
+  data1.setName("ndn:/A"); // violates MinSuffixComponents
+  data1.wireEncode();
   BOOST_CHECK_EQUAL(interest.matchesData(data1), false);
 
+  interest.setMinSuffixComponents(1);
+  BOOST_CHECK_EQUAL(interest.matchesData(data1), true);
+  interest.setMinSuffixComponents(2);
+
   Data data2 = data;
-  data2.setName("ndn:/A/E/F");// violates MaxSuffixComponents
+  data2.setName("ndn:/A/E/F"); // violates MaxSuffixComponents
+  data2.wireEncode();
   BOOST_CHECK_EQUAL(interest.matchesData(data2), false);
 
+  interest.setMaxSuffixComponents(3);
+  BOOST_CHECK_EQUAL(interest.matchesData(data2), true);
+  interest.setMaxSuffixComponents(2);
+
   Data data3 = data;
   SignatureSha256WithRsa signature3;
-  signature3.setKeyLocator(KeyLocator("ndn:/G"));// violates PublisherPublicKeyLocator
+  signature3.setKeyLocator(KeyLocator("ndn:/G")); // violates PublisherPublicKeyLocator
   data3.setSignature(signature3);
+  data3.wireEncode();
   BOOST_CHECK_EQUAL(interest.matchesData(data3), false);
 
+  interest.setPublisherPublicKeyLocator(KeyLocator("ndn:/G"));
+  BOOST_CHECK_EQUAL(interest.matchesData(data3), true);
+  interest.setPublisherPublicKeyLocator(KeyLocator("ndn:/B"));
+
   Data data4 = data;
-  SignatureSha256 signature4;// violates PublisherPublicKeyLocator
+  SignatureSha256 signature4; // violates PublisherPublicKeyLocator
   data4.setSignature(signature4);
+  data4.wireEncode();
   BOOST_CHECK_EQUAL(interest.matchesData(data4), false);
 
+  interest.setPublisherPublicKeyLocator(KeyLocator());
+  BOOST_CHECK_EQUAL(interest.matchesData(data4), true);
+  interest.setPublisherPublicKeyLocator(KeyLocator("ndn:/B"));
+
   Data data5 = data;
-  data5.setName("ndn:/A/C");// violates Exclude
+  data5.setName("ndn:/A/C"); // violates Exclude
+  data5.wireEncode();
   BOOST_CHECK_EQUAL(interest.matchesData(data5), false);
 
+  interest.setExclude(Exclude().excludeBefore(name::Component("A")));
+  BOOST_CHECK_EQUAL(interest.matchesData(data5), true);
+  interest.setExclude(Exclude().excludeBefore(name::Component("C")));
+
   Data data6 = data;
-  data6.setName("ndn:/H/I");// violates Name
+  data6.setName("ndn:/H/I"); // violates Name
+  data6.wireEncode();
   BOOST_CHECK_EQUAL(interest.matchesData(data6), false);
+
+  Data data7 = data;
+  data7.setName("ndn:/A/B");
+  data7.wireEncode();
+
+  interest = Interest()
+    .setName("/A/B/%D5H%DE%CE%FCK%88%07%20%DC%92W%A8%D8%15%E9%DFDe%E67B%EEU%C2%913%05%5D%AAg%C2");
+  BOOST_CHECK_EQUAL(interest.matchesData(data7), true);
+
+  interest = Interest()
+    .setName("/A/B/%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00"
+                  "%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00");
+  BOOST_CHECK_EQUAL(interest.matchesData(data7), false); // violates implicit digest
 }
 
+
+
 BOOST_AUTO_TEST_CASE(InterestFilterMatching)
 {
   BOOST_CHECK_EQUAL(InterestFilter("/a").doesMatch("/a/b"), true);