suffix length config

Change-Id: Ic1fd46a7d9b21a342cd9bf693169dc5df34fc651
diff --git a/src/ca-config.cpp b/src/ca-config.cpp
index c8bc298..c9721fb 100644
--- a/src/ca-config.cpp
+++ b/src/ca-config.cpp
@@ -59,7 +59,10 @@
   // CA max validity period
   m_maxValidityPeriod = time::seconds(configJson.get(CONFIG_MAX_VALIDITY_PERIOD, 3600));
   // CA max suffix length
-  m_maxSuffixLength = configJson.get(CONFIG_MAX_SUFFIX_LENGTH, 1);
+  m_maxSuffixLength = configJson.get<size_t>(CONFIG_MAX_SUFFIX_LENGTH, 1);
+  if (m_maxSuffixLength == 0) {
+      m_maxSuffixLength = 1;
+  }
   // probe
   m_probeParameterKeys.clear();
   auto probeParameters = configJson.get_child_optional(CONFIG_PROBE_PARAMETERS);
diff --git a/src/ca-module.cpp b/src/ca-module.cpp
index 15c3055..82f0eec 100644
--- a/src/ca-module.cpp
+++ b/src/ca-module.cpp
@@ -301,7 +301,9 @@
     // verify the self-signed certificate, the request, and the token
     if (!m_config.m_caPrefix.isPrefixOf(clientCert->getName()) // under ca prefix
         || !security::v2::Certificate::isValidName(clientCert->getName()) // is valid cert name
-        || clientCert->getName().size() < m_config.m_caPrefix.size() + IS_SUBNAME_MIN_OFFSET) {
+        || clientCert->getName().size() < m_config.m_caPrefix.size() + IS_SUBNAME_MIN_OFFSET
+        || clientCert->getName().size() >
+                m_config.m_caPrefix.size() + IS_SUBNAME_MIN_OFFSET - 1 + m_config.m_maxSuffixLength) {
       _LOG_ERROR("Invalid self-signed certificate name " << clientCert->getName());
       return;
     }
@@ -330,7 +332,9 @@
     // verify the certificate
     if (!m_config.m_caPrefix.isPrefixOf(clientCert->getName()) // under ca prefix
         || !security::v2::Certificate::isValidName(clientCert->getName()) // is valid cert name
-        || clientCert->getName().size() < m_config.m_caPrefix.size() + IS_SUBNAME_MIN_OFFSET) {
+        || clientCert->getName().size() < m_config.m_caPrefix.size() + IS_SUBNAME_MIN_OFFSET
+        || clientCert->getName().size() >
+                m_config.m_caPrefix.size() + IS_SUBNAME_MIN_OFFSET - 1 + m_config.m_maxSuffixLength) {
       _LOG_ERROR("Invalid certificate name " << clientCert->getName());
       return;
     }
diff --git a/src/client-config.cpp b/src/client-config.cpp
index 06299b5..2396cab 100644
--- a/src/client-config.cpp
+++ b/src/client-config.cpp
@@ -93,6 +93,7 @@
   item.m_caInfo = configSection.get("ca-info", "");
   item.m_probe = configSection.get("probe", "");
   std::istringstream ss(configSection.get("certificate", ""));
+  item.m_maxSuffixLength = configSection.get<size_t>(CONFIG_MAX_SUFFIX_LENGTH, 1);
   auto anchor = io::load<security::v2::Certificate>(ss);
   if (anchor == nullptr) {
     BOOST_THROW_EXCEPTION(Error("Cannot load the certificate from config file"));
diff --git a/src/client-config.hpp b/src/client-config.hpp
index 80d5241..2d74cf6 100644
--- a/src/client-config.hpp
+++ b/src/client-config.hpp
@@ -53,6 +53,11 @@
    * CA's certificate.
    */
   security::v2::Certificate m_anchor;
+  /**
+   * Maximum allowed suffix length of requested name.
+   * E.g., When its value is 2, at most 2 name components can be assigned after m_caPrefix.
+   */
+  size_t m_maxSuffixLength;
 
   //=======old
 
diff --git a/src/protocol-detail/info.cpp b/src/protocol-detail/info.cpp
index 51dc3f8..59cc48e 100644
--- a/src/protocol-detail/info.cpp
+++ b/src/protocol-detail/info.cpp
@@ -41,6 +41,7 @@
   }
   content.push_back(makeNonNegativeIntegerBlock(tlv_max_validity_period, caConfig.m_maxValidityPeriod.count()));
   content.push_back(makeNestedBlock(tlv_ca_certificate, certificate));
