Merge "CryptoHelper: Fix a logic error in HKDF and use OpenSSL HMAC"
diff --git a/src/ca-module.cpp b/src/ca-module.cpp
index 715fe1d..c7b4291 100644
--- a/src/ca-module.cpp
+++ b/src/ca-module.cpp
@@ -32,6 +32,7 @@
 
 static const int IS_SUBNAME_MIN_OFFSET = 5;
 static const time::seconds DEFAULT_DATA_FRESHNESS_PERIOD = 1_s;
+static const time::seconds REQUEST_VALIDITY_PERIOD_NOT_BEFORE_GRACE_PERIOD = 120_s;
 
 _LOG_INIT(ndncert.ca);
 
@@ -195,6 +196,18 @@
     _LOG_ERROR("Unrecognized certificate request " << e.what());
     return;
   }
+  // check the validity period
+  auto expectedPeriod = clientCert->getValidityPeriod().getPeriod();
+  auto currentTime = time::system_clock::now();
+  if (expectedPeriod.first < currentTime - REQUEST_VALIDITY_PERIOD_NOT_BEFORE_GRACE_PERIOD) {
+    _LOG_ERROR("Client requests a too old notBefore timepoint.");
+    return;
+  }
+  if (expectedPeriod.second > currentTime + m_config.m_validityPeriod ||
+      expectedPeriod.second <= expectedPeriod.first) {
+    _LOG_ERROR("Client requests an invalid validity period or a notAfter timepoint beyond the allowed time period.");
+    return;
+  }
 
   // parse probe token if any
   std::string probeTokenStr = parameterJson.get("probe-token", "");
@@ -410,24 +423,7 @@
 {
   auto expectedPeriod =
     certRequest.m_cert.getValidityPeriod().getPeriod();
-
-  time::system_clock::TimePoint startingTime, endingTime;
-  if (expectedPeriod.first > time::system_clock::now()
-      && expectedPeriod.first <  time::system_clock::now()
-      + m_config.m_validityPeriod)
-    {
-      startingTime = expectedPeriod.first;
-    }
-  else {
-    startingTime = time::system_clock::now();
-  }
-  if (expectedPeriod.second < time::system_clock::now() + m_config.m_validityPeriod) {
-    endingTime = expectedPeriod.second;
-  }
-  else {
-    endingTime = time::system_clock::now() + m_config.m_validityPeriod;
-  }
-  security::ValidityPeriod period(startingTime, endingTime);
+  security::ValidityPeriod period(expectedPeriod.first, expectedPeriod.second);
   security::v2::Certificate newCert;
 
   Name certName = certRequest.m_cert.getKeyName();
diff --git a/tests/unit-tests/ca-module.t.cpp b/tests/unit-tests/ca-module.t.cpp
index bb6153f..1468aa5 100644
--- a/tests/unit-tests/ca-module.t.cpp
+++ b/tests/unit-tests/ca-module.t.cpp
@@ -187,6 +187,39 @@
   BOOST_CHECK_EQUAL(count, 1);
 }
 
+BOOST_AUTO_TEST_CASE(HandleNewWithInvalidValidityPeriod1)
+{
+  auto identity = addIdentity(Name("/ndn"));
+  auto key = identity.getDefaultKey();
+  auto cert = key.getDefaultCertificate();
+
+  util::DummyClientFace face(io, {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_caName = 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::hours(1),
+                                              Name("/ndn/zhiyi"));
+  auto interest2 = client.generateNewInterest(current_tp, current_tp + time::days(361),
+                                              Name("/ndn/zhiyi"));
+  auto interest3 = client.generateNewInterest(current_tp - time::hours(1),
+                                              current_tp + time::hours(2),
+                                              Name("/ndn/zhiyi"));
+  face.onSendData.connect([&] (const Data& response) {
+      BOOST_CHECK(false);
+    });
+  face.receive(*interest1);
+  face.receive(*interest2);
+  face.receive(*interest3);
+
+  advanceClocks(time::milliseconds(20), 60);
+}
+
 BOOST_AUTO_TEST_CASE(HandleNewWithProbeToken)
 {
   auto identity = addIdentity(Name("/ndn"));
diff --git a/tools/ndncert-client.cpp b/tools/ndncert-client.cpp
index f6460fd..66a81ef 100644
--- a/tools/ndncert-client.cpp
+++ b/tools/ndncert-client.cpp
@@ -219,11 +219,22 @@
             << " Type in a number in unit of hour. The CA may change the validity"
             << " period if your expected period is too long." << std::endl;
   std::string periodStr;
+  int hours = 0;
   getline(std::cin, periodStr);
-  int hours = std::stoi(periodStr);
+  hours = std::stoi(periodStr);
+  while (hours <= 0) {
+    std::cerr << "Invalid period time: " << "Please input the period again." << std::endl;
+    getline(std::cin, periodStr);
+    try {
+      hours = std::stoi(periodStr);
+    }
+    catch (const std::exception& e) {
+      hours = -1;
+    }
+  }
   auto probeToken = make_shared<Data>(reply);
-  face.expressInterest(*client.generateNewInterest(time::system_clock::now(),
-                                                   time::system_clock::now() + time::hours(hours),
+  auto now = time::system_clock::now();
+  face.expressInterest(*client.generateNewInterest(now, now + time::hours(hours),
                                                    Name(), probeToken),
                        bind(&newCb, _2),
                        bind(&onNackCb),
@@ -297,8 +308,8 @@
       std::string periodStr;
       getline(std::cin, periodStr);
       int hours = std::stoi(periodStr);
-      face.expressInterest(*client.generateNewInterest(time::system_clock::now(),
-                                                       time::system_clock::now() + time::hours(hours),
+      auto now = time::system_clock::now();
+      face.expressInterest(*client.generateNewInterest(now, now + time::hours(hours),
                                                        Name(identityNameStr)),
                            bind(&newCb, _2),
                            bind(&onNackCb),