diff options
| author | Treeston <treeston.mmoc@gmail.com> | 2020-07-29 00:07:41 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-07-29 00:07:41 +0200 |
| commit | 7f7fa8b23d71297f75ff4ca3c1d6e38333a5cc76 (patch) | |
| tree | 18a47372b2ac3e086e0e96074562af6a1172b223 /src/server | |
| parent | 210f552ac56979430f1349006c1945b29883a2bc (diff) | |
Core/Authserver: Split SRP6 into its own file (PR #25131)
Diffstat (limited to 'src/server')
| -rw-r--r-- | src/server/authserver/Server/AuthSession.cpp | 149 | ||||
| -rw-r--r-- | src/server/authserver/Server/AuthSession.h | 6 | ||||
| -rw-r--r-- | src/server/database/Database/Implementation/LoginDatabase.cpp | 4 | ||||
| -rw-r--r-- | src/server/database/Database/Implementation/LoginDatabase.h | 2 | ||||
| -rw-r--r-- | src/server/game/Accounts/AccountMgr.cpp | 2 | ||||
| -rw-r--r-- | src/server/game/Server/WorldSession.cpp | 2 | ||||
| -rw-r--r-- | src/server/game/Server/WorldSession.h | 3 | ||||
| -rw-r--r-- | src/server/game/Server/WorldSocket.cpp | 2 | ||||
| -rw-r--r-- | src/server/game/Warden/Warden.h | 3 | ||||
| -rw-r--r-- | src/server/game/Warden/WardenMac.cpp | 2 | ||||
| -rw-r--r-- | src/server/game/Warden/WardenMac.h | 2 | ||||
| -rw-r--r-- | src/server/game/Warden/WardenWin.cpp | 2 | ||||
| -rw-r--r-- | src/server/game/Warden/WardenWin.h | 2 |
13 files changed, 52 insertions, 129 deletions
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; |
