diff --git a/src/net/connection/NetClient.cpp b/src/net/connection/NetClient.cpp index 400f518..292e879 100644 --- a/src/net/connection/NetClient.cpp +++ b/src/net/connection/NetClient.cpp @@ -140,7 +140,7 @@ void NetClient::Connect(const char* addrStr) { port = atoi(portDelim + 1); } - this->m_serverConnection->SetEncryptionType(WC_ENCRYPT_0); + this->m_serverConnection->SetEncryption(false); this->m_netState = NS_INITIALIZED; this->ConnectInternal(host, port); } @@ -166,6 +166,21 @@ void NetClient::DelRef() { } } +void NetClient::EnableEncryption(WowConnection* conn, uint8_t* seed, uint8_t seedLen) { + conn->SetEncryptionKey( + this->m_loginData.m_sessionKey, + sizeof(this->m_loginData.m_sessionKey), + 1, + seed, + seedLen + ); + + conn->uint375 = 4; + conn->uint376 = 2; + + conn->SetEncryption(true); +} + bool NetClient::GetDelete() { return this->m_deleteMe; } @@ -261,6 +276,12 @@ void NetClient::Send(CDataStore* msg) { this->m_serverConnection->Send(msg, 0); // TODO + + this->m_bytesSent += v4; + + if (!this->m_serverConnection->m_encrypt) { + this->EnableEncryption(this->m_serverConnection, nullptr, 0); + } } } diff --git a/src/net/connection/NetClient.hpp b/src/net/connection/NetClient.hpp index 74021fb..29f1429 100644 --- a/src/net/connection/NetClient.hpp +++ b/src/net/connection/NetClient.hpp @@ -64,6 +64,7 @@ class NetClient : public WowConnectionResponse { void Connect(const char* addrStr); int32_t ConnectInternal(const char* host, uint16_t port); void DelRef(); + void EnableEncryption(WowConnection* conn, uint8_t* seed, uint8_t seedLen); bool GetDelete(); const LoginData& GetLoginData(); NETSTATE GetState(); diff --git a/src/net/connection/RealmConnection.cpp b/src/net/connection/RealmConnection.cpp index 9935444..5fe5fe5 100644 --- a/src/net/connection/RealmConnection.cpp +++ b/src/net/connection/RealmConnection.cpp @@ -1,5 +1,8 @@ #include "net/connection/RealmConnection.hpp" #include "net/Types.hpp" +#include +#include +#include SCritSect RealmConnection::s_AllRealmConnectionsCrit; STORM_LIST(RealmConnection::REALMCONNECTIONNODE) RealmConnection::s_AllRealmConnections; @@ -46,6 +49,53 @@ RealmConnection::RealmConnection(RealmResponse* realmResponse) { int32_t RealmConnection::HandleAuthChallenge(AuthenticationChallenge* challenge) { // TODO + + // TODO switch to WDataStore + CDataStore msg; + + uint32_t localChallenge; + + msg.Put(static_cast(CMSG_AUTH_SESSION)); + + msg.Put(static_cast(12340)); + msg.Put(static_cast(this->GetLoginData().m_loginServerID)); + msg.PutString(this->GetLoginData().m_account); + msg.Put(static_cast(this->GetLoginData().m_loginServerType)); + + // TODO + msg.Put(localChallenge); + + // TODO + msg.Put(static_cast(0)); + msg.Put(static_cast(0)); + msg.Put(static_cast(1)); + + // TODO + msg.Put(static_cast(0)); + + uint32_t msgId = 0; + + SHA1_CONTEXT ctx; + SHA1_Init(&ctx); + + SHA1_Update(&ctx, reinterpret_cast(this->GetLoginData().m_account), SStrLen(this->GetLoginData().m_account)); + SHA1_Update(&ctx, reinterpret_cast(&msgId), sizeof(msgId)); + SHA1_Update(&ctx, reinterpret_cast(&localChallenge), sizeof(localChallenge)); + SHA1_Update(&ctx, reinterpret_cast(&challenge->uint0), sizeof(challenge->uint0)); + SHA1_Update(&ctx, this->GetLoginData().m_sessionKey, sizeof(this->GetLoginData().m_sessionKey)); + + uint8_t clientProof[SHA1_DIGEST_SIZE]; + SHA1_Final(clientProof, &ctx); + + msg.PutData(clientProof, sizeof(clientProof)); + + // TODO addons + msg.Put(static_cast(0)); + + msg.Finalize(); + + this->Send(&msg); + return 1; } diff --git a/src/net/connection/WowConnection.cpp b/src/net/connection/WowConnection.cpp index 68dfab9..5d6d0fa 100644 --- a/src/net/connection/WowConnection.cpp +++ b/src/net/connection/WowConnection.cpp @@ -1,6 +1,7 @@ #include "net/connection/WowConnection.hpp" #include "net/connection/WowConnectionNet.hpp" #include "net/connection/WowConnectionResponse.hpp" +#include "util/HMAC.hpp" #include #include #include @@ -33,6 +34,15 @@ WowConnectionNet* WowConnection::s_network; ATOMIC32 WowConnection::s_numWowConnections; bool (*WowConnection::s_verifyAddr)(const NETADDR*); +static uint8_t s_arc4drop1024[1024] = { 0x00 }; +static uint8_t s_arc4seed[] = { + // Receive key + 0xCC, 0x98, 0xAE, 0x04, 0xE8, 0x97, 0xEA, 0xCA, 0x12, 0xDD, 0xC0, 0x93, 0x42, 0x91, 0x53, 0x57, + + // Send key + 0xC2, 0xB3, 0x72, 0x3C, 0xC6, 0xAE, 0xD9, 0xB5, 0x34, 0x3C, 0x53, 0xEE, 0x2F, 0x43, 0x67, 0xCE, +}; + WowConnection::SENDNODE::SENDNODE(void* data, int32_t size, uint8_t* buf, bool raw) : TSLinkedNode() { if (data) { this->data = buf; @@ -451,7 +461,18 @@ void WowConnection::DoMessageReads() { } if (this->m_encrypt) { - // TODO encryption + auto v22 = headerSize + this->uint376 - this->m_readBytes; + auto v23 = v22 <= 0 ? 0 : v22; + if (v23 >= bytesRead) { + v23 = bytesRead; + } + + SARC4ProcessBuffer( + &this->m_readBuffer[this->m_readBytes], + v23, + &this->m_receiveKey, + &this->m_receiveKey + ); } this->m_readBytes += bytesRead; @@ -832,8 +853,43 @@ WC_SEND_RESULT WowConnection::SendRaw(uint8_t* data, int32_t len, bool a4) { return WC_SEND_ERROR; } -void WowConnection::SetEncryptionType(WC_ENCRYPT_TYPE encryptType) { - // TODO +void WowConnection::SetEncryption(bool enabled) { + this->m_lock.Enter(); + + this->m_encrypt = enabled; + + SARC4PrepareKey(this->m_sendKeyInit, sizeof(this->m_sendKeyInit), &this->m_sendKey); + SARC4PrepareKey(this->m_receiveKeyInit, sizeof(this->m_receiveKeyInit), &this->m_receiveKey); + + SARC4ProcessBuffer(s_arc4drop1024, sizeof(s_arc4drop1024), &this->m_sendKey, &this->m_sendKey); + SARC4ProcessBuffer(s_arc4drop1024, sizeof(s_arc4drop1024), &this->m_receiveKey, &this->m_receiveKey); + + this->m_lock.Leave(); +} + +void WowConnection::SetEncryptionKey(const uint8_t* key, uint8_t keyLen, uint8_t a4, const uint8_t* seedData, uint8_t seedLen) { + if (!seedData) { + seedData = s_arc4seed; + seedLen = sizeof(s_arc4seed); + } + + const uint8_t* seeds[] = { + seedData, + &seedData[seedLen / 2] + }; + + // Note: The original HMAC-SHA1 implementation uses a second SHA1 implementation shipped in + // the client. For simplicity's sake, we're currently using a custom util function built on + // top of the SHA1 implementation used for SRP6 authentication. + + HMAC_SHA1(seeds[a4], seedLen / 2, key, keyLen, this->m_sendKeyInit); + HMAC_SHA1(seeds[a4 ^ 1], seedLen / 2, key, keyLen, this->m_receiveKeyInit); + + SARC4PrepareKey(this->m_sendKeyInit, sizeof(this->m_sendKeyInit), &this->m_sendKey); + SARC4PrepareKey(this->m_receiveKeyInit, sizeof(this->m_receiveKeyInit), &this->m_receiveKey); + + SARC4ProcessBuffer(s_arc4drop1024, sizeof(s_arc4drop1024), &this->m_sendKey, &this->m_sendKey); + SARC4ProcessBuffer(s_arc4drop1024, sizeof(s_arc4drop1024), &this->m_receiveKey, &this->m_receiveKey); } void WowConnection::SetState(WOW_CONN_STATE state) { diff --git a/src/net/connection/WowConnection.hpp b/src/net/connection/WowConnection.hpp index 366b1f3..0433dfb 100644 --- a/src/net/connection/WowConnection.hpp +++ b/src/net/connection/WowConnection.hpp @@ -4,6 +4,7 @@ #include "net/Types.hpp" #include #include +#include #include #include @@ -70,7 +71,13 @@ class WowConnection { ATOMIC32 m_serviceCount; void* m_event; WOWC_TYPE m_type; + SARC4Key m_sendKey; + SARC4Key m_receiveKey; + uint8_t m_sendKeyInit[20]; + uint8_t m_receiveKeyInit[20]; bool m_encrypt; + uint8_t uint375; + uint8_t uint376; // Member functions WowConnection(WowConnectionResponse* response, void (*func)(void)); @@ -97,7 +104,8 @@ class WowConnection { void ReleaseResponseRef(); WC_SEND_RESULT Send(CDataStore* msg, int32_t a3); WC_SEND_RESULT SendRaw(uint8_t* data, int32_t len, bool a4); - void SetEncryptionType(WC_ENCRYPT_TYPE encryptType); + void SetEncryption(bool enabled); + void SetEncryptionKey(const uint8_t* key, uint8_t keyLen, uint8_t a4, const uint8_t* seed, uint8_t seedLen); void SetState(WOW_CONN_STATE state); void SetType(WOWC_TYPE type); void StartConnect();