From b5795bcfb55f0248016eae84b5de0e66955121f7 Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Thu, 4 Sep 2025 02:21:18 +0300 Subject: [PATCH] fix(utils): detect time failures via errno Use errno to distinguish real failures when std::time returns -1 and add regression tests for both error and non-error cases. --- hmac_utils.cpp | 19 +++++++++++++------ test_all.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/hmac_utils.cpp b/hmac_utils.cpp index c83cc39..0c0875e 100644 --- a/hmac_utils.cpp +++ b/hmac_utils.cpp @@ -1,5 +1,6 @@ #include "hmac_utils.hpp" #include +#include #include #include @@ -20,8 +21,9 @@ namespace hmac { if (interval_sec <= 0) { throw std::invalid_argument("interval_sec must be positive"); } + errno = 0; std::time_t now = std::time(nullptr); - if (now == static_cast(-1)) { + if (now == static_cast(-1) && errno != 0) { throw std::runtime_error("std::time failed"); } std::time_t rounded = (now / interval_sec) * interval_sec; @@ -32,8 +34,9 @@ namespace hmac { if (interval_sec <= 0) { throw std::invalid_argument("interval_sec must be positive"); } + errno = 0; std::time_t now = std::time(nullptr); - if (now == static_cast(-1)) { + if (now == static_cast(-1) && errno != 0) { throw std::runtime_error("std::time failed"); } std::time_t rounded = (now / interval_sec) * interval_sec; @@ -51,8 +54,9 @@ namespace hmac { if (interval_sec <= 0) { throw std::invalid_argument("interval_sec must be positive"); } + errno = 0; std::time_t now = std::time(nullptr); - if (now == static_cast(-1)) { + if (now == static_cast(-1) && errno != 0) { throw std::runtime_error("std::time failed"); } std::time_t rounded = (now / interval_sec) * interval_sec; @@ -64,8 +68,9 @@ namespace hmac { if (interval_sec <= 0) { throw std::invalid_argument("interval_sec must be positive"); } + errno = 0; std::time_t now = std::time(nullptr); - if (now == static_cast(-1)) { + if (now == static_cast(-1) && errno != 0) { throw std::runtime_error("std::time failed"); } std::time_t rounded = (now / interval_sec) * interval_sec; @@ -144,8 +149,9 @@ namespace hmac { if (digits < 1 || digits > 9) { throw std::invalid_argument("TOTP: digits must be in range [1, 9]"); } + errno = 0; std::time_t now = std::time(nullptr); - if (now == static_cast(-1)) { + if (now == static_cast(-1) && errno != 0) { throw std::runtime_error("std::time failed"); } uint64_t timestamp = static_cast(now); @@ -192,8 +198,9 @@ namespace hmac { if (digits < 1 || digits > 9) { throw std::invalid_argument("TOTP: digits must be in range [1, 9]"); } + errno = 0; std::time_t now = std::time(nullptr); - if (now == static_cast(-1)) { + if (now == static_cast(-1) && errno != 0) { throw std::runtime_error("std::time failed"); } uint64_t timestamp = static_cast(now); diff --git a/test_all.cpp b/test_all.cpp index d070c29..fc54e57 100644 --- a/test_all.cpp +++ b/test_all.cpp @@ -3,13 +3,16 @@ #include #include #include +#include #include "hmac.hpp" #include "hmac_utils.hpp" static std::time_t mock_time_value = 0; +static int mock_errno_value = 0; extern "C" std::time_t time(std::time_t* t) { if (t) *t = mock_time_value; + errno = mock_errno_value; return mock_time_value; } @@ -157,6 +160,44 @@ TEST(TokenBoundaryFingerprintTest, MinTime) { EXPECT_TRUE(hmac::is_token_valid(token_next, key, fingerprint, interval)); } +TEST(TimeErrorTest, MinusOneNoErrno) { + const std::string key = "12345"; + mock_time_value = static_cast(-1); + mock_errno_value = 0; + std::string token; + EXPECT_NO_THROW(token = hmac::generate_time_token(key)); + EXPECT_EQ(token, hmac::get_hmac(key, "0", hmac::TypeHash::SHA256)); + mock_time_value = 0; + mock_errno_value = 0; +} + +TEST(TimeErrorTest, MinusOneWithErrno) { + const std::string key = "12345"; + mock_time_value = static_cast(-1); + mock_errno_value = EINVAL; + EXPECT_THROW(hmac::generate_time_token(key), std::runtime_error); + mock_errno_value = 0; + mock_time_value = 0; +} + +TEST(TotpTimeErrorTest, MinusOneNoErrno) { + const std::string key = "12345"; + mock_time_value = static_cast(-1); + mock_errno_value = 0; + EXPECT_NO_THROW(hmac::get_totp_code(key)); + mock_time_value = 0; + mock_errno_value = 0; +} + +TEST(TotpTimeErrorTest, MinusOneWithErrno) { + const std::string key = "12345"; + mock_time_value = static_cast(-1); + mock_errno_value = EINVAL; + EXPECT_THROW(hmac::get_totp_code(key), std::runtime_error); + mock_errno_value = 0; + mock_time_value = 0; +} + int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS();