mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-16 07:30:42 +01:00
Core/Crypto: Switch away from most deprecated openssl functions and removed upper version limit
This commit is contained in:
@@ -16,22 +16,12 @@
|
||||
*/
|
||||
|
||||
#include "RSA.h"
|
||||
#include "BigNumber.h"
|
||||
#include <openssl/bn.h>
|
||||
#include "HMAC.h"
|
||||
#include <openssl/pem.h>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#define CHECK_AND_DECLARE_FUNCTION_TYPE(name, publicKey, privateKey) \
|
||||
static_assert(std::is_same<decltype(&publicKey), decltype(&privateKey)>::value, \
|
||||
"Public key and private key functions must have the same signature"); \
|
||||
using name ## _t = decltype(&publicKey); \
|
||||
template <typename KeyTag> inline name ## _t get_ ## name () { return nullptr; } \
|
||||
template <> inline name ## _t get_ ## name<Trinity::Crypto::RSA::PublicKey>() { return &publicKey; } \
|
||||
template <> inline name ## _t get_ ## name<Trinity::Crypto::RSA::PrivateKey>() { return &privateKey; }
|
||||
|
||||
namespace
|
||||
{
|
||||
struct BIODeleter
|
||||
@@ -42,41 +32,170 @@ struct BIODeleter
|
||||
}
|
||||
};
|
||||
|
||||
CHECK_AND_DECLARE_FUNCTION_TYPE(PEM_read, PEM_read_bio_RSAPublicKey, PEM_read_bio_RSAPrivateKey);
|
||||
CHECK_AND_DECLARE_FUNCTION_TYPE(RSA_encrypt, RSA_public_encrypt, RSA_private_encrypt);
|
||||
}
|
||||
|
||||
Trinity::Crypto::RSA::RSA()
|
||||
struct HMAC_SHA256_MD
|
||||
{
|
||||
_rsa = RSA_new();
|
||||
struct CTX_DATA
|
||||
{
|
||||
Trinity::Crypto::HMAC_SHA256* hmac;
|
||||
};
|
||||
|
||||
#if TRINITY_COMPILER == TRINITY_COMPILER_GNU
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4996)
|
||||
#endif
|
||||
|
||||
HMAC_SHA256_MD()
|
||||
{
|
||||
_md = EVP_MD_meth_new(NID_sha256, NID_sha256WithRSAEncryption);
|
||||
EVP_MD_meth_set_result_size(_md, Trinity::Crypto::Constants::SHA256_DIGEST_LENGTH_BYTES);
|
||||
EVP_MD_meth_set_flags(_md, EVP_MD_FLAG_DIGALGID_ABSENT);
|
||||
EVP_MD_meth_set_init(_md, &Init);
|
||||
EVP_MD_meth_set_update(_md, &UpdateData);
|
||||
EVP_MD_meth_set_final(_md, &Finalize);
|
||||
EVP_MD_meth_set_copy(_md, &Copy);
|
||||
EVP_MD_meth_set_cleanup(_md, &Cleanup);
|
||||
EVP_MD_meth_set_input_blocksize(_md, SHA256_CBLOCK);
|
||||
EVP_MD_meth_set_app_datasize(_md, sizeof(EVP_MD*) + sizeof(CTX_DATA*));
|
||||
}
|
||||
|
||||
HMAC_SHA256_MD(HMAC_SHA256_MD const&) = delete;
|
||||
HMAC_SHA256_MD(HMAC_SHA256_MD&&) = delete;
|
||||
|
||||
HMAC_SHA256_MD& operator=(HMAC_SHA256_MD const&) = delete;
|
||||
HMAC_SHA256_MD& operator=(HMAC_SHA256_MD&&) = delete;
|
||||
|
||||
~HMAC_SHA256_MD()
|
||||
{
|
||||
EVP_MD_meth_free(_md);
|
||||
_md = nullptr;
|
||||
}
|
||||
|
||||
#if TRINITY_COMPILER == TRINITY_COMPILER_GNU
|
||||
#pragma GCC diagnostic pop
|
||||
#else
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
operator EVP_MD const* () const
|
||||
{
|
||||
return _md;
|
||||
}
|
||||
|
||||
static int Init(EVP_MD_CTX* ctx)
|
||||
{
|
||||
Cleanup(ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int UpdateData(EVP_MD_CTX* ctx, const void* data, size_t count)
|
||||
{
|
||||
CTX_DATA* ctxData = reinterpret_cast<CTX_DATA*>(EVP_MD_CTX_md_data(ctx));
|
||||
if (!ctxData->hmac)
|
||||
return 0;
|
||||
|
||||
ctxData->hmac->UpdateData(reinterpret_cast<uint8 const*>(data), count);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int Finalize(EVP_MD_CTX* ctx, unsigned char* md)
|
||||
{
|
||||
CTX_DATA* ctxData = reinterpret_cast<CTX_DATA*>(EVP_MD_CTX_md_data(ctx));
|
||||
if (!ctxData->hmac)
|
||||
return 0;
|
||||
|
||||
ctxData->hmac->Finalize();
|
||||
memcpy(md, ctxData->hmac->GetDigest().data(), ctxData->hmac->GetDigest().size());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// post-processing after openssl memcpys from source to dest (no need to cleanup dest)
|
||||
static int Copy(EVP_MD_CTX* to, EVP_MD_CTX const* from)
|
||||
{
|
||||
CTX_DATA const* ctxDataFrom = reinterpret_cast<CTX_DATA const*>(EVP_MD_CTX_md_data(from));
|
||||
CTX_DATA* ctxDataTo = reinterpret_cast<CTX_DATA*>(EVP_MD_CTX_md_data(to));
|
||||
|
||||
if (ctxDataFrom->hmac)
|
||||
ctxDataTo->hmac = new Trinity::Crypto::HMAC_SHA256(*ctxDataFrom->hmac);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int Cleanup(EVP_MD_CTX* ctx)
|
||||
{
|
||||
CTX_DATA* data = reinterpret_cast<CTX_DATA*>(EVP_MD_CTX_md_data(ctx));
|
||||
if (data->hmac)
|
||||
{
|
||||
delete data->hmac;
|
||||
data->hmac = nullptr;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private:
|
||||
EVP_MD* _md;
|
||||
} HmacSha256Md;
|
||||
}
|
||||
|
||||
Trinity::Crypto::RSA::RSA(RSA&& rsa)
|
||||
namespace Trinity::Crypto
|
||||
{
|
||||
_rsa = rsa._rsa;
|
||||
rsa._rsa = RSA_new();
|
||||
}
|
||||
|
||||
Trinity::Crypto::RSA::~RSA()
|
||||
EVP_MD const* RsaSignature::SHA256::GetGenerator() const
|
||||
{
|
||||
RSA_free(_rsa);
|
||||
return EVP_sha256();
|
||||
}
|
||||
|
||||
template <typename KeyTag>
|
||||
bool Trinity::Crypto::RSA::LoadFromFile(std::string const& fileName, KeyTag)
|
||||
void RsaSignature::SHA256::PostInitCustomizeContext(EVP_MD_CTX*)
|
||||
{
|
||||
}
|
||||
|
||||
EVP_MD const* RsaSignature::HMAC_SHA256::GetGenerator() const
|
||||
{
|
||||
return HmacSha256Md;
|
||||
}
|
||||
|
||||
void RsaSignature::HMAC_SHA256::PostInitCustomizeContext(EVP_MD_CTX* ctx)
|
||||
{
|
||||
HMAC_SHA256_MD::CTX_DATA* ctxData = reinterpret_cast<HMAC_SHA256_MD::CTX_DATA*>(EVP_MD_CTX_md_data(ctx));
|
||||
if (ctxData->hmac)
|
||||
delete ctxData->hmac;
|
||||
|
||||
ctxData->hmac = new Crypto::HMAC_SHA256(_key, _keyLength);
|
||||
}
|
||||
|
||||
RsaSignature::RsaSignature()
|
||||
{
|
||||
_ctx = Impl::GenericHashImpl::MakeCTX();
|
||||
}
|
||||
|
||||
RsaSignature::RsaSignature(RsaSignature&& rsa) noexcept
|
||||
{
|
||||
_ctx = rsa._ctx;
|
||||
rsa._ctx = Impl::GenericHashImpl::MakeCTX();
|
||||
}
|
||||
|
||||
RsaSignature::~RsaSignature()
|
||||
{
|
||||
EVP_MD_CTX_free(_ctx);
|
||||
}
|
||||
|
||||
bool RsaSignature::LoadKeyFromFile(std::string const& fileName)
|
||||
{
|
||||
std::unique_ptr<BIO, BIODeleter> keyBIO(BIO_new_file(fileName.c_str(), "r"));
|
||||
if (!keyBIO)
|
||||
return false;
|
||||
|
||||
if (!get_PEM_read<KeyTag>()(keyBIO.get(), &_rsa, nullptr, nullptr))
|
||||
_key = EVP_PKEY_new();
|
||||
if (!PEM_read_bio_PrivateKey(keyBIO.get(), &_key, nullptr, nullptr))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename KeyTag>
|
||||
bool Trinity::Crypto::RSA::LoadFromString(std::string const& keyPem, KeyTag)
|
||||
bool RsaSignature::LoadKeyFromString(std::string const& keyPem)
|
||||
{
|
||||
std::unique_ptr<BIO, BIODeleter> keyBIO(BIO_new_mem_buf(
|
||||
const_cast<char*>(keyPem.c_str()) /*api hack - this function assumes memory is readonly but lacks const modifier*/,
|
||||
@@ -84,51 +203,26 @@ bool Trinity::Crypto::RSA::LoadFromString(std::string const& keyPem, KeyTag)
|
||||
if (!keyBIO)
|
||||
return false;
|
||||
|
||||
if (!get_PEM_read<KeyTag>()(keyBIO.get(), &_rsa, nullptr, nullptr))
|
||||
_key = EVP_PKEY_new();
|
||||
if (!PEM_read_bio_PrivateKey(keyBIO.get(), &_key, nullptr, nullptr))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BigNumber Trinity::Crypto::RSA::GetModulus() const
|
||||
bool RsaSignature::Sign(uint8 const* message, std::size_t messageLength, DigestGenerator& generator, std::vector<uint8>& output)
|
||||
{
|
||||
BigNumber bn;
|
||||
#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
const BIGNUM* rsa_n;
|
||||
RSA_get0_key(_rsa, &rsa_n, nullptr, nullptr);
|
||||
BN_copy(bn.BN(), rsa_n);
|
||||
#else
|
||||
BN_copy(bn.BN(), _rsa->n);
|
||||
#endif
|
||||
return bn;
|
||||
}
|
||||
size_t signatureLength = 0;
|
||||
EVP_DigestSignInit(_ctx, nullptr, generator.GetGenerator(), nullptr, _key);
|
||||
generator.PostInitCustomizeContext(_ctx);
|
||||
EVP_DigestSignUpdate(_ctx, message, messageLength);
|
||||
int result = EVP_DigestSignFinal(_ctx, nullptr, &signatureLength);
|
||||
if (result == 0)
|
||||
return false;
|
||||
|
||||
template <typename KeyTag>
|
||||
bool Trinity::Crypto::RSA::Encrypt(uint8 const* data, std::size_t dataLength, uint8* output, int32 paddingType)
|
||||
{
|
||||
std::vector<uint8> inputData(std::make_reverse_iterator(data + dataLength), std::make_reverse_iterator(data));
|
||||
int result = get_RSA_encrypt<KeyTag>()(inputData.size(), inputData.data(), output, _rsa, paddingType);
|
||||
std::reverse(output, output + GetOutputSize());
|
||||
return result != -1;
|
||||
}
|
||||
|
||||
bool Trinity::Crypto::RSA::Sign(int32 hashType, uint8 const* dataHash, std::size_t dataHashLength, uint8* output)
|
||||
{
|
||||
uint32 signatureLength = 0;
|
||||
int result = RSA_sign(hashType, dataHash, dataHashLength, output, &signatureLength, _rsa);
|
||||
std::reverse(output, output + GetOutputSize());
|
||||
output.resize(signatureLength);
|
||||
result = EVP_DigestSignFinal(_ctx, output.data(), &signatureLength);
|
||||
std::reverse(output.begin(), output.end());
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
namespace Trinity
|
||||
{
|
||||
namespace Crypto
|
||||
{
|
||||
template TC_COMMON_API bool RSA::LoadFromFile(std::string const& fileName, RSA::PublicKey);
|
||||
template TC_COMMON_API bool RSA::LoadFromFile(std::string const& fileName, RSA::PrivateKey);
|
||||
template TC_COMMON_API bool RSA::LoadFromString(std::string const& keyPem, RSA::PublicKey);
|
||||
template TC_COMMON_API bool RSA::LoadFromString(std::string const& keyPem, RSA::PrivateKey);
|
||||
template TC_COMMON_API bool RSA::Encrypt<RSA::PublicKey>(uint8 const* data, std::size_t dataLength, uint8* output, int32 paddingType);
|
||||
template TC_COMMON_API bool RSA::Encrypt<RSA::PrivateKey>(uint8 const* data, std::size_t dataLength, uint8* output, int32 paddingType);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user