#include "auth/auth_packets.hpp" #include "core/logger.hpp" #include #include #include namespace wowee { namespace auth { network::Packet LogonChallengePacket::build(const std::string& account, const ClientInfo& info) { // Convert account to uppercase std::string upperAccount = account; std::transform(upperAccount.begin(), upperAccount.end(), upperAccount.begin(), ::toupper); // Calculate packet size // Opcode(1) + unknown(1) + size(2) + game(4) + version(3) + build(2) + // platform(4) + os(4) + locale(4) + timezone(4) + ip(4) + accountLen(1) + account(N) uint16_t payloadSize = 30 + upperAccount.length(); network::Packet packet(static_cast(AuthOpcode::LOGON_CHALLENGE)); // Unknown byte packet.writeUInt8(0x00); // Payload size packet.writeUInt16(payloadSize); // Game name (4 bytes, null-padded) packet.writeBytes(reinterpret_cast(info.game.c_str()), std::min(4, info.game.length())); for (size_t i = info.game.length(); i < 4; ++i) { packet.writeUInt8(0); } // Version (3 bytes) packet.writeUInt8(info.majorVersion); packet.writeUInt8(info.minorVersion); packet.writeUInt8(info.patchVersion); // Build (2 bytes) packet.writeUInt16(info.build); // Platform (4 bytes, null-padded) packet.writeBytes(reinterpret_cast(info.platform.c_str()), std::min(4, info.platform.length())); for (size_t i = info.platform.length(); i < 4; ++i) { packet.writeUInt8(0); } // OS (4 bytes, null-padded) packet.writeBytes(reinterpret_cast(info.os.c_str()), std::min(4, info.os.length())); for (size_t i = info.os.length(); i < 4; ++i) { packet.writeUInt8(0); } // Locale (4 bytes, null-padded) packet.writeBytes(reinterpret_cast(info.locale.c_str()), std::min(4, info.locale.length())); for (size_t i = info.locale.length(); i < 4; ++i) { packet.writeUInt8(0); } // Timezone packet.writeUInt32(info.timezone); // IP address (always 0) packet.writeUInt32(0); // Account length and name packet.writeUInt8(static_cast(upperAccount.length())); packet.writeBytes(reinterpret_cast(upperAccount.c_str()), upperAccount.length()); LOG_DEBUG("Built LOGON_CHALLENGE packet for account: ", upperAccount); LOG_DEBUG(" Payload size: ", payloadSize, " bytes"); LOG_DEBUG(" Total size: ", packet.getSize(), " bytes"); return packet; } bool LogonChallengeResponseParser::parse(network::Packet& packet, LogonChallengeResponse& response) { // Read opcode (should be LOGON_CHALLENGE) uint8_t opcode = packet.readUInt8(); if (opcode != static_cast(AuthOpcode::LOGON_CHALLENGE)) { LOG_ERROR("Invalid opcode in LOGON_CHALLENGE response: ", (int)opcode); return false; } // Unknown byte packet.readUInt8(); // Status response.result = static_cast(packet.readUInt8()); LOG_INFO("LOGON_CHALLENGE response: ", getAuthResultString(response.result)); if (response.result != AuthResult::SUCCESS) { return true; // Valid packet, but authentication failed } // B (server public ephemeral) - 32 bytes response.B.resize(32); for (int i = 0; i < 32; ++i) { response.B[i] = packet.readUInt8(); } // g length and value uint8_t gLen = packet.readUInt8(); response.g.resize(gLen); for (uint8_t i = 0; i < gLen; ++i) { response.g[i] = packet.readUInt8(); } // N length and value uint8_t nLen = packet.readUInt8(); response.N.resize(nLen); for (uint8_t i = 0; i < nLen; ++i) { response.N[i] = packet.readUInt8(); } // Salt - 32 bytes response.salt.resize(32); for (int i = 0; i < 32; ++i) { response.salt[i] = packet.readUInt8(); } // Unknown/padding - 16 bytes for (int i = 0; i < 16; ++i) { packet.readUInt8(); } // Security flags response.securityFlags = packet.readUInt8(); LOG_DEBUG("Parsed LOGON_CHALLENGE response:"); LOG_DEBUG(" B size: ", response.B.size(), " bytes"); LOG_DEBUG(" g size: ", response.g.size(), " bytes"); LOG_DEBUG(" N size: ", response.N.size(), " bytes"); LOG_DEBUG(" salt size: ", response.salt.size(), " bytes"); LOG_DEBUG(" Security flags: ", (int)response.securityFlags); return true; } network::Packet LogonProofPacket::build(const std::vector& A, const std::vector& M1) { if (A.size() != 32) { LOG_ERROR("Invalid A size: ", A.size(), " (expected 32)"); } if (M1.size() != 20) { LOG_ERROR("Invalid M1 size: ", M1.size(), " (expected 20)"); } network::Packet packet(static_cast(AuthOpcode::LOGON_PROOF)); // A (client public ephemeral) - 32 bytes packet.writeBytes(A.data(), A.size()); // M1 (client proof) - 20 bytes packet.writeBytes(M1.data(), M1.size()); // CRC hash - 20 bytes (zeros) for (int i = 0; i < 20; ++i) { packet.writeUInt8(0); } // Number of keys packet.writeUInt8(0); // Security flags packet.writeUInt8(0); LOG_DEBUG("Built LOGON_PROOF packet:"); LOG_DEBUG(" A size: ", A.size(), " bytes"); LOG_DEBUG(" M1 size: ", M1.size(), " bytes"); LOG_DEBUG(" Total size: ", packet.getSize(), " bytes"); return packet; } bool LogonProofResponseParser::parse(network::Packet& packet, LogonProofResponse& response) { // Read opcode (should be LOGON_PROOF) uint8_t opcode = packet.readUInt8(); if (opcode != static_cast(AuthOpcode::LOGON_PROOF)) { LOG_ERROR("Invalid opcode in LOGON_PROOF response: ", (int)opcode); return false; } // Status response.status = packet.readUInt8(); LOG_INFO("LOGON_PROOF response status: ", (int)response.status); if (response.status != 0) { LOG_ERROR("LOGON_PROOF failed with status: ", (int)response.status); return true; // Valid packet, but proof failed } // M2 (server proof) - 20 bytes response.M2.resize(20); for (int i = 0; i < 20; ++i) { response.M2[i] = packet.readUInt8(); } LOG_DEBUG("Parsed LOGON_PROOF response:"); LOG_DEBUG(" M2 size: ", response.M2.size(), " bytes"); return true; } network::Packet RealmListPacket::build() { network::Packet packet(static_cast(AuthOpcode::REALM_LIST)); // Unknown uint32 (per WoWDev documentation) packet.writeUInt32(0x00); LOG_DEBUG("Built REALM_LIST request packet"); LOG_DEBUG(" Total size: ", packet.getSize(), " bytes"); return packet; } bool RealmListResponseParser::parse(network::Packet& packet, RealmListResponse& response) { // Read opcode (should be REALM_LIST) uint8_t opcode = packet.readUInt8(); if (opcode != static_cast(AuthOpcode::REALM_LIST)) { LOG_ERROR("Invalid opcode in REALM_LIST response: ", (int)opcode); return false; } // Packet size (2 bytes) - we already know the size, skip it uint16_t packetSize = packet.readUInt16(); LOG_DEBUG("REALM_LIST response packet size: ", packetSize, " bytes"); // Unknown uint32 packet.readUInt32(); // Realm count uint16_t realmCount = packet.readUInt16(); LOG_INFO("REALM_LIST response: ", realmCount, " realms"); response.realms.clear(); response.realms.reserve(realmCount); for (uint16_t i = 0; i < realmCount; ++i) { Realm realm; // Icon realm.icon = packet.readUInt8(); // Lock realm.lock = packet.readUInt8(); // Flags realm.flags = packet.readUInt8(); // Name (C-string) realm.name = packet.readString(); // Address (C-string) realm.address = packet.readString(); // Population (float) // Read 4 bytes as little-endian float uint32_t populationBits = packet.readUInt32(); std::memcpy(&realm.population, &populationBits, sizeof(float)); // Characters realm.characters = packet.readUInt8(); // Timezone realm.timezone = packet.readUInt8(); // ID realm.id = packet.readUInt8(); // Version info (conditional - only if flags & 0x04) if (realm.hasVersionInfo()) { realm.majorVersion = packet.readUInt8(); realm.minorVersion = packet.readUInt8(); realm.patchVersion = packet.readUInt8(); realm.build = packet.readUInt16(); LOG_DEBUG(" Realm ", (int)i, " (", realm.name, ") version: ", (int)realm.majorVersion, ".", (int)realm.minorVersion, ".", (int)realm.patchVersion, " (", realm.build, ")"); } else { LOG_DEBUG(" Realm ", (int)i, " (", realm.name, ") - no version info"); } LOG_DEBUG(" Realm ", (int)i, " details:"); LOG_DEBUG(" Name: ", realm.name); LOG_DEBUG(" Address: ", realm.address); LOG_DEBUG(" ID: ", (int)realm.id); LOG_DEBUG(" Icon: ", (int)realm.icon); LOG_DEBUG(" Lock: ", (int)realm.lock); LOG_DEBUG(" Flags: ", (int)realm.flags); LOG_DEBUG(" Population: ", realm.population); LOG_DEBUG(" Characters: ", (int)realm.characters); LOG_DEBUG(" Timezone: ", (int)realm.timezone); response.realms.push_back(realm); } LOG_INFO("Parsed ", response.realms.size(), " realms successfully"); return true; } } // namespace auth } // namespace wowee