New GUID generation & migration
This commit is contained in:
parent
229861cde9
commit
4ff1337358
@ -20,10 +20,12 @@ namespace Components
|
|||||||
std::vector<std::uint64_t> Auth::BannedUids =
|
std::vector<std::uint64_t> Auth::BannedUids =
|
||||||
{
|
{
|
||||||
// No longer necessary
|
// No longer necessary
|
||||||
/* 0xf4d2c30b712ac6e3,
|
0xf4d2c30b712ac6e3,
|
||||||
0xf7e33c4081337fa3,
|
0xf7e33c4081337fa3,
|
||||||
0x6f5597f103cc50e9,
|
0x6f5597f103cc50e9,
|
||||||
0xecd542eee54ffccf,*/
|
0xecd542eee54ffccf,
|
||||||
|
0xA46B84C54694FD5B,
|
||||||
|
0xECD542EEE54FFCCF,
|
||||||
};
|
};
|
||||||
|
|
||||||
bool Auth::HasAccessToReservedSlot;
|
bool Auth::HasAccessToReservedSlot;
|
||||||
@ -365,7 +367,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
GuidToken.clear();
|
GuidToken.clear();
|
||||||
ComputeToken.clear();
|
ComputeToken.clear();
|
||||||
GuidKey = Utils::Cryptography::ECC::GenerateKey(512);
|
GuidKey = Utils::Cryptography::ECC::GenerateKey(512, GetMachineEntropy());
|
||||||
StoreKey();
|
StoreKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,21 +376,32 @@ namespace Components
|
|||||||
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled()) return;
|
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled()) return;
|
||||||
if (!force && GuidKey.isValid()) return;
|
if (!force && GuidKey.isValid()) return;
|
||||||
|
|
||||||
// We no longer read the key from disk
|
const auto appdata = Components::FileSystem::GetAppdataPath();
|
||||||
// While having obvious advantages to palliate the fact that some users are not playing on Steam,
|
Utils::IO::CreateDir(appdata.string());
|
||||||
// it is creating a lot of issues because GUID files get packaged with the game when people share it
|
|
||||||
// and it makes it harder for server owners to identify players uniquely
|
const auto guidPath = appdata / "guid.dat";
|
||||||
// Note that we could store it in Appdata, but then it would be dissociated from the rest of player files,
|
|
||||||
// so for now we're doing something else: the key is generated uniquely from the machine's characteristics
|
#ifndef REGENERATE_INVALID_KEY
|
||||||
// It is not (necessarily) stored and therefore, not loaded, so it could make it harder to evade bans without
|
// Migrate old file
|
||||||
// using a custom client that would need regeneration at each update.
|
const auto oldGuidPath = "players/guid.dat";
|
||||||
|
if (Utils::IO::FileExists(oldGuidPath))
|
||||||
|
{
|
||||||
|
if (MoveFileA(oldGuidPath, guidPath.string().data()))
|
||||||
|
{
|
||||||
|
Utils::IO::RemoveFile(oldGuidPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const auto guidFile = Utils::IO::ReadFile(guidPath.string());
|
||||||
|
|
||||||
Proto::Auth::Certificate cert;
|
Proto::Auth::Certificate cert;
|
||||||
if (cert.ParseFromString(::Utils::IO::ReadFile("players/guid.dat")))
|
if (cert.ParseFromString(guidFile))
|
||||||
{
|
{
|
||||||
GuidKey.deserialize(cert.privatekey());
|
GuidKey.deserialize(cert.privatekey());
|
||||||
GuidToken = cert.token();
|
GuidToken = cert.token();
|
||||||
ComputeToken = cert.ctoken();
|
ComputeToken = cert.ctoken();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GuidKey.free();
|
GuidKey.free();
|
||||||
@ -396,16 +409,15 @@ namespace Components
|
|||||||
|
|
||||||
if (GuidKey.isValid())
|
if (GuidKey.isValid())
|
||||||
{
|
{
|
||||||
auto machineKey = Utils::Cryptography::ECC::GenerateKey(512);
|
#ifdef REGENERATE_INVALID_KEY
|
||||||
if (GetKeyHash(machineKey.getPublicKey()) == GetKeyHash())
|
auto machineKey = Utils::Cryptography::ECC::GenerateKey(512, GetMachineEntropy());
|
||||||
|
if (GetKeyHash(machineKey.getPublicKey()) != GetKeyHash())
|
||||||
{
|
{
|
||||||
//All good, nothing to do
|
// kill! The user has changed machine or copied files from another
|
||||||
}
|
Auth::GenerateKey();
|
||||||
else
|
|
||||||
{
|
|
||||||
// kill! The user has changed machine or copied files from another
|
|
||||||
Auth::GenerateKey();
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
//All good, nothing to do
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -512,6 +524,77 @@ namespace Components
|
|||||||
token = computeToken;
|
token = computeToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A somewhat hardware tied 48 bit value
|
||||||
|
std::string Auth::GetMachineEntropy()
|
||||||
|
{
|
||||||
|
std::string entropy{};
|
||||||
|
DWORD volumeID;
|
||||||
|
if (GetVolumeInformationA("C:\\",
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&volumeID,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
))
|
||||||
|
{
|
||||||
|
// Drive info
|
||||||
|
entropy += std::to_string(volumeID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MAC Address
|
||||||
|
{
|
||||||
|
unsigned long outBufLen = 0;
|
||||||
|
DWORD dwResult = GetAdaptersInfo(NULL, &outBufLen);
|
||||||
|
if (dwResult == ERROR_BUFFER_OVERFLOW) // This is what we're expecting
|
||||||
|
{
|
||||||
|
// Now allocate a structure of the required size.
|
||||||
|
PIP_ADAPTER_INFO pIpAdapterInfo = reinterpret_cast<PIP_ADAPTER_INFO>(malloc(outBufLen));
|
||||||
|
dwResult = GetAdaptersInfo(pIpAdapterInfo, &outBufLen);
|
||||||
|
if (dwResult == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
while (pIpAdapterInfo)
|
||||||
|
{
|
||||||
|
switch (pIpAdapterInfo->Type)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
pIpAdapterInfo = pIpAdapterInfo->Next;
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case IF_TYPE_IEEE80211:
|
||||||
|
case MIB_IF_TYPE_ETHERNET:
|
||||||
|
{
|
||||||
|
|
||||||
|
std::string macAddress{};
|
||||||
|
for (size_t i = 0; i < ARRAYSIZE(pIpAdapterInfo->Address); i++)
|
||||||
|
{
|
||||||
|
entropy += std::to_string(pIpAdapterInfo->Address[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free before going next because clearly this is not working
|
||||||
|
free(pIpAdapterInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entropy.empty())
|
||||||
|
{
|
||||||
|
// ultimate fallback
|
||||||
|
return std::to_string(Utils::Cryptography::Rand::GenerateLong());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return entropy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Auth::Auth()
|
Auth::Auth()
|
||||||
{
|
{
|
||||||
TokenContainer.cancel = false;
|
TokenContainer.cancel = false;
|
||||||
|
@ -24,6 +24,8 @@ namespace Components
|
|||||||
static uint32_t GetZeroBits(Utils::Cryptography::Token token, const std::string& publicKey);
|
static uint32_t GetZeroBits(Utils::Cryptography::Token token, const std::string& publicKey);
|
||||||
static void IncrementToken(Utils::Cryptography::Token& token, Utils::Cryptography::Token& computeToken, const std::string& publicKey, uint32_t zeroBits, bool* cancel = nullptr, uint64_t* count = nullptr);
|
static void IncrementToken(Utils::Cryptography::Token& token, Utils::Cryptography::Token& computeToken, const std::string& publicKey, uint32_t zeroBits, bool* cancel = nullptr, uint64_t* count = nullptr);
|
||||||
|
|
||||||
|
static std::string GetMachineEntropy();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
class TokenIncrementing
|
class TokenIncrementing
|
||||||
|
@ -153,7 +153,7 @@ namespace Components
|
|||||||
CoTaskMemFree(path);
|
CoTaskMemFree(path);
|
||||||
});
|
});
|
||||||
|
|
||||||
return std::filesystem::path(path) / "xlabs";
|
return std::filesystem::path(path) / "iw4x";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> FileSystem::GetFileList(const std::string& path, const std::string& extension)
|
std::vector<std::string> FileSystem::GetFileList(const std::string& path, const std::string& extension)
|
||||||
|
@ -11,77 +11,6 @@ namespace Utils
|
|||||||
Rand::Initialize();
|
Rand::Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetEntropy()
|
|
||||||
{
|
|
||||||
DWORD volumeID;
|
|
||||||
if (GetVolumeInformationA("C:\\",
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
&volumeID,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
NULL
|
|
||||||
))
|
|
||||||
{
|
|
||||||
// Drive info
|
|
||||||
return std::to_string(volumeID);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Resort to mac address
|
|
||||||
unsigned long outBufLen = 0;
|
|
||||||
DWORD dwResult = GetAdaptersInfo(NULL, &outBufLen);
|
|
||||||
if (dwResult == ERROR_BUFFER_OVERFLOW) // This is what we're expecting
|
|
||||||
{
|
|
||||||
// Now allocate a structure of the required size.
|
|
||||||
PIP_ADAPTER_INFO pIpAdapterInfo = reinterpret_cast<PIP_ADAPTER_INFO>(malloc(outBufLen));
|
|
||||||
dwResult = GetAdaptersInfo(pIpAdapterInfo, &outBufLen);
|
|
||||||
if (dwResult == ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
while (pIpAdapterInfo)
|
|
||||||
{
|
|
||||||
switch (pIpAdapterInfo->Type)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
pIpAdapterInfo = pIpAdapterInfo->Next;
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case IF_TYPE_IEEE80211:
|
|
||||||
case MIB_IF_TYPE_ETHERNET:
|
|
||||||
{
|
|
||||||
|
|
||||||
std::string macAddress{};
|
|
||||||
for (size_t i = 0; i < ARRAYSIZE(pIpAdapterInfo->Address); i++)
|
|
||||||
{
|
|
||||||
macAddress += std::to_string(pIpAdapterInfo->Address[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(pIpAdapterInfo);
|
|
||||||
return macAddress;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// :(
|
|
||||||
// Continue to fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free before going next because clearly this is not working
|
|
||||||
free(pIpAdapterInfo);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// No MAC, no C: drive? Come on
|
|
||||||
}
|
|
||||||
|
|
||||||
// ultimate fallback
|
|
||||||
return std::to_string(Rand::GenerateInt());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma region Rand
|
#pragma region Rand
|
||||||
|
|
||||||
prng_state Rand::State;
|
prng_state Rand::State;
|
||||||
@ -98,6 +27,13 @@ namespace Utils
|
|||||||
return std::string{ buffer, static_cast<std::size_t>(pos) };
|
return std::string{ buffer, static_cast<std::size_t>(pos) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::uint64_t Rand::GenerateLong()
|
||||||
|
{
|
||||||
|
std::uint64_t number = 0;
|
||||||
|
fortuna_read(reinterpret_cast<std::uint8_t*>(&number), sizeof(number), &Rand::State);
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
std::uint32_t Rand::GenerateInt()
|
std::uint32_t Rand::GenerateInt()
|
||||||
{
|
{
|
||||||
std::uint32_t number = 0;
|
std::uint32_t number = 0;
|
||||||
@ -116,20 +52,30 @@ namespace Utils
|
|||||||
|
|
||||||
#pragma region ECC
|
#pragma region ECC
|
||||||
|
|
||||||
ECC::Key ECC::GenerateKey(int bits)
|
ECC::Key ECC::GenerateKey(int bits, const std::string& entropy)
|
||||||
{
|
{
|
||||||
Key key;
|
Key key;
|
||||||
|
|
||||||
ltc_mp = ltm_desc;
|
ltc_mp = ltm_desc;
|
||||||
int descriptorIndex = register_prng(&chacha20_prng_desc);
|
|
||||||
|
|
||||||
// allocate state
|
if (entropy.empty())
|
||||||
{
|
{
|
||||||
|
register_prng(&sprng_desc);
|
||||||
|
const auto result = ecc_make_key(nullptr, find_prng("sprng"), bits / 8, key.getKeyPtr());
|
||||||
|
if (result != CRYPT_OK)
|
||||||
|
{
|
||||||
|
Components::Logger::PrintError(Game::conChannel_t::CON_CHANNEL_ERROR, "There was an issue generating a secured random key! Please contact support");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int descriptorIndex = register_prng(&chacha20_prng_desc);
|
||||||
|
|
||||||
|
// allocate state
|
||||||
prng_state* state = new prng_state();
|
prng_state* state = new prng_state();
|
||||||
|
|
||||||
chacha20_prng_start(state);
|
chacha20_prng_start(state);
|
||||||
|
|
||||||
const auto entropy = Cryptography::GetEntropy();
|
|
||||||
chacha20_prng_add_entropy(reinterpret_cast<const unsigned char*>(entropy.data()), entropy.size(), state);
|
chacha20_prng_add_entropy(reinterpret_cast<const unsigned char*>(entropy.data()), entropy.size(), state);
|
||||||
|
|
||||||
chacha20_prng_ready(state);
|
chacha20_prng_ready(state);
|
||||||
|
@ -132,6 +132,7 @@ namespace Utils
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static std::string GenerateChallenge();
|
static std::string GenerateChallenge();
|
||||||
|
static std::uint64_t GenerateLong();
|
||||||
static std::uint32_t GenerateInt();
|
static std::uint32_t GenerateInt();
|
||||||
static void Initialize();
|
static void Initialize();
|
||||||
|
|
||||||
@ -236,7 +237,7 @@ namespace Utils
|
|||||||
std::shared_ptr<ecc_key> keyStorage;
|
std::shared_ptr<ecc_key> keyStorage;
|
||||||
};
|
};
|
||||||
|
|
||||||
static Key GenerateKey(int bits);
|
static Key GenerateKey(int bits, const std::string& entropy = {});
|
||||||
static std::string SignMessage(Key key, const std::string& message);
|
static std::string SignMessage(Key key, const std::string& message);
|
||||||
static bool VerifyMessage(Key key, const std::string& message, const std::string& signature);
|
static bool VerifyMessage(Key key, const std::string& message, const std::string& signature);
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user