diff --git a/ndn-cxx/security/validity-period.cpp b/ndn-cxx/security/validity-period.cpp index 1c911a8e..1d491473 100644 --- a/ndn-cxx/security/validity-period.cpp +++ b/ndn-cxx/security/validity-period.cpp @@ -45,9 +45,8 @@ ValidityPeriod::ValidityPeriod() ValidityPeriod::ValidityPeriod(const time::system_clock::time_point& notBefore, const time::system_clock::time_point& notAfter) - : m_notBefore(time_point_cast(notBefore + TimePoint::duration(1) - - time::system_clock::time_point::duration(1))) - , m_notAfter(time_point_cast(notAfter)) + : m_notBefore(toTimePointCeil(notBefore)) + , m_notAfter(toTimePointFloor(notAfter)) { } @@ -114,24 +113,56 @@ ValidityPeriod::wireDecode(const Block& wire) } try { - m_notBefore = time_point_cast( - time::fromIsoString(readString(m_wire.elements()[NOT_BEFORE_OFFSET]))); - m_notAfter = time_point_cast( - time::fromIsoString(readString(m_wire.elements()[NOT_AFTER_OFFSET]))); + m_notBefore = decodeTimePoint(m_wire.elements()[NOT_BEFORE_OFFSET]); + m_notAfter = decodeTimePoint(m_wire.elements()[NOT_AFTER_OFFSET]); } catch (const std::bad_cast&) { NDN_THROW(Error("Invalid date format in NOT-BEFORE or NOT-AFTER field")); } } +ValidityPeriod::TimePoint +ValidityPeriod::toTimePointFloor(const time::system_clock::time_point& t) +{ + return TimePoint(boost::chrono::floor(t.time_since_epoch())); +} + +ValidityPeriod::TimePoint +ValidityPeriod::toTimePointCeil(const time::system_clock::time_point& t) +{ + return TimePoint(boost::chrono::ceil(t.time_since_epoch())); +} + +ValidityPeriod::TimePoint +ValidityPeriod::decodeTimePoint(const Block& element) +{ + // Bug #5176, prevent time::system_clock::time_point under/overflow + static const auto minTime = toTimePointCeil(time::system_clock::time_point::min()); + static const auto maxTime = toTimePointFloor(time::system_clock::time_point::max()); + static const auto minValue = time::toIsoString(minTime); + static const auto maxValue = time::toIsoString(maxTime); + BOOST_ASSERT(minValue.size() == ISO_DATETIME_SIZE); + BOOST_ASSERT(maxValue.size() == ISO_DATETIME_SIZE); + + auto value = readString(element); + BOOST_ASSERT(value.size() == ISO_DATETIME_SIZE); + + if (value < minValue) { + return minTime; + } + if (value > maxValue) { + return maxTime; + } + return time_point_cast(time::fromIsoString(value)); +} + ValidityPeriod& ValidityPeriod::setPeriod(const time::system_clock::time_point& notBefore, const time::system_clock::time_point& notAfter) { m_wire.reset(); - m_notBefore = time_point_cast(notBefore + TimePoint::duration(1) - - time::system_clock::time_point::duration(1)); - m_notAfter = time_point_cast(notAfter); + m_notBefore = toTimePointCeil(notBefore); + m_notAfter = toTimePointFloor(notAfter); return *this; } diff --git a/ndn-cxx/security/validity-period.hpp b/ndn-cxx/security/validity-period.hpp index 4a9f69ea..f4da2f48 100644 --- a/ndn-cxx/security/validity-period.hpp +++ b/ndn-cxx/security/validity-period.hpp @@ -53,67 +53,88 @@ public: makeRelative(time::seconds validFrom, time::seconds validUntil, const time::system_clock::time_point& now = time::system_clock::now()); - /** @brief Set validity period [UNIX epoch + 1 nanosecond, UNIX epoch] that is always invalid + /** + * @brief Create a validity period that is invalid for any timepoint. */ ValidityPeriod(); - /** @brief Create validity period from @p block + /** + * @brief Decode validity period from @p block . */ explicit ValidityPeriod(const Block& block); - /** @brief Create validity period [@p notBefore, @p notAfter] - * @param notBefore exclusive beginning of the validity period range - * @param notAfter exclusive end of the validity period range - * - * @note The supplied time points will be rounded up to the whole seconds: - * - @p notBefore is rounded up the next whole second - * - @p notAfter is truncated to the previous whole second + /** + * @brief Create validity period [@p notBefore, @p notAfter]. + * @param notBefore exclusive beginning of the validity period range, + * to be rounded up to the next whole second. + * @param notAfter exclusive end of the validity period range, + * to be rounded down to the previous whole second. */ ValidityPeriod(const time::system_clock::time_point& notBefore, const time::system_clock::time_point& notAfter); - /** @brief Check if @p now falls within the validity period - * @param now Time point to check if it falls within the period - * @return periodBegin <= @p now and @p now <= periodEnd + /** + * @brief Check if @p now falls within the validity period. + * @param now Time point to check if it falls within the period + * @return notBefore <= @p now and @p now <= notAfter. */ bool isValid(const time::system_clock::time_point& now = time::system_clock::now()) const; - /** @brief Set validity period [@p notBefore, @p notAfter] - * @param notBefore exclusive beginning of the validity period range - * @param notAfter exclusive end of the validity period range - * - * @note The supplied time points will be rounded up to the whole seconds: - * - @p notBefore is rounded up the next whole second - * - @p notAfter is truncated to the previous whole second + /** + * @brief Set validity period [@p notBefore, @p notAfter]. + * @param notBefore exclusive beginning of the validity period range, + * to be rounded up to the next whole second. + * @param notAfter exclusive end of the validity period range, + * to be rounded down to the previous whole second. */ ValidityPeriod& setPeriod(const time::system_clock::time_point& notBefore, const time::system_clock::time_point& notAfter); - /** @brief Get the stored validity period + /** + * @brief Get the stored validity period. */ std::pair getPeriod() const; - /** @brief Fast encoding or block size estimation + /** + * @brief Fast encoding or block size estimation. */ template size_t wireEncode(EncodingImpl& encoder) const; - /** @brief Encode ValidityPeriod into TLV block + /** + * @brief Encode ValidityPeriod into TLV block. */ const Block& wireEncode() const; - /** @brief Decode ValidityPeriod from TLV block - * @throw Error when an invalid TLV block supplied + /** + * @brief Decode ValidityPeriod from TLV block. + * @throw Error when an invalid TLV block supplied. + * + * @note If either timestamp in @p wire is earlier than 1677-09-21 or later than 2262-04-11, + * it will be adjusted to these dates so that they are representable as + * @c time::system_clock::time_point type returned by getPeriod() method. */ void wireDecode(const Block& wire); +private: + using TimePoint = boost::chrono::time_point; + + static TimePoint + toTimePointFloor(const time::system_clock::time_point& t); + + static TimePoint + toTimePointCeil(const time::system_clock::time_point& t); + + static TimePoint + decodeTimePoint(const Block& element); + private: // EqualityComparable concept // NOTE: the following "hidden friend" operators are available via // argument-dependent lookup only and must be defined inline. @@ -132,8 +153,6 @@ private: // EqualityComparable concept } private: - using TimePoint = boost::chrono::time_point; - TimePoint m_notBefore; TimePoint m_notAfter; diff --git a/tests/unit/security/validity-period.t.cpp b/tests/unit/security/validity-period.t.cpp index 1fbd6b19..6281289b 100644 --- a/tests/unit/security/validity-period.t.cpp +++ b/tests/unit/security/validity-period.t.cpp @@ -99,10 +99,25 @@ BOOST_FIXTURE_TEST_CASE(ConstructorSetter, ClockFixture) "(19700101T000000, 19791230T000000)"); validity1.setPeriod(time::getUnixEpoch() + 1_ns, - time::getUnixEpoch() + (10 * 365_days) + 1_ns); + time::getUnixEpoch() + 3650_days + 1_ns); BOOST_CHECK_EQUAL(boost::lexical_cast(validity1), "(19700101T000001, 19791230T000000)"); + validity1.setPeriod(time::getUnixEpoch() + 999999999_ns, + time::getUnixEpoch() + 3650_days + 999999999_ns); + BOOST_CHECK_EQUAL(boost::lexical_cast(validity1), + "(19700101T000001, 19791230T000000)"); + + validity1.setPeriod(time::getUnixEpoch() - 2_days + 1_ns, + time::getUnixEpoch() - 1_day + 1_ns); + BOOST_CHECK_EQUAL(boost::lexical_cast(validity1), + "(19691230T000001, 19691231T000000)"); + + validity1.setPeriod(time::getUnixEpoch() - 2_days + 999999999_ns, + time::getUnixEpoch() - 1_day + 999999999_ns); + BOOST_CHECK_EQUAL(boost::lexical_cast(validity1), + "(19691230T000001, 19691231T000000)"); + BOOST_CHECK_EQUAL(ValidityPeriod(now, now).isValid(), true); BOOST_CHECK_EQUAL(ValidityPeriod(now + 1_s, now).isValid(), false); } @@ -129,8 +144,28 @@ BOOST_AUTO_TEST_CASE(EncodingDecoding) BOOST_CHECK(v1.getPeriod() == v2.getPeriod()); } +const uint8_t VP2[] = { + 0xfd, 0x00, 0xfd, 0x26, // ValidityPeriod + 0xfd, 0x00, 0xfe, 0x0f, // NotBefore + 0x30, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x31, // 00010101T000000 + 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0xfd, 0x00, 0xff, 0x0f, // NotAfter + 0x39, 0x39, 0x39, 0x39, 0x31, 0x32, 0x33, 0x31, // 99991231T235959 + 0x54, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39 +}; + +BOOST_AUTO_TEST_CASE(DecodingLarge) +{ + ValidityPeriod v(Block{VP2}); + BOOST_CHECK(v.isValid(time::fromIsoString("16770921T001245"))); + BOOST_CHECK(v.isValid(time::fromIsoString("19010120T120000"))); + BOOST_CHECK(v.isValid(time::fromIsoString("20230725T120000"))); + BOOST_CHECK(v.isValid(time::fromIsoString("22001030T120000"))); + BOOST_CHECK(v.isValid(time::fromIsoString("22620411T234716"))); +} + const uint8_t VP_E1[] = { - 0xfd, 0x00, 0xff, 0x26, // ValidityPeriod (error) + 0xfd, 0x00, 0xff, 0x26, // ValidityPeriod (wrong TLV-TYPE) 0xfd, 0x00, 0xfe, 0x0f, // NotBefore 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, // 19700101T000000 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, @@ -141,7 +176,7 @@ const uint8_t VP_E1[] = { const uint8_t VP_E2[] = { 0xfd, 0x00, 0xfd, 0x26, // ValidityPeriod - 0xfd, 0x00, 0xff, 0x0f, // NotBefore (error) + 0xfd, 0x00, 0xff, 0x0f, // NotBefore (wrong TLV-TYPE) 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, // 19700101T000000 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfd, 0x00, 0xff, 0x0f, // NotAfter @@ -154,7 +189,7 @@ const uint8_t VP_E3[] = { 0xfd, 0x00, 0xfe, 0x0f, // NotBefore 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, // 19700101T000000 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0xfd, 0x00, 0xfe, 0x0f, // NotAfter (error) + 0xfd, 0x00, 0xfe, 0x0f, // NotAfter (wrong TLV-TYPE) 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x32, // 19700102T000000 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30 }; @@ -167,7 +202,7 @@ const uint8_t VP_E4[] = { 0xfd, 0x00, 0xff, 0x0f, // NotAfter 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x32, // 19700102T000000 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0xfd, 0x00, 0xff, 0x0f, // NotAfter (error) + 0xfd, 0x00, 0xff, 0x0f, // NotAfter (duplicate) 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x32, // 19700102T000000 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30 }; @@ -177,6 +212,7 @@ const uint8_t VP_E5[] = { 0xfd, 0x00, 0xfe, 0x0f, // NotBefore 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, // 19700101T000000 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30 + // missing NotAfter }; const uint8_t VP_E6[] = {