Utils: prefer CRYPTO_memcmp in ConstantTimeEquals when OpenSSL is available

Per review feedback on #2017: a hand-rolled byte loop can in principle
be folded back into a short-circuiting compare by an aggressive optimizer.
Use CRYPTO_memcmp under HAVE_LIBSSL since OpenSSL is already a build
dependency for the SHA256 path. For non-OpenSSL builds, mark the
accumulator and pointers volatile and note in a comment that this is
best-effort.
This commit is contained in:
MarkLee131
2026-04-29 17:06:49 +08:00
parent f0c3341e35
commit c9d0677e4f
+13 -3
View File
@@ -28,6 +28,7 @@
#ifdef HAVE_LIBSSL
#include <openssl/ssl.h>
#include <openssl/bn.h>
#include <openssl/crypto.h>
#include <openssl/rsa.h>
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x20700000L))
#define X509_getm_notBefore X509_get_notBefore
@@ -279,13 +280,22 @@ bool CUtils::ConstantTimeEquals(const CString& a, const CString& b) {
if (a.length() != b.length()) {
return false;
}
unsigned char acc = 0;
const unsigned char* pa = reinterpret_cast<const unsigned char*>(a.data());
const unsigned char* pb = reinterpret_cast<const unsigned char*>(b.data());
#ifdef HAVE_LIBSSL
return CRYPTO_memcmp(a.data(), b.data(), a.length()) == 0;
#else
// Best-effort fallback when OpenSSL is unavailable: an optimizer is
// in principle allowed to short-circuit this loop, so the volatile
// accumulator and pointers are a hint rather than a guarantee.
volatile unsigned char acc = 0;
const volatile unsigned char* pa =
reinterpret_cast<const volatile unsigned char*>(a.data());
const volatile unsigned char* pb =
reinterpret_cast<const volatile unsigned char*>(b.data());
for (size_t i = 0; i < a.length(); ++i) {
acc |= static_cast<unsigned char>(pa[i] ^ pb[i]);
}
return acc == 0;
#endif
}
CString CUtils::GetPass(const CString& sPrompt) {