Skip to content

Commit 85b0c93

Browse files
authored
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.
1 parent 3a853a4 commit 85b0c93

2 files changed

Lines changed: 54 additions & 6 deletions

File tree

hmac_utils.cpp

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "hmac_utils.hpp"
22
#include <ctime>
3+
#include <cerrno>
34
#include <stdexcept>
45
#include <limits>
56

@@ -20,8 +21,9 @@ namespace hmac {
2021
if (interval_sec <= 0) {
2122
throw std::invalid_argument("interval_sec must be positive");
2223
}
24+
errno = 0;
2325
std::time_t now = std::time(nullptr);
24-
if (now == static_cast<std::time_t>(-1)) {
26+
if (now == static_cast<std::time_t>(-1) && errno != 0) {
2527
throw std::runtime_error("std::time failed");
2628
}
2729
std::time_t rounded = (now / interval_sec) * interval_sec;
@@ -32,8 +34,9 @@ namespace hmac {
3234
if (interval_sec <= 0) {
3335
throw std::invalid_argument("interval_sec must be positive");
3436
}
37+
errno = 0;
3538
std::time_t now = std::time(nullptr);
36-
if (now == static_cast<std::time_t>(-1)) {
39+
if (now == static_cast<std::time_t>(-1) && errno != 0) {
3740
throw std::runtime_error("std::time failed");
3841
}
3942
std::time_t rounded = (now / interval_sec) * interval_sec;
@@ -51,8 +54,9 @@ namespace hmac {
5154
if (interval_sec <= 0) {
5255
throw std::invalid_argument("interval_sec must be positive");
5356
}
57+
errno = 0;
5458
std::time_t now = std::time(nullptr);
55-
if (now == static_cast<std::time_t>(-1)) {
59+
if (now == static_cast<std::time_t>(-1) && errno != 0) {
5660
throw std::runtime_error("std::time failed");
5761
}
5862
std::time_t rounded = (now / interval_sec) * interval_sec;
@@ -64,8 +68,9 @@ namespace hmac {
6468
if (interval_sec <= 0) {
6569
throw std::invalid_argument("interval_sec must be positive");
6670
}
71+
errno = 0;
6772
std::time_t now = std::time(nullptr);
68-
if (now == static_cast<std::time_t>(-1)) {
73+
if (now == static_cast<std::time_t>(-1) && errno != 0) {
6974
throw std::runtime_error("std::time failed");
7075
}
7176
std::time_t rounded = (now / interval_sec) * interval_sec;
@@ -144,8 +149,9 @@ namespace hmac {
144149
if (digits < 1 || digits > 9) {
145150
throw std::invalid_argument("TOTP: digits must be in range [1, 9]");
146151
}
152+
errno = 0;
147153
std::time_t now = std::time(nullptr);
148-
if (now == static_cast<std::time_t>(-1)) {
154+
if (now == static_cast<std::time_t>(-1) && errno != 0) {
149155
throw std::runtime_error("std::time failed");
150156
}
151157
uint64_t timestamp = static_cast<uint64_t>(now);
@@ -192,8 +198,9 @@ namespace hmac {
192198
if (digits < 1 || digits > 9) {
193199
throw std::invalid_argument("TOTP: digits must be in range [1, 9]");
194200
}
201+
errno = 0;
195202
std::time_t now = std::time(nullptr);
196-
if (now == static_cast<std::time_t>(-1)) {
203+
if (now == static_cast<std::time_t>(-1) && errno != 0) {
197204
throw std::runtime_error("std::time failed");
198205
}
199206
uint64_t timestamp = static_cast<uint64_t>(now);

test_all.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@
33
#include <stdexcept>
44
#include <vector>
55
#include <limits>
6+
#include <cerrno>
67

78
#include "hmac.hpp"
89
#include "hmac_utils.hpp"
910

1011
static std::time_t mock_time_value = 0;
12+
static int mock_errno_value = 0;
1113
extern "C" std::time_t time(std::time_t* t) {
1214
if (t) *t = mock_time_value;
15+
errno = mock_errno_value;
1316
return mock_time_value;
1417
}
1518

@@ -175,6 +178,44 @@ TEST(TokenBoundaryFingerprintTest, MinTime) {
175178
EXPECT_TRUE(hmac::is_token_valid(token_next, key, fingerprint, interval));
176179
}
177180

181+
TEST(TimeErrorTest, MinusOneNoErrno) {
182+
const std::string key = "12345";
183+
mock_time_value = static_cast<std::time_t>(-1);
184+
mock_errno_value = 0;
185+
std::string token;
186+
EXPECT_NO_THROW(token = hmac::generate_time_token(key));
187+
EXPECT_EQ(token, hmac::get_hmac(key, "0", hmac::TypeHash::SHA256));
188+
mock_time_value = 0;
189+
mock_errno_value = 0;
190+
}
191+
192+
TEST(TimeErrorTest, MinusOneWithErrno) {
193+
const std::string key = "12345";
194+
mock_time_value = static_cast<std::time_t>(-1);
195+
mock_errno_value = EINVAL;
196+
EXPECT_THROW(hmac::generate_time_token(key), std::runtime_error);
197+
mock_errno_value = 0;
198+
mock_time_value = 0;
199+
}
200+
201+
TEST(TotpTimeErrorTest, MinusOneNoErrno) {
202+
const std::string key = "12345";
203+
mock_time_value = static_cast<std::time_t>(-1);
204+
mock_errno_value = 0;
205+
EXPECT_NO_THROW(hmac::get_totp_code(key));
206+
mock_time_value = 0;
207+
mock_errno_value = 0;
208+
}
209+
210+
TEST(TotpTimeErrorTest, MinusOneWithErrno) {
211+
const std::string key = "12345";
212+
mock_time_value = static_cast<std::time_t>(-1);
213+
mock_errno_value = EINVAL;
214+
EXPECT_THROW(hmac::get_totp_code(key), std::runtime_error);
215+
mock_errno_value = 0;
216+
mock_time_value = 0;
217+
}
218+
178219
int main(int argc, char **argv) {
179220
::testing::InitGoogleTest(&argc, argv);
180221
return RUN_ALL_TESTS();

0 commit comments

Comments
 (0)