From 6d6cc8ca95c29ee9402b79a90293d666630df3ad Mon Sep 17 00:00:00 2001 From: Knightly <55611098+Knightly1@users.noreply.github.com> Date: Sat, 25 Apr 2026 13:49:03 -1000 Subject: [PATCH] Update openssl calls to use EVP interface (#5072) --- loginserver/encryption.cpp | 133 ++++++++++++++++++++++++++++--------- loginserver/encryption.h | 10 +++ loginserver/main.cpp | 8 +++ 3 files changed, 119 insertions(+), 32 deletions(-) diff --git a/loginserver/encryption.cpp b/loginserver/encryption.cpp index dabbef761..3fa140768 100644 --- a/loginserver/encryption.cpp +++ b/loginserver/encryption.cpp @@ -19,9 +19,9 @@ #include "common/compiler_macros.h" #ifdef EQEMU_USE_OPENSSL -#include -#include -#include +#include +#include +#include #endif #ifdef EQEMU_USE_MBEDTLS #include @@ -33,6 +33,8 @@ #include #include +#include + #ifdef ENABLE_SECURITY #include @@ -128,23 +130,92 @@ const char *eqcrypt_block(const char *buffer_in, size_t buffer_in_sz, char *buff #endif #ifdef EQEMU_USE_OPENSSL - DES_key_schedule k; - DES_cblock v; - - memset(&k, 0, sizeof(DES_key_schedule)); - memset(&v, 0, sizeof(DES_cblock)); - + // Decrypt requires block-aligned input; encrypt zero-pads a trailing + // partial block to match the legacy DES_ncbc_encrypt semantics the + // game protocol expects. if (!enc && buffer_in_sz && buffer_in_sz % 8 != 0) { return nullptr; } - PUSH_DISABLE_DEPRECATED_WARNINGS() - DES_ncbc_encrypt((const unsigned char*)buffer_in, (unsigned char*)buffer_out, (long)buffer_in_sz, &k, &v, enc); - POP_DISABLE_DEPRECATED_WARNINGS() + unsigned char key[8] = {0}; + unsigned char iv[8] = {0}; + + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + if (!ctx) { + return nullptr; + } + + bool result = EVP_CipherInit_ex2(ctx, EVP_des_cbc(), key, iv, enc, nullptr) == 1; + if (result) { + EVP_CIPHER_CTX_set_padding(ctx, 0); + + const unsigned char* src = reinterpret_cast(buffer_in); + size_t src_len = buffer_in_sz; + std::unique_ptr padded; + + if (enc && buffer_in_sz % 8 != 0) { + src_len = ((buffer_in_sz / 8) + 1) * 8; + padded.reset(new unsigned char[src_len]()); + memcpy(padded.get(), buffer_in, buffer_in_sz); + src = padded.get(); + } + + int outl = 0; + int final_len = 0; + result = EVP_CipherUpdate(ctx, reinterpret_cast(buffer_out), &outl, src, static_cast(src_len)) == 1 + && EVP_CipherFinal_ex(ctx, reinterpret_cast(buffer_out) + outl, &final_len) == 1; + } + + EVP_CIPHER_CTX_free(ctx); + if (!result) { + return nullptr; + } #endif return buffer_out; } +#ifdef EQEMU_USE_OPENSSL +static OSSL_PROVIDER *s_legacy_provider = nullptr; +static OSSL_PROVIDER *s_default_provider = nullptr; +#endif + +bool eqcrypt_init() +{ +#ifdef EQEMU_USE_OPENSSL + if (!s_default_provider) { + s_default_provider = OSSL_PROVIDER_load(nullptr, "default"); + } + if (!s_legacy_provider) { + s_legacy_provider = OSSL_PROVIDER_load(nullptr, "legacy"); + } + + if (!s_default_provider || !s_legacy_provider) { + char buf[256]; + while (auto err = ERR_get_error()) { + ERR_error_string_n(err, buf, sizeof(buf)); + LogError("OpenSSL provider load failure: {}", buf); + } + return false; + } +#endif + + return true; +} + +void eqcrypt_shutdown() +{ +#ifdef EQEMU_USE_OPENSSL + if (s_legacy_provider) { + OSSL_PROVIDER_unload(s_legacy_provider); + s_legacy_provider = nullptr; + } + if (s_default_provider) { + OSSL_PROVIDER_unload(s_default_provider); + s_default_provider = nullptr; + } +#endif +} + std::string eqcrypt_md5(const std::string &msg) { std::string ret; @@ -167,14 +238,12 @@ std::string eqcrypt_md5(const std::string &msg) unsigned char md5_digest[16]; char tmp[4]; - PUSH_DISABLE_DEPRECATED_WARNINGS() - MD5((const unsigned char*)msg.c_str(), msg.length(), md5_digest); - POP_DISABLE_DEPRECATED_WARNINGS() - - for (int i = 0; i < 16; ++i) { - sprintf(&tmp[0], "%02x", md5_digest[i]); - ret.push_back(tmp[0]); - ret.push_back(tmp[1]); + if (EVP_Digest(msg.data(), msg.length(), md5_digest, nullptr, EVP_md5(), nullptr) == 1) { + for (int i = 0; i < 16; ++i) { + sprintf(&tmp[0], "%02x", md5_digest[i]); + ret.push_back(tmp[0]); + ret.push_back(tmp[1]); + } } #endif @@ -203,12 +272,12 @@ std::string eqcrypt_sha1(const std::string &msg) unsigned char sha_digest[20]; char tmp[4]; - SHA1((const unsigned char*)msg.c_str(), msg.length(), sha_digest); - - for (int i = 0; i < 20; ++i) { - sprintf(&tmp[0], "%02x", sha_digest[i]); - ret.push_back(tmp[0]); - ret.push_back(tmp[1]); + if (EVP_Digest(msg.data(), msg.length(), sha_digest, nullptr, EVP_sha1(), nullptr) == 1) { + for (int i = 0; i < 20; ++i) { + sprintf(&tmp[0], "%02x", sha_digest[i]); + ret.push_back(tmp[0]); + ret.push_back(tmp[1]); + } } #endif @@ -237,12 +306,12 @@ std::string eqcrypt_sha512(const std::string &msg) unsigned char sha_digest[64]; char tmp[4]; - SHA512((const unsigned char*)msg.c_str(), msg.length(), sha_digest); - - for (int i = 0; i < 64; ++i) { - sprintf(&tmp[0], "%02x", sha_digest[i]); - ret.push_back(tmp[0]); - ret.push_back(tmp[1]); + if (EVP_Digest(msg.data(), msg.length(), sha_digest, nullptr, EVP_sha512(), nullptr) == 1) { + for (int i = 0; i < 64; ++i) { + sprintf(&tmp[0], "%02x", sha_digest[i]); + ret.push_back(tmp[0]); + ret.push_back(tmp[1]); + } } #endif diff --git a/loginserver/encryption.h b/loginserver/encryption.h index 4f188789d..d1f683d06 100644 --- a/loginserver/encryption.h +++ b/loginserver/encryption.h @@ -48,10 +48,20 @@ namespace CryptoHash { } std::string GetEncryptionByModeId(uint32 mode); + +// DES-CBC with an all-zero key and IV (EQ login protocol obfuscation, not security). +// On encrypt, a trailing partial block is zero-padded to the next 8-byte boundary, so +// buffer_out must be at least ((buffer_in_sz + 7) / 8) * 8 bytes. On decrypt, buffer_in_sz +// must already be a multiple of 8 or the call returns nullptr. const char *eqcrypt_block(const char *buffer_in, size_t buffer_in_sz, char *buffer_out, bool enc); std::string eqcrypt_hash(const std::string &username, const std::string &password, int mode); bool eqcrypt_verify_hash(const std::string &username, const std::string &password, const std::string &pwhash, int mode); +// OpenSSL 3.0 moved DES behind the "legacy" provider; these load/unload it +// for the lifetime of the process. No-op when built against mbedtls. +bool eqcrypt_init(); +void eqcrypt_shutdown(); + struct EncryptionResult { std::string password; int mode = 0; diff --git a/loginserver/main.cpp b/loginserver/main.cpp index faa22e955..9b9766223 100644 --- a/loginserver/main.cpp +++ b/loginserver/main.cpp @@ -26,6 +26,7 @@ #include "common/platform.h" #include "common/timer.h" #include "common/types.h" +#include "loginserver/encryption.h" #include "loginserver/login_server.h" #include "loginserver/loginserver_command_handler.h" #include "loginserver/loginserver_webserver.h" @@ -160,6 +161,11 @@ int main(int argc, char **argv) RegisterExecutablePlatform(ExePlatformLogin); set_exception_handler(); + if (!eqcrypt_init()) { + LogError("Failed to initialize crypto providers"); + return 1; + } + LogInfo("Logging System Init"); if (argc == 1) { @@ -280,5 +286,7 @@ int main(int argc, char **argv) LogInfo("Server Manager Shutdown"); delete server.server_manager; + eqcrypt_shutdown(); + return 0; }