Merge branch 'master' into Playerbot

# Conflicts:
#	src/server/game/Server/WorldSession.cpp
#	src/server/game/Server/WorldSession.h
This commit is contained in:
郑佩茹
2022-12-07 14:46:21 -07:00
196 changed files with 9181 additions and 1080 deletions

View File

@@ -1,6 +1,18 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AES.h"
@@ -30,34 +42,24 @@ bool Acore::Crypto::AES::Process(IV const& iv, uint8* data, size_t length, Tag&
ASSERT(length <= static_cast<size_t>(std::numeric_limits<int>::max()));
int len = static_cast<int>(length);
if (!EVP_CipherInit_ex(_ctx, nullptr, nullptr, nullptr, iv.data(), -1))
{
return false;
}
int outLen;
if (!EVP_CipherUpdate(_ctx, data, &outLen, data, len))
{
return false;
}
len -= outLen;
if (!_encrypting && !EVP_CIPHER_CTX_ctrl(_ctx, EVP_CTRL_GCM_SET_TAG, sizeof(tag), tag))
{
return false;
}
if (!EVP_CipherFinal_ex(_ctx, data + outLen, &outLen))
{
return false;
}
ASSERT(len == outLen);
if (_encrypting && !EVP_CIPHER_CTX_ctrl(_ctx, EVP_CTRL_GCM_GET_TAG, sizeof(tag), tag))
{
return false;
}
return true;
}

View File

@@ -1,10 +1,22 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef Warhead_AES_h__
#define Warhead_AES_h__
#ifndef Azeroth_AES_h__
#define Azeroth_AES_h__
#include "Define.h"
#include <array>
@@ -36,4 +48,4 @@ namespace Acore::Crypto
};
}
#endif // Warhead_AES_h__
#endif // Azeroth_AES_h__

View File

@@ -18,17 +18,26 @@
#include "ARC4.h"
#include "Errors.h"
Acore::Crypto::ARC4::ARC4()
: _ctx(EVP_CIPHER_CTX_new())
Acore::Crypto::ARC4::ARC4() : _ctx(EVP_CIPHER_CTX_new())
{
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
_cipher = EVP_CIPHER_fetch(nullptr, "RC4", nullptr);
#else
EVP_CIPHER const* _cipher = EVP_rc4();
#endif
EVP_CIPHER_CTX_init(_ctx);
int result = EVP_EncryptInit_ex(_ctx, EVP_rc4(), nullptr, nullptr, nullptr);
int result = EVP_EncryptInit_ex(_ctx, _cipher, nullptr, nullptr, nullptr);
ASSERT(result == 1);
}
Acore::Crypto::ARC4::~ARC4()
{
EVP_CIPHER_CTX_free(_ctx);
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
EVP_CIPHER_free(_cipher);
#endif
}
void Acore::Crypto::ARC4::Init(uint8 const* seed, size_t len)

View File

@@ -24,20 +24,25 @@
namespace Acore::Crypto
{
class ARC4
class AC_COMMON_API ARC4
{
public:
ARC4();
~ARC4();
void Init(uint8 const* seed, size_t len);
template <typename Container>
void Init(Container const& c) { Init(std::data(c), std::size(c)); }
void UpdateData(uint8* data, size_t len);
template <typename Container>
void UpdateData(Container& c) { UpdateData(std::data(c), std::size(c)); }
private:
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
EVP_CIPHER* _cipher;
#endif
EVP_CIPHER_CTX* _ctx;
};
}

View File

@@ -1,6 +1,18 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Argon2.h"
@@ -20,9 +32,7 @@
);
if (status == ARGON2_OK)
{
return std::string(buf);
}
return {};
}

View File

