mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-17 01:23:51 +00:00
Initial commit: wowee native WoW 3.3.5a client
This commit is contained in:
commit
ce6cb8f38e
147 changed files with 32347 additions and 0 deletions
289
src/auth/auth_handler.cpp
Normal file
289
src/auth/auth_handler.cpp
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
#include "auth/auth_handler.hpp"
|
||||
#include "network/tcp_socket.hpp"
|
||||
#include "network/packet.hpp"
|
||||
#include "core/logger.hpp"
|
||||
|
||||
namespace wowee {
|
||||
namespace auth {
|
||||
|
||||
AuthHandler::AuthHandler() {
|
||||
LOG_DEBUG("AuthHandler created");
|
||||
}
|
||||
|
||||
AuthHandler::~AuthHandler() {
|
||||
disconnect();
|
||||
}
|
||||
|
||||
bool AuthHandler::connect(const std::string& host, uint16_t port) {
|
||||
LOG_INFO("Connecting to auth server: ", host, ":", port);
|
||||
|
||||
socket = std::make_unique<network::TCPSocket>();
|
||||
|
||||
// Set up packet callback
|
||||
socket->setPacketCallback([this](const network::Packet& packet) {
|
||||
// Create a mutable copy for handling
|
||||
network::Packet mutablePacket = packet;
|
||||
handlePacket(mutablePacket);
|
||||
});
|
||||
|
||||
if (!socket->connect(host, port)) {
|
||||
LOG_ERROR("Failed to connect to auth server");
|
||||
setState(AuthState::FAILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
setState(AuthState::CONNECTED);
|
||||
LOG_INFO("Connected to auth server");
|
||||
return true;
|
||||
}
|
||||
|
||||
void AuthHandler::disconnect() {
|
||||
if (socket) {
|
||||
socket->disconnect();
|
||||
socket.reset();
|
||||
}
|
||||
setState(AuthState::DISCONNECTED);
|
||||
LOG_INFO("Disconnected from auth server");
|
||||
}
|
||||
|
||||
bool AuthHandler::isConnected() const {
|
||||
return socket && socket->isConnected();
|
||||
}
|
||||
|
||||
void AuthHandler::requestRealmList() {
|
||||
if (!isConnected()) {
|
||||
LOG_ERROR("Cannot request realm list: not connected to auth server");
|
||||
return;
|
||||
}
|
||||
|
||||
if (state != AuthState::AUTHENTICATED) {
|
||||
LOG_ERROR("Cannot request realm list: not authenticated (state: ", (int)state, ")");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("Requesting realm list");
|
||||
sendRealmListRequest();
|
||||
}
|
||||
|
||||
void AuthHandler::authenticate(const std::string& user, const std::string& pass) {
|
||||
if (!isConnected()) {
|
||||
LOG_ERROR("Cannot authenticate: not connected to auth server");
|
||||
fail("Not connected");
|
||||
return;
|
||||
}
|
||||
|
||||
if (state != AuthState::CONNECTED) {
|
||||
LOG_ERROR("Cannot authenticate: invalid state");
|
||||
fail("Invalid state");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("Starting authentication for user: ", user);
|
||||
|
||||
username = user;
|
||||
password = pass;
|
||||
|
||||
// Initialize SRP
|
||||
srp = std::make_unique<SRP>();
|
||||
srp->initialize(username, password);
|
||||
|
||||
// Send LOGON_CHALLENGE
|
||||
sendLogonChallenge();
|
||||
}
|
||||
|
||||
void AuthHandler::sendLogonChallenge() {
|
||||
LOG_DEBUG("Sending LOGON_CHALLENGE");
|
||||
|
||||
auto packet = LogonChallengePacket::build(username, clientInfo);
|
||||
socket->send(packet);
|
||||
|
||||
setState(AuthState::CHALLENGE_SENT);
|
||||
}
|
||||
|
||||
void AuthHandler::handleLogonChallengeResponse(network::Packet& packet) {
|
||||
LOG_DEBUG("Handling LOGON_CHALLENGE response");
|
||||
|
||||
LogonChallengeResponse response;
|
||||
if (!LogonChallengeResponseParser::parse(packet, response)) {
|
||||
fail("Failed to parse LOGON_CHALLENGE response");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!response.isSuccess()) {
|
||||
fail(std::string("LOGON_CHALLENGE failed: ") + getAuthResultString(response.result));
|
||||
return;
|
||||
}
|
||||
|
||||
// Feed SRP with server challenge data
|
||||
srp->feed(response.B, response.g, response.N, response.salt);
|
||||
|
||||
setState(AuthState::CHALLENGE_RECEIVED);
|
||||
|
||||
// Send LOGON_PROOF immediately
|
||||
sendLogonProof();
|
||||
}
|
||||
|
||||
void AuthHandler::sendLogonProof() {
|
||||
LOG_DEBUG("Sending LOGON_PROOF");
|
||||
|
||||
auto A = srp->getA();
|
||||
auto M1 = srp->getM1();
|
||||
|
||||
auto packet = LogonProofPacket::build(A, M1);
|
||||
socket->send(packet);
|
||||
|
||||
setState(AuthState::PROOF_SENT);
|
||||
}
|
||||
|
||||
void AuthHandler::handleLogonProofResponse(network::Packet& packet) {
|
||||
LOG_DEBUG("Handling LOGON_PROOF response");
|
||||
|
||||
LogonProofResponse response;
|
||||
if (!LogonProofResponseParser::parse(packet, response)) {
|
||||
fail("Failed to parse LOGON_PROOF response");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!response.isSuccess()) {
|
||||
fail("LOGON_PROOF failed: invalid proof");
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify server proof
|
||||
if (!srp->verifyServerProof(response.M2)) {
|
||||
fail("Server proof verification failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Authentication successful!
|
||||
sessionKey = srp->getSessionKey();
|
||||
setState(AuthState::AUTHENTICATED);
|
||||
|
||||
LOG_INFO("========================================");
|
||||
LOG_INFO(" AUTHENTICATION SUCCESSFUL!");
|
||||
LOG_INFO("========================================");
|
||||
LOG_INFO("User: ", username);
|
||||
LOG_INFO("Session key size: ", sessionKey.size(), " bytes");
|
||||
|
||||
if (onSuccess) {
|
||||
onSuccess(sessionKey);
|
||||
}
|
||||
}
|
||||
|
||||
void AuthHandler::sendRealmListRequest() {
|
||||
LOG_DEBUG("Sending REALM_LIST request");
|
||||
|
||||
auto packet = RealmListPacket::build();
|
||||
socket->send(packet);
|
||||
|
||||
setState(AuthState::REALM_LIST_REQUESTED);
|
||||
}
|
||||
|
||||
void AuthHandler::handleRealmListResponse(network::Packet& packet) {
|
||||
LOG_DEBUG("Handling REALM_LIST response");
|
||||
|
||||
RealmListResponse response;
|
||||
if (!RealmListResponseParser::parse(packet, response)) {
|
||||
LOG_ERROR("Failed to parse REALM_LIST response");
|
||||
return;
|
||||
}
|
||||
|
||||
realms = response.realms;
|
||||
setState(AuthState::REALM_LIST_RECEIVED);
|
||||
|
||||
LOG_INFO("========================================");
|
||||
LOG_INFO(" REALM LIST RECEIVED!");
|
||||
LOG_INFO("========================================");
|
||||
LOG_INFO("Total realms: ", realms.size());
|
||||
|
||||
for (size_t i = 0; i < realms.size(); ++i) {
|
||||
const auto& realm = realms[i];
|
||||
LOG_INFO("Realm ", (i + 1), ": ", realm.name);
|
||||
LOG_INFO(" Address: ", realm.address);
|
||||
LOG_INFO(" ID: ", (int)realm.id);
|
||||
LOG_INFO(" Population: ", realm.population);
|
||||
LOG_INFO(" Characters: ", (int)realm.characters);
|
||||
if (realm.hasVersionInfo()) {
|
||||
LOG_INFO(" Version: ", (int)realm.majorVersion, ".",
|
||||
(int)realm.minorVersion, ".", (int)realm.patchVersion,
|
||||
" (build ", realm.build, ")");
|
||||
}
|
||||
}
|
||||
|
||||
if (onRealmList) {
|
||||
onRealmList(realms);
|
||||
}
|
||||
}
|
||||
|
||||
void AuthHandler::handlePacket(network::Packet& packet) {
|
||||
if (packet.getSize() < 1) {
|
||||
LOG_WARNING("Received empty packet");
|
||||
return;
|
||||
}
|
||||
|
||||
// Read opcode
|
||||
uint8_t opcodeValue = packet.readUInt8();
|
||||
// Note: packet now has read position advanced past opcode
|
||||
|
||||
AuthOpcode opcode = static_cast<AuthOpcode>(opcodeValue);
|
||||
|
||||
LOG_DEBUG("Received auth packet, opcode: 0x", std::hex, (int)opcodeValue, std::dec);
|
||||
|
||||
switch (opcode) {
|
||||
case AuthOpcode::LOGON_CHALLENGE:
|
||||
if (state == AuthState::CHALLENGE_SENT) {
|
||||
handleLogonChallengeResponse(packet);
|
||||
} else {
|
||||
LOG_WARNING("Unexpected LOGON_CHALLENGE response in state: ", (int)state);
|
||||
}
|
||||
break;
|
||||
|
||||
case AuthOpcode::LOGON_PROOF:
|
||||
if (state == AuthState::PROOF_SENT) {
|
||||
handleLogonProofResponse(packet);
|
||||
} else {
|
||||
LOG_WARNING("Unexpected LOGON_PROOF response in state: ", (int)state);
|
||||
}
|
||||
break;
|
||||
|
||||
case AuthOpcode::REALM_LIST:
|
||||
if (state == AuthState::REALM_LIST_REQUESTED) {
|
||||
handleRealmListResponse(packet);
|
||||
} else {
|
||||
LOG_WARNING("Unexpected REALM_LIST response in state: ", (int)state);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_WARNING("Unhandled auth opcode: 0x", std::hex, (int)opcodeValue, std::dec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AuthHandler::update(float /*deltaTime*/) {
|
||||
if (!socket) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update socket (processes incoming data and calls packet callback)
|
||||
socket->update();
|
||||
}
|
||||
|
||||
void AuthHandler::setState(AuthState newState) {
|
||||
if (state != newState) {
|
||||
LOG_DEBUG("Auth state: ", (int)state, " -> ", (int)newState);
|
||||
state = newState;
|
||||
}
|
||||
}
|
||||
|
||||
void AuthHandler::fail(const std::string& reason) {
|
||||
LOG_ERROR("Authentication failed: ", reason);
|
||||
setState(AuthState::FAILED);
|
||||
|
||||
if (onFailure) {
|
||||
onFailure(reason);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace auth
|
||||
} // namespace wowee
|
||||
32
src/auth/auth_opcodes.cpp
Normal file
32
src/auth/auth_opcodes.cpp
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#include "auth/auth_opcodes.hpp"
|
||||
|
||||
namespace wowee {
|
||||
namespace auth {
|
||||
|
||||
const char* getAuthResultString(AuthResult result) {
|
||||
switch (result) {
|
||||
case AuthResult::SUCCESS: return "Success";
|
||||
case AuthResult::UNKNOWN0: return "Unknown Error 0";
|
||||
case AuthResult::UNKNOWN1: return "Unknown Error 1";
|
||||
case AuthResult::ACCOUNT_BANNED: return "Account Banned";
|
||||
case AuthResult::ACCOUNT_INVALID: return "Account Invalid";
|
||||
case AuthResult::PASSWORD_INVALID: return "Password Invalid";
|
||||
case AuthResult::ALREADY_ONLINE: return "Already Online";
|
||||
case AuthResult::OUT_OF_CREDIT: return "Out of Credit";
|
||||
case AuthResult::BUSY: return "Server Busy";
|
||||
case AuthResult::BUILD_INVALID: return "Build Invalid";
|
||||
case AuthResult::BUILD_UPDATE: return "Build Update Required";
|
||||
case AuthResult::INVALID_SERVER: return "Invalid Server";
|
||||
case AuthResult::ACCOUNT_SUSPENDED: return "Account Suspended";
|
||||
case AuthResult::ACCESS_DENIED: return "Access Denied";
|
||||
case AuthResult::SURVEY: return "Survey Required";
|
||||
case AuthResult::PARENTAL_CONTROL: return "Parental Control";
|
||||
case AuthResult::LOCK_ENFORCED: return "Lock Enforced";
|
||||
case AuthResult::TRIAL_EXPIRED: return "Trial Expired";
|
||||
case AuthResult::BATTLE_NET: return "Battle.net Error";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace auth
|
||||
} // namespace wowee
|
||||
312
src/auth/auth_packets.cpp
Normal file
312
src/auth/auth_packets.cpp
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
#include "auth/auth_packets.hpp"
|
||||
#include "core/logger.hpp"
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
|
||||
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<uint16_t>(AuthOpcode::LOGON_CHALLENGE));
|
||||
|
||||
// Unknown byte
|
||||
packet.writeUInt8(0x00);
|
||||
|
||||
// Payload size
|
||||
packet.writeUInt16(payloadSize);
|
||||
|
||||
// Game name (4 bytes, null-padded)
|
||||
packet.writeBytes(reinterpret_cast<const uint8_t*>(info.game.c_str()),
|
||||
std::min<size_t>(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<const uint8_t*>(info.platform.c_str()),
|
||||
std::min<size_t>(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<const uint8_t*>(info.os.c_str()),
|
||||
std::min<size_t>(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<const uint8_t*>(info.locale.c_str()),
|
||||
std::min<size_t>(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<uint8_t>(upperAccount.length()));
|
||||
packet.writeBytes(reinterpret_cast<const uint8_t*>(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<uint8_t>(AuthOpcode::LOGON_CHALLENGE)) {
|
||||
LOG_ERROR("Invalid opcode in LOGON_CHALLENGE response: ", (int)opcode);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Unknown byte
|
||||
packet.readUInt8();
|
||||
|
||||
// Status
|
||||
response.result = static_cast<AuthResult>(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<uint8_t>& A,
|
||||
const std::vector<uint8_t>& 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<uint16_t>(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<uint8_t>(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<uint16_t>(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<uint8_t>(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
|
||||
152
src/auth/big_num.cpp
Normal file
152
src/auth/big_num.cpp
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
#include "auth/big_num.hpp"
|
||||
#include "core/logger.hpp"
|
||||
#include <openssl/rand.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace wowee {
|
||||
namespace auth {
|
||||
|
||||
BigNum::BigNum() : bn(BN_new()) {
|
||||
if (!bn) {
|
||||
LOG_ERROR("Failed to create BIGNUM");
|
||||
}
|
||||
}
|
||||
|
||||
BigNum::BigNum(uint32_t value) : bn(BN_new()) {
|
||||
BN_set_word(bn, value);
|
||||
}
|
||||
|
||||
BigNum::BigNum(const std::vector<uint8_t>& bytes, bool littleEndian) : bn(BN_new()) {
|
||||
if (littleEndian) {
|
||||
// Convert little-endian to big-endian for OpenSSL
|
||||
std::vector<uint8_t> reversed = bytes;
|
||||
std::reverse(reversed.begin(), reversed.end());
|
||||
BN_bin2bn(reversed.data(), reversed.size(), bn);
|
||||
} else {
|
||||
BN_bin2bn(bytes.data(), bytes.size(), bn);
|
||||
}
|
||||
}
|
||||
|
||||
BigNum::~BigNum() {
|
||||
if (bn) {
|
||||
BN_free(bn);
|
||||
}
|
||||
}
|
||||
|
||||
BigNum::BigNum(const BigNum& other) : bn(BN_dup(other.bn)) {}
|
||||
|
||||
BigNum& BigNum::operator=(const BigNum& other) {
|
||||
if (this != &other) {
|
||||
BN_free(bn);
|
||||
bn = BN_dup(other.bn);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigNum::BigNum(BigNum&& other) noexcept : bn(other.bn) {
|
||||
other.bn = nullptr;
|
||||
}
|
||||
|
||||
BigNum& BigNum::operator=(BigNum&& other) noexcept {
|
||||
if (this != &other) {
|
||||
BN_free(bn);
|
||||
bn = other.bn;
|
||||
other.bn = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigNum BigNum::fromRandom(int bytes) {
|
||||
std::vector<uint8_t> randomBytes(bytes);
|
||||
RAND_bytes(randomBytes.data(), bytes);
|
||||
return BigNum(randomBytes, true);
|
||||
}
|
||||
|
||||
BigNum BigNum::fromHex(const std::string& hex) {
|
||||
BigNum result;
|
||||
BN_hex2bn(&result.bn, hex.c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
BigNum BigNum::fromDecimal(const std::string& dec) {
|
||||
BigNum result;
|
||||
BN_dec2bn(&result.bn, dec.c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
BigNum BigNum::add(const BigNum& other) const {
|
||||
BigNum result;
|
||||
BN_add(result.bn, bn, other.bn);
|
||||
return result;
|
||||
}
|
||||
|
||||
BigNum BigNum::subtract(const BigNum& other) const {
|
||||
BigNum result;
|
||||
BN_sub(result.bn, bn, other.bn);
|
||||
return result;
|
||||
}
|
||||
|
||||
BigNum BigNum::multiply(const BigNum& other) const {
|
||||
BigNum result;
|
||||
BN_CTX* ctx = BN_CTX_new();
|
||||
BN_mul(result.bn, bn, other.bn, ctx);
|
||||
BN_CTX_free(ctx);
|
||||
return result;
|
||||
}
|
||||
|
||||
BigNum BigNum::mod(const BigNum& modulus) const {
|
||||
BigNum result;
|
||||
BN_CTX* ctx = BN_CTX_new();
|
||||
BN_mod(result.bn, bn, modulus.bn, ctx);
|
||||
BN_CTX_free(ctx);
|
||||
return result;
|
||||
}
|
||||
|
||||
BigNum BigNum::modPow(const BigNum& exponent, const BigNum& modulus) const {
|
||||
BigNum result;
|
||||
BN_CTX* ctx = BN_CTX_new();
|
||||
BN_mod_exp(result.bn, bn, exponent.bn, modulus.bn, ctx);
|
||||
BN_CTX_free(ctx);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool BigNum::equals(const BigNum& other) const {
|
||||
return BN_cmp(bn, other.bn) == 0;
|
||||
}
|
||||
|
||||
bool BigNum::isZero() const {
|
||||
return BN_is_zero(bn);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> BigNum::toArray(bool littleEndian, int minSize) const {
|
||||
int size = BN_num_bytes(bn);
|
||||
if (minSize > size) {
|
||||
size = minSize;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> bytes(size, 0);
|
||||
int actualSize = BN_bn2bin(bn, bytes.data() + (size - BN_num_bytes(bn)));
|
||||
|
||||
if (littleEndian) {
|
||||
std::reverse(bytes.begin(), bytes.end());
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
std::string BigNum::toHex() const {
|
||||
char* hex = BN_bn2hex(bn);
|
||||
std::string result(hex);
|
||||
OPENSSL_free(hex);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string BigNum::toDecimal() const {
|
||||
char* dec = BN_bn2dec(bn);
|
||||
std::string result(dec);
|
||||
OPENSSL_free(dec);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace auth
|
||||
} // namespace wowee
|
||||
33
src/auth/crypto.cpp
Normal file
33
src/auth/crypto.cpp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#include "auth/crypto.hpp"
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/hmac.h>
|
||||
|
||||
namespace wowee {
|
||||
namespace auth {
|
||||
|
||||
std::vector<uint8_t> Crypto::sha1(const std::vector<uint8_t>& data) {
|
||||
std::vector<uint8_t> hash(SHA_DIGEST_LENGTH);
|
||||
SHA1(data.data(), data.size(), hash.data());
|
||||
return hash;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Crypto::sha1(const std::string& data) {
|
||||
std::vector<uint8_t> bytes(data.begin(), data.end());
|
||||
return sha1(bytes);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Crypto::hmacSHA1(const std::vector<uint8_t>& key,
|
||||
const std::vector<uint8_t>& data) {
|
||||
std::vector<uint8_t> hash(SHA_DIGEST_LENGTH);
|
||||
unsigned int length = 0;
|
||||
|
||||
HMAC(EVP_sha1(),
|
||||
key.data(), key.size(),
|
||||
data.data(), data.size(),
|
||||
hash.data(), &length);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
} // namespace auth
|
||||
} // namespace wowee
|
||||
75
src/auth/rc4.cpp
Normal file
75
src/auth/rc4.cpp
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
#include "auth/rc4.hpp"
|
||||
#include "core/logger.hpp"
|
||||
#include <cstring>
|
||||
|
||||
namespace wowee {
|
||||
namespace auth {
|
||||
|
||||
RC4::RC4() : x(0), y(0) {
|
||||
// Initialize state to identity
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
state[i] = static_cast<uint8_t>(i);
|
||||
}
|
||||
}
|
||||
|
||||
void RC4::init(const std::vector<uint8_t>& key) {
|
||||
if (key.empty()) {
|
||||
LOG_ERROR("RC4: Cannot initialize with empty key");
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset indices
|
||||
x = 0;
|
||||
y = 0;
|
||||
|
||||
// Initialize state
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
state[i] = static_cast<uint8_t>(i);
|
||||
}
|
||||
|
||||
// Key scheduling algorithm (KSA)
|
||||
uint8_t j = 0;
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
j = j + state[i] + key[i % key.size()];
|
||||
|
||||
// Swap state[i] and state[j]
|
||||
uint8_t temp = state[i];
|
||||
state[i] = state[j];
|
||||
state[j] = temp;
|
||||
}
|
||||
|
||||
LOG_DEBUG("RC4: Initialized with ", key.size(), "-byte key");
|
||||
}
|
||||
|
||||
void RC4::process(uint8_t* data, size_t length) {
|
||||
if (!data || length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Pseudo-random generation algorithm (PRGA)
|
||||
for (size_t n = 0; n < length; ++n) {
|
||||
// Increment indices
|
||||
x = x + 1;
|
||||
y = y + state[x];
|
||||
|
||||
// Swap state[x] and state[y]
|
||||
uint8_t temp = state[x];
|
||||
state[x] = state[y];
|
||||
state[y] = temp;
|
||||
|
||||
// Generate keystream byte and XOR with data
|
||||
uint8_t keystreamByte = state[(state[x] + state[y]) & 0xFF];
|
||||
data[n] ^= keystreamByte;
|
||||
}
|
||||
}
|
||||
|
||||
void RC4::drop(size_t count) {
|
||||
// Drop keystream bytes by processing zeros
|
||||
std::vector<uint8_t> dummy(count, 0);
|
||||
process(dummy.data(), count);
|
||||
|
||||
LOG_DEBUG("RC4: Dropped ", count, " keystream bytes");
|
||||
}
|
||||
|
||||
} // namespace auth
|
||||
} // namespace wowee
|
||||
269
src/auth/srp.cpp
Normal file
269
src/auth/srp.cpp
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
#include "auth/srp.hpp"
|
||||
#include "auth/crypto.hpp"
|
||||
#include "core/logger.hpp"
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
||||
namespace wowee {
|
||||
namespace auth {
|
||||
|
||||
SRP::SRP() : k(K_VALUE) {
|
||||
LOG_DEBUG("SRP instance created");
|
||||
}
|
||||
|
||||
void SRP::initialize(const std::string& username, const std::string& password) {
|
||||
LOG_DEBUG("Initializing SRP with username: ", username);
|
||||
|
||||
// Store credentials for later use
|
||||
stored_username = username;
|
||||
stored_password = password;
|
||||
|
||||
initialized = true;
|
||||
LOG_DEBUG("SRP initialized");
|
||||
}
|
||||
|
||||
void SRP::feed(const std::vector<uint8_t>& B_bytes,
|
||||
const std::vector<uint8_t>& g_bytes,
|
||||
const std::vector<uint8_t>& N_bytes,
|
||||
const std::vector<uint8_t>& salt_bytes) {
|
||||
|
||||
if (!initialized) {
|
||||
LOG_ERROR("SRP not initialized! Call initialize() first.");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG("Feeding SRP challenge data");
|
||||
LOG_DEBUG(" B size: ", B_bytes.size(), " bytes");
|
||||
LOG_DEBUG(" g size: ", g_bytes.size(), " bytes");
|
||||
LOG_DEBUG(" N size: ", N_bytes.size(), " bytes");
|
||||
LOG_DEBUG(" salt size: ", salt_bytes.size(), " bytes");
|
||||
|
||||
// Store server values (all little-endian)
|
||||
this->B = BigNum(B_bytes, true);
|
||||
this->g = BigNum(g_bytes, true);
|
||||
this->N = BigNum(N_bytes, true);
|
||||
this->s = BigNum(salt_bytes, true);
|
||||
|
||||
LOG_DEBUG("SRP challenge data loaded");
|
||||
|
||||
// Now compute everything in sequence
|
||||
|
||||
// 1. Compute auth hash: H(I:P)
|
||||
std::vector<uint8_t> auth_hash = computeAuthHash(stored_username, stored_password);
|
||||
|
||||
// 2. Compute x = H(s | H(I:P))
|
||||
std::vector<uint8_t> x_input;
|
||||
x_input.insert(x_input.end(), salt_bytes.begin(), salt_bytes.end());
|
||||
x_input.insert(x_input.end(), auth_hash.begin(), auth_hash.end());
|
||||
std::vector<uint8_t> x_bytes = Crypto::sha1(x_input);
|
||||
x = BigNum(x_bytes, true);
|
||||
LOG_DEBUG("Computed x (salted password hash)");
|
||||
|
||||
// 3. Generate client ephemeral (a, A)
|
||||
computeClientEphemeral();
|
||||
|
||||
// 4. Compute session key (S, K)
|
||||
computeSessionKey();
|
||||
|
||||
// 5. Compute proofs (M1, M2)
|
||||
computeProofs(stored_username);
|
||||
|
||||
LOG_INFO("SRP authentication data ready!");
|
||||
}
|
||||
|
||||
std::vector<uint8_t> SRP::computeAuthHash(const std::string& username,
|
||||
const std::string& password) const {
|
||||
// Convert to uppercase (WoW requirement)
|
||||
std::string upperUser = username;
|
||||
std::string upperPass = password;
|
||||
std::transform(upperUser.begin(), upperUser.end(), upperUser.begin(), ::toupper);
|
||||
std::transform(upperPass.begin(), upperPass.end(), upperPass.begin(), ::toupper);
|
||||
|
||||
// H(I:P)
|
||||
std::string combined = upperUser + ":" + upperPass;
|
||||
return Crypto::sha1(combined);
|
||||
}
|
||||
|
||||
void SRP::computeClientEphemeral() {
|
||||
LOG_DEBUG("Computing client ephemeral");
|
||||
|
||||
// Generate random private ephemeral a (19 bytes = 152 bits)
|
||||
// Keep trying until we get a valid A
|
||||
int attempts = 0;
|
||||
while (attempts < 100) {
|
||||
a = BigNum::fromRandom(19);
|
||||
|
||||
// A = g^a mod N
|
||||
A = g.modPow(a, N);
|
||||
|
||||
// Ensure A is not zero
|
||||
if (!A.mod(N).isZero()) {
|
||||
LOG_DEBUG("Generated valid client ephemeral after ", attempts + 1, " attempts");
|
||||
break;
|
||||
}
|
||||
attempts++;
|
||||
}
|
||||
|
||||
if (attempts >= 100) {
|
||||
LOG_ERROR("Failed to generate valid client ephemeral after 100 attempts!");
|
||||
}
|
||||
}
|
||||
|
||||
void SRP::computeSessionKey() {
|
||||
LOG_DEBUG("Computing session key");
|
||||
|
||||
// u = H(A | B) - scrambling parameter
|
||||
std::vector<uint8_t> A_bytes = A.toArray(true, 32); // 32 bytes, little-endian
|
||||
std::vector<uint8_t> B_bytes = B.toArray(true, 32); // 32 bytes, little-endian
|
||||
|
||||
std::vector<uint8_t> AB;
|
||||
AB.insert(AB.end(), A_bytes.begin(), A_bytes.end());
|
||||
AB.insert(AB.end(), B_bytes.begin(), B_bytes.end());
|
||||
|
||||
std::vector<uint8_t> u_bytes = Crypto::sha1(AB);
|
||||
u = BigNum(u_bytes, true);
|
||||
|
||||
LOG_DEBUG("Scrambler u calculated");
|
||||
|
||||
// Compute session key: S = (B - kg^x)^(a + ux) mod N
|
||||
|
||||
// Step 1: kg^x
|
||||
BigNum gx = g.modPow(x, N);
|
||||
BigNum kgx = k.multiply(gx);
|
||||
|
||||
// Step 2: B - kg^x
|
||||
BigNum B_minus_kgx = B.subtract(kgx);
|
||||
|
||||
// Step 3: ux
|
||||
BigNum ux = u.multiply(x);
|
||||
|
||||
// Step 4: a + ux
|
||||
BigNum aux = a.add(ux);
|
||||
|
||||
// Step 5: (B - kg^x)^(a + ux) mod N
|
||||
S = B_minus_kgx.modPow(aux, N);
|
||||
|
||||
LOG_DEBUG("Session key S calculated");
|
||||
|
||||
// Interleave the session key to create K
|
||||
// Split S into even and odd bytes, hash each half, then interleave
|
||||
std::vector<uint8_t> S_bytes = S.toArray(true, 32); // 32 bytes for WoW
|
||||
|
||||
std::vector<uint8_t> S1, S2;
|
||||
for (size_t i = 0; i < 16; ++i) {
|
||||
S1.push_back(S_bytes[i * 2]); // Even indices
|
||||
S2.push_back(S_bytes[i * 2 + 1]); // Odd indices
|
||||
}
|
||||
|
||||
// Hash each half
|
||||
std::vector<uint8_t> S1_hash = Crypto::sha1(S1); // 20 bytes
|
||||
std::vector<uint8_t> S2_hash = Crypto::sha1(S2); // 20 bytes
|
||||
|
||||
// Interleave the hashes to create K (40 bytes total)
|
||||
K.clear();
|
||||
K.reserve(40);
|
||||
for (size_t i = 0; i < 20; ++i) {
|
||||
K.push_back(S1_hash[i]);
|
||||
K.push_back(S2_hash[i]);
|
||||
}
|
||||
|
||||
LOG_DEBUG("Interleaved session key K created (", K.size(), " bytes)");
|
||||
}
|
||||
|
||||
void SRP::computeProofs(const std::string& username) {
|
||||
LOG_DEBUG("Computing authentication proofs");
|
||||
|
||||
// Convert username to uppercase
|
||||
std::string upperUser = username;
|
||||
std::transform(upperUser.begin(), upperUser.end(), upperUser.begin(), ::toupper);
|
||||
|
||||
// Compute H(N) and H(g)
|
||||
std::vector<uint8_t> N_bytes = N.toArray(true, 256); // Full 256 bytes
|
||||
std::vector<uint8_t> g_bytes = g.toArray(true);
|
||||
|
||||
std::vector<uint8_t> N_hash = Crypto::sha1(N_bytes);
|
||||
std::vector<uint8_t> g_hash = Crypto::sha1(g_bytes);
|
||||
|
||||
// XOR them: H(N) ^ H(g)
|
||||
std::vector<uint8_t> Ng_xor(20);
|
||||
for (size_t i = 0; i < 20; ++i) {
|
||||
Ng_xor[i] = N_hash[i] ^ g_hash[i];
|
||||
}
|
||||
|
||||
// Compute H(username)
|
||||
std::vector<uint8_t> user_hash = Crypto::sha1(upperUser);
|
||||
|
||||
// Get A, B, and salt as byte arrays
|
||||
std::vector<uint8_t> A_bytes = A.toArray(true, 32);
|
||||
std::vector<uint8_t> B_bytes = B.toArray(true, 32);
|
||||
std::vector<uint8_t> s_bytes = s.toArray(true, 32);
|
||||
|
||||
// M1 = H( H(N)^H(g) | H(I) | s | A | B | K )
|
||||
std::vector<uint8_t> M1_input;
|
||||
M1_input.insert(M1_input.end(), Ng_xor.begin(), Ng_xor.end()); // 20 bytes
|
||||
M1_input.insert(M1_input.end(), user_hash.begin(), user_hash.end()); // 20 bytes
|
||||
M1_input.insert(M1_input.end(), s_bytes.begin(), s_bytes.end()); // 32 bytes
|
||||
M1_input.insert(M1_input.end(), A_bytes.begin(), A_bytes.end()); // 32 bytes
|
||||
M1_input.insert(M1_input.end(), B_bytes.begin(), B_bytes.end()); // 32 bytes
|
||||
M1_input.insert(M1_input.end(), K.begin(), K.end()); // 40 bytes
|
||||
|
||||
M1 = Crypto::sha1(M1_input); // 20 bytes
|
||||
|
||||
LOG_DEBUG("Client proof M1 calculated (", M1.size(), " bytes)");
|
||||
|
||||
// M2 = H( A | M1 | K )
|
||||
std::vector<uint8_t> M2_input;
|
||||
M2_input.insert(M2_input.end(), A_bytes.begin(), A_bytes.end()); // 32 bytes
|
||||
M2_input.insert(M2_input.end(), M1.begin(), M1.end()); // 20 bytes
|
||||
M2_input.insert(M2_input.end(), K.begin(), K.end()); // 40 bytes
|
||||
|
||||
M2 = Crypto::sha1(M2_input); // 20 bytes
|
||||
|
||||
LOG_DEBUG("Expected server proof M2 calculated (", M2.size(), " bytes)");
|
||||
}
|
||||
|
||||
std::vector<uint8_t> SRP::getA() const {
|
||||
if (A.isZero()) {
|
||||
LOG_WARNING("Client ephemeral A not yet computed!");
|
||||
}
|
||||
return A.toArray(true, 32); // 32 bytes, little-endian
|
||||
}
|
||||
|
||||
std::vector<uint8_t> SRP::getM1() const {
|
||||
if (M1.empty()) {
|
||||
LOG_WARNING("Client proof M1 not yet computed!");
|
||||
}
|
||||
return M1;
|
||||
}
|
||||
|
||||
bool SRP::verifyServerProof(const std::vector<uint8_t>& serverM2) const {
|
||||
if (M2.empty()) {
|
||||
LOG_ERROR("Expected server proof M2 not computed!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (serverM2.size() != M2.size()) {
|
||||
LOG_ERROR("Server proof size mismatch: ", serverM2.size(), " vs ", M2.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool match = std::equal(M2.begin(), M2.end(), serverM2.begin());
|
||||
|
||||
if (match) {
|
||||
LOG_INFO("Server proof verified successfully!");
|
||||
} else {
|
||||
LOG_ERROR("Server proof verification FAILED!");
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> SRP::getSessionKey() const {
|
||||
if (K.empty()) {
|
||||
LOG_WARNING("Session key K not yet computed!");
|
||||
}
|
||||
return K;
|
||||
}
|
||||
|
||||
} // namespace auth
|
||||
} // namespace wowee
|
||||
Loading…
Add table
Add a link
Reference in a new issue