Skip to content

Commit e3a8e88

Browse files
authored
fix(totp): handle negative time and refine rounding
1 parent d8e6b37 commit e3a8e88

2 files changed

Lines changed: 24 additions & 7 deletions

File tree

hmac_utils.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ namespace hmac {
2626
if (now == static_cast<std::time_t>(-1) && errno != 0) {
2727
throw std::runtime_error("std::time failed");
2828
}
29-
std::time_t rounded = (now / interval_sec) * interval_sec;
29+
std::time_t rounded = now - (now % interval_sec);
3030
return get_hmac(key, std::to_string(rounded), hash_type);
3131
}
3232

@@ -39,7 +39,7 @@ namespace hmac {
3939
if (now == static_cast<std::time_t>(-1) && errno != 0) {
4040
throw std::runtime_error("std::time failed");
4141
}
42-
std::time_t rounded = (now / interval_sec) * interval_sec;
42+
std::time_t rounded = now - (now % interval_sec);
4343
if (constant_time_equals(token, get_hmac(key, std::to_string(rounded), hash_type))) return true;
4444
if (rounded >= std::numeric_limits<std::time_t>::min() + interval_sec) {
4545
if (constant_time_equals(token, get_hmac(key, std::to_string(rounded - interval_sec), hash_type))) return true;
@@ -59,7 +59,7 @@ namespace hmac {
5959
if (now == static_cast<std::time_t>(-1) && errno != 0) {
6060
throw std::runtime_error("std::time failed");
6161
}
62-
std::time_t rounded = (now / interval_sec) * interval_sec;
62+
std::time_t rounded = now - (now % interval_sec);
6363
std::string payload = std::to_string(rounded) + "|" + fingerprint;
6464
return get_hmac(key, payload, hash_type);
6565
}
@@ -73,7 +73,7 @@ namespace hmac {
7373
if (now == static_cast<std::time_t>(-1) && errno != 0) {
7474
throw std::runtime_error("std::time failed");
7575
}
76-
std::time_t rounded = (now / interval_sec) * interval_sec;
76+
std::time_t rounded = now - (now % interval_sec);
7777
std::string prefix = "|" + fingerprint;
7878
std::string payload = std::to_string(rounded) + prefix;
7979
if (constant_time_equals(token, get_hmac(key, payload, hash_type))) return true;
@@ -164,6 +164,9 @@ namespace hmac {
164164
if (now == static_cast<std::time_t>(-1) && errno != 0) {
165165
throw std::runtime_error("std::time failed");
166166
}
167+
if (now < 0) {
168+
throw std::runtime_error("std::time returned negative value");
169+
}
167170
uint64_t timestamp = static_cast<uint64_t>(now);
168171
return get_totp_code_at(key_ptr, key_len, timestamp, period, digits, hash_type);
169172
}
@@ -213,6 +216,9 @@ namespace hmac {
213216
if (now == static_cast<std::time_t>(-1) && errno != 0) {
214217
throw std::runtime_error("std::time failed");
215218
}
219+
if (now < 0) {
220+
throw std::runtime_error("std::time returned negative value");
221+
}
216222
uint64_t timestamp = static_cast<uint64_t>(now);
217223
uint64_t counter = timestamp / period;
218224
if (token == get_hotp_code(key_ptr, key_len, counter, digits, hash_type)) return true;

test_all.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -220,16 +220,16 @@ TEST(TimeErrorTest, MinusOneWithErrno) {
220220
mock_time_value = 0;
221221
}
222222

223-
TEST(TotpTimeErrorTest, MinusOneNoErrno) {
223+
TEST(TotpTimeErrorTest, NegativeTimeThrows) {
224224
const std::string key = "12345";
225225
mock_time_value = static_cast<std::time_t>(-1);
226226
mock_errno_value = 0;
227-
EXPECT_NO_THROW(hmac::get_totp_code(key));
227+
EXPECT_THROW(hmac::get_totp_code(key), std::runtime_error);
228228
mock_time_value = 0;
229229
mock_errno_value = 0;
230230
}
231231

232-
TEST(TotpTimeErrorTest, MinusOneWithErrno) {
232+
TEST(TotpTimeErrorTest, NegativeTimeThrowsErrno) {
233233
const std::string key = "12345";
234234
mock_time_value = static_cast<std::time_t>(-1);
235235
mock_errno_value = EINVAL;
@@ -238,6 +238,17 @@ TEST(TotpTimeErrorTest, MinusOneWithErrno) {
238238
mock_time_value = 0;
239239
}
240240

241+
TEST(TotpTimeErrorTest, ValidityNegativeTimeThrows) {
242+
const std::string key = "12345";
243+
mock_time_value = static_cast<std::time_t>(-1);
244+
mock_errno_value = 0;
245+
EXPECT_THROW(
246+
hmac::is_totp_token_valid(0, key.data(), key.size(), 30, 6, hmac::TypeHash::SHA1),
247+
std::runtime_error);
248+
mock_time_value = 0;
249+
mock_errno_value = 0;
250+
}
251+
241252
int main(int argc, char **argv) {
242253
::testing::InitGoogleTest(&argc, argv);
243254
return RUN_ALL_TESTS();

0 commit comments

Comments
 (0)