From 1e48e2bee321bb3597f06dd22a500c8fcda1b028 Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Thu, 4 Sep 2025 01:03:41 +0300 Subject: [PATCH] feat(utils): add constant-time token comparison Introduce constant_time_equals for byte-wise string checks and use it in token validation to avoid early exit. --- hmac_utils.cpp | 21 +++++++++++++++------ hmac_utils.hpp | 6 ++++++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/hmac_utils.cpp b/hmac_utils.cpp index 820953b..18ee104 100644 --- a/hmac_utils.cpp +++ b/hmac_utils.cpp @@ -3,6 +3,15 @@ #include namespace hmac { + + bool constant_time_equals(const std::string &a, const std::string &b) { + if (a.size() != b.size()) return false; + unsigned char diff = 0; + for (size_t i = 0; i < a.size(); ++i) { + diff |= static_cast(a[i]) ^ static_cast(b[i]); + } + return diff == 0; + } std::string generate_time_token(const std::string &key, int interval_sec, TypeHash hash_type) { if (interval_sec <= 0) { @@ -25,9 +34,9 @@ namespace hmac { throw std::runtime_error("std::time failed"); } std::time_t rounded = (now / interval_sec) * interval_sec; - if (token == get_hmac(key, std::to_string(rounded), hash_type)) return true; - if (token == get_hmac(key, std::to_string(rounded - interval_sec), hash_type)) return true; - if (token == get_hmac(key, std::to_string(rounded + interval_sec), hash_type)) return true; + if (constant_time_equals(token, get_hmac(key, std::to_string(rounded), hash_type))) return true; + if (constant_time_equals(token, get_hmac(key, std::to_string(rounded - interval_sec), hash_type))) return true; + if (constant_time_equals(token, get_hmac(key, std::to_string(rounded + interval_sec), hash_type))) return true; return false; } @@ -55,11 +64,11 @@ namespace hmac { std::time_t rounded = (now / interval_sec) * interval_sec; std::string prefix = "|" + fingerprint; std::string payload = std::to_string(rounded) + prefix; - if (token == get_hmac(key, payload, hash_type)) return true; + if (constant_time_equals(token, get_hmac(key, payload, hash_type))) return true; payload = std::to_string(rounded - interval_sec) + prefix; - if (token == get_hmac(key, payload, hash_type)) return true; + if (constant_time_equals(token, get_hmac(key, payload, hash_type))) return true; payload = std::to_string(rounded + interval_sec) + prefix; - if (token == get_hmac(key, payload, hash_type)) return true; + if (constant_time_equals(token, get_hmac(key, payload, hash_type))) return true; return false; } diff --git a/hmac_utils.hpp b/hmac_utils.hpp index 5d1d7d0..314b779 100644 --- a/hmac_utils.hpp +++ b/hmac_utils.hpp @@ -7,6 +7,12 @@ namespace hmac { + /// \brief Compares two strings in constant time + /// \param a First string + /// \param b Second string + /// \return true if both strings are equal + bool constant_time_equals(const std::string &a, const std::string &b); + /// \brief Generates a time-based HMAC-SHA256 token /// \param key Secret key used for HMAC /// \param interval_sec Interval in seconds that defines token rotation. Must be positive. Default is 60 seconds