Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions hmac_utils.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "hmac_utils.hpp"
#include <ctime>
#include <stdexcept>
#include <limits>

namespace hmac {

Expand Down Expand Up @@ -133,7 +134,9 @@ namespace hmac {
}
uint64_t counter = timestamp / period;
if (token == get_hotp_code(key_ptr, key_len, counter, digits, hash_type)) return true;
if (token == get_hotp_code(key_ptr, key_len, counter + 1, digits, hash_type)) return true;
if (counter != std::numeric_limits<uint64_t>::max() &&
token == get_hotp_code(key_ptr, key_len, counter + 1, digits, hash_type))
return true;
if (counter > 0 &&
token == get_hotp_code(key_ptr, key_len, counter - 1, digits, hash_type))
return true;
Expand All @@ -157,7 +160,9 @@ namespace hmac {
uint64_t timestamp = static_cast<uint64_t>(std::time(nullptr));
uint64_t counter = timestamp / period;
if (token == get_hotp_code(key_ptr, key_len, counter, digits, hash_type)) return true;
if (token == get_hotp_code(key_ptr, key_len, counter + 1, digits, hash_type)) return true;
if (counter != std::numeric_limits<uint64_t>::max() &&
token == get_hotp_code(key_ptr, key_len, counter + 1, digits, hash_type))
return true;
if (counter > 0 &&
token == get_hotp_code(key_ptr, key_len, counter - 1, digits, hash_type))
return true;
Expand Down
24 changes: 18 additions & 6 deletions hmac_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,9 @@ namespace hmac {
/// \param period Time step in seconds (default: 30)
/// \param digits Expected number of digits in the OTP (default: 6)
/// \param hash_type Hash algorithm to use (SHA1, SHA256, SHA512). Default is SHA1
/// \return true if the token is valid within [-1, 0, +1] time step range; false otherwise
/// \return true if the token is valid within [-1, 0, +1] time step range; false otherwise.
/// The +1 step check is skipped when the computed counter equals
/// std::numeric_limits<uint64_t>::max().
/// \throws std::invalid_argument if period <= 0 or digits not in [1,9]
bool is_totp_token_valid(
int token,
Expand All @@ -194,7 +196,9 @@ namespace hmac {
/// \param period Time step in seconds (default: 30)
/// \param digits Number of digits in the OTP (default: 6)
/// \param hash_type Hash function to use (default: SHA1)
/// \return true if the token is valid within [-1, 0, +1] time step range; false otherwise
/// \return true if the token is valid within [-1, 0, +1] time step range; false otherwise.
/// The +1 step check is skipped when the computed counter equals
/// std::numeric_limits<uint64_t>::max().
/// \throws std::invalid_argument if period <= 0 or digits not in [1,9]
template<typename T>
inline bool is_totp_token_valid(
Expand All @@ -216,7 +220,9 @@ namespace hmac {
/// \param period Time step in seconds (default: 30)
/// \param digits Number of digits in the OTP (default: 6)
/// \param hash_type Hash function to use (default: SHA1)
/// \return true if the token is valid within [-1, 0, +1] time step range; false otherwise
/// \return true if the token is valid within [-1, 0, +1] time step range; false otherwise.
/// The +1 step check is skipped when the computed counter equals
/// std::numeric_limits<uint64_t>::max().
/// \throws std::invalid_argument if period <= 0 or digits not in [1,9]
inline bool is_totp_token_valid(
int token,
Expand All @@ -235,7 +241,9 @@ namespace hmac {
/// \param period Time step in seconds (default: 30)
/// \param digits Expected number of digits in the OTP (default: 6)
/// \param hash_type Hash algorithm to use (default: SHA1)
/// \return true if the token is valid within [-1, 0, +1] time step range; false otherwise
/// \return true if the token is valid within [-1, 0, +1] time step range; false otherwise.
/// The +1 step check is skipped when the computed counter equals
/// std::numeric_limits<uint64_t>::max().
/// \throws std::invalid_argument if period <= 0 or digits not in [1,9]
bool is_totp_token_valid(
int token,
Expand All @@ -252,7 +260,9 @@ namespace hmac {
/// \param period Time step in seconds (default: 30)
/// \param digits Number of digits in the OTP (default: 6)
/// \param hash_type Hash function to use (default: SHA1)
/// \return true if the token is valid within [-1, 0, +1] time step range; false otherwise
/// \return true if the token is valid within [-1, 0, +1] time step range; false otherwise.
/// The +1 step check is skipped when the computed counter equals
/// std::numeric_limits<uint64_t>::max().
/// \throws std::invalid_argument if period <= 0 or digits not in [1,9]
template<typename T>
inline bool is_totp_token_valid(
Expand All @@ -272,7 +282,9 @@ namespace hmac {
/// \param period Time step in seconds (default: 30)
/// \param digits Number of digits in the OTP (default: 6)
/// \param hash_type Hash function to use (default: SHA1)
/// \return true if the token is valid within [-1, 0, +1] time step range; false otherwise
/// \return true if the token is valid within [-1, 0, +1] time step range; false otherwise.
/// The +1 step check is skipped when the computed counter equals
/// std::numeric_limits<uint64_t>::max().
/// \throws std::invalid_argument if period <= 0 or digits not in [1,9]
inline bool is_totp_token_valid(
int token,
Expand Down
13 changes: 12 additions & 1 deletion test_totp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@ int main() {
bool valid_max = hmac::is_totp_token_valid(token_max, key.data(), key.size(), early_timestamp, period, digits, hmac::TypeHash::SHA1);
assert(!valid_max);

std::cout << "TOTP early timestamp test passed" << std::endl;
// At maximum timestamp, ensure overflow does not validate counter 0 token
uint64_t max_timestamp = std::numeric_limits<uint64_t>::max();
int token_zero = hmac::get_hotp_code(key.data(), key.size(), 0, digits, hmac::TypeHash::SHA1);
bool valid_zero_at_max = hmac::is_totp_token_valid(token_zero, key.data(), key.size(), max_timestamp, 1, digits, hmac::TypeHash::SHA1);
assert(!valid_zero_at_max);

// Token for max counter should still be valid at that timestamp
int token_max_ts = hmac::get_hotp_code(key.data(), key.size(), max_counter, digits, hmac::TypeHash::SHA1);
bool valid_max_ts = hmac::is_totp_token_valid(token_max_ts, key.data(), key.size(), max_timestamp, 1, digits, hmac::TypeHash::SHA1);
assert(valid_max_ts);

std::cout << "TOTP tests passed" << std::endl;
return 0;
}
Loading