diff --git a/HermesProxy/World/Client/LegacyWorldCrypt.cs b/HermesProxy/World/Client/LegacyWorldCrypt.cs index f1d0ee0f..15f8b14c 100644 --- a/HermesProxy/World/Client/LegacyWorldCrypt.cs +++ b/HermesProxy/World/Client/LegacyWorldCrypt.cs @@ -77,6 +77,17 @@ public class TbcWorldCrypt : LegacyWorldCrypt public void Initialize(byte[] sessionKey) { + /* + case BUILD_243: + { + _send_i = _send_j = _recv_i = _recv_j = 0; + + static uint8 seed[SEED_KEY_SIZE] = { 0x38, 0xA7, 0x83, 0x15, 0xF8, 0x92, 0x25, 0x30, 0x71, 0x98, 0x67, 0xB1, 0x8C, 0x4, 0xE2, 0xAA }; + _key = Trinity::Crypto::HMAC_SHA1::GetDigestOf(seed, K); + break; + } + + */ byte[] recvSeed = new byte[16] { 0x38, 0xA7, 0x83, 0x15, 0xF8, 0x92, 0x25, 0x30, 0x71, 0x98, 0x67, 0xB1, 0x8C, 0x4, 0xE2, 0xAA }; HmacHash recvHash = new HmacHash(recvSeed); recvHash.Finish(sessionKey, sessionKey.Count()); @@ -130,49 +141,127 @@ public class WlkWorldCrypt : LegacyWorldCrypt public void Initialize(byte[] sessionKey) { - byte[] recvSeed = new byte[16] { 0xBA, 0xA3, 0x1E, 0x99, 0xA0, 0x0B, 0x21, 0x57, 0xFC, 0x37, 0x3F, 0xB3, 0x69, 0xCD, 0xD2, 0xF1 }; - HmacHash recvHash = new HmacHash(recvSeed); - recvHash.Finish(sessionKey, sessionKey.Count()); - m_key = recvHash.Digest.ToArray(); - m_send_i = m_send_j = m_recv_i = m_recv_j = 0; + /* + +AuthCrypt::AuthCrypt(ClientBuild build) : + _initialized(false), clientBuild(build) +{ } + +void AuthCrypt::Init(SessionKey const& K) +{ + switch(clientBuild) + { +#ifdef LICH_KING_CLIENT + case BUILD_335: + { + _serverEncrypt = std::make_unique(); + _clientDecrypt = std::make_unique(); + + static uint8 ServerEncryptionKey[SEED_KEY_SIZE] = { 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)); + static uint8 ServerDecryptionKey[SEED_KEY_SIZE] = { 0xC2, 0xB3, 0x72, 0x3C, 0xC6, 0xAE, 0xD9, 0xB5, 0x34, 0x3C, 0x53, 0xEE, 0x2F, 0x43, 0x67, 0xCE }; + _clientDecrypt->Init(Trinity::Crypto::HMAC_SHA1::GetDigestOf(ServerDecryptionKey, K)); + + // Drop first 1024 bytes, as WoW uses ARC4-drop1024. + std::array syncBuf; + _serverEncrypt->UpdateData(syncBuf); + _clientDecrypt->UpdateData(syncBuf); + + _initialized = true; + break; + } +#else + case BUILD_243: + { + _send_i = _send_j = _recv_i = _recv_j = 0; + + static uint8 seed[SEED_KEY_SIZE] = { 0x38, 0xA7, 0x83, 0x15, 0xF8, 0x92, 0x25, 0x30, 0x71, 0x98, 0x67, 0xB1, 0x8C, 0x4, 0xE2, 0xAA }; + _key = Trinity::Crypto::HMAC_SHA1::GetDigestOf(seed, K); + break; + } +#endif + default: + TC_LOG_ERROR("network", "AuthCrypt::Init, wrong build %u given, cannot initialize", uint32(clientBuild)); + return; + } + _initialized = true; +} + +void AuthCrypt::DecryptRecv(uint8* data, size_t len) +{ + ASSERT(_initialized); + +#ifdef LICH_KING_CLIENT + _clientDecrypt->UpdateData(data, len); +#else + if (len < CRYPTED_SEND_LEN) + return; + + for (size_t t = 0; t < CRYPTED_RECV_LEN; t++) + { + _recv_i %= _key.size(); + uint8 x = uint8((data[t] - _recv_j) ^ _key[_recv_i]); // calc can overflow uint8, seems to work though + ++_recv_i; + _recv_j = data[t]; + data[t] = x; + } +#endif +} + +void AuthCrypt::EncryptSend(uint8 *data, size_t len) +{ + ASSERT(_initialized); + +#ifdef LICH_KING_CLIENT + _serverEncrypt->UpdateData(data, len); +#else + if (len < CRYPTED_SEND_LEN) + return; + + for (size_t t = 0; t < CRYPTED_SEND_LEN; t++) + { + _send_i %= _key.size(); + uint8 x = uint8((data[t] ^ _key[_send_i]) + _send_j); // calc can overflow uint8, seems to work though + ++_send_i; + data[t] = _send_j = x; + } +#endif +} + + + */ + + byte[] ServerEncryptionKey = new byte[16] { 0xCC, 0x98, 0xAE, 0x04, 0xE8, 0x97, 0xEA, 0xCA, 0x12, 0xDD, 0xC0, 0x93, 0x42, 0x91, 0x53, 0x57 }; + _serverEncrypt = new SARC4(); + _serverEncrypt.PrepareKey(ServerEncryptionKey); + byte[] ServerDecryptionKey = new byte[16] { 0xC2, 0xB3, 0x72, 0x3C, 0xC6, 0xAE, 0xD9, 0xB5, 0x34, 0x3C, 0x53, 0xEE, 0x2F, 0x43, 0x67, 0xCE }; + _clientDecrypt = new SARC4(); + _clientDecrypt.PrepareKey(ServerDecryptionKey); + + byte[] syncBuf = new byte[1024]; + _serverEncrypt.ProcessBuffer(syncBuf,1024); + _clientDecrypt.ProcessBuffer(syncBuf, 1024); + + m_isInitialized = true; } public void Decrypt(byte[] data, int len) { - if (len < CRYPTED_RECV_LEN) - return; - - for (byte t = 0; t < CRYPTED_RECV_LEN; t++) - { - m_recv_i %= (byte)m_key.Count(); - byte x = (byte)((data[t] - m_recv_j) ^ m_key[m_recv_i]); - ++m_recv_i; - m_recv_j = data[t]; - data[t] = x; - } + _clientDecrypt.ProcessBuffer(data, len); } public void Encrypt(byte[] data, int len) { - if (!m_isInitialized) - return; - - if (len < CRYPTED_SEND_LEN) - return; - - for (byte t = 0; t < CRYPTED_SEND_LEN; t++) - { - m_send_i %= (byte)m_key.Count(); - byte x = (byte)((data[t] ^ m_key[m_send_i]) + m_send_j); - ++m_send_i; - data[t] = m_send_j = x; - } + _serverEncrypt.ProcessBuffer(data, len); } byte[] m_key; byte m_send_i, m_send_j, m_recv_i, m_recv_j; bool m_isInitialized; + + SARC4 _clientDecrypt; + SARC4 _serverEncrypt; } } diff --git a/HermesProxy/World/Client/WorldClient.cs b/HermesProxy/World/Client/WorldClient.cs index f1cba506..2663dfc7 100644 --- a/HermesProxy/World/Client/WorldClient.cs +++ b/HermesProxy/World/Client/WorldClient.cs @@ -182,7 +182,7 @@ private async Task ReceiveLoop() LegacyServerPacketHeader header = new LegacyServerPacketHeader(); header.Read(headerBuffer); - ushort packetSize = header.Size; + uint packetSize = header.Size; if (packetSize != 0) { @@ -444,7 +444,7 @@ public void SendAuthResponse(uint clientSeed, uint serverSeed) if (Settings.ServerBuild >= ClientVersionBuild.V3_0_2_9056) packet.WriteUInt32(zero); // LoginServerType - packet.WriteUInt32(clientSeed); + packet.WriteUInt32(clientSeed); //recvPacket.read(authSession->LocalChallenge); std::array if (Settings.ServerBuild >= ClientVersionBuild.V3_3_5_12340) { diff --git a/HermesProxy/World/Packet.cs b/HermesProxy/World/Packet.cs index b16e4130..7aa32a6e 100644 --- a/HermesProxy/World/Packet.cs +++ b/HermesProxy/World/Packet.cs @@ -338,22 +338,69 @@ public void Write(ByteBuffer byteBuffer) public bool IsValidSize() { return Size < 0x40000; } } + /* + struct ServerPktHeader + + * size is the length of the payload _plus_ the length of the opcode + + ServerPktHeader(uint32 size, uint16 cmd) : size(size) + { + uint8 headerIndex = 0; + if (isLargePacket()) + { + LOG_DEBUG("network", "initializing large server to client packet. Size: {}, cmd: {}", size, cmd); + header[headerIndex++] = 0x80 | (0xFF & (size >> 16)); + } + header[headerIndex++] = 0xFF & (size >> 8); + header[headerIndex++] = 0xFF & size; + + header[headerIndex++] = 0xFF & cmd; + header[headerIndex++] = 0xFF & (cmd >> 8); + } + + uint8 getHeaderLength() + { + // cmd = 2 bytes, size= 2||3bytes + return 2 + (isLargePacket() ? 3 : 2); + } + + bool isLargePacket() const + { + return size > 0x7FFF; + } + +const uint32 size; +uint8 header[5]; +}; + +*/ public class LegacyServerPacketHeader { - public const int StructSize = sizeof(ushort) + sizeof(ushort); - public ushort Size; + public const int StructSize = sizeof(uint) + sizeof(ushort); + public uint Size; public ushort Opcode; public void Read(byte[] buffer) { - Size = Framework.Util.NetworkUtility.EndianConvert(BitConverter.ToUInt16(buffer, 0)); + Size = Framework.Util.NetworkUtility.EndianConvert(BitConverter.ToUInt32(buffer, 0)); Opcode = BitConverter.ToUInt16(buffer, sizeof(ushort)); } public void Write(ByteBuffer byteBuffer) { - byteBuffer.WriteUInt16(Size); + byteBuffer.WriteUInt32(Size); byteBuffer.WriteUInt16(Opcode); } - }; + + int getHeaderLength() + { + // cmd = 2 bytes, size= 2||3bytes + return 2 + (isLargePacket() ? 3 : 2); + } + + bool isLargePacket() + { + return Size > 0x7FFF; + } +}; public class LegacyClientPacketHeader { diff --git a/HermesProxy/World/Server/WorldSocket.cs b/HermesProxy/World/Server/WorldSocket.cs index a350b3cb..ed8848fa 100644 --- a/HermesProxy/World/Server/WorldSocket.cs +++ b/HermesProxy/World/Server/WorldSocket.cs @@ -534,6 +534,7 @@ void HandleAuthSessionCallback(AuthSession authSession) _realmId = new RealmId((byte)authSession.RegionID, (byte)authSession.BattlegroupID, authSession.RealmID); GetSession().WorldClient = new Client.WorldClient(); + if (!GetSession().WorldClient.ConnectToWorldServer(GetSession().RealmManager.GetRealm(_realmId), GetSession())) { SendAuthResponseError(BattlenetRpcErrorCode.BadServer);