127 lines
3.6 KiB
C++
127 lines
3.6 KiB
C++
#include "Hooks/Crypt.hpp"
|
|
|
|
Crypt::Crypt()
|
|
{
|
|
if (!CryptAcquireContext(&context, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT))
|
|
{
|
|
if (::GetLastError() == NTE_BAD_KEYSET)
|
|
{
|
|
if (!CryptAcquireContext(&context, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT | CRYPT_NEWKEYSET))
|
|
{
|
|
throw std::runtime_error("Error during CryptAcquireContext 2");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw std::runtime_error("Error during CryptAcquireContext");
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
BYTE* blob = new BYTE[Helpers::publicKey.size()];
|
|
std::copy(Helpers::publicKey.begin(), Helpers::publicKey.end(), blob);
|
|
|
|
if (!CryptImportKey(context, blob, Helpers::publicKey.size(), 0, 0, &key))
|
|
{
|
|
throw std::runtime_error("");
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
#ifdef _DEBUG
|
|
throw std::runtime_error("Failed to import public key");
|
|
#else
|
|
throw std::runtime_error("Error during CryptImportKey");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
Crypt::~Crypt()
|
|
{
|
|
CryptDestroyKey(key);
|
|
CryptReleaseContext(context, 0);
|
|
}
|
|
|
|
bool Crypt::verifySignatureBase64(std::string message, std::string signatureBase64, ALG_ID algorithm = CALG_SHA_256)
|
|
{
|
|
// Check for a reasonable signature length before verifying
|
|
if (signatureBase64.length() > 4096)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
HCRYPTHASH hash;
|
|
|
|
if (!CryptCreateHash(context, algorithm, NULL, 0, &hash))
|
|
{
|
|
throw std::runtime_error("");
|
|
}
|
|
|
|
try
|
|
{
|
|
if (!CryptHashData(hash, (BYTE*)message.c_str(), message.size(), 0))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
std::vector<BYTE> signature = Helpers::base64Decode(signatureBase64);
|
|
|
|
/*
|
|
The native cryptography API uses little-endian byte order
|
|
while OpenSSL uses big-endian byte order.
|
|
|
|
If you are verifying a signature generated by using a OpenSSL API
|
|
(or similar), you must swap the order of signature bytes before
|
|
calling the CryptVerifySignature function to verify the signature.
|
|
*/
|
|
|
|
std::reverse(signature.begin(), signature.end());
|
|
|
|
BYTE* signatureData = new BYTE[signature.size()];
|
|
std::copy(signature.begin(), signature.end(), signatureData);
|
|
|
|
if (!CryptVerifySignature(hash, signatureData, signature.size(), key, NULL, 0))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
::CryptDestroyHash(hash);
|
|
return false;
|
|
}
|
|
|
|
::CryptDestroyHash(hash);
|
|
|
|
return true;
|
|
}
|
|
|
|
Crypt__verifySignatureBase64_t Crypt__verifySignatureBase64 = (Crypt__verifySignatureBase64_t)ADDRESS_CRYPT__VERIFYSIGNATUREBASE64;
|
|
|
|
// Crypt::verifySignatureBase64(std::string message, std::string signatureBase64)
|
|
void __fastcall Crypt__verifySignatureBase64_hook(HCRYPTPROV* _this, void*, int a2, BYTE* pbData, int a4, int a5, int a6, DWORD dwDataLen, int a8, int a9, int a10, int a11, int a12, int a13, int a14, int a15)
|
|
{
|
|
// We have to reconstruct the parameters by hand since using the original function signature causes crashes
|
|
|
|
std::string message;
|
|
std::string signatureBase64;
|
|
|
|
// Get message
|
|
message = std::string(reinterpret_cast<const char*>(pbData), dwDataLen);
|
|
|
|
// Get signatureBase64
|
|
int* v21 = (int*)a10;
|
|
if ((unsigned int)a15 < 0x10)
|
|
{
|
|
v21 = &a10;
|
|
}
|
|
|
|
signatureBase64 = std::string(reinterpret_cast<const char*>(v21), a14);
|
|
|
|
// Verify signature
|
|
if (!Crypt().verifySignatureBase64(message, signatureBase64, CALG_SHA_256))
|
|
{
|
|
throw std::runtime_error("");
|
|
}
|
|
} |