diff options
21 files changed, 303 insertions, 134 deletions
diff --git a/src/common/Common.h b/src/common/Common.h index 2d497b213f7..92c0453ddc0 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -19,6 +19,7 @@ #define TRINITYCORE_COMMON_H #include "Define.h" +#include <array> #include <memory> #include <string> #include <utility> diff --git a/src/common/Cryptography/Authentication/AuthCrypt.cpp b/src/common/Cryptography/Authentication/AuthCrypt.cpp index 9d50fb026c3..86126b3ba81 100644 --- a/src/common/Cryptography/Authentication/AuthCrypt.cpp +++ b/src/common/Cryptography/Authentication/AuthCrypt.cpp @@ -26,7 +26,7 @@ AuthCrypt::AuthCrypt() : _initialized(false) { } -void AuthCrypt::Init(std::array<uint8, 40> const& K) +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(Trinity::Crypto::HMAC_SHA1::GetDigestOf(ServerEncryptionKey, K)); diff --git a/src/common/Cryptography/Authentication/AuthCrypt.h b/src/common/Cryptography/Authentication/AuthCrypt.h index cb1a217297a..86c186bef69 100644 --- a/src/common/Cryptography/Authentication/AuthCrypt.h +++ b/src/common/Cryptography/Authentication/AuthCrypt.h @@ -19,6 +19,7 @@ #define _AUTHCRYPT_H #include "ARC4.h" +#include "AuthDefines.h" #include <array> class TC_COMMON_API AuthCrypt @@ -26,7 +27,7 @@ class TC_COMMON_API AuthCrypt public: AuthCrypt(); - void Init(std::array<uint8, 40> const& K); + void Init(SessionKey const& K); void DecryptRecv(uint8* data, size_t len); void EncryptSend(uint8* data, size_t len); diff --git a/src/common/Cryptography/Authentication/AuthDefines.h b/src/common/Cryptography/Authentication/AuthDefines.h new file mode 100644 index 00000000000..a32de20dcc6 --- /dev/null +++ b/src/common/Cryptography/Authentication/AuthDefines.h @@ -0,0 +1,27 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TRINITY_AUTHDEFINES_H +#define TRINITY_AUTHDEFINES_H + +#include "Define.h" +#include <array> + +constexpr size_t SESSION_KEY_LENGTH = 40; +using SessionKey = std::array<uint8, SESSION_KEY_LENGTH>; + +#endif diff --git a/src/common/Cryptography/Authentication/SRP6.cpp b/src/common/Cryptography/Authentication/SRP6.cpp new file mode 100644 index 00000000000..de05ffd1693 --- /dev/null +++ b/src/common/Cryptography/Authentication/SRP6.cpp @@ -0,0 +1,118 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "SRP6.h" +#include "CryptoRandom.h" +#include "Util.h" +#include <algorithm> +#include <functional> + +using SHA1 = Trinity::Crypto::SHA1; +using SRP6 = Trinity::Crypto::SRP6; + +/*static*/ std::array<uint8, 1> const SRP6::g = { 7 }; +/*static*/ std::array<uint8, 32> const SRP6::N = HexStrToByteArray<32>("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7", true); +/*static*/ BigNumber const SRP6::_g(SRP6::g); +/*static*/ BigNumber const SRP6::_N(N); + +/*static*/ std::pair<SRP6::Salt, SRP6::Verifier> SRP6::MakeRegistrationData(std::string const& username, std::string const& password) +{ + std::pair<SRP6::Salt, SRP6::Verifier> res; + Crypto::GetRandomBytes(res.first); // random salt + res.second = CalculateVerifier(username, password, res.first); + return res; +} + +/*static*/ std::pair<SRP6::Salt, SRP6::Verifier> SRP6::MakeRegistrationDataFromHash_DEPRECATED_DONOTUSE(SHA1::Digest const& hash) +{ + std::pair<SRP6::Salt, SRP6::Verifier> res; + Crypto::GetRandomBytes(res.first); + res.second = CalculateVerifierFromHash(hash, 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 CalculateVerifierFromHash(SHA1::GetDigestOf(username, ":", password), salt); +} + +// merge this into CalculateVerifier once the sha_pass hack finally gets nuked from orbit +/*static*/ SRP6::Verifier SRP6::CalculateVerifierFromHash(SHA1::Digest const& hash, SRP6::Salt const& salt) +{ + return _g.ModExp(SHA1::GetDigestOf(salt, hash), _N).ToByteArray<32>(false); +} + +/*static*/ SessionKey SRP6::SHA1Interleave(SRP6::EphemeralKey const& S) +{ + // split S into two buffers + std::array<uint8, EPHEMERAL_KEY_LENGTH/2> buf0, buf1; + for (size_t i = 0; i < EPHEMERAL_KEY_LENGTH/2; ++i) + { + 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<19>()), _v(verifier, false), s(salt), B(_B(_b, _v)) {} + +std::optional<SessionKey> SRP6::VerifyChallengeResponse(EphemeralKey const& A, SHA1::Digest const& clientM) +{ + ASSERT(!_used, "A single SRP6 object must only ever be used to verify ONCE!"); + _used = true; + + BigNumber const _A(A); + if ((_A % _N).IsZero()) + return std::nullopt; + + BigNumber const u(SHA1::GetDigestOf(A, B)); + EphemeralKey const S = (_A * (_v.ModExp(u, _N))).ModExp(_b, N).ToByteArray<32>(); + + 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/Authentication/SRP6.h b/src/common/Cryptography/Authentication/SRP6.h new file mode 100644 index 00000000000..ab4dfc56889 --- /dev/null +++ b/src/common/Cryptography/Authentication/SRP6.h @@ -0,0 +1,89 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TRINITY_SRP6_H +#define TRINITY_SRP6_H + +#include "AuthDefines.h" +#include "BigNumber.h" +#include "Define.h" +#include "Common.h" +#include "CryptoHash.h" +#include <array> +#include <optional> + +namespace Trinity::Crypto +{ + class TC_COMMON_API SRP6 + { + public: + static constexpr size_t SALT_LENGTH = 32; + using Salt = std::array<uint8, SALT_LENGTH>; + static constexpr size_t VERIFIER_LENGTH = 32; + using Verifier = std::array<uint8, VERIFIER_LENGTH>; + static constexpr size_t EPHEMERAL_KEY_LENGTH = 32; + using EphemeralKey = std::array<uint8, EPHEMERAL_KEY_LENGTH>; + + static std::array<uint8, 1> const g; + static std::array<uint8, 32> const N; + + // this is the old sha_pass_hash hack + // YOU SHOULD NEVER STORE THIS HASH, if you do you are breaking SRP6 guarantees + // use MakeRegistrationData instead + static std::pair<Salt, Verifier> MakeRegistrationDataFromHash_DEPRECATED_DONOTUSE(SHA1::Digest const& hash); + + // username + password must be passed through Utf8ToUpperOnlyLatin FIRST! + static std::pair<Salt, Verifier> MakeRegistrationData(std::string const& username, std::string const& password); + // username + password must be passed through Utf8ToUpperOnlyLatin FIRST! + static bool CheckLogin(std::string const& username, std::string const& password, Salt const& salt, Verifier const& verifier) + { + 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<SessionKey> 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 Verifier CalculateVerifierFromHash(SHA1::Digest const& hash, 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<EPHEMERAL_KEY_LENGTH>(); } + + /* 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/BigNumber.cpp b/src/common/Cryptography/BigNumber.cpp index 5b4e2942453..62e459c6dce 100644 --- a/src/common/Cryptography/BigNumber.cpp +++ b/src/common/Cryptography/BigNumber.cpp @@ -27,7 +27,7 @@ BigNumber::BigNumber() { } BigNumber::BigNumber(BigNumber const& bn) - : _bn(BN_dup(bn._bn)) + : _bn(BN_dup(bn.BN())) { } BigNumber::~BigNumber() @@ -35,6 +35,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); diff --git a/src/common/Cryptography/BigNumber.h b/src/common/Cryptography/BigNumber.h index f269a813075..e61a8073c31 100644 --- a/src/common/Cryptography/BigNumber.h +++ b/src/common/Cryptography/BigNumber.h @@ -32,12 +32,14 @@ class TC_COMMON_API BigNumber BigNumber(); BigNumber(BigNumber const& bn); BigNumber(uint32 v) : BigNumber() { SetDword(v); } + BigNumber(int32 v) : BigNumber() { SetDword(v); } BigNumber(std::string const& v) : BigNumber() { SetHexStr(v); } template <size_t Size> BigNumber(std::array<uint8, Size> 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, bool littleEndian = true); @@ -107,7 +109,8 @@ class TC_COMMON_API BigNumber int32 GetNumBytes() const; - struct bignum_st *BN() { return _bn; } + struct bignum_st* BN() { return _bn; } + struct bignum_st const* BN() const { return _bn; } uint32 AsDword() const; @@ -126,7 +129,7 @@ class TC_COMMON_API BigNumber std::string AsDecStr() const; private: - struct bignum_st *_bn; + struct bignum_st* _bn; }; #endif diff --git a/src/server/authserver/Server/AuthSession.cpp b/src/server/authserver/Server/AuthSession.cpp index c1f4703ea47..1eb64a7830e 100644 --- a/src/server/authserver/Server/AuthSession.cpp +++ b/src/server/authserver/Server/AuthSession.cpp @@ -74,8 +74,9 @@ static_assert(sizeof(sAuthLogonChallenge_C) == (1 + 1 + 2 + 4 + 1 + 1 + 1 + 2 + typedef struct AUTH_LOGON_PROOF_C { uint8 cmd; - uint8 A[32]; - Trinity::Crypto::SHA1::Digest M1, crc_hash; + Trinity::Crypto::SRP6::EphemeralKey A; + Trinity::Crypto::SHA1::Digest clientM; + Trinity::Crypto::SHA1::Digest crc_hash; uint8 number_of_keys; uint8 securityFlags; } sAuthLogonProof_C; @@ -114,12 +115,6 @@ static_assert(sizeof(sAuthReconnectProof_C) == (1 + 16 + 20 + 20 + 1)); std::array<uint8, 16> VersionChallenge = { { 0xBA, 0xA3, 0x1E, 0x99, 0xA0, 0x0B, 0x21, 0x57, 0xFC, 0x37, 0x3F, 0xB3, 0x69, 0xCD, 0xD2, 0xF1 } }; -struct BufferSizes -{ - static constexpr size_t SRP_6_V = 0x20; - static constexpr size_t SRP_6_S = 0x20; -}; - #define MAX_ACCEPTED_CHALLENGE_SIZE (sizeof(AUTH_LOGON_CHALLENGE_C) + 16) #define AUTH_LOGON_CHALLENGE_INITIAL_SIZE 4 @@ -165,11 +160,7 @@ void AccountInfo::LoadResult(Field* fields) } AuthSession::AuthSession(tcp::socket&& socket) : Socket(std::move(socket)), -_status(STATUS_CHALLENGE), _build(0), _expversion(0) -{ - N.SetHexStr("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7"); - g.SetDword(7); -} +_status(STATUS_CHALLENGE), _build(0), _expversion(0) { } void AuthSession::Start() { @@ -405,42 +396,41 @@ void AuthSession::LogonChallengeCallback(PreparedQueryResult result) } } - // Get the password from the account table, upper it, and make the SRP6 calculation - std::string rI = fields[10].GetString(); - - // Don't calculate (v, s) if there are already some in the database - std::string databaseV = fields[11].GetString(); - std::string databaseS = fields[12].GetString(); - - TC_LOG_DEBUG("network", "database authentication values: v='%s' s='%s'", databaseV.c_str(), databaseS.c_str()); - - // multiply with 2 since bytes are stored as hexstring - if (databaseV.size() != size_t(BufferSizes::SRP_6_V) * 2 || databaseS.size() != size_t(BufferSizes::SRP_6_S) * 2) - SetVSFields(rI); + if (!fields[10].IsNull()) + { + // if this is reached, s/v are empty and we need to recalculate them + Trinity::Crypto::SHA1::Digest sha_pass_hash; + HexStrToByteArray(fields[10].GetString(), sha_pass_hash); + + auto [salt, verifier] = Trinity::Crypto::SRP6::MakeRegistrationDataFromHash_DEPRECATED_DONOTUSE(sha_pass_hash); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_SV); + stmt->setString(0, ByteArrayToHexStr(salt, true)); /* this is actually flipped in the DB right now, old core did hexstr (big endian) -> bignum -> byte array (little-endian) */ + stmt->setString(1, ByteArrayToHexStr(verifier)); + stmt->setUInt32(2, _accountInfo.Id); + LoginDatabase.Execute(stmt); + + _srp6.emplace(_accountInfo.Login, salt, verifier); + } else { - s.SetHexStr(databaseS.c_str()); - v.SetHexStr(databaseV.c_str()); + Trinity::Crypto::SRP6::Salt salt; + Trinity::Crypto::SRP6::Verifier verifier; + HexStrToByteArray(fields[11].GetString(), salt, true); /* this is actually flipped in the DB right now, old core did hexstr (big endian) -> bignum -> byte array (little-endian) */ + HexStrToByteArray(fields[12].GetString(), verifier); + _srp6.emplace(_accountInfo.Login, salt, verifier); } - b.SetRand(19 * 8); - BigNumber gmod = g.ModExp(b, N); - B = ((v * 3) + gmod) % N; - - ASSERT(gmod.GetNumBytes() <= 32); - // Fill the response packet with the result if (AuthHelper::IsAcceptedClientBuild(_build)) { pkt << uint8(WOW_SUCCESS); - // B may be calculated < 32B so we force minimal length to 32B - pkt.append(B.ToByteArray<32>()); // 32 bytes + pkt.append(_srp6->B); pkt << uint8(1); - pkt.append(g.ToByteArray<1>()); + pkt.append(_srp6->g); pkt << uint8(32); - pkt.append(N.ToByteArray<32>()); - pkt.append(s.ToByteArray<BufferSizes::SRP_6_S>()); // 32 bytes + pkt.append(_srp6->N); + pkt.append(_srp6->s); pkt.append(VersionChallenge.data(), VersionChallenge.size()); pkt << uint8(securityFlags); // security flags (0x0...0x04) @@ -490,52 +480,10 @@ bool AuthSession::HandleLogonProof() return false; } - // Continue the SRP6 calculation based on data received from the client - BigNumber A; - - A.SetBinary(logonProof->A, 32); - - // SRP safeguard: abort if A == 0 - if ((A % N).IsZero()) - return false; - - BigNumber u(Trinity::Crypto::SHA1::GetDigestOf(A.ToByteArray<32>(), B.ToByteArray<32>())); - BigNumber S = (A * (v.ModExp(u, N))).ModExp(b, N); - - std::array<uint8, 32> t = S.ToByteArray<32>(); - std::array<uint8, 16> buf; - Trinity::Crypto::SHA1::Digest part; - std::array<uint8, 40> sessionKey; - - for (size_t i = 0; i < 16; ++i) - buf[i] = t[i * 2]; - part = Trinity::Crypto::SHA1::GetDigestOf(buf); - for (size_t i = 0; i < Trinity::Crypto::SHA1::DIGEST_LENGTH; ++i) - sessionKey[i * 2] = part[i]; - - for (size_t i = 0; i < 16; ++i) - buf[i] = t[i * 2 + 1]; - part = Trinity::Crypto::SHA1::GetDigestOf(buf); - for (size_t i = 0; i < Trinity::Crypto::SHA1::DIGEST_LENGTH; ++i) - sessionKey[i * 2 + 1] = part[i]; - - Trinity::Crypto::SHA1::Digest hash = Trinity::Crypto::SHA1::GetDigestOf(N.ToByteArray<32>()); - Trinity::Crypto::SHA1::Digest hash2 = Trinity::Crypto::SHA1::GetDigestOf(g.ToByteArray<1>()); - std::transform(hash.begin(), hash.end(), hash2.begin(), hash.begin(), std::bit_xor<>()); // hash = H(N) xor H(g) - - Trinity::Crypto::SHA1 sha; - sha.UpdateData(hash); - sha.UpdateData(Trinity::Crypto::SHA1::GetDigestOf(_accountInfo.Login)); - sha.UpdateData(s.ToByteArray<BufferSizes::SRP_6_S>()); - sha.UpdateData(A.ToByteArray<32>()); - sha.UpdateData(B.ToByteArray<32>()); - sha.UpdateData(sessionKey); - sha.Finalize(); - Trinity::Crypto::SHA1::Digest M = sha.GetDigest(); - // Check if SRP6 results match (password is correct), else send an error - if (M == logonProof->M1) + if (std::optional<SessionKey> K = _srp6->VerifyChallengeResponse(logonProof->A, logonProof->clientM)) { + _sessionKey = *K; // Check auth token bool tokenSuccess = false; bool sentToken = (logonProof->securityFlags & 0x04); @@ -562,7 +510,7 @@ bool AuthSession::HandleLogonProof() return true; } - if (!VerifyVersion(logonProof->A, sizeof(logonProof->A), logonProof->crc_hash, false)) + if (!VerifyVersion(logonProof->A.data(), logonProof->A.size(), logonProof->crc_hash, false)) { ByteBuffer packet; packet << uint8(AUTH_LOGON_PROOF); @@ -577,7 +525,7 @@ bool AuthSession::HandleLogonProof() // No SQL injection (escaped user name) and IP address as received by socket LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGONPROOF); - stmt->setString(0, ByteArrayToHexStr(sessionKey)); + stmt->setString(0, ByteArrayToHexStr(_sessionKey)); stmt->setString(1, GetRemoteIpAddress().to_string()); stmt->setUInt32(2, GetLocaleByName(_localizationName)); stmt->setString(3, _os); @@ -585,7 +533,7 @@ bool AuthSession::HandleLogonProof() LoginDatabase.DirectExecute(stmt); // Finish SRP6 and send the final result to the client - Trinity::Crypto::SHA1::Digest M2 = Trinity::Crypto::SHA1::GetDigestOf(A.ToByteArray<32>(), M, sessionKey); + Trinity::Crypto::SHA1::Digest M2 = Trinity::Crypto::SRP6::GetSessionVerifier(logonProof->A, logonProof->clientM, _sessionKey); ByteBuffer packet; if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients @@ -727,7 +675,7 @@ void AuthSession::ReconnectChallengeCallback(PreparedQueryResult result) Field* fields = result->Fetch(); _accountInfo.LoadResult(fields); - HexStrToByteArray(fields[9].GetCString(), sessionKey.data()); + HexStrToByteArray(fields[9].GetString(), _sessionKey); Trinity::Crypto::GetRandomBytes(_reconnectProof); _status = STATUS_RECONNECT_PROOF; @@ -755,7 +703,7 @@ bool AuthSession::HandleReconnectProof() sha.UpdateData(_accountInfo.Login); sha.UpdateData(t1.ToByteArray<16>()); sha.UpdateData(_reconnectProof); - sha.UpdateData(sessionKey); + sha.UpdateData(_sessionKey); sha.Finalize(); if (sha.GetDigest() == reconnectProof->R2) @@ -898,33 +846,6 @@ void AuthSession::RealmListCallback(PreparedQueryResult result) _status = STATUS_AUTHED; } -// Make the SRP6 calculation from hash in dB -void AuthSession::SetVSFields(const std::string& rI) -{ - s.SetRand(int32(BufferSizes::SRP_6_S) * 8); - - BigNumber I; - I.SetHexStr(rI.c_str()); - - // In case of leading zeros in the rI hash, restore them - std::array<uint8, Trinity::Crypto::SHA1::DIGEST_LENGTH> mDigest = I.ToByteArray<Trinity::Crypto::SHA1::DIGEST_LENGTH>(false); - - Trinity::Crypto::SHA1 sha; - sha.UpdateData(s.ToByteArray<BufferSizes::SRP_6_S>()); - sha.UpdateData(mDigest); - sha.Finalize(); - BigNumber x; - x.SetBinary(sha.GetDigest()); - v = g.ModExp(x, N); - - // No SQL injection (username escaped) - LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_VS); - stmt->setString(0, v.AsHexStr()); - stmt->setString(1, s.AsHexStr()); - stmt->setString(2, _accountInfo.Login); - LoginDatabase.Execute(stmt); -} - bool AuthSession::VerifyVersion(uint8 const* a, int32 aLength, Trinity::Crypto::SHA1::Digest const& versionProof, bool isReconnect) { if (!sConfigMgr->GetBoolDefault("StrictVersionCheck", false)) diff --git a/src/server/authserver/Server/AuthSession.h b/src/server/authserver/Server/AuthSession.h index 44454f6138b..9982399bcbc 100644 --- a/src/server/authserver/Server/AuthSession.h +++ b/src/server/authserver/Server/AuthSession.h @@ -25,6 +25,7 @@ #include "CryptoHash.h" #include "Optional.h" #include "Socket.h" +#include "SRP6.h" #include "QueryResult.h" #include <memory> #include <boost/asio/ip/tcp.hpp> @@ -92,9 +93,8 @@ private: bool VerifyVersion(uint8 const* a, int32 aLength, Trinity::Crypto::SHA1::Digest const& versionProof, bool isReconnect); - BigNumber N, s, g, v; - BigNumber b, B; - std::array<uint8, 40> sessionKey; + Optional<Trinity::Crypto::SRP6> _srp6; + SessionKey _sessionKey; std::array<uint8, 16> _reconnectProof; AuthStatus _status; diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp index e43d3b1496c..64986ee955d 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.cpp +++ b/src/server/database/Database/Implementation/LoginDatabase.cpp @@ -35,10 +35,10 @@ 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 = ? GROUP BY account.id", CONNECTION_SYNCH); PrepareStatement(LOGIN_INS_ACCOUNT_AUTO_BANNED, "INSERT INTO account_banned (id, bandate, unbandate, bannedby, banreason, active) VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, 'Trinity Auth', 'Failed login autoban', 1)", CONNECTION_ASYNC); PrepareStatement(LOGIN_DEL_ACCOUNT_BANNED, "DELETE FROM account_banned WHERE id = ?", CONNECTION_ASYNC); - PrepareStatement(LOGIN_UPD_VS, "UPDATE account SET v = ?, s = ? WHERE username = ?", CONNECTION_ASYNC); + PrepareStatement(LOGIN_UPD_SV, "UPDATE account SET s = ?, v = ? WHERE id = ?", 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.id, a.username, a.locked, a.lock_country, a.last_ip, a.failed_logins, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, " - "ab.unbandate = ab.bandate, aa.SecurityLevel, a.totp_secret, a.sha_pass_hash, a.v, a.s " + "ab.unbandate = ab.bandate, aa.SecurityLevel, a.totp_secret, IF(length(a.s)<2 OR length(a.v)<2, a.sha_pass_hash, NULL), a.s, a.v " "FROM account a LEFT JOIN account_access aa ON a.id = aa.AccountID LEFT JOIN account_banned ab ON ab.id = a.id AND ab.active = 1 WHERE a.username = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_RECONNECTCHALLENGE, "SELECT a.id, UPPER(a.username), a.locked, a.lock_country, a.last_ip, a.failed_logins, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, " "ab.unbandate = ab.bandate, aa.SecurityLevel, a.sessionKey " diff --git a/src/server/database/Database/Implementation/LoginDatabase.h b/src/server/database/Database/Implementation/LoginDatabase.h index 3c9b08f984f..f7ae3a349d0 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.h +++ b/src/server/database/Database/Implementation/LoginDatabase.h @@ -38,7 +38,7 @@ enum LoginDatabaseStatements : uint32 LOGIN_SEL_ACCOUNT_BANNED_BY_USERNAME, LOGIN_INS_ACCOUNT_AUTO_BANNED, LOGIN_DEL_ACCOUNT_BANNED, - LOGIN_UPD_VS, + LOGIN_UPD_SV, LOGIN_UPD_LOGONPROOF, LOGIN_SEL_LOGONCHALLENGE, LOGIN_SEL_RECONNECTCHALLENGE, diff --git a/src/server/game/Accounts/AccountMgr.cpp b/src/server/game/Accounts/AccountMgr.cpp index 4b370181dee..310c0c19289 100644 --- a/src/server/game/Accounts/AccountMgr.cpp +++ b/src/server/game/Accounts/AccountMgr.cpp @@ -202,7 +202,7 @@ AccountOpResult AccountMgr::ChangePassword(uint32 accountId, std::string newPass LoginDatabase.Execute(stmt); - stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_VS); + stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_SV); stmt->setString(0, ""); stmt->setString(1, ""); diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 9ec0d9244b3..4ae28b0bf12 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -1212,7 +1212,7 @@ TransactionCallback& WorldSession::AddTransactionCallback(TransactionCallback&& return _transactionCallbacks.AddCallback(std::move(callback)); } -void WorldSession::InitWarden(std::array<uint8, 40> const& 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 9d7cbc2cde7..e2e0369eb5a 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -24,6 +24,7 @@ #include "Common.h" #include "AsyncCallbackProcessor.h" +#include "AuthDefines.h" #include "DatabaseEnvFwd.h" #include "LockedQueue.h" #include "ObjectGuid.h" @@ -422,7 +423,7 @@ class TC_GAME_API WorldSession void SetPlayer(Player* player); uint8 Expansion() const { return m_expansion; } - void InitWarden(std::array<uint8, 40> const& k, std::string const& os); + void InitWarden(SessionKey const& k, 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 79f461eaad9..f124f05662f 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -245,7 +245,7 @@ struct AuthSession struct AccountInfo { uint32 Id; - std::array<uint8, 40> SessionKey; + SessionKey SessionKey; std::string LastIP; bool IsLockedToIP; std::string LockCountry; diff --git a/src/server/game/Warden/Warden.h b/src/server/game/Warden/Warden.h index 3d9543a7412..a5fe29fe889 100644 --- a/src/server/game/Warden/Warden.h +++ b/src/server/game/Warden/Warden.h @@ -19,6 +19,7 @@ #define _WARDEN_BASE_H #include "ARC4.h" +#include "AuthDefines.h" #include "ByteBuffer.h" #include "WardenCheckMgr.h" #include <array> @@ -99,7 +100,7 @@ class TC_GAME_API Warden Warden(); virtual ~Warden(); - virtual void Init(WorldSession* session, std::array<uint8, 40> const& K) = 0; + virtual void Init(WorldSession* session, SessionKey const& K) = 0; virtual ClientWardenModule* GetModuleForClient() = 0; virtual void InitializeModule() = 0; virtual void RequestHash() = 0; diff --git a/src/server/game/Warden/WardenMac.cpp b/src/server/game/Warden/WardenMac.cpp index e8a5b45cd76..36ea7cf218a 100644 --- a/src/server/game/Warden/WardenMac.cpp +++ b/src/server/game/Warden/WardenMac.cpp @@ -34,7 +34,7 @@ WardenMac::WardenMac() : Warden() { } WardenMac::~WardenMac() { } -void WardenMac::Init(WorldSession* pClient, std::array<uint8, 40> const& K) +void WardenMac::Init(WorldSession* pClient, SessionKey const& K) { _session = pClient; // Generate Warden Key diff --git a/src/server/game/Warden/WardenMac.h b/src/server/game/Warden/WardenMac.h index c186e546f9e..18cf5c09fe6 100644 --- a/src/server/game/Warden/WardenMac.h +++ b/src/server/game/Warden/WardenMac.h @@ -31,7 +31,7 @@ class TC_GAME_API WardenMac : public Warden WardenMac(); ~WardenMac(); - void Init(WorldSession* session, std::array<uint8, 40> const& 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 f423ab2486e..3f219f11db8 100644 --- a/src/server/game/Warden/WardenWin.cpp +++ b/src/server/game/Warden/WardenWin.cpp @@ -39,7 +39,7 @@ WardenWin::WardenWin() : Warden(), _serverTicks(0) {} WardenWin::~WardenWin() { } -void WardenWin::Init(WorldSession* session, std::array<uint8, 40> const& K) +void WardenWin::Init(WorldSession* session, SessionKey const& K) { _session = session; // Generate Warden Key diff --git a/src/server/game/Warden/WardenWin.h b/src/server/game/Warden/WardenWin.h index 77f66f621a3..ff4093738df 100644 --- a/src/server/game/Warden/WardenWin.h +++ b/src/server/game/Warden/WardenWin.h @@ -67,7 +67,7 @@ class TC_GAME_API WardenWin : public Warden WardenWin(); ~WardenWin(); - void Init(WorldSession* session, std::array<uint8, 40> const& K) override; + void Init(WorldSession* session, SessionKey const& K) override; ClientWardenModule* GetModuleForClient() override; void InitializeModule() override; void RequestHash() override; |