+  content.push_back(makeNonNegativeIntegerBlock(tlv_max_suffix_length, caConfig.m_maxSuffixLength));
   content.encode();
   return content;
 }
@@ -67,6 +68,8 @@
     else if (item.type() == tlv_ca_certificate) {
       item.parse();
       result.m_anchor.wireDecode(item.get(tlv::Data));
+    } else if (item.type() == tlv_max_suffix_length) {
+      result.m_maxSuffixLength = readNonNegativeInteger(item);
     }
     else {
       continue;
diff --git a/tests/unit-tests/ca-module.t.cpp b/tests/unit-tests/ca-module.t.cpp
index 3860060..2ac15da 100644
--- a/tests/unit-tests/ca-module.t.cpp
+++ b/tests/unit-tests/ca-module.t.cpp
@@ -278,6 +278,64 @@
   BOOST_CHECK_EQUAL(count, 1);
 }
 
+BOOST_AUTO_TEST_CASE(HandleNewWithLongSuffix)
+{
+  auto identity = addIdentity(Name("/ndn"));
+  auto key = identity.getDefaultKey();
+  auto cert = key.getDefaultCertificate();
+
+  util::DummyClientFace face(io, m_keyChain, { true, true });
+  CaModule ca(face, m_keyChain, "tests/unit-tests/ca.conf.test", "ca-storage-memory");
+  advanceClocks(time::milliseconds(20), 60);
+
+  ClientModule client(m_keyChain);
+  ClientCaItem item;
+  item.m_caPrefix = Name("/ndn");
+  item.m_anchor = cert;
+  client.getClientConf().m_caItems.push_back(item);
+
+  auto interest = client.generateNewInterest(time::system_clock::now(),
+            time::system_clock::now() + time::days(1),
+            Name("/ndn/a/b/c"));
+
+  int count = 0;
+  face.onSendData.connect([&](const Data& response) {
+    count++;
+  });
+  face.receive(*interest);
+
+  advanceClocks(time::milliseconds(20), 60);
+  BOOST_CHECK_EQUAL(count, 1);
+}
+
+
+BOOST_AUTO_TEST_CASE(HandleNewWithInvalidLength1)
+{
+  auto identity = addIdentity(Name("/ndn"));
+  auto key = identity.getDefaultKey();
+  auto cert = key.getDefaultCertificate();
+
+  util::DummyClientFace face(io, m_keyChain, {true, true});
+  CaModule ca(face, m_keyChain, "tests/unit-tests/ca.conf.test");
+  advanceClocks(time::milliseconds(20), 60);
+
+  ClientModule client(m_keyChain);
+  ClientCaItem item;
+  item.m_caPrefix = Name("/ndn");
+  item.m_anchor = cert;
+  client.getClientConf().m_caItems.push_back(item);
+  auto current_tp = time::system_clock::now();
+  auto interest1 = client.generateNewInterest(current_tp, current_tp + time::days(1),Name("/ndn"));
+  auto interest2 = client.generateNewInterest(current_tp, current_tp + time::days(1),Name("/ndn/a/b/c/d"));
+  face.onSendData.connect([&](const Data& response) {
+      BOOST_CHECK(false);
+  });
+  face.receive(*interest1);
+  face.receive(*interest2);
+
+  advanceClocks(time::milliseconds(20), 60);
+}
+
 BOOST_AUTO_TEST_CASE(HandleChallenge)
 {
   auto identity = addIdentity(Name("/ndn"));
diff --git a/tests/unit-tests/ca.conf.test b/tests/unit-tests/ca.conf.test
index f742a2b..bf795fd 100644
--- a/tests/unit-tests/ca.conf.test
+++ b/tests/unit-tests/ca.conf.test
@@ -9,5 +9,6 @@
   "supported-challenges":
   [
       { "challenge": "PIN" }
-  ]
+  ],
+  "max-suffix-length": 3
 }
\ No newline at end of file