From 485f7e76391a5bbf1af6b3219d27db721af38440 Mon Sep 17 00:00:00 2001 From: UltraNix <80540499+UltraNix@users.noreply.github.com> Date: Sun, 21 Mar 2021 15:17:57 +0100 Subject: [PATCH] feat(Core/DB/Authserver): remove `sha_pass_hash` (#4827) --- .../rev_1615629613255169700.sql | 27 +++ src/common/Cryptography/ARC4.cpp | 38 ++- src/common/Cryptography/ARC4.h | 30 ++- src/common/Cryptography/AuthDefines.h | 15 ++ .../Cryptography/Authentication/AuthCrypt.cpp | 55 ++--- .../Cryptography/Authentication/AuthCrypt.h | 16 +- src/common/Cryptography/BigNumber.cpp | 77 ++++--- src/common/Cryptography/BigNumber.h | 42 +++- src/common/Cryptography/CryptoConstants.h | 20 ++ src/common/Cryptography/CryptoGenerics.h | 99 ++++++++ src/common/Cryptography/CryptoHash.h | 107 +++++++++ src/common/Cryptography/CryptoRandom.cpp | 14 ++ src/common/Cryptography/CryptoRandom.h | 31 +++ src/common/Cryptography/HMAC.h | 118 ++++++++++ src/common/Cryptography/HMACSHA1.cpp | 61 ----- src/common/Cryptography/HMACSHA1.h | 34 --- src/common/Cryptography/SHA1.cpp | 55 ----- src/common/Cryptography/SHA1.h | 37 --- src/common/Cryptography/SRP6.cpp | 97 ++++++++ src/common/Cryptography/SRP6.h | 71 ++++++ src/common/Cryptography/SessionKeyGenerator.h | 50 ++++ src/common/Cryptography/WardenKeyGeneration.h | 67 ------ src/common/Database/Field.cpp | 28 ++- src/common/Database/Field.h | 14 +- .../Database/Implementation/LoginDatabase.cpp | 19 +- .../Database/Implementation/LoginDatabase.h | 3 +- src/common/Database/PreparedStatement.cpp | 37 ++- src/common/Database/PreparedStatement.h | 12 +- src/common/Database/QueryResult.cpp | 9 +- src/common/Logging/Log.cpp | 2 +- src/common/Packets/ByteBuffer.h | 13 ++ src/common/Utilities/Util.cpp | 8 +- src/common/Utilities/Util.h | 27 ++- src/server/authserver/Server/AuthSocket.cpp | 218 +++--------------- src/server/authserver/Server/AuthSocket.h | 12 +- src/server/game/Accounts/AccountMgr.cpp | 53 ++--- src/server/game/Accounts/AccountMgr.h | 3 +- src/server/game/Guilds/Guild.cpp | 4 +- src/server/game/Handlers/MiscHandler.cpp | 1 - src/server/game/Server/WorldSession.cpp | 2 +- src/server/game/Server/WorldSession.h | 4 +- src/server/game/Server/WorldSocket.cpp | 56 +++-- src/server/game/Server/WorldSocket.h | 2 +- src/server/game/Warden/Warden.cpp | 6 +- src/server/game/Warden/Warden.h | 12 +- src/server/game/Warden/WardenCheckMgr.cpp | 22 -- src/server/game/Warden/WardenMac.cpp | 28 +-- src/server/game/Warden/WardenMac.h | 3 +- src/server/game/Warden/WardenWin.cpp | 54 +++-- src/server/game/Warden/WardenWin.h | 2 +- src/server/game/World/IWorld.h | 1 + src/server/game/World/World.cpp | 2 + src/server/scripts/Commands/cs_account.cpp | 2 +- .../worldserver/RemoteAccess/RASocket.cpp | 19 +- 54 files changed, 1095 insertions(+), 744 deletions(-) create mode 100644 data/sql/updates/pending_db_auth/rev_1615629613255169700.sql create mode 100644 src/common/Cryptography/AuthDefines.h create mode 100644 src/common/Cryptography/CryptoConstants.h create mode 100644 src/common/Cryptography/CryptoGenerics.h create mode 100644 src/common/Cryptography/CryptoHash.h create mode 100644 src/common/Cryptography/CryptoRandom.cpp create mode 100644 src/common/Cryptography/CryptoRandom.h create mode 100644 src/common/Cryptography/HMAC.h delete mode 100644 src/common/Cryptography/HMACSHA1.cpp delete mode 100644 src/common/Cryptography/HMACSHA1.h delete mode 100644 src/common/Cryptography/SHA1.cpp delete mode 100644 src/common/Cryptography/SHA1.h create mode 100644 src/common/Cryptography/SRP6.cpp create mode 100644 src/common/Cryptography/SRP6.h create mode 100644 src/common/Cryptography/SessionKeyGenerator.h delete mode 100644 src/common/Cryptography/WardenKeyGeneration.h diff --git a/data/sql/updates/pending_db_auth/rev_1615629613255169700.sql b/data/sql/updates/pending_db_auth/rev_1615629613255169700.sql new file mode 100644 index 000000000..dc757406c --- /dev/null +++ b/data/sql/updates/pending_db_auth/rev_1615629613255169700.sql @@ -0,0 +1,27 @@ +INSERT INTO `version_db_auth` (`sql_rev`) VALUES ('1615629613255169700'); + +-- update `account` structure +-- sha_pass_hash/s/v kept around for now, for backwards compatibility +ALTER TABLE `account` + DROP COLUMN `sessionkey`, + ADD COLUMN `salt` BINARY(32) AFTER `username`, + ADD COLUMN `verifier` BINARY(32) AFTER `salt`, + ADD COLUMN `session_key` BINARY(40) AFTER `verifier`, + MODIFY COLUMN `s` VARCHAR(64) NOT NULL DEFAULT 'dummy value, use `salt` instead', + MODIFY COLUMN `v` VARCHAR(64) NOT NULL DEFAULT 'dummy value, use `verifier` instead'; + +UPDATE `account` SET `salt`=REVERSE(UNHEX(`s`)), `s`=DEFAULT WHERE LENGTH(`s`)=64; +UPDATE `account` SET `verifier`=REVERSE(UNHEX(`v`)), `v`=DEFAULT WHERE LENGTH(`v`)=64; + +ALTER TABLE `account` + DROP COLUMN `session_key`, + ADD COLUMN `session_key` BINARY(40) DEFAULT NULL AFTER `verifier`; + +UPDATE `account` SET `salt`=UNHEX(CONCAT(MD5(RAND()),MD5(RAND()))), `verifier`=UNHEX(CONCAT(MD5(RAND()),MD5(RAND()))) WHERE `salt` IS NULL OR `verifier` IS NULL; + +ALTER TABLE `account` + DROP COLUMN `s`, + DROP COLUMN `v`, + DROP COLUMN `sha_pass_hash`, + MODIFY COLUMN `salt` BINARY(32) NOT NULL, + MODIFY COLUMN `verifier` BINARY(32) NOT NULL; diff --git a/src/common/Cryptography/ARC4.cpp b/src/common/Cryptography/ARC4.cpp index e27fb9515..0eb2b7175 100644 --- a/src/common/Cryptography/ARC4.cpp +++ b/src/common/Cryptography/ARC4.cpp @@ -5,36 +5,34 @@ */ #include "ARC4.h" -#include +#include "Errors.h" -ARC4::ARC4(uint32 len) : m_ctx(EVP_CIPHER_CTX_new()) +acore::Crypto::ARC4::ARC4() + : _ctx(EVP_CIPHER_CTX_new()) { - EVP_CIPHER_CTX_init(m_ctx); - EVP_EncryptInit_ex(m_ctx, EVP_rc4(), nullptr, nullptr, nullptr); - EVP_CIPHER_CTX_set_key_length(m_ctx, len); + EVP_CIPHER_CTX_init(_ctx); + int result = EVP_EncryptInit_ex(_ctx, EVP_rc4(), nullptr, nullptr, nullptr); + ASSERT(result == 1); } -ARC4::ARC4(uint8* seed, uint32 len) : m_ctx(EVP_CIPHER_CTX_new()) +acore::Crypto::ARC4::~ARC4() { - EVP_CIPHER_CTX_init(m_ctx); - EVP_EncryptInit_ex(m_ctx, EVP_rc4(), nullptr, nullptr, nullptr); - EVP_CIPHER_CTX_set_key_length(m_ctx, len); - EVP_EncryptInit_ex(m_ctx, nullptr, nullptr, seed, nullptr); + EVP_CIPHER_CTX_free(_ctx); } -ARC4::~ARC4() +void acore::Crypto::ARC4::Init(uint8 const* seed, size_t len) { - EVP_CIPHER_CTX_free(m_ctx); + int result1 = EVP_CIPHER_CTX_set_key_length(_ctx, len); + ASSERT(result1 == 1); + int result2 = EVP_EncryptInit_ex(_ctx, nullptr, nullptr, seed, nullptr); + ASSERT(result2 == 1); } -void ARC4::Init(uint8* seed) -{ - EVP_EncryptInit_ex(m_ctx, nullptr, nullptr, seed, nullptr); -} - -void ARC4::UpdateData(int len, uint8* data) +void acore::Crypto::ARC4::UpdateData(uint8* data, size_t len) { int outlen = 0; - EVP_EncryptUpdate(m_ctx, data, &outlen, data, len); - EVP_EncryptFinal_ex(m_ctx, data, &outlen); + int result1 = EVP_EncryptUpdate(_ctx, data, &outlen, data, len); + ASSERT(result1 == 1); + int result2 = EVP_EncryptFinal_ex(_ctx, data, &outlen); + ASSERT(result2 == 1); } diff --git a/src/common/Cryptography/ARC4.h b/src/common/Cryptography/ARC4.h index 2efbfd4e2..4ed8876e0 100644 --- a/src/common/Cryptography/ARC4.h +++ b/src/common/Cryptography/ARC4.h @@ -8,18 +8,28 @@ #define _AUTH_SARC4_H #include "Define.h" + +#include #include -class ARC4 +namespace acore::Crypto { -public: - ARC4(uint32 len); - ARC4(uint8* seed, uint32 len); - ~ARC4(); - void Init(uint8* seed); - void UpdateData(int len, uint8* data); -private: - EVP_CIPHER_CTX* m_ctx; -}; + class ARC4 + { + public: + ARC4(); + ~ARC4(); + + void Init(uint8 const* seed, size_t len); + template + void Init(Container const& c) { Init(std::data(c), std::size(c)); } + + void UpdateData(uint8* data, size_t len); + template + void UpdateData(Container& c) { UpdateData(std::data(c), std::size(c)); } + private: + EVP_CIPHER_CTX* _ctx; + }; +} #endif diff --git a/src/common/Cryptography/AuthDefines.h b/src/common/Cryptography/AuthDefines.h new file mode 100644 index 000000000..11ed127ab --- /dev/null +++ b/src/common/Cryptography/AuthDefines.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2008-2021 TrinityCore + */ + +#ifndef AZEROTHCORE_AUTHDEFINES_H +#define AZEROTHCORE_AUTHDEFINES_H + +#include "Define.h" +#include + +constexpr size_t SESSION_KEY_LENGTH = 40; +using SessionKey = std::array; + +#endif diff --git a/src/common/Cryptography/Authentication/AuthCrypt.cpp b/src/common/Cryptography/Authentication/AuthCrypt.cpp index 5416f1d92..5bf8d43cb 100644 --- a/src/common/Cryptography/Authentication/AuthCrypt.cpp +++ b/src/common/Cryptography/Authentication/AuthCrypt.cpp @@ -5,56 +5,37 @@ */ #include "AuthCrypt.h" -#include "Cryptography/HMACSHA1.h" -#include "Cryptography/BigNumber.h" +#include "BigNumber.h" +#include "Errors.h" +#include "HMAC.h" -AuthCrypt::AuthCrypt() : - _clientDecrypt(SHA_DIGEST_LENGTH), _serverEncrypt(SHA_DIGEST_LENGTH), - _initialized(false) -{ } - -void AuthCrypt::Init(BigNumber* K) +AuthCrypt::AuthCrypt() : _initialized(false) { - uint8 ServerEncryptionKey[SEED_KEY_SIZE] = { 0xCC, 0x98, 0xAE, 0x04, 0xE8, 0x97, 0xEA, 0xCA, 0x12, 0xDD, 0xC0, 0x93, 0x42, 0x91, 0x53, 0x57 }; - HmacHash serverEncryptHmac(SEED_KEY_SIZE, (uint8*)ServerEncryptionKey); - uint8* encryptHash = serverEncryptHmac.ComputeHash(K); +} - uint8 ServerDecryptionKey[SEED_KEY_SIZE] = { 0xC2, 0xB3, 0x72, 0x3C, 0xC6, 0xAE, 0xD9, 0xB5, 0x34, 0x3C, 0x53, 0xEE, 0x2F, 0x43, 0x67, 0xCE }; - HmacHash clientDecryptHmac(SEED_KEY_SIZE, (uint8*)ServerDecryptionKey); - uint8* decryptHash = clientDecryptHmac.ComputeHash(K); - - //ARC4 _serverDecrypt(encryptHash); - _clientDecrypt.Init(decryptHash); - _serverEncrypt.Init(encryptHash); - //ARC4 _clientEncrypt(decryptHash); +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. - uint8 syncBuf[1024]; - memset(syncBuf, 0, 1024); - - _serverEncrypt.UpdateData(1024, syncBuf); - //_clientEncrypt.UpdateData(1024, syncBuf); - - memset(syncBuf, 0, 1024); - - //_serverDecrypt.UpdateData(1024, syncBuf); - _clientDecrypt.UpdateData(1024, syncBuf); + std::array syncBuf; + _serverEncrypt.UpdateData(syncBuf); + _clientDecrypt.UpdateData(syncBuf); _initialized = true; } void AuthCrypt::DecryptRecv(uint8* data, size_t len) { - if (!_initialized) - return; - - _clientDecrypt.UpdateData(len, data); + ASSERT(_initialized); + _clientDecrypt.UpdateData(data, len); } void AuthCrypt::EncryptSend(uint8* data, size_t len) { - if (!_initialized) - return; - - _serverEncrypt.UpdateData(len, data); + ASSERT(_initialized); + _serverEncrypt.UpdateData(data, len); } diff --git a/src/common/Cryptography/Authentication/AuthCrypt.h b/src/common/Cryptography/Authentication/AuthCrypt.h index 0f9a7159a..b8b5ef2e0 100644 --- a/src/common/Cryptography/Authentication/AuthCrypt.h +++ b/src/common/Cryptography/Authentication/AuthCrypt.h @@ -7,24 +7,24 @@ #ifndef _AUTHCRYPT_H #define _AUTHCRYPT_H -#include "Cryptography/ARC4.h" - -class BigNumber; +#include "ARC4.h" +#include "AuthDefines.h" +#include class AuthCrypt { public: AuthCrypt(); - void Init(BigNumber* K); - void DecryptRecv(uint8*, size_t); - void EncryptSend(uint8*, size_t); + void Init(SessionKey const& K); + void DecryptRecv(uint8* data, size_t len); + void EncryptSend(uint8* data, size_t len); bool IsInitialized() const { return _initialized; } private: - ARC4 _clientDecrypt; - ARC4 _serverEncrypt; + acore::Crypto::ARC4 _clientDecrypt; + acore::Crypto::ARC4 _serverEncrypt; bool _initialized; }; #endif diff --git a/src/common/Cryptography/BigNumber.cpp b/src/common/Cryptography/BigNumber.cpp index 973896ff0..5bc634f83 100644 --- a/src/common/Cryptography/BigNumber.cpp +++ b/src/common/Cryptography/BigNumber.cpp @@ -11,16 +11,12 @@ BigNumber::BigNumber() : _bn(BN_new()) -{ } +{ +} BigNumber::BigNumber(BigNumber const& bn) - : _bn(BN_dup(bn._bn)) -{ } - -BigNumber::BigNumber(uint32 val) - : _bn(BN_new()) + : _bn(BN_dup(bn.BN())) { - BN_set_word(_bn, val); } BigNumber::~BigNumber() @@ -28,6 +24,13 @@ BigNumber::~BigNumber() BN_free(_bn); } +void BigNumber::SetDword(int32 val) +{ + SetDword(uint32(abs(val))); + if (val < 0) + BN_set_negative(_bn, 1); +} + void BigNumber::SetDword(uint32 val) { BN_set_word(_bn, val); @@ -40,16 +43,25 @@ void BigNumber::SetQword(uint64 val) BN_add_word(_bn, (uint32)(val & 0xFFFFFFFF)); } -void BigNumber::SetBinary(uint8 const* bytes, int32 len) +void BigNumber::SetBinary(uint8 const* bytes, int32 len, bool littleEndian) { - uint8* array = new uint8[len]; + if (littleEndian) + { +#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100000L + uint8* array = new uint8[len]; - for (int i = 0; i < len; i++) - array[i] = bytes[len - 1 - i]; + for (int i = 0; i < len; i++) + array[i] = bytes[len - 1 - i]; - BN_bin2bn(array, len, _bn); + BN_bin2bn(array, len, _bn); - delete[] array; + delete[] array; +#else + BN_lebin2bn(bytes, len, _bn); +#endif + } + else + BN_bin2bn(bytes, len, _bn); } void BigNumber::SetHexStr(char const* str) @@ -116,7 +128,7 @@ BigNumber BigNumber::operator%=(BigNumber const& bn) return *this; } -BigNumber BigNumber::Exp(BigNumber const& bn) +BigNumber BigNumber::Exp(BigNumber const& bn) const { BigNumber ret; BN_CTX* bnctx; @@ -128,7 +140,7 @@ BigNumber BigNumber::Exp(BigNumber const& bn) return ret; } -BigNumber BigNumber::ModExp(BigNumber const& bn1, BigNumber const& bn2) +BigNumber BigNumber::ModExp(BigNumber const& bn1, BigNumber const& bn2) const { BigNumber ret; BN_CTX* bnctx; @@ -140,7 +152,7 @@ BigNumber BigNumber::ModExp(BigNumber const& bn1, BigNumber const& bn2) return ret; } -int32 BigNumber::GetNumBytes(void) +int32 BigNumber::GetNumBytes() const { return BN_num_bytes(_bn); } @@ -155,25 +167,38 @@ bool BigNumber::isZero() const return BN_is_zero(_bn); } -std::unique_ptr BigNumber::AsByteArray(int32 minSize, bool littleEndian) +void BigNumber::GetBytes(uint8* buf, size_t bufsize, bool littleEndian) const { - int numBytes = GetNumBytes(); - int length = (minSize >= numBytes) ? minSize : numBytes; +#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100000L + int nBytes = GetNumBytes(); + ASSERT(nBytes >= 0, "Bignum has negative number of bytes (%d).", nBytes); + std::size_t numBytes = static_cast(nBytes); - uint8* array = new uint8[length]; + // too large to store + ASSERT(numBytes <= bufsize, "Buffer of size %zu is too small to hold bignum with %zu bytes.\n", bufsize, numBytes); // If we need more bytes than length of BigNumber set the rest to 0 - if (length > numBytes) - memset((void*)array, 0, length); + if (numBytes < bufsize) + memset((void*)buf, 0, bufsize); - BN_bn2bin(_bn, (unsigned char*)array); + 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(array, array + length); + 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 %zu is too small to hold bignum with %d bytes.\n", bufsize, BN_num_bytes(_bn)); +#endif +} - std::unique_ptr ret(array); - return ret; +std::vector BigNumber::ToByteVector(int32 minSize, bool littleEndian) const +{ + std::size_t length = std::max(GetNumBytes(), minSize); + std::vector v; + v.resize(length); + GetBytes(v.data(), length, littleEndian); + return v; } char* BigNumber::AsHexStr() const diff --git a/src/common/Cryptography/BigNumber.h b/src/common/Cryptography/BigNumber.h index 6234ceb28..d2fbd7b78 100644 --- a/src/common/Cryptography/BigNumber.h +++ b/src/common/Cryptography/BigNumber.h @@ -12,6 +12,7 @@ #include #include #include +#include struct bignum_st; @@ -20,48 +21,57 @@ class BigNumber public: BigNumber(); BigNumber(BigNumber const& bn); - BigNumber(uint32); + BigNumber(uint32 v) : BigNumber() { SetDword(v); } + BigNumber(int32 v) : BigNumber() { SetDword(v); } + BigNumber(std::string const& v) : BigNumber() { SetHexStr(v); } + template + BigNumber(std::array const& v, bool littleEndian = true) : BigNumber() { SetBinary(v.data(), Size, littleEndian); } + ~BigNumber(); + void SetDword(int32); void SetDword(uint32); void SetQword(uint64); - void SetBinary(uint8 const* bytes, int32 len); + void SetBinary(uint8 const* bytes, int32 len, bool littleEndian = true); + template + auto SetBinary(Container const& c, bool littleEndian = true) -> std::enable_if_t>> { SetBinary(std::data(c), std::size(c), littleEndian); } void SetHexStr(char const* str); + void SetHexStr(std::string const& str) { SetHexStr(str.c_str()); } void SetRand(int32 numbits); BigNumber& operator=(BigNumber const& bn); BigNumber operator+=(BigNumber const& bn); - BigNumber operator+(BigNumber const& bn) + BigNumber operator+(BigNumber const& bn) const { BigNumber t(*this); return t += bn; } BigNumber operator-=(BigNumber const& bn); - BigNumber operator-(BigNumber const& bn) + BigNumber operator-(BigNumber const& bn) const { BigNumber t(*this); return t -= bn; } BigNumber operator*=(BigNumber const& bn); - BigNumber operator*(BigNumber const& bn) + BigNumber operator*(BigNumber const& bn) const { BigNumber t(*this); return t *= bn; } BigNumber operator/=(BigNumber const& bn); - BigNumber operator/(BigNumber const& bn) + BigNumber operator/(BigNumber const& bn) const { BigNumber t(*this); return t /= bn; } BigNumber operator%=(BigNumber const& bn); - BigNumber operator%(BigNumber const& bn) + BigNumber operator%(BigNumber const& bn) const { BigNumber t(*this); return t %= bn; @@ -69,16 +79,26 @@ public: [[nodiscard]] bool isZero() const; - BigNumber ModExp(BigNumber const& bn1, BigNumber const& bn2); - BigNumber Exp(BigNumber const&); + BigNumber ModExp(BigNumber const& bn1, BigNumber const& bn2) const; + BigNumber Exp(BigNumber const&) const; - int32 GetNumBytes(); + int32 GetNumBytes() const; struct bignum_st* BN() { return _bn; } + struct bignum_st const* BN() const { return _bn; } uint32 AsDword(); - std::unique_ptr AsByteArray(int32 minSize = 0, bool littleEndian = true); + void GetBytes(uint8* buf, size_t bufsize, bool littleEndian = true) const; + std::vector ToByteVector(int32 minSize = 0, bool littleEndian = true) const; + + template + std::array ToByteArray(bool littleEndian = true) const + { + std::array buf; + GetBytes(buf.data(), Size, littleEndian); + return buf; + } [[nodiscard]] char* AsHexStr() const; [[nodiscard]] char* AsDecStr() const; diff --git a/src/common/Cryptography/CryptoConstants.h b/src/common/Cryptography/CryptoConstants.h new file mode 100644 index 000000000..b0c530bcc --- /dev/null +++ b/src/common/Cryptography/CryptoConstants.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2008-2021 TrinityCore + */ + +#ifndef AZEROTHCORE_CRYPTO_CONSTANTS_H +#define AZEROTHCORE_CRYPTO_CONSTANTS_H + +#include "Define.h" + +namespace acore::Crypto +{ + struct Constants + { + static constexpr size_t SHA1_DIGEST_LENGTH_BYTES = 20; + static constexpr size_t SHA256_DIGEST_LENGTH_BYTES = 32; + }; +} + +#endif diff --git a/src/common/Cryptography/CryptoGenerics.h b/src/common/Cryptography/CryptoGenerics.h new file mode 100644 index 000000000..103026512 --- /dev/null +++ b/src/common/Cryptography/CryptoGenerics.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2008-2021 TrinityCore + */ + +#ifndef AZEROTHCORE_CRYPTO_GENERICS_HPP +#define AZEROTHCORE_CRYPTO_GENERICS_HPP + +#include "BigNumber.h" +#include "CryptoRandom.h" +#include "Define.h" +#include "Errors.h" +#include +#include + +namespace acore::Impl +{ + struct CryptoGenericsImpl + { + template + static typename Cipher::IV GenerateRandomIV() + { + typename Cipher::IV iv; + acore::Crypto::GetRandomBytes(iv); + return iv; + } + + template + static void AppendToBack(std::vector& data, Container const& tail) + { + data.insert(data.end(), std::begin(tail), std::end(tail)); + } + + template + static void SplitFromBack(std::vector& data, Container& tail) + { + ASSERT(data.size() >= std::size(tail)); + for (size_t i = 1, N = std::size(tail); i <= N; ++i) + { + tail[N - i] = data.back(); + data.pop_back(); + } + } + }; +} + +namespace acore::Crypto +{ + template + void AEEncryptWithRandomIV(std::vector& data, typename Cipher::Key const& key) + { + using IV = typename Cipher::IV; + using Tag = typename Cipher::Tag; + // select random IV + IV iv = acore::Impl::CryptoGenericsImpl::GenerateRandomIV(); + Tag tag; + + // encrypt data + Cipher cipher(true); + cipher.Init(key); + bool success = cipher.Process(iv, data.data(), data.size(), tag); + ASSERT(success); + + // append trailing IV and tag + acore::Impl::CryptoGenericsImpl::AppendToBack(data, iv); + acore::Impl::CryptoGenericsImpl::AppendToBack(data, tag); + } + + template + void AEEncryptWithRandomIV(std::vector& data, BigNumber const& key) + { + AEEncryptWithRandomIV(data, key.ToByteArray()); + } + + template + bool AEDecrypt(std::vector& data, typename Cipher::Key const& key) + { + using IV = typename Cipher::IV; + using Tag = typename Cipher::Tag; + // extract trailing IV and tag + IV iv; + Tag tag; + acore::Impl::CryptoGenericsImpl::SplitFromBack(data, tag); + acore::Impl::CryptoGenericsImpl::SplitFromBack(data, iv); + + // decrypt data + Cipher cipher(false); + cipher.Init(key); + return cipher.Process(iv, data.data(), data.size(), tag); + } + + template + bool AEDecrypt(std::vector& data, BigNumber const& key) + { + return AEDecrypt(data, key.ToByteArray()); + } +} + +#endif diff --git a/src/common/Cryptography/CryptoHash.h b/src/common/Cryptography/CryptoHash.h new file mode 100644 index 000000000..cdc325883 --- /dev/null +++ b/src/common/Cryptography/CryptoHash.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2008-2021 TrinityCore + */ + +#ifndef AZEROTHCORE_CRYPTOHASH_H +#define AZEROTHCORE_CRYPTOHASH_H + +#include "CryptoConstants.h" +#include "Define.h" +#include "Errors.h" +#include +#include +#include +#include + +class BigNumber; + +namespace acore::Impl +{ + struct GenericHashImpl + { + 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 void DestroyCTX(EVP_MD_CTX* ctx) { EVP_MD_CTX_destroy(ctx); } +#else + static EVP_MD_CTX* MakeCTX() { return EVP_MD_CTX_new(); } + static void DestroyCTX(EVP_MD_CTX* ctx) { EVP_MD_CTX_free(ctx); } +#endif + }; + + template + class GenericHash + { + public: + static constexpr size_t DIGEST_LENGTH = DigestLength; + using Digest = std::array; + + static Digest GetDigestOf(uint8 const* data, size_t len) + { + GenericHash hash; + hash.UpdateData(data, len); + hash.Finalize(); + return hash.GetDigest(); + } + + template + static auto GetDigestOf(Ts&&... pack) -> std::enable_if_t> || ...), Digest> + { + GenericHash hash; + (hash.UpdateData(std::forward(pack)), ...); + hash.Finalize(); + return hash.GetDigest(); + } + + GenericHash() : _ctx(GenericHashImpl::MakeCTX()) + { + int result = EVP_DigestInit_ex(_ctx, HashCreator(), nullptr); + ASSERT(result == 1); + } + + ~GenericHash() + { + if (!_ctx) + return; + 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(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 + 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); + GenericHashImpl::DestroyCTX(_ctx); + _ctx = nullptr; + } + + Digest const& GetDigest() const { return _digest; } + + private: + EVP_MD_CTX* _ctx; + Digest _digest = { }; + }; +} + +namespace acore::Crypto +{ + using SHA1 = acore::Impl::GenericHash; + using SHA256 = acore::Impl::GenericHash; +} + +#endif diff --git a/src/common/Cryptography/CryptoRandom.cpp b/src/common/Cryptography/CryptoRandom.cpp new file mode 100644 index 000000000..29496819c --- /dev/null +++ b/src/common/Cryptography/CryptoRandom.cpp @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2008-2021 TrinityCore + */ + +#include "CryptoRandom.h" +#include "Errors.h" +#include + +void acore::Crypto::GetRandomBytes(uint8* buf, size_t len) +{ + int result = RAND_bytes(buf, len); + ASSERT(result == 1); +} diff --git a/src/common/Cryptography/CryptoRandom.h b/src/common/Cryptography/CryptoRandom.h new file mode 100644 index 000000000..e5de60300 --- /dev/null +++ b/src/common/Cryptography/CryptoRandom.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2008-2021 TrinityCore + */ + +#ifndef AZEROTHCORE_CRYPTORANDOM_H +#define AZEROTHCORE_CRYPTORANDOM_H + +#include "Define.h" +#include + +namespace acore::Crypto +{ + void GetRandomBytes(uint8* buf, size_t len); + + template + void GetRandomBytes(Container& c) + { + GetRandomBytes(std::data(c), std::size(c)); + } + + template + std::array GetRandomBytes() + { + std::array arr; + GetRandomBytes(arr); + return arr; + } +} + +#endif diff --git a/src/common/Cryptography/HMAC.h b/src/common/Cryptography/HMAC.h new file mode 100644 index 000000000..696539ef4 --- /dev/null +++ b/src/common/Cryptography/HMAC.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2008-2021 TrinityCore + */ + +#ifndef AZEROTHCORE_HMAC_H +#define AZEROTHCORE_HMAC_H + +#include "CryptoConstants.h" +#include "Define.h" +#include "Errors.h" +#include +#include +#include +#include + +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 + class GenericHMAC + { + public: + static constexpr size_t DIGEST_LENGTH = DigestLength; + using Digest = std::array; + + template + 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 + static auto GetDigestOf(Container const& seed, Ts&&... pack) -> std::enable_if_t> || ...), Digest> + { + GenericHMAC hash(seed); + (hash.UpdateData(std::forward(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 + GenericHMAC(Container const& container) : GenericHMAC(std::data(container), std::size(container)) {} + + ~GenericHMAC() + { + if (!_ctx) + return; + 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(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 + void UpdateData(Container const& c) { UpdateData(std::data(c), std::size(c)); } + + 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; + } + + Digest const& GetDigest() const { return _digest; } + private: + HMAC_CTX* _ctx; + Digest _digest = { }; + }; +} + +namespace acore::Crypto +{ + using HMAC_SHA1 = acore::Impl::GenericHMAC; + using HMAC_SHA256 = acore::Impl::GenericHMAC; +} +#endif diff --git a/src/common/Cryptography/HMACSHA1.cpp b/src/common/Cryptography/HMACSHA1.cpp deleted file mode 100644 index 5dfa825f1..000000000 --- a/src/common/Cryptography/HMACSHA1.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2016+ AzerothCore , 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) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS - */ - -#include "HMACSHA1.h" -#include "BigNumber.h" -#include "Errors.h" -#include - -#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100000L -HMAC_CTX* HMAC_CTX_new() -{ - HMAC_CTX* ctx = new HMAC_CTX(); - HMAC_CTX_init(ctx); - return ctx; -} - -void HMAC_CTX_free(HMAC_CTX* ctx) -{ - HMAC_CTX_cleanup(ctx); - delete ctx; -} -#endif - -HmacHash::HmacHash(uint32 len, uint8* seed) -{ - m_ctx = HMAC_CTX_new(); - HMAC_Init_ex(m_ctx, seed, len, EVP_sha1(), nullptr); - memset(m_digest, 0, sizeof(m_digest)); -} - -HmacHash::~HmacHash() -{ - HMAC_CTX_free(m_ctx); -} - -void HmacHash::UpdateData(std::string const& str) -{ - HMAC_Update(m_ctx, reinterpret_cast(str.c_str()), str.length()); -} - -void HmacHash::UpdateData(uint8 const* data, size_t len) -{ - HMAC_Update(m_ctx, data, len); -} - -void HmacHash::Finalize() -{ - uint32 length = 0; - HMAC_Final(m_ctx, m_digest, &length); - ASSERT(length == SHA_DIGEST_LENGTH); -} - -uint8* HmacHash::ComputeHash(BigNumber* bn) -{ - HMAC_Update(m_ctx, bn->AsByteArray().get(), bn->GetNumBytes()); - Finalize(); - return m_digest; -} diff --git a/src/common/Cryptography/HMACSHA1.h b/src/common/Cryptography/HMACSHA1.h deleted file mode 100644 index a989cc1e8..000000000 --- a/src/common/Cryptography/HMACSHA1.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2016+ AzerothCore , 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) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS - */ - -#ifndef _AUTH_HMAC_H -#define _AUTH_HMAC_H - -#include "Define.h" -#include -#include -#include - -class BigNumber; - -#define SEED_KEY_SIZE 16 - -class HmacHash -{ -public: - HmacHash(uint32 len, uint8* seed); - ~HmacHash(); - void UpdateData(std::string const& str); - void UpdateData(uint8 const* data, size_t len); - void Finalize(); - uint8* ComputeHash(BigNumber* bn); - uint8* GetDigest() { return m_digest; } - int GetLength() const { return SHA_DIGEST_LENGTH; } -private: - HMAC_CTX* m_ctx; - uint8 m_digest[SHA_DIGEST_LENGTH]; -}; -#endif diff --git a/src/common/Cryptography/SHA1.cpp b/src/common/Cryptography/SHA1.cpp deleted file mode 100644 index 997967150..000000000 --- a/src/common/Cryptography/SHA1.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2016+ AzerothCore , 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) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS - */ - -#include "SHA1.h" -#include "BigNumber.h" -#include - -SHA1Hash::SHA1Hash() -{ - SHA1_Init(&mC); - memset(mDigest, 0, SHA_DIGEST_LENGTH * sizeof(uint8)); -} - -SHA1Hash::~SHA1Hash() -{ - SHA1_Init(&mC); -} - -void SHA1Hash::UpdateData(const uint8* dta, int len) -{ - SHA1_Update(&mC, dta, len); -} - -void SHA1Hash::UpdateData(const std::string& str) -{ - UpdateData((uint8 const*)str.c_str(), str.length()); -} - -void SHA1Hash::UpdateBigNumbers(BigNumber* bn0, ...) -{ - va_list v; - BigNumber* bn; - - va_start(v, bn0); - bn = bn0; - while (bn) - { - UpdateData(bn->AsByteArray().get(), bn->GetNumBytes()); - bn = va_arg(v, BigNumber*); - } - va_end(v); -} - -void SHA1Hash::Initialize() -{ - SHA1_Init(&mC); -} - -void SHA1Hash::Finalize(void) -{ - SHA1_Final(mDigest, &mC); -} diff --git a/src/common/Cryptography/SHA1.h b/src/common/Cryptography/SHA1.h deleted file mode 100644 index 4a3209b8e..000000000 --- a/src/common/Cryptography/SHA1.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2016+ AzerothCore , 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) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS - */ - -#ifndef _AUTH_SHA1_H -#define _AUTH_SHA1_H - -#include "Define.h" -#include -#include - -class BigNumber; - -class SHA1Hash -{ -public: - SHA1Hash(); - ~SHA1Hash(); - - void UpdateBigNumbers(BigNumber* bn0, ...); - - void UpdateData(const uint8* dta, int len); - void UpdateData(const std::string& str); - - void Initialize(); - void Finalize(); - - uint8* GetDigest() { return mDigest; }; - [[nodiscard]] int GetLength() const { return SHA_DIGEST_LENGTH; }; - -private: - SHA_CTX mC; - uint8 mDigest[SHA_DIGEST_LENGTH]; -}; -#endif diff --git a/src/common/Cryptography/SRP6.cpp b/src/common/Cryptography/SRP6.cpp new file mode 100644 index 000000000..ee7f1a98f --- /dev/null +++ b/src/common/Cryptography/SRP6.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2008-2021 TrinityCore + */ + +#include "SRP6.h" +#include "CryptoRandom.h" +#include "Util.h" +#include +#include + +using SHA1 = acore::Crypto::SHA1; +using SRP6 = acore::Crypto::SRP6; + +/*static*/ std::array const SRP6::g = { 7 }; +/*static*/ std::array const SRP6::N = HexStrToByteArray<32>("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7", true); +/*static*/ BigNumber const SRP6::_g(SRP6::g); +/*static*/ BigNumber const SRP6::_N(N); + +/*static*/ std::pair SRP6::MakeRegistrationData(std::string const& username, std::string const& password) +{ + std::pair res; + Crypto::GetRandomBytes(res.first); // random salt + res.second = CalculateVerifier(username, password, res.first); + return res; +} + +/*static*/ SRP6::Verifier SRP6::CalculateVerifier(std::string const& username, std::string const& password, SRP6::Salt const& salt) +{ + // v = g ^ H(s || H(u || ':' || p)) mod N + return _g.ModExp( + SHA1::GetDigestOf( + salt, + SHA1::GetDigestOf(username, ":", password) + ) + ,_N).ToByteArray<32>(); +} + +/*static*/ SessionKey SRP6::SHA1Interleave(SRP6::EphemeralKey const& S) +{ + // split S into two buffers + std::array 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]; + } + + // 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 + p /= 2; // offset into buffers + + // hash each of the halves, starting at the first nonzero byte + SHA1::Digest const hash0 = SHA1::GetDigestOf(buf0.data() + p, EPHEMERAL_KEY_LENGTH/2 - p); + SHA1::Digest const hash1 = SHA1::GetDigestOf(buf1.data() + p, EPHEMERAL_KEY_LENGTH/2 - p); + + // stick the two hashes back together + SessionKey K; + for (size_t i = 0; i < SHA1::DIGEST_LENGTH; ++i) + { + K[2 * i + 0] = hash0[i]; + K[2 * i + 1] = hash1[i]; + } + return K; +} + +SRP6::SRP6(std::string const& username, Salt const& salt, Verifier const& verifier) + : _I(SHA1::GetDigestOf(username)), _b(Crypto::GetRandomBytes<32>()), _v(verifier), s(salt), B(_B(_b, _v)) {} + +std::optional SRP6::VerifyChallengeResponse(EphemeralKey const& A, SHA1::Digest const& clientM) +{ + ASSERT(!_used); + _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>(); + + SessionKey K = SHA1Interleave(S); + + // NgHash = H(N) xor H(g) + SHA1::Digest const NHash = SHA1::GetDigestOf(N); + SHA1::Digest const gHash = SHA1::GetDigestOf(g); + SHA1::Digest NgHash; + std::transform(NHash.begin(), NHash.end(), gHash.begin(), NgHash.begin(), std::bit_xor<>()); + + SHA1::Digest const ourM = SHA1::GetDigestOf(NgHash, _I, s, A, B, K); + if (ourM == clientM) + return K; + else + return std::nullopt; +} diff --git a/src/common/Cryptography/SRP6.h b/src/common/Cryptography/SRP6.h new file mode 100644 index 000000000..9621cf8cc --- /dev/null +++ b/src/common/Cryptography/SRP6.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2008-2021 TrinityCore + */ + +#ifndef AZEROTHCORE_SRP6_H +#define AZEROTHCORE_SRP6_H + +#include "AuthDefines.h" +#include "BigNumber.h" +#include "Define.h" +#include "Common.h" +#include "CryptoHash.h" +#include +#include + +namespace acore::Crypto +{ + class SRP6 + { + public: + static constexpr size_t SALT_LENGTH = 32; + using Salt = std::array; + static constexpr size_t VERIFIER_LENGTH = 32; + using Verifier = std::array; + static constexpr size_t EPHEMERAL_KEY_LENGTH = 32; + using EphemeralKey = std::array; + + static std::array const g; + static std::array const N; + + // username + password must be passed through Utf8ToUpperOnlyLatin FIRST! + static std::pair 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) + { + return (verifier == CalculateVerifier(username, password, salt)); + } + + static SHA1::Digest GetSessionVerifier(EphemeralKey const& A, SHA1::Digest const& clientM, SessionKey const& K) + { + return SHA1::GetDigestOf(A, clientM, K); + } + + SRP6(std::string const& username, Salt const& salt, Verifier const& verifier); + std::optional VerifyChallengeResponse(EphemeralKey const& A, SHA1::Digest const& clientM); + + private: + bool _used = false; // a single instance can only be used to verify once + + static Verifier CalculateVerifier(std::string const& username, std::string const& password, Salt const& salt); + static SessionKey SHA1Interleave(EphemeralKey const& S); + + /* global algorithm parameters */ + static BigNumber const _g; // a [g]enerator for the ring of integers mod N, algorithm parameter + static BigNumber const _N; // the modulus, an algorithm parameter; all operations are mod this + + static EphemeralKey _B(BigNumber const& b, BigNumber const& v) { return ((_g.ModExp(b,_N) + (v * 3)) % N).ToByteArray(); } + + /* per-instantiation parameters, set on construction */ + SHA1::Digest const _I; // H(I) - the username, all uppercase + BigNumber const _b; // b - randomly chosen by the server, 19 bytes, never given out + BigNumber const _v; // v - the user's password verifier, derived from s + H(USERNAME || ":" || PASSWORD) + + public: + Salt const s; // s - the user's password salt, random, used to calculate v on registration + EphemeralKey const B; // B = 3v + g^b + }; +} + +#endif diff --git a/src/common/Cryptography/SessionKeyGenerator.h b/src/common/Cryptography/SessionKeyGenerator.h new file mode 100644 index 000000000..be7d03862 --- /dev/null +++ b/src/common/Cryptography/SessionKeyGenerator.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2008-2021 TrinityCore + */ + +#include +#include "CryptoHash.h" + +#ifndef AZEROTHCORE_SESSIONKEYGENERATOR_HPP +#define AZEROTHCORE_SESSIONKEYGENERATOR_HPP + +template +class SessionKeyGenerator +{ + public: + template + SessionKeyGenerator(C const& buf) : + o0it(o0.begin()) + { + uint8 const* data = std::data(buf); + size_t const len = std::size(buf); + size_t const halflen = (len / 2); + + o1 = Hash::GetDigestOf(data, halflen); + o2 = Hash::GetDigestOf(data + halflen, len - halflen); + o0 = Hash::GetDigestOf(o1, o0, o2); + } + + void Generate(uint8* buf, uint32 sz) + { + for (uint32 i = 0; i < sz; ++i) + { + if (o0it == o0.end()) + { + o0 = Hash::GetDigestOf(o1, o0, o2); + o0it = o0.begin(); + } + + buf[i] = *(o0it++); + } + } + + private: + typename Hash::Digest o0 = { }; + typename Hash::Digest o1 = { }; + typename Hash::Digest o2 = { }; + typename Hash::Digest::const_iterator o0it; + }; + +#endif diff --git a/src/common/Cryptography/WardenKeyGeneration.h b/src/common/Cryptography/WardenKeyGeneration.h deleted file mode 100644 index d451cb51a..000000000 --- a/src/common/Cryptography/WardenKeyGeneration.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2016+ AzerothCore , 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) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS - */ - -#include "SHA1.h" - -#ifndef _WARDEN_KEY_GENERATION_H -#define _WARDEN_KEY_GENERATION_H - -class SHA1Randx -{ -public: - SHA1Randx(uint8* buff, uint32 size) - { - uint32 taken = size / 2; - - sh.Initialize(); - sh.UpdateData(buff, taken); - sh.Finalize(); - - memcpy(o1, sh.GetDigest(), 20); - - sh.Initialize(); - sh.UpdateData(buff + taken, size - taken); - sh.Finalize(); - - memcpy(o2, sh.GetDigest(), 20); - - memset(o0, 0x00, 20); - - FillUp(); - } - - void Generate(uint8* buf, uint32 sz) - { - for (uint32 i = 0; i < sz; ++i) - { - if (taken == 20) - FillUp(); - - buf[i] = o0[taken]; - taken++; - } - } - -private: - void FillUp() - { - sh.Initialize(); - sh.UpdateData(o1, 20); - sh.UpdateData(o0, 20); - sh.UpdateData(o2, 20); - sh.Finalize(); - - memcpy(o0, sh.GetDigest(), 20); - - taken = 0; - } - - SHA1Hash sh; - uint32 taken; - uint8 o0[20], o1[20], o2[20]; -}; - -#endif diff --git a/src/common/Database/Field.cpp b/src/common/Database/Field.cpp index c528beddc..d1e01a7a2 100644 --- a/src/common/Database/Field.cpp +++ b/src/common/Database/Field.cpp @@ -5,6 +5,7 @@ */ #include "Field.h" +#include "Errors.h" Field::Field() { @@ -35,7 +36,7 @@ void Field::SetByteValue(const void* newValue, const size_t newSize, enum_field_ data.raw = true; } -void Field::SetStructuredValue(char* newValue, enum_field_types newType) +void Field::SetStructuredValue(char* newValue, enum_field_types newType, uint32 length) { if (data.value) CleanUp(); @@ -43,12 +44,29 @@ void Field::SetStructuredValue(char* newValue, enum_field_types newType) // This value stores somewhat structured data that needs function style casting if (newValue) { - size_t size = strlen(newValue); - data.value = new char [size + 1]; - strcpy((char*)data.value, newValue); - data.length = size; + data.value = new char[length + 1]; + memcpy(data.value, newValue, length); + *(reinterpret_cast(data.value) + length) = '\0'; + data.length = length; } data.type = newType; data.raw = false; } + +std::vector Field::GetBinary() const +{ + std::vector result; + if (!data.value || !data.length) + return result; + + result.resize(data.length); + memcpy(result.data(), data.value, data.length); + return result; +} + +void Field::GetBinarySizeChecked(uint8* buf, size_t length) const +{ + ASSERT(data.value && (data.length == length)); + memcpy(buf, data.value, length); +} diff --git a/src/common/Database/Field.h b/src/common/Database/Field.h index df3bee325..f24e448a1 100644 --- a/src/common/Database/Field.h +++ b/src/common/Database/Field.h @@ -11,6 +11,7 @@ #include "Log.h" #include +#include class Field { @@ -245,6 +246,15 @@ public: return data.value == nullptr; } + [[nodiscard]] std::vector GetBinary() const; + template + [[nodiscard]] std::array GetBinary() const + { + std::array buf; + GetBinarySizeChecked(buf.data(), S); + return buf; + } + protected: Field(); ~Field(); @@ -268,7 +278,7 @@ protected: #endif void SetByteValue(void const* newValue, size_t const newSize, enum_field_types newType, uint32 length); - void SetStructuredValue(char* newValue, enum_field_types newType); + void SetStructuredValue(char* newValue, enum_field_types newType, uint32 length); void CleanUp() { @@ -342,6 +352,8 @@ protected: data.type == MYSQL_TYPE_LONGLONG ); } + void GetBinarySizeChecked(uint8* buf, size_t size) const; + private: #ifdef ACORE_DEBUG static char const* FieldTypeToString(enum_field_types type) diff --git a/src/common/Database/Implementation/LoginDatabase.cpp b/src/common/Database/Implementation/LoginDatabase.cpp index c6e13da0c..8d5c1b252 100644 --- a/src/common/Database/Implementation/LoginDatabase.cpp +++ b/src/common/Database/Implementation/LoginDatabase.cpp @@ -23,16 +23,16 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_SEL_ACCOUNT_BANNED_BY_USERNAME, "SELECT account.id, username FROM account, account_banned WHERE account.id = account_banned.id AND active = 1 AND username LIKE CONCAT('%%', ?, '%%') GROUP BY account.id", CONNECTION_SYNCH); PrepareStatement(LOGIN_INS_ACCOUNT_AUTO_BANNED, "INSERT INTO account_banned VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, 'Trinity realmd', 'Failed login autoban', 1)", CONNECTION_ASYNC); PrepareStatement(LOGIN_DEL_ACCOUNT_BANNED, "DELETE FROM account_banned WHERE id = ?", CONNECTION_ASYNC); - PrepareStatement(LOGIN_SEL_SESSIONKEY, "SELECT a.sessionkey, a.id, aa.gmlevel FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE username = ?", CONNECTION_SYNCH); - PrepareStatement(LOGIN_UPD_VS, "UPDATE account SET v = ?, s = ? WHERE username = ?", CONNECTION_ASYNC); - PrepareStatement(LOGIN_UPD_LOGONPROOF, "UPDATE account SET sessionkey = ?, last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE username = ?", CONNECTION_SYNCH); - PrepareStatement(LOGIN_SEL_LOGONCHALLENGE, "SELECT a.sha_pass_hash, a.id, a.locked, a.lock_country, a.last_ip, aa.gmlevel, a.v, a.s, a.token_key FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE a.username = ?", CONNECTION_SYNCH); + PrepareStatement(LOGIN_SEL_SESSIONKEY, "SELECT a.session_key, a.id, aa.gmlevel FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE username = ?", CONNECTION_SYNCH); + PrepareStatement(LOGIN_UPD_LOGON, "UPDATE account SET salt = ?, verifier = ? WHERE id = ?", CONNECTION_ASYNC); + PrepareStatement(LOGIN_UPD_LOGONPROOF, "UPDATE account SET session_key = ?, last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE username = ?", CONNECTION_SYNCH); + PrepareStatement(LOGIN_SEL_LOGONCHALLENGE, "SELECT a.id, a.locked, a.lock_country, a.last_ip, aa.gmlevel, a.salt, a.verifier, a.token_key FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE a.username = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_LOGON_COUNTRY, "SELECT country FROM ip2nation WHERE ip < ? ORDER BY ip DESC LIMIT 0,1", CONNECTION_SYNCH); PrepareStatement(LOGIN_UPD_FAILEDLOGINS, "UPDATE account SET failed_logins = failed_logins + 1 WHERE username = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_FAILEDLOGINS, "SELECT id, failed_logins FROM account WHERE username = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_ID_BY_NAME, "SELECT id FROM account WHERE username = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_LIST_BY_NAME, "SELECT id, username FROM account WHERE username = ?", CONNECTION_SYNCH); - PrepareStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME, "SELECT id, sessionkey, last_ip, locked, lock_country, expansion, mutetime, locale, recruiter, os, totaltime FROM account WHERE username = ?", CONNECTION_SYNCH); + PrepareStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME, "SELECT id, session_key, last_ip, locked, lock_country, expansion, mutetime, locale, recruiter, os, totaltime FROM account WHERE username = ? AND session_key IS NOT NULL", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_LIST_BY_EMAIL, "SELECT id, username FROM account WHERE email = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_NUM_CHARS_ON_REALM, "SELECT numchars FROM realmcharacters WHERE realmid = ? AND acctid= ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_BY_IP, "SELECT id, username FROM account WHERE last_ip = ?", CONNECTION_SYNCH); @@ -45,13 +45,12 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_DEL_REALM_CHARACTERS, "DELETE FROM realmcharacters WHERE acctid = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_INS_REALM_CHARACTERS, "INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_SUM_REALM_CHARACTERS, "SELECT SUM(numchars) FROM realmcharacters WHERE acctid = ?", CONNECTION_ASYNC); - PrepareStatement(LOGIN_INS_ACCOUNT, "INSERT INTO account(username, sha_pass_hash, expansion, joindate) VALUES(?, ?, ?, NOW())", CONNECTION_ASYNC); + PrepareStatement(LOGIN_INS_ACCOUNT, "INSERT INTO account(username, salt, verifier, expansion, joindate) VALUES(?, ?, ?, ?, NOW())", CONNECTION_ASYNC); PrepareStatement(LOGIN_INS_REALM_CHARACTERS_INIT, "INSERT INTO realmcharacters (realmid, acctid, numchars) SELECT realmlist.id, account.id, 0 FROM realmlist, account LEFT JOIN realmcharacters ON acctid=account.id WHERE acctid IS NULL", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_EXPANSION, "UPDATE account SET expansion = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_ACCOUNT_LOCK, "UPDATE account SET locked = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_ACCOUNT_LOCK_CONTRY, "UPDATE account SET lock_country = ? WHERE id = ?", CONNECTION_ASYNC); - PrepareStatement(LOGIN_UPD_USERNAME, "UPDATE account SET v = 0, s = 0, username = ?, sha_pass_hash = ? WHERE id = ?", CONNECTION_ASYNC); - PrepareStatement(LOGIN_UPD_PASSWORD, "UPDATE account SET v = 0, s = 0, sha_pass_hash = ? WHERE id = ?", CONNECTION_ASYNC); + PrepareStatement(LOGIN_UPD_USERNAME, "UPDATE account SET username = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_MUTE_TIME, "UPDATE account SET mutetime = ? , mutereason = ? , muteby = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_MUTE_TIME_LOGIN, "UPDATE account SET mutetime = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_LAST_IP, "UPDATE account SET last_ip = ? WHERE username = ?", CONNECTION_ASYNC); @@ -66,8 +65,8 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_GET_ACCOUNT_ACCESS_GMLEVEL, "SELECT gmlevel FROM account_access WHERE id = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_GET_GMLEVEL_BY_REALMID, "SELECT gmlevel FROM account_access WHERE id = ? AND (RealmID = ? OR RealmID = -1)", CONNECTION_SYNCH); PrepareStatement(LOGIN_GET_USERNAME_BY_ID, "SELECT username FROM account WHERE id = ?", CONNECTION_SYNCH); - PrepareStatement(LOGIN_SEL_CHECK_PASSWORD, "SELECT 1 FROM account WHERE id = ? AND sha_pass_hash = ?", CONNECTION_SYNCH); - PrepareStatement(LOGIN_SEL_CHECK_PASSWORD_BY_NAME, "SELECT 1 FROM account WHERE username = ? AND sha_pass_hash = ?", CONNECTION_SYNCH); + PrepareStatement(LOGIN_SEL_CHECK_PASSWORD, "SELECT salt, verifier FROM account WHERE id = ?", CONNECTION_SYNCH); + PrepareStatement(LOGIN_SEL_CHECK_PASSWORD_BY_NAME, "SELECT salt, verifier FROM account WHERE username = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_PINFO, "SELECT a.username, aa.gmlevel, a.email, a.reg_mail, a.last_ip, DATE_FORMAT(a.last_login, '%Y-%m-%d %T'), a.mutetime, a.mutereason, a.muteby, a.failed_logins, a.locked, a.OS FROM account a LEFT JOIN account_access aa ON (a.id = aa.id AND (aa.RealmID = ? OR aa.RealmID = -1)) WHERE a.id = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_PINFO_BANS, "SELECT unbandate, bandate = unbandate, bannedby, banreason FROM account_banned WHERE id = ? AND active ORDER BY bandate ASC LIMIT 1", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_GM_ACCOUNTS, "SELECT a.username, aa.gmlevel FROM account a, account_access aa WHERE a.id=aa.id AND aa.gmlevel >= ? AND (aa.realmid = -1 OR aa.realmid = ?)", CONNECTION_SYNCH); diff --git a/src/common/Database/Implementation/LoginDatabase.h b/src/common/Database/Implementation/LoginDatabase.h index 61811d0d5..85fe56af1 100644 --- a/src/common/Database/Implementation/LoginDatabase.h +++ b/src/common/Database/Implementation/LoginDatabase.h @@ -42,7 +42,7 @@ enum LoginDatabaseStatements LOGIN_INS_ACCOUNT_AUTO_BANNED, LOGIN_DEL_ACCOUNT_BANNED, LOGIN_SEL_SESSIONKEY, - LOGIN_UPD_VS, + LOGIN_UPD_LOGON, LOGIN_UPD_LOGONPROOF, LOGIN_SEL_LOGONCHALLENGE, LOGIN_SEL_LOGON_COUNTRY, @@ -71,7 +71,6 @@ enum LoginDatabaseStatements LOGIN_UPD_ACCOUNT_LOCK, LOGIN_UPD_ACCOUNT_LOCK_CONTRY, LOGIN_UPD_USERNAME, - LOGIN_UPD_PASSWORD, LOGIN_UPD_MUTE_TIME, LOGIN_UPD_MUTE_TIME_LOGIN, LOGIN_UPD_LAST_IP, diff --git a/src/common/Database/PreparedStatement.cpp b/src/common/Database/PreparedStatement.cpp index c9df0a88f..05c073701 100644 --- a/src/common/Database/PreparedStatement.cpp +++ b/src/common/Database/PreparedStatement.cpp @@ -61,7 +61,10 @@ void PreparedStatement::BindParameters() m_stmt->setDouble(i, statement_data[i].data.d); break; case TYPE_STRING: - m_stmt->setString(i, statement_data[i].str.c_str()); + m_stmt->setBinary(i, statement_data[i].binary, true); + break; + case TYPE_BINARY: + m_stmt->setBinary(i, statement_data[i].binary, false); break; case TYPE_NULL: m_stmt->setNull(i); @@ -179,10 +182,20 @@ void PreparedStatement::setString(const uint8 index, const std::string& value) if (index >= statement_data.size()) statement_data.resize(index + 1); - statement_data[index].str = value; + statement_data[index].binary.resize(value.length() + 1); + memcpy(statement_data[index].binary.data(), value.c_str(), value.length() + 1); statement_data[index].type = TYPE_STRING; } +void PreparedStatement::setBinary(const uint8 index, const std::vector& value) +{ + if (index >= statement_data.size()) + statement_data.resize(index + 1); + + statement_data[index].binary = value; + statement_data[index].type = TYPE_BINARY; +} + void PreparedStatement::setNull(const uint8 index) { if (index >= statement_data.size()) @@ -332,21 +345,26 @@ void MySQLPreparedStatement::setDouble(const uint8 index, const double value) setValue(param, MYSQL_TYPE_DOUBLE, &value, sizeof(double), (value > 0.0f)); } -void MySQLPreparedStatement::setString(const uint8 index, const char* value) +void MySQLPreparedStatement::setBinary(const uint8 index, const std::vector& value, bool isString) { CheckValidIndex(index); m_paramsSet[index] = true; MYSQL_BIND* param = &m_bind[index]; - size_t len = strlen(value) + 1; - param->buffer_type = MYSQL_TYPE_VAR_STRING; + uint32 len = uint32(value.size()); + param->buffer_type = MYSQL_TYPE_BLOB; delete [] static_cast(param->buffer); param->buffer = new char[len]; param->buffer_length = len; param->is_null_value = 0; delete param->length; - param->length = new unsigned long(len - 1); + param->length = new unsigned long(len); + if (isString) + { + *param->length -= 1; + param->buffer_type = MYSQL_TYPE_VAR_STRING; + } - memcpy(param->buffer, value, len); + memcpy(param->buffer, value.data(), len); } void MySQLPreparedStatement::setNull(const uint8 index) @@ -422,7 +440,10 @@ std::string MySQLPreparedStatement::getQueryString(std::string const& sqlPattern ss << m_stmt->statement_data[i].data.d; break; case TYPE_STRING: - ss << '\'' << m_stmt->statement_data[i].str << '\''; + ss << '\'' << (char const*)m_stmt->statement_data[i].binary.data() << '\''; + break; + case TYPE_BINARY: + ss << "BINARY"; break; case TYPE_NULL: ss << "nullptr"; diff --git a/src/common/Database/PreparedStatement.h b/src/common/Database/PreparedStatement.h index 8cb513948..c9d8a62f9 100644 --- a/src/common/Database/PreparedStatement.h +++ b/src/common/Database/PreparedStatement.h @@ -45,6 +45,7 @@ enum PreparedStatementValueType TYPE_FLOAT, TYPE_DOUBLE, TYPE_STRING, + TYPE_BINARY, TYPE_NULL }; @@ -52,7 +53,7 @@ struct PreparedStatementData { PreparedStatementDataUnion data; PreparedStatementValueType type; - std::string str; + std::vector binary; }; //- Forward declare @@ -81,6 +82,13 @@ public: void setFloat(const uint8 index, const float value); void setDouble(const uint8 index, const double value); void setString(const uint8 index, const std::string& value); + void setBinary(const uint8 index, const std::vector& value); + template + void setBinary(const uint8 index, std::array const& value) + { + std::vector vec(value.begin(), value.end()); + setBinary(index, vec); + } void setNull(const uint8 index); protected: @@ -115,7 +123,7 @@ public: void setInt64(const uint8 index, const int64 value); void setFloat(const uint8 index, const float value); void setDouble(const uint8 index, const double value); - void setString(const uint8 index, const char* value); + void setBinary(const uint8 index, const std::vector& value, bool isString); void setNull(const uint8 index); protected: diff --git a/src/common/Database/QueryResult.cpp b/src/common/Database/QueryResult.cpp index 0a939b3d9..6b3a5081f 100644 --- a/src/common/Database/QueryResult.cpp +++ b/src/common/Database/QueryResult.cpp @@ -151,8 +151,15 @@ bool ResultSet::NextRow() return false; } + unsigned long* lengths = mysql_fetch_lengths(_result); + if (!lengths) + { + CleanUp(); + return false; + } + for (uint32 i = 0; i < _fieldCount; i++) - _currentRow[i].SetStructuredValue(row[i], _fields[i].type); + _currentRow[i].SetStructuredValue(row[i], _fields[i].type, lengths[i]); return true; } diff --git a/src/common/Logging/Log.cpp b/src/common/Logging/Log.cpp index f7ebb3638..f70d03355 100644 --- a/src/common/Logging/Log.cpp +++ b/src/common/Logging/Log.cpp @@ -9,9 +9,9 @@ #include "WorldPacket.h" #include "Configuration/Config.h" #include "Util.h" -#include "SHA1.h" #include "Implementation/LoginDatabase.h" // For logging + extern LoginDatabaseWorkerPool LoginDatabase; #include diff --git a/src/common/Packets/ByteBuffer.h b/src/common/Packets/ByteBuffer.h index 0547b6066..979446ee8 100644 --- a/src/common/Packets/ByteBuffer.h +++ b/src/common/Packets/ByteBuffer.h @@ -17,6 +17,7 @@ #include #include #include +#include // Root of ByteBuffer exception hierarchy class ByteBufferException : public std::exception @@ -330,6 +331,12 @@ public: _rpos += len; } + template + void read(std::array& arr) + { + read(arr.data(), Size); + } + void readPackGUID(uint64& guid) { if (rpos() + 1 > size()) @@ -452,6 +459,12 @@ public: append(buffer.contents(), buffer.wpos()); } + template + void append(std::array const& arr) + { + append(arr.data(), Size); + } + // can be used in SMSG_MONSTER_MOVE opcode void appendPackXYZ(float x, float y, float z) { diff --git a/src/common/Utilities/Util.cpp b/src/common/Utilities/Util.cpp index bb9de6a2b..0aca756ec 100644 --- a/src/common/Utilities/Util.cpp +++ b/src/common/Utilities/Util.cpp @@ -633,7 +633,7 @@ bool Utf8ToUpperOnlyLatin(std::string& utf8String) return WStrToUtf8(wstr, utf8String); } -std::string ByteArrayToHexStr(uint8 const* bytes, uint32 arrayLen, bool reverse /* = false */) +std::string acore::Impl::ByteArrayToHexStr(uint8 const* bytes, size_t arrayLen, bool reverse /* = false */) { int32 init = 0; int32 end = arrayLen; @@ -657,11 +657,9 @@ std::string ByteArrayToHexStr(uint8 const* bytes, uint32 arrayLen, bool reverse return ss.str(); } -void HexStrToByteArray(std::string const& str, uint8* out, bool reverse /*= false*/) +void acore::Impl::HexStrToByteArray(std::string const& str, uint8* out, size_t outlen, bool reverse /*= false*/) { - // string must have even number of characters - if (str.length() & 1) - return; + ASSERT(str.size() == (2 * outlen)); int32 init = 0; int32 end = int32(str.length()); diff --git a/src/common/Utilities/Util.h b/src/common/Utilities/Util.h index e9f3d44ae..8c7628d0c 100644 --- a/src/common/Utilities/Util.h +++ b/src/common/Utilities/Util.h @@ -17,6 +17,7 @@ #include #include #include +#include // Searcher for map of structs template struct Finder @@ -345,8 +346,30 @@ uint32 GetPID(); bool StringEqualI(std::string_view str1, std::string_view str2); -std::string ByteArrayToHexStr(uint8 const* bytes, uint32 length, bool reverse = false); -void HexStrToByteArray(std::string const& str, uint8* out, bool reverse = false); +namespace acore::Impl +{ + std::string ByteArrayToHexStr(uint8 const* bytes, size_t length, bool reverse = false); + void HexStrToByteArray(std::string const& str, uint8* out, size_t outlen, bool reverse = false); +} + +template +std::string ByteArrayToHexStr(Container const& c, bool reverse = false) +{ + return acore::Impl::ByteArrayToHexStr(std::data(c), std::size(c), reverse); +} + +template +void HexStrToByteArray(std::string const& str, std::array& buf, bool reverse = false) +{ + acore::Impl::HexStrToByteArray(str, buf.data(), Size, reverse); +} +template +std::array HexStrToByteArray(std::string const& str, bool reverse = false) +{ + std::array arr; + HexStrToByteArray(str, arr, reverse); + return arr; +} bool StringContainsStringI(std::string const& haystack, std::string const& needle); template diff --git a/src/server/authserver/Server/AuthSocket.cpp b/src/server/authserver/Server/AuthSocket.cpp index 6580d0502..3262ddc59 100644 --- a/src/server/authserver/Server/AuthSocket.cpp +++ b/src/server/authserver/Server/AuthSocket.cpp @@ -8,6 +8,8 @@ #include #include "Common.h" +#include "CryptoRandom.h" +#include "CryptoHash.h" #include "Database/DatabaseEnv.h" #include "ByteBuffer.h" #include "Configuration/Config.h" @@ -16,7 +18,6 @@ #include "AuthSocket.h" #include "AuthCodes.h" #include "TOTP.h" -#include "SHA1.h" #include "openssl/crypto.h" #define ChunkSize 2048 @@ -64,9 +65,9 @@ typedef struct AUTH_LOGON_CHALLENGE_C typedef struct AUTH_LOGON_PROOF_C { uint8 cmd; - uint8 A[32]; - uint8 M1[20]; - uint8 crc_hash[20]; + acore::Crypto::SRP6::EphemeralKey A; + acore::Crypto::SHA1::Digest clientM; + acore::Crypto::SHA1::Digest crc_hash; uint8 number_of_keys; uint8 securityFlags; // 0x00-0x04 } sAuthLogonProof_C; @@ -75,7 +76,7 @@ typedef struct AUTH_LOGON_PROOF_S { uint8 cmd; uint8 error; - uint8 M2[20]; + acore::Crypto::SHA1::Digest M2; uint32 unk1; uint32 unk2; uint16 unk3; @@ -85,7 +86,7 @@ typedef struct AUTH_LOGON_PROOF_S_OLD { uint8 cmd; uint8 error; - uint8 M2[20]; + acore::Crypto::SHA1::Digest M2; uint32 unk2; } sAuthLogonProof_S_Old; @@ -93,8 +94,7 @@ typedef struct AUTH_RECONNECT_PROOF_C { uint8 cmd; uint8 R1[16]; - uint8 R2[20]; - uint8 R3[20]; + acore::Crypto::SHA1::Digest R2, R3; uint8 number_of_keys; } sAuthReconnectProof_C; @@ -183,8 +183,6 @@ AuthSocket::AuthSocket(RealmSocket& socket) : pPatch(nullptr), socket_(socket), _status(STATUS_CHALLENGE), _build(0), _expversion(0), _accountSecurityLevel(SEC_PLAYER) { - N.SetHexStr("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7"); - g.SetDword(7); } // Close patch file descriptor before leaving @@ -273,45 +271,6 @@ void AuthSocket::OnRead() } } -// Make the SRP6 calculation from hash in dB -void AuthSocket::_SetVSFields(const std::string& rI) -{ - s.SetRand(s_BYTE_SIZE * 8); - - BigNumber I; - I.SetHexStr(rI.c_str()); - - // In case of leading zeros in the rI hash, restore them - uint8 mDigest[SHA_DIGEST_LENGTH]; - memset(mDigest, 0, SHA_DIGEST_LENGTH); - if (I.GetNumBytes() <= SHA_DIGEST_LENGTH) - memcpy(mDigest, I.AsByteArray().get(), I.GetNumBytes()); - - std::reverse(mDigest, mDigest + SHA_DIGEST_LENGTH); - - SHA1Hash sha; - sha.UpdateData(s.AsByteArray().get(), s.GetNumBytes()); - sha.UpdateData(mDigest, SHA_DIGEST_LENGTH); - sha.Finalize(); - BigNumber x; - x.SetBinary(sha.GetDigest(), sha.GetLength()); - v = g.ModExp(x, N); - - // No SQL injection (username escaped) - char* v_hex, *s_hex; - v_hex = v.AsHexStr(); - s_hex = s.AsHexStr(); - - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_VS); - stmt->setString(0, v_hex); - stmt->setString(1, s_hex); - stmt->setString(2, _login); - LoginDatabase.Execute(stmt); - - OPENSSL_free(v_hex); - OPENSSL_free(s_hex); -} - std::map LastLoginAttemptTimeForIP; uint32 LastLoginAttemptCleanTime = 0; ACE_Thread_Mutex LastLoginAttemptMutex; @@ -434,12 +393,12 @@ bool AuthSocket::_HandleLogonChallenge() // If the IP is 'locked', check that the player comes indeed from the correct IP address bool locked = false; - if (fields[2].GetUInt8() == 1) // if ip is locked + if (fields[1].GetUInt8() == 1) // if ip is locked { - sLog->outDebug(LOG_FILTER_NETWORKIO, "[AuthChallenge] Account '%s' is locked to IP - '%s'", _login.c_str(), fields[3].GetCString()); + sLog->outDebug(LOG_FILTER_NETWORKIO, "[AuthChallenge] Account '%s' is locked to IP - '%s'", _login.c_str(), fields[2].GetCString()); sLog->outDebug(LOG_FILTER_NETWORKIO, "[AuthChallenge] Player address is '%s'", ip_address.c_str()); - if (strcmp(fields[4].GetCString(), ip_address.c_str()) != 0) + if (strcmp(fields[3].GetCString(), ip_address.c_str()) != 0) { sLog->outDebug(LOG_FILTER_NETWORKIO, "[AuthChallenge] Account IP differs"); pkt << uint8(WOW_FAIL_LOCKED_ENFORCED); @@ -451,7 +410,7 @@ bool AuthSocket::_HandleLogonChallenge() else { sLog->outDebug(LOG_FILTER_NETWORKIO, "[AuthChallenge] Account '%s' is not locked to ip", _login.c_str()); - std::string accountCountry = fields[3].GetString(); + std::string accountCountry = fields[2].GetString(); if (accountCountry.empty() || accountCountry == "00") sLog->outDebug(LOG_FILTER_NETWORKIO, "[AuthChallenge] Account '%s' is not locked to country", _login.c_str()); else if (!accountCountry.empty()) @@ -486,7 +445,7 @@ bool AuthSocket::_HandleLogonChallenge() // If the account is banned, reject the logon attempt stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BANNED); - stmt->setUInt32(0, fields[1].GetUInt32()); + stmt->setUInt32(0, fields[0].GetUInt32()); PreparedQueryResult banresult = LoginDatabase.Query(stmt); if (banresult) { @@ -503,31 +462,7 @@ bool AuthSocket::_HandleLogonChallenge() } else { - // Get the password from the account table, upper it, and make the SRP6 calculation - std::string rI = fields[0].GetString(); - - // Don't calculate (v, s) if there are already some in the database - std::string databaseV = fields[6].GetString(); - std::string databaseS = fields[7].GetString(); - -#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) - sLog->outDebug(LOG_FILTER_NETWORKIO, "database authentication values: v='%s' s='%s'", databaseV.c_str(), databaseS.c_str()); -#endif - - // multiply with 2 since bytes are stored as hexstring - if (databaseV.size() != s_BYTE_SIZE * 2 || databaseS.size() != s_BYTE_SIZE * 2) - _SetVSFields(rI); - else - { - s.SetHexStr(databaseS.c_str()); - v.SetHexStr(databaseV.c_str()); - } - - b.SetRand(19 * 8); - BigNumber gmod = g.ModExp(b, N); - B = ((v * 3) + gmod) % N; - - ASSERT(gmod.GetNumBytes() <= 32); + _srp6.emplace(_login, fields[5].GetBinary(), fields[6].GetBinary()); BigNumber unk3; unk3.SetRand(16 * 8); @@ -539,17 +474,17 @@ bool AuthSocket::_HandleLogonChallenge() pkt << uint8(WOW_FAIL_VERSION_INVALID); // B may be calculated < 32B so we force minimal length to 32B - pkt.append(B.AsByteArray(32).get(), 32); // 32 bytes + pkt.append(_srp6->B); pkt << uint8(1); - pkt.append(g.AsByteArray().get(), 1); + pkt.append(_srp6->g); pkt << uint8(32); - pkt.append(N.AsByteArray(32).get(), 32); - pkt.append(s.AsByteArray().get(), s.GetNumBytes()); // 32 bytes - pkt.append(unk3.AsByteArray(16).get(), 16); + pkt.append(_srp6->N); + pkt.append(_srp6->s); + pkt.append(unk3.ToByteArray<16>()); uint8 securityFlags = 0; // Check if token is used - _tokenKey = fields[8].GetString(); + _tokenKey = fields[7].GetString(); if (!_tokenKey.empty()) securityFlags = 4; @@ -573,7 +508,7 @@ bool AuthSocket::_HandleLogonChallenge() if (securityFlags & 0x04) // Security token input pkt << uint8(1); - uint8 secLevel = fields[5].GetUInt8(); + uint8 secLevel = fields[4].GetUInt8(); _accountSecurityLevel = secLevel <= SEC_ADMINISTRATOR ? AccountTypes(secLevel) : SEC_ADMINISTRATOR; _localizationName.resize(4); @@ -622,107 +557,25 @@ bool AuthSocket::_HandleLogonProof() return true; } - // Continue the SRP6 calculation based on data received from the client - BigNumber A; - - A.SetBinary(lp.A, 32); - - // SRP safeguard: abort if A == 0 - if ((A % N).isZero()) - { - socket().shutdown(); - return true; - } - - SHA1Hash sha; - sha.UpdateBigNumbers(&A, &B, nullptr); - sha.Finalize(); - BigNumber u; - u.SetBinary(sha.GetDigest(), 20); - BigNumber S = (A * (v.ModExp(u, N))).ModExp(b, N); - - uint8 t[32]; - uint8 t1[16]; - uint8 vK[40]; - memcpy(t, S.AsByteArray(32).get(), 32); - - for (int i = 0; i < 16; ++i) - t1[i] = t[i * 2]; - - sha.Initialize(); - sha.UpdateData(t1, 16); - sha.Finalize(); - - for (int i = 0; i < 20; ++i) - vK[i * 2] = sha.GetDigest()[i]; - - for (int i = 0; i < 16; ++i) - t1[i] = t[i * 2 + 1]; - - sha.Initialize(); - sha.UpdateData(t1, 16); - sha.Finalize(); - - for (int i = 0; i < 20; ++i) - vK[i * 2 + 1] = sha.GetDigest()[i]; - - K.SetBinary(vK, 40); - - uint8 hash[20]; - - sha.Initialize(); - sha.UpdateBigNumbers(&N, nullptr); - sha.Finalize(); - memcpy(hash, sha.GetDigest(), 20); - sha.Initialize(); - sha.UpdateBigNumbers(&g, nullptr); - sha.Finalize(); - - for (int i = 0; i < 20; ++i) - hash[i] ^= sha.GetDigest()[i]; - - BigNumber t3; - t3.SetBinary(hash, 20); - - sha.Initialize(); - sha.UpdateData(_login); - sha.Finalize(); - uint8 t4[SHA_DIGEST_LENGTH]; - memcpy(t4, sha.GetDigest(), SHA_DIGEST_LENGTH); - - sha.Initialize(); - sha.UpdateBigNumbers(&t3, nullptr); - sha.UpdateData(t4, SHA_DIGEST_LENGTH); - sha.UpdateBigNumbers(&s, &A, &B, &K, nullptr); - sha.Finalize(); - BigNumber M; - M.SetBinary(sha.GetDigest(), 20); - - // Check if SRP6 results match (password is correct), else send an error - if (!memcmp(M.AsByteArray().get(), lp.M1, 20)) + if (std::optional K = _srp6->VerifyChallengeResponse(lp.A, lp.clientM)) { + _sessionKey = *K; #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDebug(LOG_FILTER_NETWORKIO, "'%s:%d' User '%s' successfully authenticated", socket().getRemoteAddress().c_str(), socket().getRemotePort(), _login.c_str()); #endif // Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account // No SQL injection (escaped user name) and IP address as received by socket - const char* K_hex = K.AsHexStr(); - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGONPROOF); - stmt->setString(0, K_hex); + stmt->setBinary(0, _sessionKey); stmt->setString(1, socket().getRemoteAddress().c_str()); stmt->setUInt32(2, GetLocaleByName(_localizationName)); stmt->setString(3, _os); stmt->setString(4, _login); LoginDatabase.DirectExecute(stmt); - OPENSSL_free((void*)K_hex); - // Finish SRP6 and send the final result to the client - sha.Initialize(); - sha.UpdateBigNumbers(&A, &M, &K, nullptr); - sha.Finalize(); + acore::Crypto::SHA1::Digest M2 = acore::Crypto::SRP6::GetSessionVerifier(lp.A, lp.clientM, _sessionKey); // Check auth token if ((lp.securityFlags & 0x04) || !_tokenKey.empty()) @@ -746,7 +599,7 @@ bool AuthSocket::_HandleLogonProof() if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients { sAuthLogonProof_S proof; - memcpy(proof.M2, sha.GetDigest(), 20); + proof.M2 = M2; proof.cmd = AUTH_LOGON_PROOF; proof.error = 0; proof.unk1 = 0x00800000; // Accountflags. 0x01 = GM, 0x08 = Trial, 0x00800000 = Pro pass (arena tournament) @@ -757,7 +610,7 @@ bool AuthSocket::_HandleLogonProof() else { sAuthLogonProof_S_Old proof; - memcpy(proof.M2, sha.GetDigest(), 20); + proof.M2 = M2; proof.cmd = AUTH_LOGON_PROOF; proof.error = 0; proof.unk2 = 0x00; @@ -909,7 +762,8 @@ bool AuthSocket::_HandleReconnectChallenge() uint8 secLevel = fields[2].GetUInt8(); _accountSecurityLevel = secLevel <= SEC_ADMINISTRATOR ? AccountTypes(secLevel) : SEC_ADMINISTRATOR; - K.SetHexStr ((*result)[0].GetCString()); + _sessionKey = fields[0].GetBinary(); + acore::Crypto::GetRandomBytes(_reconnectProof); ///- All good, await client's proof _status = STATUS_RECON_PROOF; @@ -918,8 +772,7 @@ bool AuthSocket::_HandleReconnectChallenge() ByteBuffer pkt; pkt << uint8(AUTH_RECONNECT_CHALLENGE); pkt << uint8(0x00); - _reconnectProof.SetRand(16 * 8); - pkt.append(_reconnectProof.AsByteArray(16).get(), 16); // 16 bytes random + pkt.append(_reconnectProof); // 16 bytes random pkt << uint64(0x00) << uint64(0x00); // 16 bytes zeros socket().send((char const*)pkt.contents(), pkt.size()); return true; @@ -938,19 +791,20 @@ bool AuthSocket::_HandleReconnectProof() _status = STATUS_CLOSED; - if (_login.empty() || !_reconnectProof.GetNumBytes() || !K.GetNumBytes()) + if (_login.empty()) return false; BigNumber t1; t1.SetBinary(lp.R1, 16); - SHA1Hash sha; - sha.Initialize(); + acore::Crypto::SHA1 sha; sha.UpdateData(_login); - sha.UpdateBigNumbers(&t1, &_reconnectProof, &K, nullptr); + sha.UpdateData(t1.ToByteArray<16>()); + sha.UpdateData(_reconnectProof); + sha.UpdateData(_sessionKey); sha.Finalize(); - if (!memcmp(sha.GetDigest(), lp.R2, SHA_DIGEST_LENGTH)) + if (sha.GetDigest() == lp.R2) { // Sending response ByteBuffer pkt; diff --git a/src/server/authserver/Server/AuthSocket.h b/src/server/authserver/Server/AuthSocket.h index ca21fe76d..267928d0b 100644 --- a/src/server/authserver/Server/AuthSocket.h +++ b/src/server/authserver/Server/AuthSocket.h @@ -8,8 +8,9 @@ #define _AUTHSOCKET_H #include "Common.h" -#include "BigNumber.h" +#include "CryptoHash.h" #include "RealmSocket.h" +#include "SRP6.h" class ACE_INET_Addr; struct Realm; @@ -50,8 +51,6 @@ public: bool _HandleXferCancel(); bool _HandleXferAccept(); - void _SetVSFields(const std::string& rI); - FILE* pPatch; ACE_Thread_Mutex patcherLock; @@ -59,10 +58,9 @@ private: RealmSocket& socket_; RealmSocket& socket() { return socket_; } - BigNumber N, s, g, v; - BigNumber b, B; - BigNumber K; - BigNumber _reconnectProof; + std::optional _srp6; + SessionKey _sessionKey = {}; + std::array _reconnectProof = {}; eStatus _status; diff --git a/src/server/game/Accounts/AccountMgr.cpp b/src/server/game/Accounts/AccountMgr.cpp index 2ae533ee4..f894d7b34 100644 --- a/src/server/game/Accounts/AccountMgr.cpp +++ b/src/server/game/Accounts/AccountMgr.cpp @@ -5,11 +5,12 @@ */ #include "AccountMgr.h" +#include "CryptoHash.h" #include "DatabaseEnv.h" #include "ObjectAccessor.h" #include "Player.h" #include "ScriptMgr.h" -#include "SHA1.h" +#include "SRP6.h" #include "Util.h" #include "WorldSession.h" @@ -28,13 +29,15 @@ namespace AccountMgr Utf8ToUpperOnlyLatin(password); if (GetId(username)) - return AOR_NAME_ALREDY_EXIST; // username does already exist + return AOR_NAME_ALREADY_EXIST; // username does already exist PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT); stmt->setString(0, username); - stmt->setString(1, CalculateShaPassHash(username, password)); - stmt->setInt8(2, uint8(sWorld->getIntConfig(CONFIG_EXPANSION))); + auto [salt, verifier] = acore::Crypto::SRP6::MakeRegistrationData(username, password); + stmt->setBinary(1, salt); + stmt->setBinary(2, verifier); + stmt->setInt8(3, uint8(sWorld->getIntConfig(CONFIG_EXPANSION))); LoginDatabase.Execute(stmt); @@ -141,11 +144,15 @@ namespace AccountMgr Utf8ToUpperOnlyLatin(newPassword); stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_USERNAME); - stmt->setString(0, newUsername); - stmt->setString(1, CalculateShaPassHash(newUsername, newPassword)); - stmt->setUInt32(2, accountId); + stmt->setUInt32(1, accountId); + LoginDatabase.Execute(stmt); + auto [salt, verifier] = acore::Crypto::SRP6::MakeRegistrationData(newUsername, newPassword); + stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGON); + stmt->setBinary(0, salt); + stmt->setBinary(1, verifier); + stmt->setUInt32(2, accountId); LoginDatabase.Execute(stmt); return AOR_OK; @@ -170,11 +177,12 @@ namespace AccountMgr Utf8ToUpperOnlyLatin(username); Utf8ToUpperOnlyLatin(newPassword); - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_PASSWORD); - - stmt->setString(0, CalculateShaPassHash(username, newPassword)); - stmt->setUInt32(1, accountId); + auto [salt, verifier] = acore::Crypto::SRP6::MakeRegistrationData(username, newPassword); + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGON); + stmt->setBinary(0, salt); + stmt->setBinary(1, verifier); + stmt->setUInt32(2, accountId);; LoginDatabase.Execute(stmt); sScriptMgr->OnPasswordChange(accountId); @@ -236,10 +244,15 @@ namespace AccountMgr PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_CHECK_PASSWORD); stmt->setUInt32(0, accountId); - stmt->setString(1, CalculateShaPassHash(username, password)); - PreparedQueryResult result = LoginDatabase.Query(stmt); + if (PreparedQueryResult result = LoginDatabase.Query(stmt)) + { + acore::Crypto::SRP6::Salt salt = (*result)[0].GetBinary(); + acore::Crypto::SRP6::Verifier verifier = (*result)[1].GetBinary(); + if (acore::Crypto::SRP6::CheckLogin(username, password, salt, verifier)) + return true; + } - return !!result; + return false; } uint32 GetCharactersCount(uint32 accountId) @@ -252,18 +265,6 @@ namespace AccountMgr return (result) ? (*result)[0].GetUInt64() : 0; } - std::string CalculateShaPassHash(std::string const& name, std::string const& password) - { - SHA1Hash sha; - sha.Initialize(); - sha.UpdateData(name); - sha.UpdateData(":"); - sha.UpdateData(password); - sha.Finalize(); - - return ByteArrayToHexStr(sha.GetDigest(), sha.GetLength()); - } - bool IsPlayerAccount(uint32 gmlevel) { return gmlevel == SEC_PLAYER; diff --git a/src/server/game/Accounts/AccountMgr.h b/src/server/game/Accounts/AccountMgr.h index ac728ece7..9f102811e 100644 --- a/src/server/game/Accounts/AccountMgr.h +++ b/src/server/game/Accounts/AccountMgr.h @@ -15,7 +15,7 @@ enum AccountOpResult AOR_OK, AOR_NAME_TOO_LONG, AOR_PASS_TOO_LONG, - AOR_NAME_ALREDY_EXIST, + AOR_NAME_ALREADY_EXIST, AOR_NAME_NOT_EXIST, AOR_DB_INTERNAL_ERROR }; @@ -36,7 +36,6 @@ namespace AccountMgr uint32 GetSecurity(uint32 accountId, int32 realmId); bool GetName(uint32 accountId, std::string& name); uint32 GetCharactersCount(uint32 accountId); - std::string CalculateShaPassHash(std::string const& name, std::string const& password); bool IsPlayerAccount(uint32 gmlevel); bool IsGMAccount(uint32 gmlevel); diff --git a/src/server/game/Guilds/Guild.cpp b/src/server/game/Guilds/Guild.cpp index 44d96ed75..450b14b39 100644 --- a/src/server/game/Guilds/Guild.cpp +++ b/src/server/game/Guilds/Guild.cpp @@ -1740,7 +1740,7 @@ void Guild::HandleMemberDepositMoney(WorldSession* session, uint32 amount) CharacterDatabase.CommitTransaction(trans); - std::string aux = ByteArrayToHexStr(reinterpret_cast(&m_bankMoney), 8, true); + std::string aux = acore::Impl::ByteArrayToHexStr(reinterpret_cast(&m_bankMoney), 8, true); _BroadcastEvent(GE_BANK_MONEY_SET, 0, aux.c_str()); if (amount > 10 * GOLD) @@ -1789,7 +1789,7 @@ bool Guild::HandleMemberWithdrawMoney(WorldSession* session, uint32 amount, bool if (amount > 10 * GOLD) CharacterDatabase.PExecute("INSERT INTO log_money VALUES(%u, %u, \"%s\", \"%s\", %u, \"%s\", %u, \" %s (guild id: %u, members: %u, new amount: %u, leader guid low: %u, char level: %u)\", NOW())", session->GetAccountId(), player->GetGUIDLow(), player->GetName().c_str(), session->GetRemoteAddress().c_str(), 0, "", amount, GetName().c_str(), GetId(), GetMemberCount(), GetTotalBankMoney(), (uint32)(GetLeaderGUID() & 0xFFFFFFFF), player->getLevel()); - std::string aux = ByteArrayToHexStr(reinterpret_cast(&m_bankMoney), 8, true); + std::string aux = acore::Impl::ByteArrayToHexStr(reinterpret_cast(&m_bankMoney), 8, true); _BroadcastEvent(GE_BANK_MONEY_SET, 0, aux.c_str()); return true; } diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index b7a221270..3116aca43 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -32,7 +32,6 @@ #include "Pet.h" #include "Player.h" #include "ScriptMgr.h" -#include "SHA1.h" #include "SocialMgr.h" #include "Spell.h" #include "UpdateData.h" diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 8b28393e8..c7a5433be 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -1331,7 +1331,7 @@ void WorldSession::ProcessQueryCallbackLogin() } } -void WorldSession::InitWarden(BigNumber* k, std::string const& os) +void WorldSession::InitWarden(SessionKey const& k, std::string const& os) { if (os == "Win") { diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 487c2a28e..a6655c4f3 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -12,10 +12,10 @@ #define __WORLDSESSION_H #include "AccountMgr.h" +#include "AuthDefines.h" #include "AddonMgr.h" #include "BanManager.h" #include "Common.h" -#include "Cryptography/BigNumber.h" #include "DatabaseEnv.h" #include "GossipDef.h" #include "Opcodes.h" @@ -239,7 +239,7 @@ public: void SetTotalTime(uint32 TotalTime) { m_total_time = TotalTime; } uint32 GetTotalTime() const { return m_total_time; } - void InitWarden(BigNumber* k, std::string const& os); + void InitWarden(SessionKey const&, std::string const& os); /// Session in auth.queue currently void SetInQueue(bool state) { m_inQueue = state; } diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index 2360b4d32..e2b2c20b3 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -8,13 +8,14 @@ #include "BigNumber.h" #include "ByteBuffer.h" #include "Common.h" +#include "CryptoHash.h" +#include "CryptoRandom.h" #include "DatabaseEnv.h" #include "Log.h" #include "Opcodes.h" #include "PacketLog.h" #include "Player.h" #include "ScriptMgr.h" -#include "SHA1.h" #include "SharedDefines.h" #include "Util.h" #include "World.h" @@ -92,9 +93,10 @@ struct ClientPktHeader WorldSocket::WorldSocket(void): WorldHandler(), m_LastPingTime(SystemTimePoint::min()), m_OverSpeedPings(0), m_Session(0), m_RecvWPct(0), m_RecvPct(), m_Header(sizeof (ClientPktHeader)), - m_OutBuffer(0), m_OutBufferSize(65536), m_OutActive(false), - m_Seed(static_cast (rand32())) + m_OutBuffer(0), m_OutBufferSize(65536), m_OutActive(false) { + acore::Crypto::GetRandomBytes(m_Seed); + reference_counting_policy().value (ACE_Event_Handler::Reference_Counting_Policy::ENABLED); msg_queue()->high_water_mark(8 * 1024 * 1024); @@ -157,7 +159,9 @@ int WorldSocket::SendPacket(WorldPacket const& pct) sPacketLog->LogPacket(pct, SERVER_TO_CLIENT); ServerPktHeader header(pct.size() + 2, pct.GetOpcode()); - m_Crypt.EncryptSend ((uint8*)header.header, header.getHeaderLength()); + + if (m_Crypt.IsInitialized()) + m_Crypt.EncryptSend((uint8*)header.header, header.getHeaderLength()); if (m_OutBuffer->space() >= pct.size() + header.getHeaderLength() && msg_queue()->is_empty()) { @@ -235,15 +239,8 @@ int WorldSocket::open(void* a) // Send startup packet. WorldPacket packet (SMSG_AUTH_CHALLENGE, 24); packet << uint32(1); // 1...31 - packet << m_Seed; - - BigNumber seed1; - seed1.SetRand(16 * 8); - packet.append(seed1.AsByteArray(16).get(), 16); // new encryption seeds - - BigNumber seed2; - seed2.SetRand(16 * 8); - packet.append(seed2.AsByteArray(16).get(), 16); // new encryption seeds + packet.append(m_Seed); + packet.append(acore::Crypto::GetRandomBytes<32>()); // new encryption seeds if (SendPacket(packet) == -1) return -1; @@ -470,7 +467,8 @@ int WorldSocket::handle_input_header(void) ACE_ASSERT (m_Header.length() == sizeof(ClientPktHeader)); - m_Crypt.DecryptRecv ((uint8*) m_Header.rd_ptr(), sizeof(ClientPktHeader)); + if (m_Crypt.IsInitialized()) + m_Crypt.DecryptRecv((uint8*) m_Header.rd_ptr(), sizeof(ClientPktHeader)); ClientPktHeader& header = *((ClientPktHeader*) m_Header.rd_ptr()); @@ -736,8 +734,6 @@ int WorldSocket::ProcessIncoming(WorldPacket* new_pct) int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) { // NOTE: ATM the socket is singlethread, have this in mind ... - uint8 digest[20]; - uint32 clientSeed; uint32 loginServerID, loginServerType, regionID, battlegroupID, realm; uint64 DosResponse; uint32 BuiltNumberClient; @@ -747,10 +743,10 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) //uint8 expansion = 0; LocaleConstant locale; std::string account; - SHA1Hash sha; WorldPacket packet, SendAddonPacked; + std::array clientSeed; + acore::Crypto::SHA1::Digest digest; - BigNumber k; bool wardenActive = sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED); if (sWorld->IsClosed()) @@ -768,12 +764,12 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) recvPacket >> loginServerID; recvPacket >> account; recvPacket >> loginServerType; - recvPacket >> clientSeed; + recvPacket.read(clientSeed); recvPacket >> regionID; recvPacket >> battlegroupID; recvPacket >> realm; recvPacket >> DosResponse; - recvPacket.read(digest, 20); + recvPacket.read(digest); #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outStaticDebug ("WorldSocket::HandleAuthSession: client %u, loginServerID %u, account %s, loginServerType %u, clientseed %u", BuiltNumberClient, loginServerID, account.c_str(), loginServerType, clientSeed); @@ -843,7 +839,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) security = SEC_ADMINISTRATOR; */ - k.SetHexStr (fields[1].GetCString()); + SessionKey sessionKey = fields[1].GetBinary(); int64 mutetime = fields[6].GetInt64(); //! Negative mutetime indicates amount of seconds to be muted effective on next login - which is now. @@ -934,17 +930,17 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) } // Check that Key and account name are the same on client and server - uint32 t = 0; - uint32 seed = m_Seed; + uint8 t[4] = { 0x00, 0x00, 0x00, 0x00 }; + acore::Crypto::SHA1 sha; sha.UpdateData (account); - sha.UpdateData ((uint8*) & t, 4); - sha.UpdateData ((uint8*) & clientSeed, 4); - sha.UpdateData ((uint8*) & seed, 4); - sha.UpdateBigNumbers (&k, nullptr); + sha.UpdateData(t); + sha.UpdateData(clientSeed); + sha.UpdateData(m_Seed); + sha.UpdateData(sessionKey); sha.Finalize(); - if (memcmp (sha.GetDigest(), digest, 20)) + if (sha.GetDigest() != digest) { packet.Initialize (SMSG_AUTH_RESPONSE, 1); packet << uint8 (AUTH_FAILED); @@ -984,7 +980,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) // NOTE ATM the socket is single-threaded, have this in mind ... ACE_NEW_RETURN(m_Session, WorldSession(id, this, AccountTypes(security), expansion, mutetime, locale, recruiter, isRecruiter, skipQueue, TotalTime), -1); - m_Crypt.Init(&k); + m_Crypt.Init(sessionKey); // First reject the connection if packet contains invalid data or realm state doesn't allow logging in if (sWorld->IsClosed()) @@ -1019,7 +1015,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) // Initialize Warden system only if it is enabled by config if (wardenActive) - m_Session->InitWarden(&k, os); + m_Session->InitWarden(sessionKey, os); // Sleep this Network thread for uint32 sleepTime = sWorld->getIntConfig(CONFIG_SESSION_ADD_DELAY); diff --git a/src/server/game/Server/WorldSocket.h b/src/server/game/Server/WorldSocket.h index fe55fa079..ee38567a6 100644 --- a/src/server/game/Server/WorldSocket.h +++ b/src/server/game/Server/WorldSocket.h @@ -189,7 +189,7 @@ private: /// True if the socket is registered with the reactor for output bool m_OutActive; - uint32 m_Seed; + std::array m_Seed; }; #endif /* _WORLDSOCKET_H */ diff --git a/src/server/game/Warden/Warden.cpp b/src/server/game/Warden/Warden.cpp index 631e212d5..ffa1272c7 100644 --- a/src/server/game/Warden/Warden.cpp +++ b/src/server/game/Warden/Warden.cpp @@ -20,7 +20,7 @@ #include #include -Warden::Warden() : _session(nullptr), _inputCrypto(16), _outputCrypto(16), _checkTimer(10000/*10 sec*/), _clientResponseTimer(0), +Warden::Warden() : _session(nullptr), _checkTimer(10000/*10 sec*/), _clientResponseTimer(0), _dataSent(false), _module(nullptr), _initialized(false) { memset(_inputKey, 0, sizeof(_inputKey)); @@ -125,12 +125,12 @@ void Warden::Update(uint32 const diff) void Warden::DecryptData(uint8* buffer, uint32 length) { - _inputCrypto.UpdateData(length, buffer); + _inputCrypto.UpdateData(buffer, length); } void Warden::EncryptData(uint8* buffer, uint32 length) { - _outputCrypto.UpdateData(length, buffer); + _outputCrypto.UpdateData(buffer, length); } bool Warden::IsValidCheckSum(uint32 checksum, const uint8* data, const uint16 length) diff --git a/src/server/game/Warden/Warden.h b/src/server/game/Warden/Warden.h index 6451a9530..bad486f4d 100644 --- a/src/server/game/Warden/Warden.h +++ b/src/server/game/Warden/Warden.h @@ -7,11 +7,11 @@ #ifndef _WARDEN_BASE_H #define _WARDEN_BASE_H +#include "ARC4.h" +#include "AuthDefines.h" #include "ByteBuffer.h" -#include "Cryptography/ARC4.h" -#include "Cryptography/BigNumber.h" #include "WardenCheckMgr.h" -#include +#include enum WardenOpcodes { @@ -97,7 +97,7 @@ public: Warden(); virtual ~Warden(); - virtual void Init(WorldSession* session, BigNumber* k) = 0; + virtual void Init(WorldSession* session, SessionKey const& k) = 0; virtual ClientWardenModule* GetModuleForClient() = 0; virtual void InitializeModule() = 0; virtual void RequestHash() = 0; @@ -123,8 +123,8 @@ private: uint8 _inputKey[16]; uint8 _outputKey[16]; uint8 _seed[16]; - ARC4 _inputCrypto; - ARC4 _outputCrypto; + acore::Crypto::ARC4 _inputCrypto; + acore::Crypto::ARC4 _outputCrypto; uint32 _checkTimer; // Timer for sending check requests uint32 _clientResponseTimer; // Timer for client response delay bool _dataSent; diff --git a/src/server/game/Warden/WardenCheckMgr.cpp b/src/server/game/Warden/WardenCheckMgr.cpp index 24732e82c..dfa580085 100644 --- a/src/server/game/Warden/WardenCheckMgr.cpp +++ b/src/server/game/Warden/WardenCheckMgr.cpp @@ -103,16 +103,6 @@ void WardenCheckMgr::LoadWardenChecks() { WardenCheckResult wr; wr.Result.SetHexStr(checkResult.c_str()); - int len = static_cast(checkResult.size()) / 2; - if (wr.Result.GetNumBytes() < len) - { - uint8* temp = new uint8[len]; - memset(temp, 0, len); - memcpy(temp, wr.Result.AsByteArray().get(), wr.Result.GetNumBytes()); - std::reverse(temp, temp + len); - wr.Result.SetBinary((uint8*)temp, len); - delete [] temp; - } CheckResultStore[id] = wr; } @@ -148,19 +138,7 @@ void WardenCheckMgr::LoadWardenChecks() default: { if (checkType == PAGE_CHECK_A || checkType == PAGE_CHECK_B || checkType == DRIVER_CHECK) - { wardenCheck.Data.SetHexStr(data.c_str()); - int len = static_cast(data.size()) / 2; - - if (wardenCheck.Data.GetNumBytes() < len) - { - uint8 temp[24]; - memset(temp, 0, len); - memcpy(temp, wardenCheck.Data.AsByteArray().get(), wardenCheck.Data.GetNumBytes()); - std::reverse(temp, temp + len); - wardenCheck.Data.SetBinary((uint8*)temp, len); - } - } CheckIdPool[WARDEN_CHECK_OTHER_TYPE].push_back(id); break; diff --git a/src/server/game/Warden/WardenMac.cpp b/src/server/game/Warden/WardenMac.cpp index 162d3f7d0..794e101dd 100644 --- a/src/server/game/Warden/WardenMac.cpp +++ b/src/server/game/Warden/WardenMac.cpp @@ -6,10 +6,10 @@ #include "ByteBuffer.h" #include "Common.h" -#include "WardenKeyGeneration.h" #include "Log.h" #include "Opcodes.h" #include "Player.h" +#include "SessionKeyGenerator.h" #include "Util.h" #include "WardenMac.h" #include "WardenModuleMac.h" @@ -26,11 +26,11 @@ WardenMac::~WardenMac() { } -void WardenMac::Init(WorldSession* pClient, BigNumber* K) +void WardenMac::Init(WorldSession* pClient, SessionKey const& K) { _session = pClient; // Generate Warden Key - SHA1Randx WK(K->AsByteArray().get(), K->GetNumBytes()); + SessionKeyGenerator WK(K); WK.Generate(_inputKey, 16); WK.Generate(_outputKey, 16); /* @@ -48,17 +48,17 @@ void WardenMac::Init(WorldSession* pClient, BigNumber* K) _outputCrypto.Init(_outputKey); #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDebug(LOG_FILTER_WARDEN, "Server side warden for client %u initializing...", pClient->GetAccountId()); - sLog->outDebug(LOG_FILTER_WARDEN, "C->S Key: %s", ByteArrayToHexStr(_inputKey, 16).c_str()); - sLog->outDebug(LOG_FILTER_WARDEN, "S->C Key: %s", ByteArrayToHexStr(_outputKey, 16).c_str()); - sLog->outDebug(LOG_FILTER_WARDEN, " Seed: %s", ByteArrayToHexStr(_seed, 16).c_str()); + sLog->outDebug(LOG_FILTER_WARDEN, "C->S Key: %s", acore::Impl::ByteArrayToHexStr(_inputKey).c_str()); + sLog->outDebug(LOG_FILTER_WARDEN, "S->C Key: %s", acore::Impl::ByteArrayToHexStr(_outputKey).c_str()); + sLog->outDebug(LOG_FILTER_WARDEN, " Seed: %s", acore::Impl::ByteArrayToHexStr(_seed).c_str()); sLog->outDebug(LOG_FILTER_WARDEN, "Loading Module..."); #endif _module = GetModuleForClient(); #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) - sLog->outDebug(LOG_FILTER_WARDEN, "Module Key: %s", ByteArrayToHexStr(_module->Key, 16).c_str()); - sLog->outDebug(LOG_FILTER_WARDEN, "Module ID: %s", ByteArrayToHexStr(_module->Id, 16).c_str()); + sLog->outDebug(LOG_FILTER_WARDEN, "Module Key: %s", acore::Impl::ByteArrayToHexStr(_module->Key).c_str()); + sLog->outDebug(LOG_FILTER_WARDEN, "Module ID: %s", acore::Impl::ByteArrayToHexStr(_module->Id).c_str()); #endif RequestModule(); } @@ -154,14 +154,14 @@ void WardenMac::HandleHashResult(ByteBuffer& buff) buff.rpos(buff.wpos()); - SHA1Hash sha1; + acore::Crypto::SHA1 sha1; sha1.UpdateData((uint8*)keyIn, 16); sha1.Finalize(); //const uint8 validHash[20] = { 0x56, 0x8C, 0x05, 0x4C, 0x78, 0x1A, 0x97, 0x2A, 0x60, 0x37, 0xA2, 0x29, 0x0C, 0x22, 0xB5, 0x25, 0x71, 0xA0, 0x6F, 0x4E }; // Verify key - if (memcmp(buff.contents() + 1, sha1.GetDigest(), 20) != 0) + if (memcmp(buff.contents() + 1, sha1.GetDigest().data(), 20) != 0) { #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDebug(LOG_FILTER_WARDEN, "Request hash reply: failed"); @@ -242,16 +242,16 @@ void WardenMac::HandleData(ByteBuffer& buff) std::string str = "Test string!"; - SHA1Hash sha1; + acore::Crypto::SHA1 sha1; sha1.UpdateData(str); uint32 magic = 0xFEEDFACE; // unsure sha1.UpdateData((uint8*)&magic, 4); sha1.Finalize(); - uint8 sha1Hash[20]; - buff.read(sha1Hash, 20); + std::array sha1Hash; + buff.read(sha1Hash.data(), sha1Hash.size()); - if (memcmp(sha1Hash, sha1.GetDigest(), 20)) + if (sha1Hash != sha1.GetDigest()) { #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDebug(LOG_FILTER_WARDEN, "Handle data failed: SHA1 hash is wrong!"); diff --git a/src/server/game/Warden/WardenMac.h b/src/server/game/Warden/WardenMac.h index 0786cc2ff..82f356731 100644 --- a/src/server/game/Warden/WardenMac.h +++ b/src/server/game/Warden/WardenMac.h @@ -9,7 +9,6 @@ #include "ByteBuffer.h" #include "ARC4.h" -#include "BigNumber.h" #include "Warden.h" #include @@ -22,7 +21,7 @@ public: WardenMac(); ~WardenMac() override; - void Init(WorldSession* session, BigNumber* k) override; + void Init(WorldSession* session, SessionKey const& k) override; ClientWardenModule* GetModuleForClient() override; void InitializeModule() override; void RequestHash() override; diff --git a/src/server/game/Warden/WardenWin.cpp b/src/server/game/Warden/WardenWin.cpp index 1e93ef888..51cf5d978 100644 --- a/src/server/game/Warden/WardenWin.cpp +++ b/src/server/game/Warden/WardenWin.cpp @@ -7,12 +7,13 @@ #include "AccountMgr.h" #include "ByteBuffer.h" #include "Common.h" -#include "Cryptography/HMACSHA1.h" -#include "Cryptography/WardenKeyGeneration.h" +#include "CryptoRandom.h" #include "Database/DatabaseEnv.h" +#include "HMAC.h" #include "Log.h" #include "Opcodes.h" #include "Player.h" +#include "SessionKeyGenerator.h" #include "Util.h" #include "WardenCheckMgr.h" #include "WardenModuleWin.h" @@ -38,7 +39,7 @@ static constexpr uint8 GetCheckPacketBaseSize(uint8 type) case LUA_EVAL_CHECK: return 1 + sizeof(_luaEvalPrefix) - 1 + sizeof(_luaEvalMidfix) - 1 + 4 + sizeof(_luaEvalPostfix) - 1; case PAGE_CHECK_A: return (4 + 1); case PAGE_CHECK_B: return (4 + 1); - case MODULE_CHECK: return (4 + SHA_DIGEST_LENGTH); + case MODULE_CHECK: return (4 + acore::Crypto::Constants::SHA1_DIGEST_LENGTH_BYTES); case MEM_CHECK: return (1 + 4 + 1); default: return 0; } @@ -90,11 +91,11 @@ WardenWin::~WardenWin() { } -void WardenWin::Init(WorldSession* session, BigNumber* k) +void WardenWin::Init(WorldSession* session, SessionKey const& k) { _session = session; // Generate Warden Key - SHA1Randx WK(k->AsByteArray().get(), k->GetNumBytes()); + SessionKeyGenerator WK(k); WK.Generate(_inputKey, 16); WK.Generate(_outputKey, 16); @@ -104,17 +105,17 @@ void WardenWin::Init(WorldSession* session, BigNumber* k) _outputCrypto.Init(_outputKey); #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDebug(LOG_FILTER_WARDEN, "Server side warden for client %u initializing...", session->GetAccountId()); - sLog->outDebug(LOG_FILTER_WARDEN, "C->S Key: %s", ByteArrayToHexStr(_inputKey, 16).c_str()); - sLog->outDebug(LOG_FILTER_WARDEN, "S->C Key: %s", ByteArrayToHexStr(_outputKey, 16).c_str()); - sLog->outDebug(LOG_FILTER_WARDEN, " Seed: %s", ByteArrayToHexStr(_seed, 16).c_str()); + sLog->outDebug(LOG_FILTER_WARDEN, "C->S Key: %s", acore::Impl::ByteArrayToHexStr(_inputKey).c_str()); + sLog->outDebug(LOG_FILTER_WARDEN, "S->C Key: %s", acore::Impl::ByteArrayToHexStr(_outputKey).c_str()); + sLog->outDebug(LOG_FILTER_WARDEN, " Seed: %s", acore::Impl::ByteArrayToHexStr(_seed).c_str()); sLog->outDebug(LOG_FILTER_WARDEN, "Loading Module..."); #endif _module = GetModuleForClient(); #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) - sLog->outDebug(LOG_FILTER_WARDEN, "Module Key: %s", ByteArrayToHexStr(_module->Key, 16).c_str()); - sLog->outDebug(LOG_FILTER_WARDEN, "Module ID: %s", ByteArrayToHexStr(_module->Id, 16).c_str()); + sLog->outDebug(LOG_FILTER_WARDEN, "Module Key: %s", acore::Impl::ByteArrayToHexStr(_module->Key).c_str()); + sLog->outDebug(LOG_FILTER_WARDEN, "Module ID: %s", acore::Impl::ByteArrayToHexStr(_module->Id).c_str()); #endif RequestModule(); } @@ -158,7 +159,7 @@ void WardenWin::InitializeModule() Request.Function1[1] = 0x000218C0; // 0x00400000 + 0x000218C0 SFileGetFileSize Request.Function1[2] = 0x00022530; // 0x00400000 + 0x00022530 SFileReadFile Request.Function1[3] = 0x00022910; // 0x00400000 + 0x00022910 SFileCloseFile - Request.CheckSumm1 = BuildChecksum(&Request.Unk1, SHA_DIGEST_LENGTH); + Request.CheckSumm1 = BuildChecksum(&Request.Unk1, acore::Crypto::Constants::SHA1_DIGEST_LENGTH_BYTES); Request.Command2 = WARDEN_SMSG_MODULE_INITIALIZE; Request.Size2 = 8; @@ -223,7 +224,7 @@ void WardenWin::HandleHashResult(ByteBuffer& buff) buff.rpos(buff.wpos()); // Verify key - if (memcmp(buff.contents() + 1, Module.ClientKeySeedHash, SHA_DIGEST_LENGTH) != 0) + if (memcmp(buff.contents() + 1, Module.ClientKeySeedHash, acore::Crypto::Constants::SHA1_DIGEST_LENGTH_BYTES) != 0) { #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDebug(LOG_FILTER_WARDEN, "Request hash reply: failed"); @@ -396,8 +397,8 @@ void WardenWin::RequestChecks() case PAGE_CHECK_A: case PAGE_CHECK_B: { - BigNumber tempNumber = check->Data; - buff.append(tempNumber.AsByteArray(0, false).get(), tempNumber.GetNumBytes()); + std::vector data = check->Data.ToByteVector(0, false); + buff.append(data.data(), data.size()); buff << uint32(check->Address); buff << uint8(check->Length); break; @@ -410,19 +411,16 @@ void WardenWin::RequestChecks() } case DRIVER_CHECK: { - BigNumber tempNumber = check->Data; - buff.append(tempNumber.AsByteArray(0, false).get(), tempNumber.GetNumBytes()); + std::vector data = check->Data.ToByteVector(0, false); + buff.append(data.data(), data.size()); buff << uint8(index++); break; } case MODULE_CHECK: { - uint32 seed = rand32(); - buff << uint32(seed); - HmacHash hmac(4, (uint8*)&seed); - hmac.UpdateData(check->Str); - hmac.Finalize(); - buff.append(hmac.GetDigest(), hmac.GetLength()); + std::array seed = acore::Crypto::GetRandomBytes<4>(); + buff.append(seed); + buff.append(acore::Crypto::HMAC_SHA1::GetDigestOf(seed, check->Str)); break; } /*case PROC_CHECK: @@ -542,8 +540,9 @@ void WardenWin::HandleData(ByteBuffer& buff) } WardenCheckResult const* rs = sWardenCheckMgr->GetWardenResultById(checkId); - BigNumber tempNumber = rs->Result; - if (memcmp(buff.contents() + buff.rpos(), tempNumber.AsByteArray(0, false).get(), rd->Length) != 0) + + std::vector result = rs->Result.ToByteVector(0, false); + if (memcmp(buff.contents() + buff.rpos(), result.data(), rd->Length) != 0) { #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDebug(LOG_FILTER_WARDEN, "RESULT MEM_CHECK fail CheckId %u account Id %u", checkId, _session->GetAccountId()); @@ -622,18 +621,17 @@ void WardenWin::HandleData(ByteBuffer& buff) } WardenCheckResult const* rs = sWardenCheckMgr->GetWardenResultById(checkId); - BigNumber tempNumber = rs->Result; - if (memcmp(buff.contents() + buff.rpos(), tempNumber.AsByteArray(0, false).get(), SHA_DIGEST_LENGTH) != 0) // SHA1 + if (memcmp(buff.contents() + buff.rpos(), rs->Result.ToByteArray<20>(false).data(), acore::Crypto::Constants::SHA1_DIGEST_LENGTH_BYTES) != 0) // SHA1 { #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDebug(LOG_FILTER_WARDEN, "RESULT MPQ_CHECK fail, CheckId %u account Id %u", checkId, _session->GetAccountId()); #endif checkFailed = checkId; - buff.rpos(buff.rpos() + SHA_DIGEST_LENGTH); // 20 bytes SHA1 + buff.rpos(buff.rpos() + acore::Crypto::Constants::SHA1_DIGEST_LENGTH_BYTES); // 20 bytes SHA1 continue; } - buff.rpos(buff.rpos() + SHA_DIGEST_LENGTH); // 20 bytes SHA1 + buff.rpos(buff.rpos() + acore::Crypto::Constants::SHA1_DIGEST_LENGTH_BYTES); // 20 bytes SHA1 #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDebug(LOG_FILTER_WARDEN, "RESULT MPQ_CHECK passed, CheckId %u account Id %u", checkId, _session->GetAccountId()); #endif diff --git a/src/server/game/Warden/WardenWin.h b/src/server/game/Warden/WardenWin.h index a00acc43f..578f4bc21 100644 --- a/src/server/game/Warden/WardenWin.h +++ b/src/server/game/Warden/WardenWin.h @@ -64,7 +64,7 @@ public: WardenWin(); ~WardenWin() override; - void Init(WorldSession* session, BigNumber* K) override; + void Init(WorldSession* session, SessionKey const& K) override; ClientWardenModule* GetModuleForClient() override; void InitializeModule() override; void RequestHash() override; diff --git a/src/server/game/World/IWorld.h b/src/server/game/World/IWorld.h index bf0aa6bf9..6d6a1a7c6 100644 --- a/src/server/game/World/IWorld.h +++ b/src/server/game/World/IWorld.h @@ -164,6 +164,7 @@ enum WorldBoolConfigs CONFIG_DEBUG_BATTLEGROUND, CONFIG_DEBUG_ARENA, CONFIG_REGEN_HP_CANNOT_REACH_TARGET_IN_RAID, + CONFIG_SET_SHAPASSHASH, BOOL_CONFIG_VALUE_COUNT }; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 4a4eaadc2..40b98cf79 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1413,6 +1413,8 @@ void World::LoadConfigSettings(bool reload) m_int_configs[CONFIG_GM_LEVEL_CHANNEL_MODERATION] = sConfigMgr->GetOption("Channel.ModerationGMLevel", 1); + m_bool_configs[CONFIG_SET_SHAPASSHASH] = sConfigMgr->GetBoolDefault("SetDeprecatedExternalPasswords", false); + // call ScriptMgr if we're reloading the configuration sScriptMgr->OnAfterConfigLoad(reload); } diff --git a/src/server/scripts/Commands/cs_account.cpp b/src/server/scripts/Commands/cs_account.cpp index 119a622ce..33ae13e32 100644 --- a/src/server/scripts/Commands/cs_account.cpp +++ b/src/server/scripts/Commands/cs_account.cpp @@ -117,7 +117,7 @@ public: handler->SendSysMessage(LANG_ACCOUNT_PASS_TOO_LONG); handler->SetSentErrorMessage(true); return false; - case AOR_NAME_ALREDY_EXIST: + case AOR_NAME_ALREADY_EXIST: handler->SendSysMessage(LANG_ACCOUNT_ALREADY_EXIST); handler->SetSentErrorMessage(true); return false; diff --git a/src/server/worldserver/RemoteAccess/RASocket.cpp b/src/server/worldserver/RemoteAccess/RASocket.cpp index 2ff2dc47b..f55bfece0 100644 --- a/src/server/worldserver/RemoteAccess/RASocket.cpp +++ b/src/server/worldserver/RemoteAccess/RASocket.cpp @@ -16,7 +16,7 @@ #include "Log.h" #include "RASocket.h" #include "ServerMotd.h" -#include "SHA1.h" +#include "SRP6.h" #include "Util.h" #include "World.h" #include @@ -212,22 +212,21 @@ int RASocket::check_password(const std::string& user, const std::string& pass) std::string safe_pass = pass; Utf8ToUpperOnlyLatin(safe_pass); - std::string hash = AccountMgr::CalculateShaPassHash(safe_user, safe_pass); - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_CHECK_PASSWORD_BY_NAME); stmt->setString(0, safe_user); - stmt->setString(1, hash); - PreparedQueryResult result = LoginDatabase.Query(stmt); - - if (!result) + if (PreparedQueryResult result = LoginDatabase.Query(stmt)) { - sLog->outRemote("Wrong password for user: %s", user.c_str()); - return -1; + acore::Crypto::SRP6::Salt salt = (*result)[0].GetBinary(); + acore::Crypto::SRP6::Verifier verifier = (*result)[1].GetBinary(); + + if (acore::Crypto::SRP6::CheckLogin(safe_user, safe_pass, salt, verifier)) + return 0; } - return 0; + sLog->outRemote("Wrong password for user: %s", user.c_str()); + return -1; } int RASocket::authenticate()