data: recognize Data in Packet Format v0.3
Data::wireDecode accepts both v0.2 and v0.3 formats,
but Data::wireEncode only encodes into v0.2 format.
refs #4568
Change-Id: I287d12c599f44d802a2a2cd2f13132c9f98a842a
diff --git a/src/data.cpp b/src/data.cpp
index 6b50dca..090b82d 100644
--- a/src/data.cpp
+++ b/src/data.cpp
@@ -49,8 +49,8 @@
{
// Data ::= DATA-TLV TLV-LENGTH
// Name
- // MetaInfo
- // Content
+ // MetaInfo?
+ // Content?
// SignatureInfo
// SignatureValue
@@ -121,26 +121,75 @@
void
Data::wireDecode(const Block& wire)
{
- m_fullName.clear();
m_wire = wire;
m_wire.parse();
+ bool hasName = false, hasSigInfo = false;
+ m_name.clear();
+ m_metaInfo = MetaInfo();
+ m_content = Block(tlv::Content);
+ m_signature = Signature();
+ m_fullName.clear();
- // Name
- m_name.wireDecode(m_wire.get(tlv::Name));
+ int lastEle = 0; // last recognized element index, in spec order
+ for (const Block& ele : m_wire.elements()) {
+ switch (ele.type()) {
+ case tlv::Name: {
+ if (lastEle >= 1) {
+ BOOST_THROW_EXCEPTION(Error("Name element is out of order"));
+ }
+ hasName = true;
+ m_name.wireDecode(ele);
+ lastEle = 1;
+ break;
+ }
+ case tlv::MetaInfo: {
+ if (lastEle >= 2) {
+ BOOST_THROW_EXCEPTION(Error("MetaInfo element is out of order"));
+ }
+ m_metaInfo.wireDecode(ele);
+ lastEle = 2;
+ break;
+ }
+ case tlv::Content: {
+ if (lastEle >= 3) {
+ BOOST_THROW_EXCEPTION(Error("Content element is out of order"));
+ }
+ m_content = ele;
+ lastEle = 3;
+ break;
+ }
+ case tlv::SignatureInfo: {
+ if (lastEle >= 4) {
+ BOOST_THROW_EXCEPTION(Error("SignatureInfo element is out of order"));
+ }
+ hasSigInfo = true;
+ m_signature.setInfo(ele);
+ lastEle = 4;
+ break;
+ }
+ case tlv::SignatureValue: {
+ if (lastEle >= 5) {
+ BOOST_THROW_EXCEPTION(Error("SignatureValue element is out of order"));
+ }
+ m_signature.setValue(ele);
+ lastEle = 5;
+ break;
+ }
+ default: {
+ if (tlv::isCriticalType(ele.type())) {
+ BOOST_THROW_EXCEPTION(Error("unrecognized element of critical type " +
+ to_string(ele.type())));
+ }
+ break;
+ }
+ }
+ }
- // MetaInfo
- m_metaInfo.wireDecode(m_wire.get(tlv::MetaInfo));
-
- // Content
- m_content = m_wire.get(tlv::Content);
-
- // SignatureInfo
- m_signature.setInfo(m_wire.get(tlv::SignatureInfo));
-
- // SignatureValue
- Block::element_const_iterator val = m_wire.find(tlv::SignatureValue);
- if (val != m_wire.elements_end()) {
- m_signature.setValue(*val);
+ if (!hasName) {
+ BOOST_THROW_EXCEPTION(Error("Name element is missing"));
+ }
+ if (!hasSigInfo) {
+ BOOST_THROW_EXCEPTION(Error("SignatureInfo element is missing"));
}
}
diff --git a/src/data.hpp b/src/data.hpp
index 8eb1b3d..2368017 100644
--- a/src/data.hpp
+++ b/src/data.hpp
@@ -45,21 +45,23 @@
}
};
- /** @brief Create a new Data with the given name and empty Content
- * @warning In certain contexts that use Data::shared_from_this(), Data must be created
- * using `make_shared`. Otherwise, .shared_from_this() will trigger undefined behavior.
+ /** @brief Construct an unsigned Data packet with given @p name and empty Content.
+ * @warning In certain contexts that use `Data::shared_from_this()`, Data must be created
+ * using `make_shared`. Otherwise, `shared_from_this()` will trigger undefined behavior.
*/
explicit
Data(const Name& name = Name());
- /** @brief Create from wire encoding
- * @warning In certain contexts that use Data::shared_from_this(), Data must be created
- * using `make_shared`. Otherwise, .shared_from_this() will trigger undefined behavior.
+ /** @brief Construct a Data packet by decoding from @p wire.
+ * @param wire @c tlv::Data element as defined in NDN Packet Format v0.2 or v0.3.
+ * It may be signed or unsigned.
+ * @warning In certain contexts that use `Data::shared_from_this()`, Data must be created
+ * using `make_shared`. Otherwise, `shared_from_this()` will trigger undefined behavior.
*/
explicit
Data(const Block& wire);
- /** @brief Fast encoding or block size estimation
+ /** @brief Prepend wire encoding to @p encoder in NDN Packet Format v0.2.
* @param encoder EncodingEstimator or EncodingBuffer instance
* @param wantUnsignedPortionOnly If true, only prepends Name, MetaInfo, Content, and
* SignatureInfo to @p encoder, but omit SignatureValue and outmost Type-Length of Data
@@ -89,17 +91,22 @@
const Block&
wireEncode(EncodingBuffer& encoder, const Block& signatureValue) const;
- /** @brief Encode to a wire format
+ /** @brief Encode to a @c Block.
+ * @pre Data is signed.
+ *
+ * Normally, this function encodes to NDN Packet Format v0.2. However, if this instance has
+ * cached wire encoding (\c hasWire() is true), the cached encoding is returned and it might
+ * be in v0.3 format.
*/
const Block&
wireEncode() const;
- /** @brief Decode from the wire format
+ /** @brief Decode from @p wire in NDN Packet Format v0.2 or v0.3.
*/
void
wireDecode(const Block& wire);
- /** @brief Check if already has wire
+ /** @brief Check if this instance has cached wire encoding.
*/
bool
hasWire() const
diff --git a/tests/unit-tests/data.t.cpp b/tests/unit-tests/data.t.cpp
index e2054a5..ccf4e5b 100644
--- a/tests/unit-tests/data.t.cpp
+++ b/tests/unit-tests/data.t.cpp
@@ -30,6 +30,7 @@
#include "security/verification-helpers.hpp"
#include "util/sha256.hpp"
+#include "block-literal.hpp"
#include "boost-test.hpp"
#include "identity-management-fixture.hpp"
#include <boost/lexical_cast.hpp>
@@ -88,7 +89,7 @@
BOOST_CHECK_EQUAL(d.getName(), "/");
BOOST_CHECK_EQUAL(d.getContentType(), tlv::ContentType_Blob);
BOOST_CHECK_EQUAL(d.getFreshnessPeriod(), DEFAULT_FRESHNESS_PERIOD);
- BOOST_CHECK_EQUAL(d.getFinalBlockId(), name::Component());
+ BOOST_CHECK(!d.getFinalBlock());
BOOST_CHECK_EQUAL(d.getContent().type(), tlv::Content);
BOOST_CHECK_EQUAL(d.getContent().value_size(), 0);
BOOST_CHECK(!d.getSignature());
@@ -205,7 +206,7 @@
dataBlock.begin(), dataBlock.end());
}
-BOOST_FIXTURE_TEST_CASE(Decode, DataSigningKeyFixture)
+BOOST_FIXTURE_TEST_CASE(Decode02, DataSigningKeyFixture)
{
Block dataBlock(DATA1, sizeof(DATA1));
Data d(dataBlock);
@@ -225,6 +226,123 @@
BOOST_CHECK(security::verifySignature(d, m_pubKey));
}
+class Decode03Fixture
+{
+protected:
+ Decode03Fixture()
+ {
+ // initialize all elements to non-empty, to verify wireDecode clears them
+ d.setName("/A");
+ d.setContentType(tlv::ContentType_Key);
+ d.setContent("1504C0C1C2C3"_block);
+ d.setSignature(Signature("160A 1B0101 1C050703080142"_block,
+ "1780 B48F1707A3BCA3CFC5F32DE51D9B46C32D7D262A21544EBDA88C3B415D637503"
+ "FC9BEF20F88202A56AF9831E0D30205FD4154B08502BCDEE860267A5C3E03D8E"
+ "A6CB74BE391C01E0A57B991B4404FC11B7D777F1B700A4B65F201118CF1840A8"
+ "30A2A7C17DB4B7A8777E58515121AF9E2498627F8475414CDFD9801B8152AD5B"_block));
+ }
+
+protected:
+ Data d;
+};
+
+BOOST_FIXTURE_TEST_SUITE(Decode03, Decode03Fixture)
+
+BOOST_AUTO_TEST_CASE(MinimalNoSigValue)
+{
+ d.wireDecode("0607 0700 16031B0100"_block);
+ BOOST_CHECK_EQUAL(d.getName(), "/"); // empty Name is allowed in Data
+ BOOST_CHECK_EQUAL(d.getMetaInfo(), MetaInfo());
+ BOOST_CHECK_EQUAL(d.getContent().value_size(), 0);
+ BOOST_CHECK_EQUAL(d.getSignature().getType(), tlv::DigestSha256);
+ BOOST_CHECK_EQUAL(d.getSignature().getValue().value_size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(Minimal)
+{
+ d.wireDecode("062C 0703080144 16031B0100 "
+ "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block);
+ BOOST_CHECK_EQUAL(d.getName(), "/D");
+ BOOST_CHECK_EQUAL(d.getMetaInfo(), MetaInfo());
+ BOOST_CHECK_EQUAL(d.getContent().value_size(), 0);
+ BOOST_CHECK_EQUAL(d.getSignature().getType(), tlv::DigestSha256);
+ BOOST_CHECK_EQUAL(d.getSignature().getValue().value_size(), 32);
+
+ // encode without modification: retain original wire encoding
+ BOOST_CHECK_EQUAL(d.wireEncode().value_size(), 44);
+
+ // modify then re-encode as v0.2 format
+ d.setName("/E");
+ BOOST_CHECK(d.wireEncode() ==
+ "0630 0703080145 1400 1500 16031B0100 "
+ "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block);
+}
+
+BOOST_AUTO_TEST_CASE(Full)
+{
+ d.wireDecode("063C FC00 0703080144 FC00 1400 FC00 1500 FC00 16031B0100 FC00 "
+ "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76 FC00"_block);
+ BOOST_CHECK_EQUAL(d.getName(), "/D");
+ BOOST_CHECK_EQUAL(d.getMetaInfo(), MetaInfo());
+ BOOST_CHECK_EQUAL(d.getContent().value_size(), 0);
+ BOOST_CHECK_EQUAL(d.getSignature().getType(), tlv::DigestSha256);
+ BOOST_CHECK_EQUAL(d.getSignature().getValue().value_size(), 32);
+
+ // encode without modification: retain original wire encoding
+ BOOST_CHECK_EQUAL(d.wireEncode().value_size(), 60);
+
+ // modify then re-encode as v0.2 format
+ d.setName("/E");
+ BOOST_CHECK(d.wireEncode() ==
+ "0630 0703080145 1400 1500 16031B0100 "
+ "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block);
+}
+
+BOOST_AUTO_TEST_CASE(CriticalElementOutOfOrder)
+{
+ BOOST_CHECK_THROW(d.wireDecode(
+ "0630 1400 0703080145 1500 16031B0100 "
+ "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block),
+ tlv::Error);
+ BOOST_CHECK_THROW(d.wireDecode(
+ "0630 0703080145 1500 1400 16031B0100 "
+ "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block),
+ tlv::Error);
+ BOOST_CHECK_THROW(d.wireDecode(
+ "0630 0703080145 1400 16031B0100 1500 "
+ "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block),
+ tlv::Error);
+ BOOST_CHECK_THROW(d.wireDecode(
+ "0630 0703080145 1400 1500 "
+ "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76 16031B0100"_block),
+ tlv::Error);
+ BOOST_CHECK_THROW(d.wireDecode(
+ "0652 0703080145 1400 1500 16031B0100 "
+ "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"
+ "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block),
+ tlv::Error);
+}
+
+BOOST_AUTO_TEST_CASE(NameMissing)
+{
+ BOOST_CHECK_THROW(d.wireDecode("0605 16031B0100"_block), tlv::Error);
+}
+
+BOOST_AUTO_TEST_CASE(SigInfoMissing)
+{
+ BOOST_CHECK_THROW(d.wireDecode("0605 0703080144"_block), tlv::Error);
+}
+
+BOOST_AUTO_TEST_CASE(UnrecognizedCriticalElement)
+{
+ BOOST_CHECK_THROW(d.wireDecode(
+ "0632 0703080145 FB00 1400 1500 16031B0100 "
+ "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block),
+ tlv::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Decode03
+
BOOST_FIXTURE_TEST_CASE(FullName, IdentityManagementFixture)
{
Data d(Name("/local/ndn/prefix"));