GUID On runtime
This commit is contained in:
parent
0d222e30cf
commit
67bd07949d
@ -873,11 +873,6 @@ namespace Assets
|
|||||||
|
|
||||||
void IXModel::ConvertPlayerModelFromSingleplayerToMultiplayer(Game::XModel* model, Utils::Memory::Allocator& allocator)
|
void IXModel::ConvertPlayerModelFromSingleplayerToMultiplayer(Game::XModel* model, Utils::Memory::Allocator& allocator)
|
||||||
{
|
{
|
||||||
if (model->name == "body_airport_com_a"s)
|
|
||||||
{
|
|
||||||
printf("");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string requiredBonesForHumanoid[] = {
|
std::string requiredBonesForHumanoid[] = {
|
||||||
"j_spinelower",
|
"j_spinelower",
|
||||||
"j_spineupper",
|
"j_spineupper",
|
||||||
|
@ -19,10 +19,11 @@ namespace Components
|
|||||||
|
|
||||||
std::vector<std::uint64_t> Auth::BannedUids =
|
std::vector<std::uint64_t> Auth::BannedUids =
|
||||||
{
|
{
|
||||||
0xf4d2c30b712ac6e3,
|
// No longer necessary
|
||||||
|
/* 0xf4d2c30b712ac6e3,
|
||||||
0xf7e33c4081337fa3,
|
0xf7e33c4081337fa3,
|
||||||
0x6f5597f103cc50e9,
|
0x6f5597f103cc50e9,
|
||||||
0xecd542eee54ffccf,
|
0xecd542eee54ffccf,*/
|
||||||
};
|
};
|
||||||
|
|
||||||
bool Auth::HasAccessToReservedSlot;
|
bool Auth::HasAccessToReservedSlot;
|
||||||
@ -45,7 +46,7 @@ namespace Components
|
|||||||
if (mseconds < 0) mseconds = 0;
|
if (mseconds < 0) mseconds = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Localization::Set("MPUI_SECURITY_INCREASE_MESSAGE", Utils::String::VA("Increasing security level from %d to %d (est. %s)",GetSecurityLevel(), TokenContainer.targetLevel, Utils::String::FormatTimeSpan(static_cast<int>(mseconds)).data()));
|
Localization::Set("MPUI_SECURITY_INCREASE_MESSAGE", Utils::String::VA("Increasing security level from %d to %d (est. %s)", GetSecurityLevel(), TokenContainer.targetLevel, Utils::String::FormatTimeSpan(static_cast<int>(mseconds)).data()));
|
||||||
}
|
}
|
||||||
else if (TokenContainer.thread.joinable())
|
else if (TokenContainer.thread.joinable())
|
||||||
{
|
{
|
||||||
@ -53,7 +54,7 @@ namespace Components
|
|||||||
TokenContainer.generating = false;
|
TokenContainer.generating = false;
|
||||||
|
|
||||||
StoreKey();
|
StoreKey();
|
||||||
Logger::Debug("Security level is {}",GetSecurityLevel());
|
Logger::Debug("Security level is {}", GetSecurityLevel());
|
||||||
Command::Execute("closemenu security_increase_popmenu", false);
|
Command::Execute("closemenu security_increase_popmenu", false);
|
||||||
|
|
||||||
if (!TokenContainer.cancel)
|
if (!TokenContainer.cancel)
|
||||||
@ -212,7 +213,7 @@ namespace Components
|
|||||||
SteamID guid;
|
SteamID guid;
|
||||||
guid.bits = xuid;
|
guid.bits = xuid;
|
||||||
|
|
||||||
if (Bans::IsBanned({guid, address.getIP()}))
|
if (Bans::IsBanned({ guid, address.getIP() }))
|
||||||
{
|
{
|
||||||
Logger::PrintFail2Ban("Failed connect attempt from IP address: {}\n", Network::AdrToString(address));
|
Logger::PrintFail2Ban("Failed connect attempt from IP address: {}\n", Network::AdrToString(address));
|
||||||
Network::Send(address, "error\nEXE_ERR_BANNED_PERM");
|
Network::Send(address, "error\nEXE_ERR_BANNED_PERM");
|
||||||
@ -304,15 +305,15 @@ namespace Components
|
|||||||
xor eax, eax
|
xor eax, eax
|
||||||
jmp safeContinue
|
jmp safeContinue
|
||||||
|
|
||||||
noAccess:
|
noAccess :
|
||||||
mov eax, dword ptr [edx + 0x10]
|
mov eax, dword ptr[edx + 0x10]
|
||||||
|
|
||||||
safeContinue:
|
safeContinue :
|
||||||
// Game code skipped by hook
|
// Game code skipped by hook
|
||||||
add esp, 0xC
|
add esp, 0xC
|
||||||
|
|
||||||
push 0x460FB3
|
push 0x460FB3
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,6 +343,8 @@ namespace Components
|
|||||||
|
|
||||||
void Auth::StoreKey()
|
void Auth::StoreKey()
|
||||||
{
|
{
|
||||||
|
// We write the key as a decoy I suppose - it's really no longer needed
|
||||||
|
// TODO Remove this part
|
||||||
if (!Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled() && GuidKey.isValid())
|
if (!Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled() && GuidKey.isValid())
|
||||||
{
|
{
|
||||||
Proto::Auth::Certificate cert;
|
Proto::Auth::Certificate cert;
|
||||||
@ -366,23 +369,31 @@ 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
|
||||||
|
// While having obvious advantages to palliate the fact that some users are not playing on Steam,
|
||||||
|
// 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
|
||||||
|
// 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
|
||||||
|
// It is not (necessarily) stored and therefore, not loaded, so it could make it harder to evade bans without
|
||||||
|
// using a custom client that would need regeneration at each update.
|
||||||
|
#if false
|
||||||
Proto::Auth::Certificate cert;
|
Proto::Auth::Certificate cert;
|
||||||
if (cert.ParseFromString(::Utils::IO::ReadFile("players/guid.dat")))
|
if (cert.ParseFromString(::Utils::IO::ReadFile("players/guid.dat")))
|
||||||
{
|
{
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!GuidKey.isValid())
|
if (!GuidKey.isValid())
|
||||||
{
|
#endif
|
||||||
Auth::GenerateKey();
|
Auth::GenerateKey();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t Auth::GetSecurityLevel()
|
uint32_t Auth::GetSecurityLevel()
|
||||||
{
|
{
|
||||||
@ -404,18 +415,18 @@ namespace Components
|
|||||||
|
|
||||||
// Start thread
|
// Start thread
|
||||||
TokenContainer.thread = std::thread([&level]()
|
TokenContainer.thread = std::thread([&level]()
|
||||||
{
|
|
||||||
TokenContainer.generating = true;
|
|
||||||
TokenContainer.hashes = 0;
|
|
||||||
TokenContainer.startTime = Game::Sys_Milliseconds();
|
|
||||||
IncrementToken(GuidToken, ComputeToken, GuidKey.getPublicKey(), TokenContainer.targetLevel, &TokenContainer.cancel, &TokenContainer.hashes);
|
|
||||||
TokenContainer.generating = false;
|
|
||||||
|
|
||||||
if (TokenContainer.cancel)
|
|
||||||
{
|
{
|
||||||
Logger::Print("Token incrementation thread terminated\n");
|
TokenContainer.generating = true;
|
||||||
}
|
TokenContainer.hashes = 0;
|
||||||
});
|
TokenContainer.startTime = Game::Sys_Milliseconds();
|
||||||
|
IncrementToken(GuidToken, ComputeToken, GuidKey.getPublicKey(), TokenContainer.targetLevel, &TokenContainer.cancel, &TokenContainer.hashes);
|
||||||
|
TokenContainer.generating = false;
|
||||||
|
|
||||||
|
if (TokenContainer.cancel)
|
||||||
|
{
|
||||||
|
Logger::Print("Token incrementation thread terminated\n");
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -521,36 +532,36 @@ namespace Components
|
|||||||
|
|
||||||
// Guid command
|
// Guid command
|
||||||
Command::Add("guid", []
|
Command::Add("guid", []
|
||||||
{
|
{
|
||||||
Logger::Print("Your guid: {:#X}\n", Steam::SteamUser()->GetSteamID().bits);
|
Logger::Print("Your guid: {:#X}\n", Steam::SteamUser()->GetSteamID().bits);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled())
|
if (!Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled())
|
||||||
{
|
{
|
||||||
Command::Add("securityLevel", [](const Command::Params* params)
|
Command::Add("securityLevel", [](const Command::Params* params)
|
||||||
{
|
|
||||||
if (params->size() < 2)
|
|
||||||
{
|
{
|
||||||
const auto level = GetZeroBits(GuidToken, GuidKey.getPublicKey());
|
if (params->size() < 2)
|
||||||
Logger::Print("Your current security level is {}\n", level);
|
{
|
||||||
Logger::Print("Your security token is: {}\n", Utils::String::DumpHex(GuidToken.toString(), ""));
|
const auto level = GetZeroBits(GuidToken, GuidKey.getPublicKey());
|
||||||
Logger::Print("Your computation token is: {}\n", Utils::String::DumpHex(ComputeToken.toString(), ""));
|
Logger::Print("Your current security level is {}\n", level);
|
||||||
|
Logger::Print("Your security token is: {}\n", Utils::String::DumpHex(GuidToken.toString(), ""));
|
||||||
|
Logger::Print("Your computation token is: {}\n", Utils::String::DumpHex(ComputeToken.toString(), ""));
|
||||||
|
|
||||||
Toast::Show("cardicon_locked", "^5Security Level", Utils::String::VA("Your security level is %d", level), 3000);
|
Toast::Show("cardicon_locked", "^5Security Level", Utils::String::VA("Your security level is %d", level), 3000);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const auto level = std::strtoul(params->get(1), nullptr, 10);
|
const auto level = std::strtoul(params->get(1), nullptr, 10);
|
||||||
IncreaseSecurityLevel(level);
|
IncreaseSecurityLevel(level);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
UIScript::Add("security_increase_cancel", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
UIScript::Add("security_increase_cancel", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
TokenContainer.cancel = true;
|
TokenContainer.cancel = true;
|
||||||
Logger::Print("Token incrementation process canceled!\n");
|
Logger::Print("Token incrementation process canceled!\n");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Auth::~Auth()
|
Auth::~Auth()
|
||||||
|
@ -463,7 +463,7 @@ namespace Components
|
|||||||
|
|
||||||
void Download::Reply(mg_connection* connection, const std::string& contentType, const std::string& data)
|
void Download::Reply(mg_connection* connection, const std::string& contentType, const std::string& data)
|
||||||
{
|
{
|
||||||
const auto formatted = std::format("Content-Type: {}\r\n", contentType);
|
const auto formatted = std::format("Content-Type: {}\r\nAccess-Control-Allow-Origin: *\r\n", contentType);
|
||||||
mg_http_reply(connection, 200, formatted.c_str(), "%s", data.c_str());
|
mg_http_reply(connection, 200, formatted.c_str(), "%s", data.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,6 +56,9 @@
|
|||||||
#include <dwmapi.h>
|
#include <dwmapi.h>
|
||||||
#pragma comment (lib, "dwmapi.lib")
|
#pragma comment (lib, "dwmapi.lib")
|
||||||
|
|
||||||
|
#include <iphlpapi.h>
|
||||||
|
#pragma comment (lib, "iphlpapi.lib")
|
||||||
|
|
||||||
// Ignore the warnings
|
// Ignore the warnings
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
#pragma warning(disable: 4100)
|
#pragma warning(disable: 4100)
|
||||||
|
@ -8,10 +8,80 @@ namespace Utils
|
|||||||
{
|
{
|
||||||
void Initialize()
|
void Initialize()
|
||||||
{
|
{
|
||||||
DES3::Initialize();
|
|
||||||
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;
|
||||||
@ -51,8 +121,29 @@ namespace Utils
|
|||||||
Key key;
|
Key key;
|
||||||
|
|
||||||
ltc_mp = ltm_desc;
|
ltc_mp = ltm_desc;
|
||||||
register_prng(&sprng_desc);
|
int descriptorIndex = register_prng(&chacha20_prng_desc);
|
||||||
ecc_make_key(nullptr, find_prng("sprng"), bits / 8, key.getKeyPtr());
|
|
||||||
|
// allocate state
|
||||||
|
{
|
||||||
|
prng_state* state = new prng_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_ready(state);
|
||||||
|
|
||||||
|
const auto result = ecc_make_key(state, descriptorIndex, bits / 8, key.getKeyPtr());
|
||||||
|
|
||||||
|
if (result != CRYPT_OK)
|
||||||
|
{
|
||||||
|
Components::Logger::PrintError(Game::conChannel_t::CON_CHANNEL_ERROR, "There was an issue generating your unique player ID! Please contact support");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deallocate state
|
||||||
|
delete state;
|
||||||
|
}
|
||||||
|
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
@ -79,8 +170,8 @@ namespace Utils
|
|||||||
|
|
||||||
int result = 0;
|
int result = 0;
|
||||||
return (ecc_verify_hash(reinterpret_cast<const std::uint8_t*>(signature.data()), signature.size(),
|
return (ecc_verify_hash(reinterpret_cast<const std::uint8_t*>(signature.data()), signature.size(),
|
||||||
reinterpret_cast<const std::uint8_t*>(message.data()), message.size(),
|
reinterpret_cast<const std::uint8_t*>(message.data()), message.size(),
|
||||||
&result, key.getKeyPtr()) == CRYPT_OK && result != 0);
|
&result, key.getKeyPtr()) == CRYPT_OK && result != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
@ -115,7 +206,7 @@ namespace Utils
|
|||||||
ltc_mp = ltm_desc;
|
ltc_mp = ltm_desc;
|
||||||
|
|
||||||
rsa_sign_hash_ex(reinterpret_cast<const std::uint8_t*>(hash.data()), hash.size(),
|
rsa_sign_hash_ex(reinterpret_cast<const std::uint8_t*>(hash.data()), hash.size(),
|
||||||
buffer, &length, LTC_PKCS_1_V1_5, nullptr, 0, hash_index, 0, key.getKeyPtr());
|
buffer, &length, LTC_PKCS_1_V1_5, nullptr, 0, hash_index, 0, key.getKeyPtr());
|
||||||
|
|
||||||
return std::string{ reinterpret_cast<char*>(buffer), length };
|
return std::string{ reinterpret_cast<char*>(buffer), length };
|
||||||
}
|
}
|
||||||
@ -133,47 +224,8 @@ namespace Utils
|
|||||||
|
|
||||||
auto result = 0;
|
auto result = 0;
|
||||||
return (rsa_verify_hash_ex(reinterpret_cast<const std::uint8_t*>(signature.data()), signature.size(),
|
return (rsa_verify_hash_ex(reinterpret_cast<const std::uint8_t*>(signature.data()), signature.size(),
|
||||||
reinterpret_cast<const std::uint8_t*>(hash.data()), hash.size(), LTC_PKCS_1_V1_5,
|
reinterpret_cast<const std::uint8_t*>(hash.data()), hash.size(), LTC_PKCS_1_V1_5,
|
||||||
hash_index, 0, &result, key.getKeyPtr()) == CRYPT_OK && result != 0);
|
hash_index, 0, &result, key.getKeyPtr()) == CRYPT_OK && result != 0);
|
||||||
}
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
#pragma region DES3
|
|
||||||
|
|
||||||
void DES3::Initialize()
|
|
||||||
{
|
|
||||||
register_cipher(&des3_desc);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string DES3::Encrypt(const std::string& text, const std::string& iv, const std::string& key)
|
|
||||||
{
|
|
||||||
std::string encData;
|
|
||||||
encData.resize(text.size());
|
|
||||||
|
|
||||||
symmetric_CBC cbc;
|
|
||||||
const auto des3 = find_cipher("3des");
|
|
||||||
|
|
||||||
cbc_start(des3, reinterpret_cast<const std::uint8_t*>(iv.data()), reinterpret_cast<const std::uint8_t*>(key.data()), static_cast<int>(key.size()), 0, &cbc);
|
|
||||||
cbc_encrypt(reinterpret_cast<const std::uint8_t*>(text.data()), reinterpret_cast<uint8_t*>(encData.data()), text.size(), &cbc);
|
|
||||||
cbc_done(&cbc);
|
|
||||||
|
|
||||||
return encData;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string DES3::Decrpyt(const std::string& data, const std::string& iv, const std::string& key)
|
|
||||||
{
|
|
||||||
std::string decData;
|
|
||||||
decData.resize(data.size());
|
|
||||||
|
|
||||||
symmetric_CBC cbc;
|
|
||||||
const auto des3 = find_cipher("3des");
|
|
||||||
|
|
||||||
cbc_start(des3, reinterpret_cast<const std::uint8_t*>(iv.data()), reinterpret_cast<const std::uint8_t*>(key.data()), static_cast<int>(key.size()), 0, &cbc);
|
|
||||||
cbc_decrypt(reinterpret_cast<const std::uint8_t*>(data.data()), reinterpret_cast<std::uint8_t*>(decData.data()), data.size(), &cbc);
|
|
||||||
cbc_done(&cbc);
|
|
||||||
|
|
||||||
return decData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
@ -5,6 +5,7 @@ namespace Utils
|
|||||||
namespace Cryptography
|
namespace Cryptography
|
||||||
{
|
{
|
||||||
void Initialize();
|
void Initialize();
|
||||||
|
std::string GetEntropy();
|
||||||
|
|
||||||
class Token
|
class Token
|
||||||
{
|
{
|
||||||
@ -327,14 +328,6 @@ namespace Utils
|
|||||||
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
class DES3
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static void Initialize();
|
|
||||||
static std::string Encrypt(const std::string& text, const std::string& iv, const std::string& key);
|
|
||||||
static std::string Decrpyt(const std::string& text, const std::string& iv, const std::string& key);
|
|
||||||
};
|
|
||||||
|
|
||||||
class Tiger
|
class Tiger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
Loading…
Reference in New Issue
Block a user