From b7eef972391c316a35fe91adb040630f9c74ab23 Mon Sep 17 00:00:00 2001 From: Eduardo Bart Date: Mon, 28 Jan 2013 20:52:03 -0200 Subject: [PATCH] Add flexibility in login packets * It's now possible to add custom data in the login packet * Add utility funciton to generate RSA keys * Make the protocol able to use RSA keys with 2048 bits or more --- modules/client_entergame/characterlist.lua | 3 +- modules/client_entergame/entergame.lua | 3 +- modules/gamelib/const.lua | 3 -- modules/gamelib/game.lua | 10 ++-- modules/gamelib/protocollogin.lua | 46 ++++++++---------- src/client/const.h | 3 -- src/client/game.cpp | 4 +- src/client/game.h | 5 +- src/client/luafunctions.cpp | 2 - src/client/protocolgame.cpp | 3 +- src/client/protocolgame.h | 3 +- src/client/protocolgamesend.cpp | 54 +++++++--------------- src/framework/luafunctions.cpp | 2 + src/framework/net/outputmessage.cpp | 6 ++- src/framework/net/outputmessage.h | 2 +- src/framework/util/crypt.cpp | 28 +++++++++-- src/framework/util/crypt.h | 2 + 17 files changed, 80 insertions(+), 99 deletions(-) diff --git a/modules/client_entergame/characterlist.lua b/modules/client_entergame/characterlist.lua index 29bda7b7..365b0c57 100644 --- a/modules/client_entergame/characterlist.lua +++ b/modules/client_entergame/characterlist.lua @@ -27,8 +27,7 @@ local function tryLogin(charInfo, tries) CharacterList.hide() - local locale = modules.client_locales.getCurrentLocale().name - g_game.loginWorld(G.account, G.password, charInfo.worldName, charInfo.worldHost, charInfo.worldPort, charInfo.characterName, locale) + g_game.loginWorld(G.account, G.password, charInfo.worldName, charInfo.worldHost, charInfo.worldPort, charInfo.characterName) loadBox = displayCancelBox(tr('Please wait'), tr('Connecting to game server...')) connect(loadBox, { onCancel = function() diff --git a/modules/client_entergame/entergame.lua b/modules/client_entergame/entergame.lua index 4378284a..4c021436 100644 --- a/modules/client_entergame/entergame.lua +++ b/modules/client_entergame/entergame.lua @@ -215,8 +215,7 @@ function EnterGame.doLogin() end if modules.game_things.isLoaded() then - local locale = modules.client_locales.getCurrentLocale().name - protocolLogin:login(G.host, G.port, G.account, G.password, locale) + protocolLogin:login(G.host, G.port, G.account, G.password) else loadBox:destroy() loadBox = nil diff --git a/modules/gamelib/const.lua b/modules/gamelib/const.lua index 03fab2e6..22024fbb 100644 --- a/modules/gamelib/const.lua +++ b/modules/gamelib/const.lua @@ -66,8 +66,6 @@ GameFormatCreatureName = 22 GameSpellList = 23 GameClientPing = 24 GameExtendedClientPing = 25 -GameUpdater = 26 -GameLoginLocale = 27 GameDoubleHealth = 28 GameDoubleSkills = 29 GameChangeMapAwareRange = 30 @@ -78,7 +76,6 @@ GameDiagonalAnimatedText = 34 GameLoginPending = 35 GameNewSpeedLaw = 36 GameForceFirstAutoWalkStep = 37 -GameLoginUUID = 38 TextColors = { red = '#f55e5e', --'#c83200' diff --git a/modules/gamelib/game.lua b/modules/gamelib/game.lua index f7a9f2e1..217163ac 100644 --- a/modules/gamelib/game.lua +++ b/modules/gamelib/game.lua @@ -5,6 +5,7 @@ function g_game.getRsa() end function g_game.chooseRsa(host) + if currentRsa ~= CIPSOFT_RSA and currentRsa ~= OTSERV_RSA then return end if string.ends(host, '.tibia.com') or string.ends(host, '.cipsoft.com') then g_game.setRsa(CIPSOFT_RSA) @@ -21,11 +22,10 @@ function g_game.chooseRsa(host) end end -function g_game.setRsa(rsa) - if currentRsa ~= rsa then - currentRsa = rsa - g_crypt.rsaSetPublicKey(currentRsa, '65537') - end +function g_game.setRsa(rsa, e) + e = e or '65537' + g_crypt.rsaSetPublicKey(rsa, e) + currentRsa = rsa end function g_game.isOfficialTibia() diff --git a/modules/gamelib/protocollogin.lua b/modules/gamelib/protocollogin.lua index e519af59..9dca3479 100644 --- a/modules/gamelib/protocollogin.lua +++ b/modules/gamelib/protocollogin.lua @@ -8,7 +8,7 @@ LoginServerUpdateNeeded = 30 LoginServerCharacterList = 100 LoginServerExtendedCharacterList = 101 -function ProtocolLogin:login(host, port, accountName, accountPassword, locale) +function ProtocolLogin:login(host, port, accountName, accountPassword) if string.len(host) == 0 or port == nil or port == 0 then signalcall(self.onError, self, tr("You must enter a valid server address and port.")) return @@ -17,7 +17,6 @@ function ProtocolLogin:login(host, port, accountName, accountPassword, locale) self.accountName = accountName self.accountPassword = accountPassword self.connectCallback = sendLoginPacket - self.locale = locale self:connect(host, port) end @@ -26,21 +25,11 @@ function ProtocolLogin:cancelLogin() self:disconnect() end -function ProtocolLogin:sendLoginPacket() +function ProtocolLogin:sendLoginPacket(extended) local msg = OutputMessage.create() msg:addU8(ClientOpcodes.ClientEnterAccount) msg:addU16(g_game.getOs()) - - if g_game.getFeature(GameUpdater) then - msg:addString(g_app.getOs()) - msg:addString(g_game.getUpdaterSignature()) - end - - if g_game.getFeature(GameLoginLocale) then - msg:addString(self.locale) - end - msg:addU16(g_game.getProtocolVersion()) if g_game.getProtocolVersion() >= 971 then @@ -55,9 +44,10 @@ function ProtocolLogin:sendLoginPacket() msg:addU8(0) -- clientType end - local paddingBytes = 128 - msg:addU8(0) -- first RSA byte must be 0 - paddingBytes = paddingBytes - 1 + local offset = msg:getMessageSize() + + -- first RSA byte must be 0 + msg:addU8(0) -- xtea key self:generateXteaKey() @@ -66,24 +56,28 @@ function ProtocolLogin:sendLoginPacket() msg:addU32(xteaKey[2]) msg:addU32(xteaKey[3]) msg:addU32(xteaKey[4]) - paddingBytes = paddingBytes - 16 - - if g_game.getFeature(GameProtocolChecksum) then - self:enableChecksum() - end if g_game.getFeature(GameAccountNames) then msg:addString(self.accountName) - msg:addString(self.accountPassword) - paddingBytes = paddingBytes - (4 + string.len(self.accountName) + string.len(self.accountPassword)) else msg:addU32(tonumber(self.accountName)) - msg:addString(self.accountPassword) - paddingBytes = paddingBytes - (6 + string.len(self.accountPassword)) end + msg:addString(self.accountPassword) + + if self.getLoginExtendedData then + local data = self:getLoginExtendedData() + msg:addString(data) + end + + local paddingBytes = g_crypt.rsaGetSize() - (msg:getMessageSize() - offset) + assert(paddingBytes >= 0) msg:addPaddingBytes(paddingBytes, 0) - msg:encryptRsa(128) + msg:encryptRsa() + + if g_game.getFeature(GameProtocolChecksum) then + self:enableChecksum() + end self:send(msg) self:enableXteaEncryption() diff --git a/src/client/const.h b/src/client/const.h index d9ae1b94..259b6e74 100644 --- a/src/client/const.h +++ b/src/client/const.h @@ -337,8 +337,6 @@ namespace Otc GameSpellList = 23, GameClientPing = 24, GameExtendedClientPing = 25, - GameUpdater = 26, - GameLoginLocale = 27, GameDoubleHealth = 28, GameDoubleSkills = 29, GameChangeMapAwareRange = 30, @@ -349,7 +347,6 @@ namespace Otc GameLoginPending = 35, GameNewSpeedLaw = 36, GameForceFirstAutoWalkStep = 37, - GameLoginUUID = 38, // 51-100 reserved to be defined in lua LastGameFeature = 101 }; diff --git a/src/client/game.cpp b/src/client/game.cpp index d075de1c..e795013a 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -473,7 +473,7 @@ void Game::processWalkCancel(Otc::Direction direction) m_localPlayer->cancelWalk(direction); } -void Game::loginWorld(const std::string& account, const std::string& password, const std::string& worldName, const std::string& worldHost, int worldPort, const std::string& characterName, const std::string& locale) +void Game::loginWorld(const std::string& account, const std::string& password, const std::string& worldName, const std::string& worldHost, int worldPort, const std::string& characterName) { if(m_protocolGame || isOnline()) stdext::throw_exception("Unable to login into a world while already online or logging."); @@ -488,7 +488,7 @@ void Game::loginWorld(const std::string& account, const std::string& password, c m_localPlayer->setName(characterName); m_protocolGame = ProtocolGamePtr(new ProtocolGame); - m_protocolGame->login(account, password, worldHost, (uint16)worldPort, characterName, locale); + m_protocolGame->login(account, password, worldHost, (uint16)worldPort, characterName); m_characterName = characterName; m_worldName = worldName; } diff --git a/src/client/game.h b/src/client/game.h index e90afd85..b071dde4 100644 --- a/src/client/game.h +++ b/src/client/game.h @@ -135,7 +135,7 @@ protected: public: // login related - void loginWorld(const std::string& account, const std::string& password, const std::string& worldName, const std::string& worldHost, int worldPort, const std::string& characterName, const std::string& locale); + void loginWorld(const std::string& account, const std::string& password, const std::string& worldName, const std::string& worldHost, int worldPort, const std::string& characterName); void cancelLogin(); void forceLogout(); void safeLogout(); @@ -270,9 +270,6 @@ public: void setCustomOs(int os) { m_clientCustomOs = os; } int getOs(); - void setUpdaterSignature(const std::string& sig) { m_clientSignature = sig; } - std::string getUpdaterSignature() { return m_clientSignature; } - bool canPerformGameAction(); bool checkBotProtection(); diff --git a/src/client/luafunctions.cpp b/src/client/luafunctions.cpp index 04185093..7d57d380 100644 --- a/src/client/luafunctions.cpp +++ b/src/client/luafunctions.cpp @@ -238,8 +238,6 @@ void Client::registerLuaFunctions() g_lua.bindSingletonFunction("g_game", "setProtocolVersion", &Game::setProtocolVersion, &g_game); g_lua.bindSingletonFunction("g_game", "getClientVersion", &Game::getClientVersion, &g_game); g_lua.bindSingletonFunction("g_game", "setClientVersion", &Game::setClientVersion, &g_game); - g_lua.bindSingletonFunction("g_game", "setUpdaterSignature", &Game::setUpdaterSignature, &g_game); - g_lua.bindSingletonFunction("g_game", "getUpdaterSignature", &Game::getUpdaterSignature, &g_game); g_lua.bindSingletonFunction("g_game", "setCustomOs", &Game::setCustomOs, &g_game); g_lua.bindSingletonFunction("g_game", "getOs", &Game::getOs, &g_game); g_lua.bindSingletonFunction("g_game", "getCharacterName", &Game::getCharacterName, &g_game); diff --git a/src/client/protocolgame.cpp b/src/client/protocolgame.cpp index 5e0135d2..c6793baf 100644 --- a/src/client/protocolgame.cpp +++ b/src/client/protocolgame.cpp @@ -26,12 +26,11 @@ #include "item.h" #include "localplayer.h" -void ProtocolGame::login(const std::string& accountName, const std::string& accountPassword, const std::string& host, uint16 port, const std::string& characterName, const std::string& locale) +void ProtocolGame::login(const std::string& accountName, const std::string& accountPassword, const std::string& host, uint16 port, const std::string& characterName) { m_accountName = accountName; m_accountPassword = accountPassword; m_characterName = characterName; - m_locale = locale; connect(host, port); } diff --git a/src/client/protocolgame.h b/src/client/protocolgame.h index cdd849c4..850f12aa 100644 --- a/src/client/protocolgame.h +++ b/src/client/protocolgame.h @@ -31,7 +31,7 @@ class ProtocolGame : public Protocol { public: - void login(const std::string& accountName, const std::string& accountPassword, const std::string& host, uint16 port, const std::string& characterName, const std::string& locale); + void login(const std::string& accountName, const std::string& accountPassword, const std::string& host, uint16 port, const std::string& characterName); void send(const OutputMessagePtr& outputMessage); void sendExtendedOpcode(uint8 opcode, const std::string& buffer); @@ -235,7 +235,6 @@ private: std::string m_accountName; std::string m_accountPassword; std::string m_characterName; - std::string m_locale; stdext::timer m_pingTimer; LocalPlayerPtr m_localPlayer; }; diff --git a/src/client/protocolgamesend.cpp b/src/client/protocolgamesend.cpp index 5e10fb7e..4d3dedc4 100644 --- a/src/client/protocolgamesend.cpp +++ b/src/client/protocolgamesend.cpp @@ -53,17 +53,7 @@ void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRando OutputMessagePtr msg(new OutputMessage); msg->addU8(Proto::ClientPendingGame); - msg->addU16(g_game.getOs()); - - if(g_game.getFeature(Otc::GameUpdater)) { - msg->addString(g_app.getOs()); - msg->addString(g_game.getUpdaterSignature()); - } - - if(g_game.getFeature(Otc::GameLoginLocale)) - msg->addString(m_locale); - msg->addU16(g_game.getProtocolVersion()); if(g_game.getProtocolVersion() >= 971) { @@ -71,9 +61,9 @@ void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRando msg->addU8(0); // clientType } - int paddingBytes = 128; + int offset = msg->getMessageSize(); + msg->addU8(0); // first RSA byte must be 0 - paddingBytes -= 1; // xtea key generateXteaKey(); @@ -82,46 +72,34 @@ void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRando msg->addU32(m_xteaKey[2]); msg->addU32(m_xteaKey[3]); msg->addU8(0); // is gm set? - paddingBytes -= 17; - - if(g_game.getFeature(Otc::GameProtocolChecksum)) - enableChecksum(); - if(g_game.getFeature(Otc::GameAccountNames)) { + if(g_game.getFeature(Otc::GameAccountNames)) msg->addString(m_accountName); - msg->addString(m_characterName); - msg->addString(m_accountPassword); - paddingBytes -= 6 + m_accountName.length() + m_characterName.length() + m_accountPassword.length(); - } else { + else msg->addU32(stdext::from_string(m_accountName)); - msg->addString(m_characterName); - msg->addString(m_accountPassword); - paddingBytes -= 8 + m_characterName.length() + m_accountPassword.length(); - } - if(g_game.getFeature(Otc::GameLoginUUID)) { - std::string uuid = g_crypt.getMachineUUID(); - msg->addString(uuid); - paddingBytes -= 2 + uuid.length(); - } + msg->addString(m_characterName); + msg->addString(m_accountPassword); if(g_game.getFeature(Otc::GameChallengeOnLogin)) { msg->addU32(challengeTimestamp); msg->addU8(challengeRandom); - paddingBytes -= 5; } - if(paddingBytes < 0) { - g_game.processLoginError("AccountName or Password or CharacterName are too big!\nPlease contact game support."); - g_game.processDisconnect(); - return; - } + std::string extended = callLuaField("getLoginExtendedData"); + if(!extended.empty()) + msg->addString(extended); - // complete the 128 bytes for rsa encryption with zeros + // complete the bytes for rsa encryption with zeros + int paddingBytes = g_crypt.rsaGetSize() - (msg->getMessageSize() - offset); + assert(paddingBytes >= 0); msg->addPaddingBytes(paddingBytes); // encrypt with RSA - msg->encryptRsa(128); + msg->encryptRsa(); + + if(g_game.getFeature(Otc::GameProtocolChecksum)) + enableChecksum(); send(msg); diff --git a/src/framework/luafunctions.cpp b/src/framework/luafunctions.cpp index ebd8d027..02f77eb4 100644 --- a/src/framework/luafunctions.cpp +++ b/src/framework/luafunctions.cpp @@ -117,9 +117,11 @@ void Application::registerLuaFunctions() g_lua.bindSingletonFunction("g_crypt", "decrypt", &Crypt::decrypt, &g_crypt); g_lua.bindSingletonFunction("g_crypt", "sha1Encode", &Crypt::sha1Encode, &g_crypt); g_lua.bindSingletonFunction("g_crypt", "md5Encode", &Crypt::md5Encode, &g_crypt); + g_lua.bindSingletonFunction("g_crypt", "rsaGenerateKey", &Crypt::rsaGenerateKey, &g_crypt); g_lua.bindSingletonFunction("g_crypt", "rsaSetPublicKey", &Crypt::rsaSetPublicKey, &g_crypt); g_lua.bindSingletonFunction("g_crypt", "rsaSetPrivateKey", &Crypt::rsaSetPrivateKey, &g_crypt); g_lua.bindSingletonFunction("g_crypt", "rsaCheckKey", &Crypt::rsaCheckKey, &g_crypt); + g_lua.bindSingletonFunction("g_crypt", "rsaGetSize", &Crypt::rsaGetSize, &g_crypt); // Clock g_lua.registerSingletonClass("g_clock"); diff --git a/src/framework/net/outputmessage.cpp b/src/framework/net/outputmessage.cpp index 3be8da0f..5f2379f7 100644 --- a/src/framework/net/outputmessage.cpp +++ b/src/framework/net/outputmessage.cpp @@ -89,12 +89,14 @@ void OutputMessage::addPaddingBytes(int bytes, uint8 byte) m_messageSize += bytes; } -void OutputMessage::encryptRsa(int size) +void OutputMessage::encryptRsa() { + int size = g_crypt.rsaGetSize(); if(m_messageSize < size) throw stdext::exception("insufficient bytes in buffer to encrypt"); - g_crypt.rsaEncrypt((unsigned char*)m_buffer + m_writePos - size, size); + if(!g_crypt.rsaEncrypt((unsigned char*)m_buffer + m_writePos - size, size)) + throw stdext::exception("rsa encryption failed"); } void OutputMessage::writeChecksum() diff --git a/src/framework/net/outputmessage.h b/src/framework/net/outputmessage.h index 0a21fb22..530224a7 100644 --- a/src/framework/net/outputmessage.h +++ b/src/framework/net/outputmessage.h @@ -49,7 +49,7 @@ public: void addString(const std::string& buffer); void addPaddingBytes(int bytes, uint8 byte = 0); - void encryptRsa(int size); + void encryptRsa(); uint16 getWritePos() { return m_writePos; } uint16 getMessageSize() { return m_messageSize; } diff --git a/src/framework/util/crypt.cpp b/src/framework/util/crypt.cpp index 52b854f7..7656cd88 100644 --- a/src/framework/util/crypt.cpp +++ b/src/framework/util/crypt.cpp @@ -292,11 +292,21 @@ std::string Crypt::sha512Encode(const std::string& decoded_string, bool upperCas return result; } -void Crypt::rsaSetPublicKey(const std::string& n, const std::string& e) + +void Crypt::rsaGenerateKey(int bits, int e) { - RSA_free(m_rsa); - m_rsa = RSA_new(); + RSA *rsa = RSA_generate_key(bits, e, nullptr, nullptr); + g_logger.info(stdext::format("%d bits (%d bytes) RSA key generated", bits, bits / 8)); + g_logger.info(std::string("p = ") + BN_bn2dec(m_rsa->p)); + g_logger.info(std::string("q = ") + BN_bn2dec(m_rsa->q)); + g_logger.info(std::string("d = ") + BN_bn2dec(m_rsa->d)); + g_logger.info(std::string("n = ") + BN_bn2dec(m_rsa->n)); + g_logger.info(std::string("e = ") + BN_bn2dec(m_rsa->e)); + RSA_free(rsa); +} +void Crypt::rsaSetPublicKey(const std::string& n, const std::string& e) +{ BN_dec2bn(&m_rsa->n, n.c_str()); BN_dec2bn(&m_rsa->e, e.c_str()); } @@ -331,12 +341,20 @@ bool Crypt::rsaCheckKey() bool Crypt::rsaEncrypt(unsigned char *msg, int size) { - assert(size <= 128); + if(size != RSA_size(m_rsa)) + return false; return RSA_public_encrypt(size, msg, msg, m_rsa, RSA_NO_PADDING) != -1; } bool Crypt::rsaDecrypt(unsigned char *msg, int size) { - assert(size <= 128); + if(size != RSA_size(m_rsa)) + return false; return RSA_private_decrypt(size, msg, msg, m_rsa, RSA_NO_PADDING) != -1; } + +int Crypt::rsaGetSize() +{ + return RSA_size(m_rsa); +} + diff --git a/src/framework/util/crypt.h b/src/framework/util/crypt.h index 79ebb6bf..b12d63dd 100644 --- a/src/framework/util/crypt.h +++ b/src/framework/util/crypt.h @@ -49,11 +49,13 @@ public: std::string sha256Encode(const std::string& decoded_string, bool upperCase); std::string sha512Encode(const std::string& decoded_string, bool upperCase); + void rsaGenerateKey(int bits, int e); void rsaSetPublicKey(const std::string& n, const std::string& e); void rsaSetPrivateKey(const std::string &p, const std::string &q, const std::string &d); bool rsaCheckKey(); bool rsaEncrypt(unsigned char *msg, int size); bool rsaDecrypt(unsigned char *msg, int size); + int rsaGetSize(); private: std::string getMachineKey();