diff --git a/CMakeLists.txt b/CMakeLists.txt index ff8e3b12c1..2fd234947c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,8 @@ include(cotire) add_compile_options(-Wall -Werror -pipe -fvisibility=hidden) +set(CMAKE_CXX_FLAGS_PERFORMANCE "${CMAKE_CXX_FLAGS_RELEASE} -march=native") + if (CMAKE_COMPILER_IS_GNUCXX) add_compile_options(-fno-strict-aliasing) endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1ec78dd6d9..ae86b6c191 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -74,5 +74,6 @@ set(tfs_SRC ${CMAKE_CURRENT_LIST_DIR}/waitlist.cpp ${CMAKE_CURRENT_LIST_DIR}/weapons.cpp ${CMAKE_CURRENT_LIST_DIR}/wildcardtree.cpp + ${CMAKE_CURRENT_LIST_DIR}/xtea.cpp PARENT_SCOPE) diff --git a/src/protocol.cpp b/src/protocol.cpp index 7c2ed2bf92..24598e1fc9 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -22,6 +22,7 @@ #include "protocol.h" #include "outputmessage.h" #include "rsa.h" +#include "xtea.h" extern RSA g_RSA; @@ -60,37 +61,14 @@ OutputMessage_ptr Protocol::getOutputBuffer(int32_t size) void Protocol::XTEA_encrypt(OutputMessage& msg) const { - const uint32_t delta = 0x61C88647; - // The message must be a multiple of 8 - size_t paddingBytes = msg.getLength() % 8; + size_t paddingBytes = msg.getLength() % 8u; if (paddingBytes != 0) { msg.addPaddingBytes(8 - paddingBytes); } uint8_t* buffer = msg.getOutputBuffer(); - const size_t messageLength = msg.getLength(); - size_t readPos = 0; - const uint32_t k[] = {key[0], key[1], key[2], key[3]}; - while (readPos < messageLength) { - uint32_t v0; - memcpy(&v0, buffer + readPos, 4); - uint32_t v1; - memcpy(&v1, buffer + readPos + 4, 4); - - uint32_t sum = 0; - - for (int32_t i = 32; --i >= 0;) { - v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + k[sum & 3]); - sum -= delta; - v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + k[(sum >> 11) & 3]); - } - - memcpy(buffer + readPos, &v0, 4); - readPos += 4; - memcpy(buffer + readPos, &v1, 4); - readPos += 4; - } + xtea::encrypt(buffer, msg.getLength(), key); } bool Protocol::XTEA_decrypt(NetworkMessage& msg) const @@ -99,34 +77,11 @@ bool Protocol::XTEA_decrypt(NetworkMessage& msg) const return false; } - const uint32_t delta = 0x61C88647; - uint8_t* buffer = msg.getBuffer() + msg.getBufferPosition(); - const size_t messageLength = (msg.getLength() - 6); - size_t readPos = 0; - const uint32_t k[] = {key[0], key[1], key[2], key[3]}; - while (readPos < messageLength) { - uint32_t v0; - memcpy(&v0, buffer + readPos, 4); - uint32_t v1; - memcpy(&v1, buffer + readPos + 4, 4); - - uint32_t sum = 0xC6EF3720; - - for (int32_t i = 32; --i >= 0;) { - v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + k[(sum >> 11) & 3]); - sum += delta; - v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + k[sum & 3]); - } - - memcpy(buffer + readPos, &v0, 4); - readPos += 4; - memcpy(buffer + readPos, &v1, 4); - readPos += 4; - } + xtea::decrypt(buffer, msg.getLength() - 6, key); - int innerLength = msg.get(); - if (innerLength > msg.getLength() - 8) { + uint16_t innerLength = msg.get(); + if (innerLength + 8 > msg.getLength()) { return false; } diff --git a/src/protocol.h b/src/protocol.h index 9864a558be..4fa6127b20 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -21,6 +21,7 @@ #define FS_PROTOCOL_H_D71405071ACF4137A4B1203899DE80E1 #include "connection.h" +#include "xtea.h" class Protocol : public std::enable_shared_from_this { @@ -71,8 +72,8 @@ class Protocol : public std::enable_shared_from_this void enableXTEAEncryption() { encryptionEnabled = true; } - void setXTEAKey(const uint32_t* key) { - memcpy(this->key, key, sizeof(*key) * 4); + void setXTEAKey(xtea::key key) { + this->key = std::move(key); } void disableChecksum() { checksumEnabled = false; @@ -95,7 +96,7 @@ class Protocol : public std::enable_shared_from_this OutputMessage_ptr outputBuffer; const ConnectionWeak_ptr connection; - uint32_t key[4] = {}; + xtea::key key; bool encryptionEnabled = false; bool checksumEnabled = true; bool rawMessages = false; diff --git a/src/protocolgame.cpp b/src/protocolgame.cpp index 4ef047b14f..23e172511f 100644 --- a/src/protocolgame.cpp +++ b/src/protocolgame.cpp @@ -252,13 +252,13 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg) return; } - uint32_t key[4]; + xtea::key key; key[0] = msg.get(); key[1] = msg.get(); key[2] = msg.get(); key[3] = msg.get(); enableXTEAEncryption(); - setXTEAKey(key); + setXTEAKey(std::move(key)); if (operatingSystem >= CLIENTOS_OTCLIENT_LINUX) { NetworkMessage opcodeMessage; diff --git a/src/protocollogin.cpp b/src/protocollogin.cpp index eee0cac990..6dadf9096a 100644 --- a/src/protocollogin.cpp +++ b/src/protocollogin.cpp @@ -150,13 +150,13 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg) return; } - uint32_t key[4]; + xtea::key key; key[0] = msg.get(); key[1] = msg.get(); key[2] = msg.get(); key[3] = msg.get(); enableXTEAEncryption(); - setXTEAKey(key); + setXTEAKey(std::move(key)); if (version < CLIENT_VERSION_MIN || version > CLIENT_VERSION_MAX) { std::ostringstream ss; diff --git a/src/protocolold.cpp b/src/protocolold.cpp index cd7f99a576..c9bb819f98 100644 --- a/src/protocolold.cpp +++ b/src/protocolold.cpp @@ -59,13 +59,13 @@ void ProtocolOld::onRecvFirstMessage(NetworkMessage& msg) return; } - uint32_t key[4]; + xtea::key key; key[0] = msg.get(); key[1] = msg.get(); key[2] = msg.get(); key[3] = msg.get(); enableXTEAEncryption(); - setXTEAKey(key); + setXTEAKey(std::move(key)); if (version <= 822) { disableChecksum(); diff --git a/src/xtea.cpp b/src/xtea.cpp new file mode 100644 index 0000000000..855e2ffa70 --- /dev/null +++ b/src/xtea.cpp @@ -0,0 +1,140 @@ +/** + * The Forgotten Server - a free and open-source MMORPG server emulator + * Copyright (C) 2018 Mark Samman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "otpch.h" + +#include "xtea.h" + +#include +#include + +namespace xtea { + +namespace { + +constexpr uint32_t delta = 0x9E3779B9; + +template +void XTEA_encrypt(uint8_t data[BLOCK_SIZE * 8], const key& k) +{ + alignas(16) uint32_t left[BLOCK_SIZE], right[BLOCK_SIZE]; + for (auto i = 0u, j = 0u; i < BLOCK_SIZE; i += 1u, j += 8u) { + left[i] = data[j] | data[j+1] << 8u | data[j+2] << 16u | data[j+3] << 24u; + right[i] = data[j+4] | data[j+5] << 8u | data[j+6] << 16u | data[j+7] << 24u; + } + + uint32_t sum = 0u; + for (auto i = 0u; i < 32; ++i) { + for (auto j = 0u; j < BLOCK_SIZE; ++j) { + left[j] += (((right[j] << 4) ^ (right[j] >> 5)) + right[j]) ^ (sum + k[sum & 3]); + } + sum += delta; + for (auto j = 0u; j < BLOCK_SIZE; ++j) { + right[j] += (((left[j] << 4) ^ (left[j] >> 5)) + left[j]) ^ (sum + k[(sum >> 11) & 3]); + } + } + + for (auto i = 0u, j = 0u; i < BLOCK_SIZE; i += 1u, j += 8u) { + data[j] = static_cast(left[i]); + data[j+1] = static_cast(left[i] >> 8u); + data[j+2] = static_cast(left[i] >> 16u); + data[j+3] = static_cast(left[i] >> 24u); + data[j+4] = static_cast(right[i]); + data[j+5] = static_cast(right[i] >> 8u); + data[j+6] = static_cast(right[i] >> 16u); + data[j+7] = static_cast(right[i] >> 24u); + } +} + +template +void XTEA_decrypt(uint8_t data[BLOCK_SIZE * 8], const key& k) +{ + alignas(16) uint32_t left[BLOCK_SIZE], right[BLOCK_SIZE]; + for (auto i = 0u, j = 0u; i < BLOCK_SIZE; i += 1u, j += 8u) { + left[i] = data[j] | data[j+1] << 8u | data[j+2] << 16u | data[j+3] << 24u; + right[i] = data[j+4] | data[j+5] << 8u | data[j+6] << 16u | data[j+7] << 24u; + } + + uint32_t sum = delta << 5; + for (auto i = 0u; i < 32; ++i) { + for (auto j = 0u; j < BLOCK_SIZE; ++j) { + right[j] -= (((left[j] << 4) ^ (left[j] >> 5)) + left[j]) ^ (sum + k[(sum >> 11) & 3]); + } + sum -= delta; + for (auto j = 0u; j < BLOCK_SIZE; ++j) { + left[j] -= (((right[j] << 4) ^ (right[j] >> 5)) + right[j]) ^ (sum + k[(sum) & 3]); + } + } + + for (auto i = 0u, j = 0u; i < BLOCK_SIZE; i += 1u, j += 8u) { + data[j] = static_cast(left[i]); + data[j+1] = static_cast(left[i] >> 8u); + data[j+2] = static_cast(left[i] >> 16u); + data[j+3] = static_cast(left[i] >> 24u); + data[j+4] = static_cast(right[i]); + data[j+5] = static_cast(right[i] >> 8u); + data[j+6] = static_cast(right[i] >> 16u); + data[j+7] = static_cast(right[i] >> 24u); + } +} + +constexpr auto InitialBlockSize = +#if defined(__AVX512F__) + 128u; +#elif defined(__AVX__) + 32u; +#elif defined(__SSE__) || defined(__ARM_FEATURE_SIMD32) + 8u; +#elif defined(__x86_64__) + 2u; +#else + 1u; +#endif + +template +struct XTEA { + static constexpr auto step = BlockSize * 8u; + + void operator()(uint8_t* input, size_t length, const key& k) const { + const auto blocks = (length & ~(step - 1)); + for (auto i = 0u; i < blocks; i += step) { + if (Encrypt) { + XTEA_encrypt(input + i, k); + } else { + XTEA_decrypt(input + i, k); + } + } + input += blocks; + length -= blocks; + + if (BlockSize != 1) { + XTEA()(input, length, k); + } + } +}; + +constexpr auto encrypt_v = XTEA(); +constexpr auto decrypt_v = XTEA(); + +} // anonymous namespace + +void encrypt(uint8_t* i, size_t l, const key& k) { encrypt_v(i, l, k); } +void decrypt(uint8_t* i, size_t l, const key& k) { decrypt_v(i, l, k); } + +} // namespace xtea diff --git a/src/xtea.h b/src/xtea.h new file mode 100644 index 0000000000..441afd0c6e --- /dev/null +++ b/src/xtea.h @@ -0,0 +1,32 @@ +/** + * The Forgotten Server - a free and open-source MMORPG server emulator + * Copyright (C) 2018 Mark Samman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef TFS_XTEA_H +#define TFS_XTEA_H + +namespace xtea { + +using key = std::array; + +void encrypt(uint8_t* data, size_t length, const key& k); +void decrypt(uint8_t* data, size_t length, const key& k); + +} // namespace xtea + +#endif // TFS_XTEA_H diff --git a/vc14/theforgottenserver.sln b/vc14/theforgottenserver.sln index 4c37956c69..b51d0f9f5a 100644 --- a/vc14/theforgottenserver.sln +++ b/vc14/theforgottenserver.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.23107.0 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.2035 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "theforgottenserver", "theforgottenserver.vcxproj", "{A10F9657-129F-0FEF-14CB-CEE0B0E5AA3E}" EndProject @@ -25,4 +25,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {81EB239E-6AEB-4015-9915-256C2C90D3FD} + EndGlobalSection EndGlobal diff --git a/vc14/theforgottenserver.vcxproj b/vc14/theforgottenserver.vcxproj index fce4521e8b..8f6ece29fd 100644 --- a/vc14/theforgottenserver.vcxproj +++ b/vc14/theforgottenserver.vcxproj @@ -21,7 +21,7 @@ Win32Proj {A10F9657-129F-0FEF-14CB-CEE0B0E5AA3E} - 10.0.16299.0 + 10.0.17134.0 @@ -69,15 +69,25 @@ - - - - + + $(ProjectName)-$(Platform) + + + $(ProjectName)-$(Platform) + + + $(ProjectName)-$(Platform) + + + $(ProjectName)-$(Platform) + _CONSOLE;$(PREPROCESSOR_DEFS);%(PreprocessorDefinitions) ProgramDatabase Disabled + true + AdvancedVectorExtensions2 MachineX86 @@ -89,6 +99,8 @@ _CONSOLE;$(PREPROCESSOR_DEFS);%(PreprocessorDefinitions) ProgramDatabase + true + AdvancedVectorExtensions2 true @@ -100,6 +112,11 @@ NDEBUG;_CONSOLE;$(PREPROCESSOR_DEFS);%(PreprocessorDefinitions) MultiThreadedDLL ProgramDatabase + MaxSpeed + true + true + true + AdvancedVectorExtensions2 MachineX86 @@ -115,6 +132,11 @@ MultiThreadedDLL ProgramDatabase Level4 + MaxSpeed + true + true + true + AdvancedVectorExtensions2 true @@ -205,6 +227,7 @@ + @@ -290,6 +313,7 @@ +