diff options
author | Arttu Tarkiainen <arttu.tarkiainen@qt.io> | 2024-03-13 12:42:13 +0200 |
---|---|---|
committer | Arttu Tarkiainen <arttu.tarkiainen@qt.io> | 2024-04-10 15:38:29 +0300 |
commit | 369b7ae255effc779c8843b1934280e2fe875aa4 (patch) | |
tree | 31afd3e360e610720e8bf946ebc6758d27b12b00 | |
parent | b0087c6f6f297fee637c3424c6da1f7eb1866573 (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.cpp | 18 | ||||
-rw-r--r-- | src/libs/qlicensecore/utils.h | 1 | ||||
-rw-r--r-- | src/libs/qlicenseservice/license.cpp | 4 | ||||
-rw-r--r-- | tests/auto/utils/tst_utils.cpp | 9 |
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); +} |