aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArttu Tarkiainen <arttu.tarkiainen@qt.io>2024-03-13 12:42:13 +0200
committerArttu Tarkiainen <arttu.tarkiainen@qt.io>2024-04-10 15:38:29 +0300
commit369b7ae255effc779c8843b1934280e2fe875aa4 (patch)
tree31afd3e360e610720e8bf946ebc6758d27b12b00
parentb0087c6f6f297fee637c3424c6da1f7eb1866573 (diff)
License: prevent secrets leaking through timing side channel
- Add function for constant time comparison of strings - Change "==" operator to use the new function for local user name and JWT members Task-number: QLS-681 Pick-to: 3.0 Change-Id: I9618f6ae60d65d005655736cf8c345d5694fd0a1 Reviewed-by: Iikka Eklund <iikka.eklund@qt.io>
-rw-r--r--src/libs/qlicensecore/utils.cpp18
-rw-r--r--src/libs/qlicensecore/utils.h1
-rw-r--r--src/libs/qlicenseservice/license.cpp4
-rw-r--r--tests/auto/utils/tst_utils.cpp9
4 files changed, 30 insertions, 2 deletions
diff --git a/src/libs/qlicensecore/utils.cpp b/src/libs/qlicensecore/utils.cpp
index 9a64671..ec594cd 100644
--- a/src/libs/qlicensecore/utils.cpp
+++ b/src/libs/qlicensecore/utils.cpp
@@ -147,6 +147,24 @@ std::string replaceCharacters(const std::string &input, const std::string &chara
return result;
}
+bool constTimeCompare(const std::string &a, const std::string &b)
+{
+ if (a.size() != b.size())
+ return false;
+
+ const volatile unsigned char *_a = reinterpret_cast<const volatile unsigned char *>(a.c_str());
+ const volatile unsigned char *_b = reinterpret_cast<const volatile unsigned char *>(b.c_str());
+
+ const size_t size = a.size();
+
+ unsigned char result = 0;
+ for (size_t i = 0; i < size; i++)
+ result |= _a[i] ^ _b[i];
+
+ // 0 if equal, nonzero otherwise
+ return (result == 0);
+}
+
std::string getUserHomeDir()
{
// Try to find current user's home
diff --git a/src/libs/qlicensecore/utils.h b/src/libs/qlicensecore/utils.h
index aeae719..0f45abd 100644
--- a/src/libs/qlicensecore/utils.h
+++ b/src/libs/qlicensecore/utils.h
@@ -44,6 +44,7 @@ int strToInt(const std::string &str);
bool strToUint64(const std::string &input, uint64_t &output);
std::string generateRandomString(size_t length);
std::string replaceCharacters(const std::string &input, const std::string &charactersToReplace, const char &replacement);
+bool constTimeCompare(const std::string &a, const std::string &b);
#if _WIN32
std::string narrow(const wchar_t *s);
std::wstring widen(const char *s);
diff --git a/src/libs/qlicenseservice/license.cpp b/src/libs/qlicenseservice/license.cpp
index cb2b9fe..e419a5a 100644
--- a/src/libs/qlicenseservice/license.cpp
+++ b/src/libs/qlicenseservice/license.cpp
@@ -215,8 +215,8 @@ bool License::operator==(const License &other) const
m_model == other.m_model &&
m_reservationType == other.m_reservationType &&
m_priority == other.m_priority &&
- m_localUser == other.m_localUser &&
- m_jwt == other.m_jwt &&
+ utils::constTimeCompare(m_localUser, other.m_localUser) &&
+ utils::constTimeCompare(m_jwt, other.m_jwt) &&
m_cs_p == other.m_cs_p &&
m_cs_s == other.m_cs_s &&
m_consumers == other.m_consumers &&
diff --git a/tests/auto/utils/tst_utils.cpp b/tests/auto/utils/tst_utils.cpp
index 2745777..353aeff 100644
--- a/tests/auto/utils/tst_utils.cpp
+++ b/tests/auto/utils/tst_utils.cpp
@@ -103,3 +103,12 @@ TEST_CASE("String to UTC time", "[utils]")
const time_t validFromEpoch = utils::stringToUtcTime(validFrom.c_str(), "%Y-%m-%dT%H:%M:%S");
REQUIRE(validFromEpoch == 0x6597f3e7);
}
+
+TEST_CASE("String compare", "[utils]")
+{
+ REQUIRE(utils::constTimeCompare("", "") == true);
+ REQUIRE(utils::constTimeCompare("123", "") == false);
+ REQUIRE(utils::constTimeCompare("123", "123") == true);
+ REQUIRE(utils::constTimeCompare("123", "12345") == false);
+ REQUIRE(utils::constTimeCompare("12356", "12345") == false);
+}