@@ -1,15 +1,25 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef WARHEAD_ARGON2_H
#define WARHEAD_ARGON2_H
#ifndef AC_ARGON2_H
#define AC_ARGON2_H
#include "BigNumber.h"
#include "Define.h"
#include "Optional.h"
#include <string>
namespace Acore::Crypto
{

View File

@@ -19,19 +19,16 @@
#include "Errors.h"
#include "HMAC.h"
AuthCrypt::AuthCrypt() : _initialized(false)
{
}
void AuthCrypt::Init(SessionKey const& K)
{
uint8 ServerEncryptionKey[] = { 0xCC, 0x98, 0xAE, 0x04, 0xE8, 0x97, 0xEA, 0xCA, 0x12, 0xDD, 0xC0, 0x93, 0x42, 0x91, 0x53, 0x57 };
_serverEncrypt.Init(Acore::Crypto::HMAC_SHA1::GetDigestOf(ServerEncryptionKey, K));
uint8 ServerDecryptionKey[] = { 0xC2, 0xB3, 0x72, 0x3C, 0xC6, 0xAE, 0xD9, 0xB5, 0x34, 0x3C, 0x53, 0xEE, 0x2F, 0x43, 0x67, 0xCE };
_clientDecrypt.Init(Acore::Crypto::HMAC_SHA1::GetDigestOf(ServerDecryptionKey, K));
// Drop first 1024 bytes, as WoW uses ARC4-drop1024.
std::array<uint8, 1024> syncBuf;
std::array<uint8, 1024> syncBuf{};
_serverEncrypt.UpdateData(syncBuf);
_clientDecrypt.UpdateData(syncBuf);

View File

@@ -20,12 +20,11 @@
#include "ARC4.h"
#include "AuthDefines.h"
#include <array>
class AuthCrypt
class AC_COMMON_API AuthCrypt
{
public:
AuthCrypt();
AuthCrypt() = default;
void Init(SessionKey const& K);
void DecryptRecv(uint8* data, size_t len);
@@ -36,6 +35,6 @@ public:
private:
Acore::Crypto::ARC4 _clientDecrypt;
Acore::Crypto::ARC4 _serverEncrypt;
bool _initialized;
bool _initialized{ false };
};
#endif

View File

@@ -50,8 +50,8 @@ using SRP6 = Acore::Crypto::SRP6;
/*static*/ SessionKey SRP6::SHA1Interleave(SRP6::EphemeralKey const& S)
{
// split S into two buffers
std::array<uint8, EPHEMERAL_KEY_LENGTH/2> buf0, buf1;
for (size_t i = 0; i < EPHEMERAL_KEY_LENGTH/2; ++i)
std::array<uint8, EPHEMERAL_KEY_LENGTH / 2> buf0{}, buf1{};
for (size_t i = 0; i < EPHEMERAL_KEY_LENGTH / 2; ++i)
{
buf0[i] = S[2 * i + 0];
buf1[i] = S[2 * i + 1];
@@ -59,8 +59,12 @@ using SRP6 = Acore::Crypto::SRP6;
// find position of first nonzero byte
size_t p = 0;
while (p < EPHEMERAL_KEY_LENGTH && !S[p]) { ++p; }
if (p & 1) { ++p; } // skip one extra byte if p is odd
while (p < EPHEMERAL_KEY_LENGTH && !S[p])
++p;
if (p & 1)
++p; // skip one extra byte if p is odd
p /= 2; // offset into buffers
// hash each of the halves, starting at the first nonzero byte
@@ -82,14 +86,12 @@ SRP6::SRP6(std::string const& username, Salt const& salt, Verifier const& verifi
std::optional<SessionKey> SRP6::VerifyChallengeResponse(EphemeralKey const& A, SHA1::Digest const& clientM)
{
ASSERT(!_used);
ASSERT(!_used, "A single SRP6 object must only ever be used to verify ONCE!");
_used = true;
BigNumber const _A(A);
if ((_A % _N).IsZero())
{
return std::nullopt;
}
BigNumber const u(SHA1::GetDigestOf(A, B));
EphemeralKey const S = (_A * (_v.ModExp(u, _N))).ModExp(_b, N).ToByteArray<32>();
@@ -104,11 +106,7 @@ std::optional<SessionKey> SRP6::VerifyChallengeResponse(EphemeralKey const& A, S
SHA1::Digest const ourM = SHA1::GetDigestOf(NgHash, _I, s, A, B, K);
if (ourM == clientM)
{
return K;
}
else
{
return std::nullopt;
}
return std::nullopt;
}

View File

@@ -20,21 +20,20 @@
#include "AuthDefines.h"
#include "BigNumber.h"
#include "Common.h"
#include "CryptoHash.h"
#include "Define.h"
#include <array>
#include <optional>
namespace Acore::Crypto
{
class SRP6
class AC_COMMON_API SRP6
{
public:
static constexpr size_t SALT_LENGTH = 32;
using Salt = std::array<uint8, SALT_LENGTH>;
static constexpr size_t VERIFIER_LENGTH = 32;
using Verifier = std::array<uint8, VERIFIER_LENGTH>;
static constexpr size_t EPHEMERAL_KEY_LENGTH = 32;
using EphemeralKey = std::array<uint8, EPHEMERAL_KEY_LENGTH>;
@@ -43,6 +42,7 @@ namespace Acore::Crypto
// username + password must be passed through Utf8ToUpperOnlyLatin FIRST!
static std::pair<Salt, Verifier> MakeRegistrationData(std::string const& username, std::string const& password);
// username + password must be passed through Utf8ToUpperOnlyLatin FIRST!
static bool CheckLogin(std::string const& username, std::string const& password, Salt const& salt, Verifier const& verifier)
{

View File

@@ -1,11 +1,25 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Cryptography/BigNumber.h"
#include "BigNumber.h"
#include "Errors.h"
#include <algorithm>
#include <cstring>
#include <memory>
#include <openssl/bn.h>
BigNumber::BigNumber()
@@ -25,9 +39,7 @@ void BigNumber::SetDword(int32 val)
{
SetDword(uint32(std::abs(val)));
if (val < 0)
{
BN_set_negative(_bn, 1);
}
}
void BigNumber::SetDword(uint32 val)
@@ -50,9 +62,7 @@ void BigNumber::SetBinary(uint8 const* bytes, int32 len, bool littleEndian)
uint8* array = new uint8[len];
for (int i = 0; i < len; i++)
{
array[i] = bytes[len - 1 - i];
}
BN_bin2bn(array, len, _bn);
@@ -62,9 +72,7 @@ void BigNumber::SetBinary(uint8 const* bytes, int32 len, bool littleEndian)
#endif
}
else
{
BN_bin2bn(bytes, len, _bn);
}
}
bool BigNumber::SetHexStr(char const* str)
@@ -81,9 +89,7 @@ void BigNumber::SetRand(int32 numbits)
BigNumber& BigNumber::operator=(BigNumber const& bn)
{
if (this == &bn)
{
return *this;
}
BN_copy(_bn, bn._bn);
return *this;
@@ -103,7 +109,7 @@ BigNumber& BigNumber::operator-=(BigNumber const& bn)
BigNumber& BigNumber::operator*=(BigNumber const& bn)
{
BN_CTX* bnctx;
BN_CTX *bnctx;
bnctx = BN_CTX_new();
BN_mul(_bn, _bn, bn._bn, bnctx);
@@ -114,7 +120,7 @@ BigNumber& BigNumber::operator*=(BigNumber const& bn)
BigNumber& BigNumber::operator/=(BigNumber const& bn)
{
BN_CTX* bnctx;
BN_CTX *bnctx;
bnctx = BN_CTX_new();
BN_div(_bn, nullptr, _bn, bn._bn, bnctx);
@@ -125,7 +131,7 @@ BigNumber& BigNumber::operator/=(BigNumber const& bn)
BigNumber& BigNumber::operator%=(BigNumber const& bn)
{
BN_CTX* bnctx;
BN_CTX *bnctx;
bnctx = BN_CTX_new();
BN_mod(_bn, _bn, bn._bn, bnctx);
@@ -148,7 +154,7 @@ int BigNumber::CompareTo(BigNumber const& bn) const
BigNumber BigNumber::Exp(BigNumber const& bn) const
{
BigNumber ret;
BN_CTX* bnctx;
BN_CTX *bnctx;
bnctx = BN_CTX_new();
BN_exp(ret._bn, _bn, bn._bn, bnctx);
@@ -160,7 +166,7 @@ BigNumber BigNumber::Exp(BigNumber const& bn) const
BigNumber BigNumber::ModExp(BigNumber const& bn1, BigNumber const& bn2) const
{
BigNumber ret;
BN_CTX* bnctx;
BN_CTX *bnctx;
bnctx = BN_CTX_new();
BN_mod_exp(ret._bn, _bn, bn1._bn, bn2._bn, bnctx);
@@ -201,17 +207,13 @@ void BigNumber::GetBytes(uint8* buf, size_t bufsize, bool littleEndian) const
// If we need more bytes than length of BigNumber set the rest to 0
if (numBytes < bufsize)
{
memset((void*)buf, 0, bufsize);
}
BN_bn2bin(_bn, buf + (bufsize - numBytes));
// openssl's BN stores data internally in big endian format, reverse if little endian desired
if (littleEndian)
{
std::reverse(buf, buf + bufsize);
}
#else
int res = littleEndian ? BN_bn2lebinpad(_bn, buf, bufsize) : BN_bn2binpad(_bn, buf, bufsize);
ASSERT(res > 0, "Buffer of size {} is too small to hold bignum with {} bytes.\n", bufsize, BN_num_bytes(_bn));

View File

@@ -1,6 +1,18 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _AUTH_BIGNUMBER_H
@@ -8,7 +20,6 @@
#include "Define.h"
#include <array>
#include <memory>
#include <string>
#include <vector>

View File

@@ -24,7 +24,8 @@ namespace Acore::Crypto
{
struct Constants
{
static constexpr size_t SHA1_DIGEST_LENGTH_BYTES = 20;
static constexpr size_t MD5_DIGEST_LENGTH_BYTES = 16;
static constexpr size_t SHA1_DIGEST_LENGTH_BYTES = 20;
static constexpr size_t SHA256_DIGEST_LENGTH_BYTES = 32;
};
}

View File

@@ -89,6 +89,7 @@ namespace Acore::Crypto
{
using IV = typename Cipher::IV;
using Tag = typename Cipher::Tag;
// extract trailing IV and tag
IV iv;
Tag tag;

View File

@@ -19,12 +19,12 @@
#define AZEROTHCORE_CRYPTOHASH_H
#include "CryptoConstants.h"
#include "Define.h"
#include "Errors.h"
#include <array>
#include <openssl/evp.h>
#include <string>
#include <string_view>
#include <utility>
class BigNumber;
@@ -35,10 +35,10 @@ namespace Acore::Impl
typedef EVP_MD const* (*HashCreator)();
#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100000L
static EVP_MD_CTX* MakeCTX() { return EVP_MD_CTX_create(); }
static EVP_MD_CTX* MakeCTX() noexcept { return EVP_MD_CTX_create(); }
static void DestroyCTX(EVP_MD_CTX* ctx) { EVP_MD_CTX_destroy(ctx); }
#else
static EVP_MD_CTX* MakeCTX() { return EVP_MD_CTX_new(); }
static EVP_MD_CTX* MakeCTX() noexcept { return EVP_MD_CTX_new(); }
static void DestroyCTX(EVP_MD_CTX* ctx) { EVP_MD_CTX_free(ctx); }
#endif
};
@@ -46,74 +46,104 @@ namespace Acore::Impl
template <GenericHashImpl::HashCreator HashCreator, size_t DigestLength>
class GenericHash
{
public:
static constexpr size_t DIGEST_LENGTH = DigestLength;
using Digest = std::array<uint8, DIGEST_LENGTH>;
public:
static constexpr size_t DIGEST_LENGTH = DigestLength;
using Digest = std::array<uint8, DIGEST_LENGTH>;
static Digest GetDigestOf(uint8 const* data, size_t len)
{
GenericHash hash;
hash.UpdateData(data, len);
hash.Finalize();
return hash.GetDigest();
}
template <typename... Ts>
static auto GetDigestOf(Ts&& ... pack) -> std::enable_if_t < !(std::is_integral_v<std::decay_t<Ts>> || ...), Digest >
{
GenericHash hash;
(hash.UpdateData(std::forward<Ts>(pack)), ...);
hash.Finalize();
return hash.GetDigest();
}
GenericHash() : _ctx(GenericHashImpl::MakeCTX())
{
int result = EVP_DigestInit_ex(_ctx, HashCreator(), nullptr);
ASSERT(result == 1);
}
~GenericHash()
{
if (!_ctx)
static Digest GetDigestOf(uint8 const* data, size_t len)
{
return;
GenericHash hash;
hash.UpdateData(data, len);
hash.Finalize();
return hash.GetDigest();
}
GenericHashImpl::DestroyCTX(_ctx);
_ctx = nullptr;
}
void UpdateData(uint8 const* data, size_t len)
{
int result = EVP_DigestUpdate(_ctx, data, len);
ASSERT(result == 1);
}
void UpdateData(std::string_view str) { UpdateData(reinterpret_cast<uint8 const*>(str.data()), str.size()); }
void UpdateData(std::string const& str) { UpdateData(std::string_view(str)); } /* explicit overload to avoid using the container template */
void UpdateData(char const* str) { UpdateData(std::string_view(str)); } /* explicit overload to avoid using the container template */
template <typename Container>
void UpdateData(Container const& c) { UpdateData(std::data(c), std::size(c)); }
template <typename... Ts>
static auto GetDigestOf(Ts&&... pack) -> std::enable_if_t<!(std::is_integral_v<std::decay_t<Ts>> || ...), Digest>
{
GenericHash hash;
(hash.UpdateData(std::forward<Ts>(pack)), ...);
hash.Finalize();
return hash.GetDigest();
}
void Finalize()
{
uint32 length;
int result = EVP_DigestFinal_ex(_ctx, _digest.data(), &length);
ASSERT(result == 1);
ASSERT(length == DIGEST_LENGTH);
GenericHashImpl::DestroyCTX(_ctx);
_ctx = nullptr;
}
GenericHash() : _ctx(GenericHashImpl::MakeCTX())
{
int result = EVP_DigestInit_ex(_ctx, HashCreator(), nullptr);
ASSERT(result == 1);
}
Digest const& GetDigest() const { return _digest; }
GenericHash(GenericHash const& right) : _ctx(GenericHashImpl::MakeCTX())
{
*this = right;
}
private:
EVP_MD_CTX* _ctx;
Digest _digest = { };
GenericHash(GenericHash&& right) noexcept
{
*this = std::move(right);
}
~GenericHash()
{
if (!_ctx)
return;
GenericHashImpl::DestroyCTX(_ctx);
_ctx = nullptr;
}
GenericHash& operator=(GenericHash const& right)
{
if (this == &right)
return *this;
int result = EVP_MD_CTX_copy_ex(_ctx, right._ctx);
ASSERT(result == 1);
_digest = right._digest;
return *this;
}
GenericHash& operator=(GenericHash&& right) noexcept
{
if (this == &right)
return *this;
_ctx = std::exchange(right._ctx, GenericHashImpl::MakeCTX());
_digest = std::exchange(right._digest, Digest{});
return *this;
}
void UpdateData(uint8 const* data, size_t len)
{
int result = EVP_DigestUpdate(_ctx, data, len);
ASSERT(result == 1);
}
void UpdateData(std::string_view str) { UpdateData(reinterpret_cast<uint8 const*>(str.data()), str.size()); }
void UpdateData(std::string const& str) { UpdateData(std::string_view(str)); } /* explicit overload to avoid using the container template */
void UpdateData(char const* str) { UpdateData(std::string_view(str)); } /* explicit overload to avoid using the container template */
template <typename Container>
void UpdateData(Container const& c) { UpdateData(std::data(c), std::size(c)); }
void Finalize()
{
uint32 length;
int result = EVP_DigestFinal_ex(_ctx, _digest.data(), &length);
ASSERT(result == 1);
ASSERT(length == DIGEST_LENGTH);
}
Digest const& GetDigest() const { return _digest; }
private:
EVP_MD_CTX* _ctx{};
Digest _digest{};
};
}
namespace Acore::Crypto
{
using MD5 = Acore::Impl::GenericHash<EVP_md5, Constants::MD5_DIGEST_LENGTH_BYTES>;
using SHA1 = Acore::Impl::GenericHash<EVP_sha1, Constants::SHA1_DIGEST_LENGTH_BYTES>;
using SHA256 = Acore::Impl::GenericHash<EVP_sha256, Constants::SHA256_DIGEST_LENGTH_BYTES>;
}

View File

@@ -22,5 +22,5 @@
void Acore::Crypto::GetRandomBytes(uint8* buf, size_t len)
{
int result = RAND_bytes(buf, len);
ASSERT(result == 1);
ASSERT(result == 1, "Not enough randomness in OpenSSL's entropy pool. What in the world are you running on?");
}

View File

@@ -23,7 +23,7 @@
namespace Acore::Crypto
{
void GetRandomBytes(uint8* buf, size_t len);
AC_COMMON_API void GetRandomBytes(uint8* buf, size_t len);
template <typename Container>
void GetRandomBytes(Container& c)

View File

@@ -19,10 +19,9 @@
#define AZEROTHCORE_HMAC_H
#include "CryptoConstants.h"
#include "Define.h"
#include "CryptoHash.h"
#include "Errors.h"
#include <array>
#include <openssl/hmac.h>
#include <string>
#include <string_view>
@@ -30,96 +29,108 @@ class BigNumber;
namespace Acore::Impl
{
struct HMACImpl
{
typedef EVP_MD const* (*HashCreator)();
#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100000L
static HMAC_CTX* MakeCTX()
{
HMAC_CTX* ctx = new HMAC_CTX();
HMAC_CTX_init(ctx);
return ctx;
}
static void DestroyCTX(HMAC_CTX* ctx)
{
HMAC_CTX_cleanup(ctx);
delete ctx;
}
#else
static HMAC_CTX* MakeCTX() { return HMAC_CTX_new(); }
static void DestroyCTX(HMAC_CTX* ctx) { HMAC_CTX_free(ctx); }
#endif
};
template <HMACImpl::HashCreator HashCreator, size_t DigestLength>
template <GenericHashImpl::HashCreator HashCreator, size_t DigestLength>
class GenericHMAC
{
public:
static constexpr size_t DIGEST_LENGTH = DigestLength;
using Digest = std::array<uint8, DIGEST_LENGTH>;
public:
static constexpr size_t DIGEST_LENGTH = DigestLength;
using Digest = std::array<uint8, DIGEST_LENGTH>;
template <typename Container>
static Digest GetDigestOf(Container const& seed, uint8 const* data, size_t len)
{
GenericHMAC hash(seed);
hash.UpdateData(data, len);
hash.Finalize();
return hash.GetDigest();
}
template <typename Container, typename... Ts>
static auto GetDigestOf(Container const& seed, Ts&& ... pack) -> std::enable_if_t < !(std::is_integral_v<std::decay_t<Ts>> || ...), Digest >
{
GenericHMAC hash(seed);
(hash.UpdateData(std::forward<Ts>(pack)), ...);
hash.Finalize();
return hash.GetDigest();
}
GenericHMAC(uint8 const* seed, size_t len) : _ctx(HMACImpl::MakeCTX())
{
int result = HMAC_Init_ex(_ctx, seed, len, HashCreator(), nullptr);
ASSERT(result == 1);
}
template <typename Container> GenericHMAC(Container const& container) : GenericHMAC(std::data(container), std::size(container)) {}
~GenericHMAC()
{
if (!_ctx)
template <typename Container>
static Digest GetDigestOf(Container const& seed, uint8 const* data, size_t len)
{
return;
GenericHMAC hash(seed);
hash.UpdateData(data, len);
hash.Finalize();
return hash.GetDigest();
}
HMACImpl::DestroyCTX(_ctx);
_ctx = nullptr;
}
void UpdateData(uint8 const* data, size_t len)
{
int result = HMAC_Update(_ctx, data, len);
ASSERT(result == 1);
}
void UpdateData(std::string_view str) { UpdateData(reinterpret_cast<uint8 const*>(str.data()), str.size()); }
void UpdateData(std::string const& str) { UpdateData(std::string_view(str)); } /* explicit overload to avoid using the container template */
void UpdateData(char const* str) { UpdateData(std::string_view(str)); } /* explicit overload to avoid using the container template */
template <typename Container>
void UpdateData(Container const& c) { UpdateData(std::data(c), std::size(c)); }
template <typename Container, typename... Ts>
static auto GetDigestOf(Container const& seed, Ts&&... pack) -> std::enable_if_t<!(std::is_integral_v<std::decay_t<Ts>> || ...), Digest>
{
GenericHMAC hash(seed);
(hash.UpdateData(std::forward<Ts>(pack)), ...);
hash.Finalize();
return hash.GetDigest();
}
void Finalize()
{
uint32 length = 0;
int result = HMAC_Final(_ctx, _digest.data(), &length);
ASSERT(result == 1);
ASSERT(length == DIGEST_LENGTH);
HMACImpl::DestroyCTX(_ctx);
_ctx = nullptr;
}
GenericHMAC(uint8 const* seed, size_t len) : _ctx(GenericHashImpl::MakeCTX()), _key(EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, nullptr, seed, len))
{
int result = EVP_DigestSignInit(_ctx, nullptr, HashCreator(), nullptr, _key);
ASSERT(result == 1);
}
Digest const& GetDigest() const { return _digest; }
private:
HMAC_CTX* _ctx;
Digest _digest = { };
template <typename Container>
GenericHMAC(Container const& container) : GenericHMAC(std::data(container), std::size(container)) {}
GenericHMAC(GenericHMAC const& right) : _ctx(GenericHashImpl::MakeCTX())
{
*this = right;
}
GenericHMAC(GenericHMAC&& right) noexcept
{
*this = std::move(right);
}
~GenericHMAC()
{
GenericHashImpl::DestroyCTX(_ctx);
_ctx = nullptr;
EVP_PKEY_free(_key);
_key = nullptr;
}
GenericHMAC& operator=(GenericHMAC const& right)
{
if (this == &right)
return *this;
int result = EVP_MD_CTX_copy_ex(_ctx, right._ctx);
ASSERT(result == 1);
_key = right._key; // EVP_PKEY uses reference counting internally, just copy the pointer
EVP_PKEY_up_ref(_key); // Bump reference count for PKEY, as every instance of this class holds two references to PKEY and destructor decrements it twice
_digest = right._digest;
return *this;
}
GenericHMAC& operator=(GenericHMAC&& right) noexcept
{
if (this == &right)
return *this;
_ctx = std::exchange(right._ctx, GenericHashImpl::MakeCTX());
_key = std::exchange(right._key, EVP_PKEY_new());
_digest = std::exchange(right._digest, Digest{});
return *this;
}
void UpdateData(uint8 const* data, size_t len)
{
int result = EVP_DigestSignUpdate(_ctx, data, len);
ASSERT(result == 1);
}
void UpdateData(std::string_view str) { UpdateData(reinterpret_cast<uint8 const*>(str.data()), str.size()); }
void UpdateData(std::string const& str) { UpdateData(std::string_view(str)); } /* explicit overload to avoid using the container template */
void UpdateData(char const* str) { UpdateData(std::string_view(str)); } /* explicit overload to avoid using the container template */
template <typename Container>
void UpdateData(Container const& c) { UpdateData(std::data(c), std::size(c)); }
void Finalize()
{
size_t length = DIGEST_LENGTH;
int result = EVP_DigestSignFinal(_ctx, _digest.data(), &length);
ASSERT(result == 1);
ASSERT(length == DIGEST_LENGTH);
}
Digest const& GetDigest() const { return _digest; }
private:
EVP_MD_CTX* _ctx{};
EVP_PKEY* _key{};
Digest _digest{};
};
}
@@ -128,4 +139,5 @@ namespace Acore::Crypto
using HMAC_SHA1 = Acore::Impl::GenericHMAC<EVP_sha1, Constants::SHA1_DIGEST_LENGTH_BYTES>;
using HMAC_SHA256 = Acore::Impl::GenericHMAC<EVP_sha256, Constants::SHA256_DIGEST_LENGTH_BYTES>;
}
#endif

View File

@@ -15,49 +15,90 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <OpenSSLCrypto.h>
#include "OpenSSLCrypto.h"
#include <openssl/crypto.h>
#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x1010000fL
#include <vector>
#include <thread>
#include <mutex>
std::vector<std::mutex*> cryptoLocks;
static void lockingCallback(int mode, int type, char const* /*file*/, int /*line*/)
{
if (mode & CRYPTO_LOCK)
{
cryptoLocks[type]->lock();
}
else
{
cryptoLocks[type]->unlock();
}
}
static void threadIdCallback(CRYPTO_THREADID* id)
static void threadIdCallback(CRYPTO_THREADID * id)
{
(void)id;
CRYPTO_THREADID_set_numeric(id, std::hash<std::thread::id>()(std::this_thread::get_id()));
}
#elif OPENSSL_VERSION_NUMBER >= 0x30000000L
#include <openssl/provider.h>
OSSL_PROVIDER* LegacyProvider;
OSSL_PROVIDER* DefaultProvider;
#endif
#if OPENSSL_VERSION_NUMBER >= 0x30000000L && AC_PLATFORM == AC_PLATFORM_WINDOWS
#include <boost/dll/runtime_symbol_info.hpp>
#include <filesystem>
void SetupLibrariesForWindows()
{
namespace fs = std::filesystem;
fs::path programLocation{ boost::dll::program_location().remove_filename().string() };
fs::path libLegacy{ boost::dll::program_location().remove_filename().string() + "/legacy.dll" };
ASSERT(fs::exists(libLegacy), "Not found 'legacy.dll'. Please copy library 'legacy.dll' from OpenSSL default dir to '{}'", programLocation.generic_string());
OSSL_PROVIDER_set_default_search_path(nullptr, programLocation.generic_string().c_str());
}
#endif
void OpenSSLCrypto::threadsSetup()
{
#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x1010000fL
cryptoLocks.resize(CRYPTO_num_locks());
for (int i = 0 ; i < CRYPTO_num_locks(); ++i)
{
cryptoLocks[i] = new std::mutex();
}
(void)&threadIdCallback;
CRYPTO_THREADID_set_callback(threadIdCallback);
(void)&lockingCallback;
CRYPTO_set_locking_callback(lockingCallback);
#elif OPENSSL_VERSION_NUMBER >= 0x30000000L
#if AC_PLATFORM == AC_PLATFORM_WINDOWS
SetupLibrariesForWindows();
#endif
LegacyProvider = OSSL_PROVIDER_load(nullptr, "legacy");
DefaultProvider = OSSL_PROVIDER_load(nullptr, "default");
#endif
}
void OpenSSLCrypto::threadsCleanup()
{
#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x1010000fL
CRYPTO_set_locking_callback(nullptr);
CRYPTO_THREADID_set_callback(nullptr);
for (int i = 0 ; i < CRYPTO_num_locks(); ++i)
{
delete cryptoLocks[i];
}
cryptoLocks.resize(0);
}
#elif OPENSSL_VERSION_NUMBER >= 0x30000000L
OSSL_PROVIDER_unload(LegacyProvider);
OSSL_PROVIDER_unload(DefaultProvider);
OSSL_PROVIDER_set_default_search_path(nullptr, nullptr);
#endif
}

View File

@@ -15,11 +15,10 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OPENSSL_CRYPTO_H
#define OPENSSL_CRYPTO_H
#ifndef _AC_OPENSSL_CRYPTO_H
#define _AC_OPENSSL_CRYPTO_H
#include "Define.h"
#include <openssl/opensslv.h>
/**
* A group of functions which setup openssl crypto module to work properly in multithreaded enviroment
@@ -27,17 +26,11 @@
*/
namespace OpenSSLCrypto
{
#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x1010000fL
/// Needs to be called before threads using openssl are spawned
void threadsSetup();
/// Needs to be called after threads using openssl are despawned
void threadsCleanup();
#else
void threadsSetup() { };
void threadsCleanup() { };
#endif
AC_COMMON_API void threadsSetup();
/// Needs to be called after threads using openssl are despawned
AC_COMMON_API void threadsCleanup();
}
#endif

View File

@@ -15,17 +15,18 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CryptoHash.h"
#include <cstring>
#ifndef AZEROTHCORE_SESSIONKEYGENERATOR_HPP
#define AZEROTHCORE_SESSIONKEYGENERATOR_HPP
#include "CryptoHash.h"
#include <cstring>
template <typename Hash>
class SessionKeyGenerator
{
public:
template <typename C> SessionKeyGenerator(C const& buf) :
template <typename C>
SessionKeyGenerator(C const& buf) :
o0it(o0.begin())
{
uint8 const* data = std::data(buf);
@@ -52,9 +53,9 @@ public:
}
private:
typename Hash::Digest o0 = { };
typename Hash::Digest o1 = { };
typename Hash::Digest o2 = { };
typename Hash::Digest o0{};
typename Hash::Digest o1{};
typename Hash::Digest o2{};
typename Hash::Digest::const_iterator o0it;
};

View File

@@ -28,11 +28,8 @@ static constexpr uint32 HMAC_RESULT_SIZE = 20;
{
timestamp /= TOTP_INTERVAL;
unsigned char challenge[8];
for (int i = 8; i--; timestamp >>= 8)
{
challenge[i] = timestamp;
}
unsigned char digest[HMAC_RESULT_SIZE];
uint32 digestSize = HMAC_RESULT_SIZE;
@@ -41,7 +38,6 @@ static constexpr uint32 HMAC_RESULT_SIZE = 20;
uint32 offset = digest[19] & 0xF;
uint32 truncated = (digest[offset] << 24) | (digest[offset + 1] << 16) | (digest[offset + 2] << 8) | (digest[offset + 3]);
truncated &= 0x7FFFFFFF;
return (truncated % 1000000);
}

View File

@@ -1,6 +1,18 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef WARHEAD_TOTP_H

View File

@@ -34,7 +34,7 @@ private:
std::atomic<bool> _shutdown;
public:
ProducerConsumerQueue<T>() : _shutdown(false) { }
ProducerConsumerQueue() : _shutdown(false) { }
void Push(const T& value)
{