encrypt password and account number

* the encryption uses a unique machine key, this means that if anyone steals config.otml with a saved password,
he will not be able to decrypt the password without the machine UUID key
* the encrypt uses a simple XOR encryption method, encoded with base64 and adler32 summing
This commit is contained in:
Eduardo Bart 2012-06-04 09:38:15 -03:00
parent 296f2a17c4
commit e5000fa577
13 changed files with 249 additions and 32 deletions

View File

@ -40,7 +40,7 @@ local function tryLogin(charInfo, tries)
CharacterList.destroyLoadBox() CharacterList.destroyLoadBox()
g_game.loginWorld(EnterGame.account, EnterGame.password, charInfo.worldName, charInfo.worldHost, charInfo.worldPort, charInfo.characterName) 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...')) loadBox = displayCancelBox(tr('Please wait'), tr('Connecting to game server...'))
connect(loadBox, { onCancel = function() connect(loadBox, { onCancel = function()
@ -81,6 +81,10 @@ function CharacterList.init()
connect(g_game, { onConnectionError = onGameConnectionError }) connect(g_game, { onConnectionError = onGameConnectionError })
connect(g_game, { onGameStart = CharacterList.destroyLoadBox }) connect(g_game, { onGameStart = CharacterList.destroyLoadBox })
connect(g_game, { onGameEnd = CharacterList.showAgain }) connect(g_game, { onGameEnd = CharacterList.showAgain })
if G.characters then
CharacterList.create(G.characters, G.premDays)
end
end end
function CharacterList.terminate() function CharacterList.terminate()
@ -100,7 +104,9 @@ function CharacterList.terminate()
end end
function CharacterList.create(characters, premDays) function CharacterList.create(characters, premDays)
CharacterList.show() G.characters = characters
G.premDays = premDays
characterList:destroyChildren() characterList:destroyChildren()
local accountStatusLabel = charactersWindow:getChildById('accountStatusLabel') local accountStatusLabel = charactersWindow:getChildById('accountStatusLabel')

View File

@ -3,8 +3,6 @@ EnterGame = { }
-- private variables -- private variables
local loadBox local loadBox
local enterGame local enterGame
local motdNumber
local motdMessage
local motdButton local motdButton
local enterGameButton local enterGameButton
@ -29,15 +27,15 @@ local function onError(protocol, message, connectionError)
end end
local function onMotd(protocol, motd) local function onMotd(protocol, motd)
motdNumber = tonumber(motd:sub(0, motd:find("\n"))) G.motdNumber = tonumber(motd:sub(0, motd:find("\n")))
motdMessage = motd:sub(motd:find("\n") + 1, #motd) G.motdMessage = motd:sub(motd:find("\n") + 1, #motd)
motdButton:show() motdButton:show()
end end
local function onCharacterList(protocol, characters, premDays) local function onCharacterList(protocol, characters, premDays)
if enterGame:getChildById('rememberPasswordBox'):isChecked() then if enterGame:getChildById('rememberPasswordBox'):isChecked() then
Settings.set('account', EnterGame.account) Settings.set('account', g_crypt.encrypt(G.account))
Settings.set('password', EnterGame.password) Settings.set('password', g_crypt.encrypt(G.password))
Settings.set('autologin', enterGame:getChildById('autoLoginBox'):isChecked()) Settings.set('autologin', enterGame:getChildById('autoLoginBox'):isChecked())
else else
clearAccountFields() clearAccountFields()
@ -47,11 +45,12 @@ local function onCharacterList(protocol, characters, premDays)
loadBox = nil loadBox = nil
CharacterList.create(characters, premDays) CharacterList.create(characters, premDays)
CharacterList.show()
local lastMotdNumber = Settings.getNumber("motd") local lastMotdNumber = Settings.getNumber("motd")
if motdNumber and motdNumber ~= lastMotdNumber then if G.motdNumber and G.motdNumber ~= lastMotdNumber then
Settings.set("motd", motdNumber) Settings.set("motd", motdNumber)
local motdBox = displayInfoBox(tr('Message of the day'), motdMessage) local motdBox = displayInfoBox(tr('Message of the day'), G.motdMessage)
connect(motdBox, { onOk = CharacterList.show }) connect(motdBox, { onOk = CharacterList.show })
CharacterList.hide() CharacterList.hide()
end end
@ -65,8 +64,12 @@ function EnterGame.init()
motdButton:hide() motdButton:hide()
Keyboard.bindKeyDown('Ctrl+G', EnterGame.openWindow) Keyboard.bindKeyDown('Ctrl+G', EnterGame.openWindow)
local account = Settings.get('account') if G.motdNumber then
local password = Settings.get('password') motdButton:show()
end
local account = g_crypt.decrypt(Settings.get('account'))
local password = g_crypt.decrypt(Settings.get('password'))
local host = Settings.get('host') local host = Settings.get('host')
local port = Settings.get('port') local port = Settings.get('port')
local autologin = Settings.getBoolean('autologin') local autologin = Settings.getBoolean('autologin')
@ -119,14 +122,14 @@ function EnterGame.openWindow()
end end
function EnterGame.doLogin() function EnterGame.doLogin()
EnterGame.account = enterGame:getChildById('accountNameTextEdit'):getText() G.account = enterGame:getChildById('accountNameTextEdit'):getText()
EnterGame.password = enterGame:getChildById('accountPasswordTextEdit'):getText() G.password = enterGame:getChildById('accountPasswordTextEdit'):getText()
EnterGame.host = enterGame:getChildById('serverHostTextEdit'):getText() G.host = enterGame:getChildById('serverHostTextEdit'):getText()
EnterGame.port = tonumber(enterGame:getChildById('serverPortTextEdit'):getText()) G.port = tonumber(enterGame:getChildById('serverPortTextEdit'):getText())
EnterGame.hide() EnterGame.hide()
Settings.set('host', EnterGame.host) Settings.set('host', G.host)
Settings.set('port', EnterGame.port) Settings.set('port', G.port)
local protocolLogin = ProtocolLogin.create() local protocolLogin = ProtocolLogin.create()
protocolLogin.onError = onError protocolLogin.onError = onError
@ -140,9 +143,9 @@ function EnterGame.doLogin()
EnterGame.show() EnterGame.show()
end }) end })
protocolLogin:login(EnterGame.host, EnterGame.port, EnterGame.account, EnterGame.password) protocolLogin:login(G.host, G.port, G.account, G.password)
end end
function EnterGame.displayMotd() function EnterGame.displayMotd()
displayInfoBox(tr('Message of the day'), motdMessage) displayInfoBox(tr('Message of the day'), G.motdMessage)
end end

View File

@ -4,6 +4,9 @@ importStyle = g_ui.importStyle
importFont = g_fonts.importFont importFont = g_fonts.importFont
setDefaultFont = g_fonts.setDefaultFont setDefaultFont = g_fonts.setDefaultFont
-- G is used as a global table to save variables in memory between reloads
G = G or {}
function loadUI(otui, parent) function loadUI(otui, parent)
local otuiFilePath = resolvepath(otui, 2) local otuiFilePath = resolvepath(otui, 2)
return g_ui.loadUI(otuiFilePath, parent) return g_ui.loadUI(otuiFilePath, parent)

View File

@ -171,6 +171,8 @@ SET(framework_SOURCES ${framework_SOURCES}
# framework util # framework util
${CMAKE_CURRENT_LIST_DIR}/util/color.cpp ${CMAKE_CURRENT_LIST_DIR}/util/color.cpp
${CMAKE_CURRENT_LIST_DIR}/util/crypt.cpp
${CMAKE_CURRENT_LIST_DIR}/util/rsa.cpp
# framework core # framework core
${CMAKE_CURRENT_LIST_DIR}/core/adaptativeframecounter.cpp ${CMAKE_CURRENT_LIST_DIR}/core/adaptativeframecounter.cpp
@ -189,7 +191,6 @@ SET(framework_SOURCES ${framework_SOURCES}
${CMAKE_CURRENT_LIST_DIR}/net/inputmessage.cpp ${CMAKE_CURRENT_LIST_DIR}/net/inputmessage.cpp
${CMAKE_CURRENT_LIST_DIR}/net/outputmessage.cpp ${CMAKE_CURRENT_LIST_DIR}/net/outputmessage.cpp
${CMAKE_CURRENT_LIST_DIR}/net/protocol.cpp ${CMAKE_CURRENT_LIST_DIR}/net/protocol.cpp
${CMAKE_CURRENT_LIST_DIR}/net/rsa.cpp
${CMAKE_CURRENT_LIST_DIR}/net/server.cpp ${CMAKE_CURRENT_LIST_DIR}/net/server.cpp
# framework platform # framework platform

View File

@ -33,6 +33,7 @@
#include <framework/core/modulemanager.h> #include <framework/core/modulemanager.h>
#include <framework/core/module.h> #include <framework/core/module.h>
#include <framework/sound/soundmanager.h> #include <framework/sound/soundmanager.h>
#include <framework/util/crypt.h>
void Application::registerLuaFunctions() void Application::registerLuaFunctions()
{ {
@ -46,6 +47,10 @@ void Application::registerLuaFunctions()
g_lua.bindGlobalFunction("colortostring", [](const Color& v) { return stdext::to_string(v); }); g_lua.bindGlobalFunction("colortostring", [](const Color& v) { return stdext::to_string(v); });
g_lua.bindGlobalFunction("sizetostring", [](const Size& v) { return stdext::to_string(v); }); g_lua.bindGlobalFunction("sizetostring", [](const Size& v) { return stdext::to_string(v); });
g_lua.registerStaticClass("g_crypt");
g_lua.bindClassStaticFunction("g_crypt", "encrypt", Crypt::encrypt);
g_lua.bindClassStaticFunction("g_crypt", "decrypt", Crypt::decrypt);
// Event // Event
g_lua.registerClass<Event>(); g_lua.registerClass<Event>();
g_lua.bindClassMemberFunction<Event>("cancel", &Event::cancel); g_lua.bindClassMemberFunction<Event>("cancel", &Event::cancel);

View File

@ -21,7 +21,7 @@
*/ */
#include "inputmessage.h" #include "inputmessage.h"
#include "rsa.h" #include <framework/util/rsa.h>
InputMessage::InputMessage() InputMessage::InputMessage()
{ {
@ -105,7 +105,7 @@ void InputMessage::setHeaderSize(uint16 size)
bool InputMessage::readChecksum() bool InputMessage::readChecksum()
{ {
uint32_t receivedCheck = getU32(); uint32_t receivedCheck = getU32();
uint32 checksum = stdext::generate_adler_checksum(m_buffer + m_readPos, getUnreadSize()); uint32 checksum = stdext::adler32(m_buffer + m_readPos, getUnreadSize());
return receivedCheck == checksum; return receivedCheck == checksum;
} }

View File

@ -21,7 +21,7 @@
*/ */
#include <framework/net/outputmessage.h> #include <framework/net/outputmessage.h>
#include "rsa.h" #include <framework/util/rsa.h>
OutputMessage::OutputMessage() OutputMessage::OutputMessage()
{ {
@ -96,7 +96,7 @@ void OutputMessage::encryptRSA(int size, const std::string& key)
void OutputMessage::writeChecksum() void OutputMessage::writeChecksum()
{ {
uint32 checksum = stdext::generate_adler_checksum(m_buffer + m_headerPos, m_messageSize); uint32 checksum = stdext::adler32(m_buffer + m_headerPos, m_messageSize);
assert(m_headerPos - 4 >= 0); assert(m_headerPos - 4 >= 0);
m_headerPos -= 4; m_headerPos -= 4;
stdext::writeLE32(m_buffer + m_headerPos, checksum); stdext::writeLE32(m_buffer + m_headerPos, checksum);

View File

@ -28,7 +28,7 @@
namespace stdext { namespace stdext {
inline uint32 generate_adler_checksum(uint8 *buffer, uint16 size) { inline uint32 adler32(const uint8 *buffer, uint16 size) {
register uint32 a = 1, b = 0, tlen; register uint32 a = 1, b = 0, tlen;
while(size > 0) { while(size > 0) {
tlen = size > 5552 ? 5552 : size; tlen = size > 5552 ? 5552 : size;
@ -57,9 +57,9 @@ inline uint32 to_power_of_two(uint32 v) {
return r; return r;
} }
inline uint16 readLE16(uchar *addr) { return (uint16)addr[1] << 8 | addr[0]; } inline uint16 readLE16(const uchar *addr) { return (uint16)addr[1] << 8 | addr[0]; }
inline uint32 readLE32(uchar *addr) { return (uint32)readLE16(addr + 2) << 16 | readLE16(addr); } inline uint32 readLE32(const uchar *addr) { return (uint32)readLE16(addr + 2) << 16 | readLE16(addr); }
inline uint64 readLE64(uchar *addr) { return (uint64)readLE32(addr + 4) << 32 | readLE32(addr); } inline uint64 readLE64(const uchar *addr) { return (uint64)readLE32(addr + 4) << 32 | readLE32(addr); }
inline void writeLE16(uchar *addr, uint16 value) { addr[1] = value >> 8; addr[0] = (uint8)value; } inline void writeLE16(uchar *addr, uint16 value) { addr[1] = value >> 8; addr[0] = (uint8)value; }
inline void writeLE32(uchar *addr, uint32 value) { writeLE16(addr + 2, value >> 16); writeLE16(addr, (uint16)value); } inline void writeLE32(uchar *addr, uint32 value) { writeLE16(addr + 2, value >> 16); writeLE16(addr, (uint16)value); }

View File

@ -161,7 +161,7 @@ inline std::string date_time_string() {
} }
/// Convert decimal to hexadecimal /// Convert decimal to hexadecimal
inline std::string dec_to_hex(unsigned int num) { inline std::string dec_to_hex(uint64 num) {
std::string str; std::string str;
std::ostringstream o; std::ostringstream o;
o << std::hex << num; o << std::hex << num;
@ -170,8 +170,8 @@ inline std::string dec_to_hex(unsigned int num) {
} }
/// Convert hexadecimal to decimal /// Convert hexadecimal to decimal
inline unsigned int hex_to_dec(const std::string& str) { inline uint64 hex_to_dec(const std::string& str) {
unsigned int num; uint64 num;
std::istringstream i(str); std::istringstream i(str);
i >> std::hex >> num; i >> std::hex >> num;
return num; return num;

View File

@ -0,0 +1,161 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "crypt.h"
#include <framework/stdext/math.h>
#include <boost/uuid/uuid.hpp>
#include <boost/functional/hash.hpp>
static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static inline bool is_base64(unsigned char c) { return (isalnum(c) || (c == '+') || (c == '/')); }
std::string Crypt::base64Encode(const std::string& decoded_string)
{
std::string ret;
int i = 0;
int j = 0;
uint8 char_array_3[3];
uint8 char_array_4[4];
int pos = 0;
int len = decoded_string.size();
while(len--) {
char_array_3[i++] = decoded_string[pos++];
if(i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for(i = 0; (i <4) ; i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if(i) {
for(j = i; j < 3; j++)
char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for(j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];
while((i++ < 3))
ret += '=';
}
return ret;
}
std::string Crypt::base64Decode(const std::string& encoded_string)
{
int len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
uint8 char_array_4[4], char_array_3[3];
std::string ret;
while(len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if(i ==4) {
for(i = 0; i <4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for(i = 0; (i < 3); i++)
ret += char_array_3[i];
i = 0;
}
}
if(i) {
for(j = i; j <4; j++)
char_array_4[j] = 0;
for(j = 0; j <4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for(j = 0; (j < i - 1); j++)
ret += char_array_3[j];
}
return ret;
}
std::string Crypt::xorCrypt(const std::string& buffer, const std::string& key)
{
std::string out;
out.resize(buffer.size());
register size_t i, j=0;
for(i=0;i<buffer.size();++i) {
out[i] = buffer[i] ^ key[j++];
if(j >= key.size())
j = 0;
}
return out;
}
std::string Crypt::genUUIDKey()
{
boost::hash<boost::uuids::uuid> uuid_hasher;
std::size_t hash = uuid_hasher(boost::uuids::uuid());
std::string key;
key.assign((const char *)&hash, sizeof(hash));
return key;
}
std::string Crypt::encrypt(const std::string& decrypted_string)
{
std::string tmp = "0000" + decrypted_string;
uint32 sum = stdext::adler32((const uint8*)decrypted_string.c_str(), decrypted_string.size());
stdext::writeLE32((uint8*)&tmp[0], sum);
std::string encrypted = base64Encode(xorCrypt(tmp, genUUIDKey()));
return encrypted;
}
std::string Crypt::decrypt(const std::string& encrypted_string)
{
std::string decoded = base64Decode(encrypted_string);
std::string tmp = xorCrypt(base64Decode(encrypted_string), genUUIDKey());
if(tmp.length() >= 4) {
uint32 readsum = stdext::readLE32((const uint8*)tmp.c_str());
std::string decrypted_string = tmp.substr(4);
uint32 sum = stdext::adler32((const uint8*)decrypted_string.c_str(), decrypted_string.size());
if(readsum == sum)
return decrypted_string;
}
return std::string();
}

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef CRYPT_H
#define CRYPT_H
#include "../stdext/types.h"
#include <string>
namespace Crypt {
std::string base64Encode(const std::string& decoded_string);
std::string base64Decode(const std::string& encoded_string);
std::string xorCrypt(const std::string& buffer, const std::string& key);
std::string genUUIDKey();
std::string encrypt(const std::string& decrypted_string);
std::string decrypt(const std::string& encrypted_string);
}
#endif