mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-11-14 07:47:13 +00:00
fix: generate session keys from the system entropy source
The key came from the shared game random engine, which is seeded from the start time. It now comes from std::random_device through a small helper. The key stays a 32 bit number, so the packets do not change. The master and world servers no longer write the key or its hash to the log. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
@@ -372,6 +372,23 @@ namespace GeneralUtils {
|
||||
return GenerateRandomNumber<T>(std::numeric_limits<T>::min(), std::numeric_limits<T>::max());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a number from the operating system's entropy source instead of the shared game engine.
|
||||
* Use this for values that must not be predictable from earlier ones, such as session keys.
|
||||
*/
|
||||
template <typename T>
|
||||
inline T GenerateUnpredictableNumber() {
|
||||
static_assert(std::is_integral_v<T> && std::is_unsigned_v<T>, "Not an unsigned integral type");
|
||||
|
||||
std::random_device device;
|
||||
T value = 0;
|
||||
for (uint32_t bytesFilled = 0; bytesFilled < sizeof(T); bytesFilled += sizeof(uint32_t)) {
|
||||
// Two half shifts, a full 32 bit shift is not valid on a 32 bit type
|
||||
value = static_cast<T>(static_cast<T>(value << 16) << 16) | static_cast<T>(device());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// https://www.quora.com/How-do-you-round-to-specific-increments-like-0-5-in-C
|
||||
// Rounds to the nearest floating point value specified.
|
||||
template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
|
||||
|
||||
@@ -847,7 +847,7 @@ namespace {
|
||||
}
|
||||
|
||||
activeSessions.insert(std::make_pair(sessionKey, username.string));
|
||||
LOG("Got sessionKey %i for user %s", sessionKey, username.string.c_str());
|
||||
LOG("Got a session key for user %s", username.string.c_str());
|
||||
}
|
||||
|
||||
void OnRequestSessionKey(const RequestSessionKey& request, const SystemAddress& sysAddr) {
|
||||
|
||||
@@ -308,7 +308,7 @@ void AuthPackets::SendLoginResponse(dServer* server, const SystemAddress& sysAdd
|
||||
GeneralUtils::TryParse<uint16_t>(Game::config->GetValue("version_minor")).value_or(ClientVersion::minor);
|
||||
|
||||
// The user key
|
||||
uint32_t sessionKey = GeneralUtils::GenerateRandomNumber<uint32_t>();
|
||||
uint32_t sessionKey = GeneralUtils::GenerateUnpredictableNumber<uint32_t>();
|
||||
std::string userHash = std::to_string(sessionKey);
|
||||
userHash = md5(userHash);
|
||||
loginResponse.userKey = LUWString(userHash);
|
||||
|
||||
@@ -882,7 +882,7 @@ namespace {
|
||||
|
||||
//Verify it:
|
||||
if (userHash != it->second.hash) {
|
||||
LOG("SOMEONE IS TRYING TO HACK? SESSION KEY MISMATCH: ours: %s != master: %s", userHash.c_str(), it->second.hash.c_str());
|
||||
LOG("Session key mismatch for user %s", username.GetAsString().c_str());
|
||||
Game::server->Disconnect(it->second.sysAddr, eServerDisconnectIdentifiers::INVALID_SESSION_KEY);
|
||||
return;
|
||||
} else {
|
||||
|
||||
@@ -10,6 +10,7 @@ set(DCOMMONTEST_SOURCES
|
||||
"TestLDFFormat.cpp"
|
||||
"TestNiPoint3.cpp"
|
||||
"TestEncoding.cpp"
|
||||
"TestGeneralUtils.cpp"
|
||||
"TestLUString.cpp"
|
||||
"TestLUWString.cpp"
|
||||
"TestBitStreamUtils.cpp"
|
||||
|
||||
35
tests/dCommonTests/TestGeneralUtils.cpp
Normal file
35
tests/dCommonTests/TestGeneralUtils.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <random>
|
||||
#include <set>
|
||||
|
||||
#include "Game.h"
|
||||
#include "GeneralUtils.h"
|
||||
|
||||
TEST(GeneralUtilsTest, UnpredictableNumberLeavesTheGameEngineAlone) {
|
||||
Game::randomEngine = std::mt19937(1234);
|
||||
std::mt19937 expected(1234);
|
||||
|
||||
GeneralUtils::GenerateUnpredictableNumber<uint32_t>();
|
||||
GeneralUtils::GenerateUnpredictableNumber<uint64_t>();
|
||||
|
||||
EXPECT_EQ(Game::randomEngine(), expected());
|
||||
}
|
||||
|
||||
TEST(GeneralUtilsTest, UnpredictableNumberDoesNotFollowTheGameEngineSeed) {
|
||||
std::set<uint64_t> seen;
|
||||
for (uint32_t i = 0; i < 16; i++) {
|
||||
// The same seed every time would give the same number every time from the game engine
|
||||
Game::randomEngine = std::mt19937(1234);
|
||||
seen.insert(GeneralUtils::GenerateUnpredictableNumber<uint64_t>());
|
||||
}
|
||||
|
||||
EXPECT_EQ(seen.size(), 16);
|
||||
}
|
||||
|
||||
TEST(GeneralUtilsTest, UnpredictableNumberFillsEveryByte) {
|
||||
uint64_t seenBits = 0;
|
||||
for (uint32_t i = 0; i < 64; i++) seenBits |= GeneralUtils::GenerateUnpredictableNumber<uint64_t>();
|
||||
|
||||
EXPECT_EQ(seenBits, 0xFFFFFFFFFFFFFFFFULL);
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
#include "Game.h"
|
||||
|
||||
#include <random>
|
||||
|
||||
class Logger;
|
||||
class dConfig;
|
||||
namespace Game
|
||||
{
|
||||
Logger* logger;
|
||||
dConfig* config = nullptr; // Permissions.cpp reads the permission levels from it
|
||||
std::mt19937 randomEngine;
|
||||
} // namespace Game
|
||||
|
||||
Reference in New Issue
Block a user