diff --git a/source/common/EQStream.h b/source/common/EQStream.h index 098d960..7b395ca 100644 --- a/source/common/EQStream.h +++ b/source/common/EQStream.h @@ -21,7 +21,7 @@ #include "Mutex.h" #include "opcodemgr.h" #include "misc.h" -#include "crypto/Crypto.h" +#include "crypto/crypto.h" #include "zlib.h" #include "timer.h" @@ -126,7 +126,7 @@ struct ServerSessionStats { uint32 unknown4; // Unknown field 4 uint32 received_packets2; // Duplicate received packets count }; - + #pragma pack() // External opcode manager @@ -167,11 +167,11 @@ protected: // Packet statistics uint32 received_packets; // Total packets received uint32 sent_packets; // Total packets sent - + // Remote endpoint information uint32 remote_ip; // Remote IP address uint16 remote_port; // Remote port number - + // Packet buffers uint8 buffer[8192]; // Main packet buffer unsigned char* oversize_buffer; // Buffer for oversized packets @@ -180,16 +180,16 @@ protected: unsigned char* rogue_buffer; // Buffer for rogue/malformed packets uint32 roguebuf_offset; // Offset in rogue buffer uint32 roguebuf_size; // Size of rogue buffer - + // Protocol configuration uint8 app_opcode_size; // Size of application opcodes EQStreamType StreamType; // Type of this stream bool compressed; // Stream supports compression bool encoded; // Stream supports encoding/encryption - + // Write buffer for outgoing packets unsigned char write_buffer[2048]; - + // Retransmission timing uint32 retransmittimer; // Current retransmit timer uint32 retransmittimeout; // Retransmit timeout value @@ -244,7 +244,7 @@ protected: long MaxAckReceived; // Highest ack sequence received long NextAckToSend; // Next ack sequence to send long LastAckSent; // Last ack sequence sent - + // Acknowledgment accessor methods long GetMaxAckReceived(); long GetNextAckToSend(); @@ -293,16 +293,16 @@ public: int32 stream_buffer_size; // Size of compression buffer bool eq2_compressed; // Stream uses EQ2 compression int8 compressed_offset; // Offset for compressed data - + // Client version management int16 client_version; // Client protocol version int16 GetClientVersion() { return client_version; } void SetClientVersion(int16 version) { client_version = version; } - + // Session attempt management void ResetSessionAttempts() { reconnectAttempt = 0; } bool HasSessionAttempts() { return reconnectAttempt > 0; } - + // Constructors EQStream() { init(); @@ -314,7 +314,7 @@ public: encoded = false; app_opcode_size = 2; } - + EQStream(sockaddr_in addr); // Destructor virtual ~EQStream() { @@ -322,32 +322,32 @@ public: MOutboundQueue.lock(); SetState(CLOSED); MOutboundQueue.unlock(); - + // Clean up data structures RemoveData(); - + // Clean up allocated resources safe_delete(crypto); safe_delete(combine_timer); safe_delete(resend_que_timer); safe_delete_array(oversize_buffer); safe_delete_array(rogue_buffer); - + // Clean up combine queue MCombineQueueLock.lock(); for (auto cmb = combine_queue.begin(); cmb != combine_queue.end(); ++cmb) { safe_delete(*cmb); } MCombineQueueLock.unlock(); - + // Clean up compression stream deflateEnd(&stream); - + // Clean up out-of-order packets for (auto oop = OutOfOrderpackets.begin(); oop != OutOfOrderpackets.end(); ++oop) { safe_delete(oop->second); } - + #ifdef WRITE_PACKETS if (write_packets) { fclose(write_packets); @@ -357,19 +357,19 @@ public: // Factory and initialization void SetFactory(EQStreamFactory* f) { Factory = f; } void init(bool resetSession = true); - + // Configuration void SetMaxLen(uint32 length) { MaxLen = length; } int8 getTimeoutDelays() { return timeout_delays; } void addTimeoutDelay() { timeout_delays++; } - + // EQ2-specific packet handling void EQ2QueuePacket(EQ2Packet* app, bool attempted_combine = false); void PreparePacket(EQ2Packet* app, int8 offset = 0); void UnPreparePacket(EQ2Packet* app); void EncryptPacket(EQ2Packet* app, int8 compress_offset, int8 offset); void FlushCombinedPacket(); - + // General packet transmission void SendPacket(EQApplicationPacket* p); void QueuePacket(EQProtocolPacket* p); @@ -390,11 +390,11 @@ public: // Stream state management void SetActive(bool val) { streamactive = val; } - + // Low-level packet operations void WritePacket(int fd, EQProtocolPacket* p); void EncryptPacket(uchar* data, int16 size); - + // Session management uint32 GetKey() { return Key; } void SetKey(uint32 k) { Key = k; } @@ -404,7 +404,7 @@ public: // Packet processing void Process(const unsigned char* data, uint32 length); void ProcessPacket(EQProtocolPacket* p, EQProtocolPacket* lastp = nullptr); - + // Embedded packet handling bool ProcessEmbeddedPacket(uchar* pBuffer, uint16 length, int8 opcode = OP_Packet); bool HandleEmbeddedPacket(EQProtocolPacket* p, int16 offset = 2, int16 length = 0); @@ -439,11 +439,11 @@ public: // Outbound queue management void OutboundQueueClear(); bool HasOutgoingData(); - + // Key exchange and RSA void SendKeyRequest(); int16 processRSAKey(EQProtocolPacket* p, uint16 subpacket_length = 0); - + // Data cleanup void RemoveData() { InboundQueueClear(); @@ -462,13 +462,13 @@ public: MInUse.unlock(); return flag; } - + void PutInUse() { MInUse.lock(); active_users++; MInUse.unlock(); } - + void ReleaseFromUse() { MInUse.lock(); if (active_users > 0) { @@ -501,7 +501,7 @@ public: void Close() { SendDisconnect(); } bool CheckActive() { return (GetState() == ESTABLISHED); } bool CheckClosed() { return GetState() == CLOSED; } - + // Stream configuration void SetOpcodeSize(uint8 s) { app_opcode_size = s; } void SetStreamType(EQStreamType t); @@ -514,7 +514,7 @@ public: // Rate limiting and flow control void Decay(); void AdjustRates(uint32 average_delta); - + // Resend timer Timer* resend_que_timer; // Timer for checking resend queue }; diff --git a/source/common/crypto/Crypto.cpp b/source/common/crypto/Crypto.cpp deleted file mode 100644 index 789dac5..0000000 --- a/source/common/crypto/Crypto.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (C) 2007-2025 EQ2EMulator -// Licensed under GPL v3 -#include "Crypto.h" -#include -#include "../common/packet_dump.h" - -using namespace std; -void test(); -int64 Crypto::RSADecrypt(uchar* text, int16 size){ - int64 ret = 0; - uchar* buffer = new uchar[8]; - for(int i=7;i>=0;i--) - buffer[7-i] = text[i]; - memcpy(&ret, buffer, 8); - safe_delete_array(buffer); - return ret; -} - -void Crypto::RC4Decrypt(uchar* text, int32 size){ - MCrypto.lock(); - client->Cypher(text, size); - MCrypto.unlock(); -} - -void Crypto::RC4Encrypt(uchar* text, int32 size){ - MCrypto.lock(); - server->Cypher(text, size); - MCrypto.unlock(); -} - diff --git a/source/common/crypto/Crypto.h b/source/common/crypto/Crypto.h deleted file mode 100644 index 0e67824..0000000 --- a/source/common/crypto/Crypto.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (C) 2007-2025 EQ2EMulator -// Licensed under GPL v3 -#ifndef _CRYPTO_H -#define _CRYPTO_H -#include -#include -#include "RC4.h" -#include "../common/types.h" - -using namespace std; -class Crypto { -public: - ~Crypto(){ safe_delete(client); safe_delete(server); } - Crypto() { rc4_key = 0; encrypted = false; client = 0; server = 0; }; - - static int64 RSADecrypt(uchar* text, int16 size); - void RC4Encrypt(uchar* text, int32 size); - void RC4Decrypt(uchar* text, int32 size); - int64 getRC4Key() { return rc4_key; } - void setRC4Key(int64 key) { - rc4_key = key; - if(key > 0){ - encrypted = true; - client = new RC4(~key); - server = new RC4(key); - uchar temp[20]; - client->Cypher(temp, 20); - server->Cypher(temp, 20); - } - else{ - encrypted = false; - safe_delete(client); - safe_delete(server); - } - } - bool isEncrypted(){ return encrypted; } - void setEncrypted(bool in_val){ encrypted = in_val; } - - -private: - RC4* server; - RC4* client; - bool encrypted; - int64 rc4_key; - mutex MCrypto; -}; - -#endif - diff --git a/source/common/crypto/RC4.cpp b/source/common/crypto/RC4.cpp deleted file mode 100644 index ef7612d..0000000 --- a/source/common/crypto/RC4.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (C) 2007-2025 EQ2EMulator -// Licensed under GPL v3 -#include "RC4.h" -#include - -static bool g_bInitStateInitialized = false; -static uchar g_byInitState[256]; - -RC4::RC4(int64 nKey) -{ - if( !g_bInitStateInitialized ) - { - for(int16 i = 0; i < 256; i++ ) - g_byInitState[i] = i; - } - Init(nKey); -} - -RC4::~RC4() -{ -} - -void RC4::Init(int64 nKey) -{ - memcpy(m_state, g_byInitState, 256); - m_x = 0; - m_y = 0; - - ulong dwKeyIndex = 0; - ulong dwStateIndex = 0; - uchar* pKey = (uchar*)&nKey; - for(int16 i = 0; i < 256; i++ ) - { - ulong dwTemp = m_state[i]; - dwStateIndex += pKey[dwKeyIndex] + dwTemp; - dwStateIndex &= 0xFF; - m_state[i] = m_state[dwStateIndex]; - m_state[dwStateIndex] = (uchar)dwTemp; - dwKeyIndex++; - dwKeyIndex &= 7; - } -} - -// A = m_state[X + 1] -// B = m_state[Y + A] -// C ^= m_state[(A + B)] - -// X = 20 -// Y = ? -// C = 0 -// m_state[(A + B)] = Cypher Byte - -void RC4::Cypher(uchar* pBuffer, int32 nLength) -{ - int32 nOffset = 0; - uchar byKey1 = m_x; - uchar byKey2 = m_y; - if( nLength > 0 ) - { - do - { - byKey1++; - uchar byKeyVal1 = m_state[byKey1]; - - byKey2 += byKeyVal1; - uchar byKeyVal2 = m_state[byKey2]; - - m_state[byKey1] = byKeyVal2; - m_state[byKey2] = byKeyVal1; - - pBuffer[nOffset++] ^= m_state[(byKeyVal1 + byKeyVal2) & 0xFF]; - } while( nOffset < nLength ); - } - m_x = byKey1; - m_y = byKey2; -} diff --git a/source/common/crypto/RC4.h b/source/common/crypto/RC4.h deleted file mode 100644 index ac48199..0000000 --- a/source/common/crypto/RC4.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (C) 2007-2025 EQ2EMulator -// Licensed under GPL v3 -#ifndef _EQ2_RC4_H -#define _EQ2_RC4_H -#include "../common/types.h" -class RC4 -{ -public: - RC4(int64 nKey); - ~RC4(); - - void Init(int64 nKey); - void Cypher(uchar* pData, int32 nLen); - -private: - uchar m_state[256]; - uchar m_x; - uchar m_y; -}; -#endif - diff --git a/source/common/crypto/crypto.cpp b/source/common/crypto/crypto.cpp new file mode 100644 index 0000000..cfe1423 --- /dev/null +++ b/source/common/crypto/crypto.cpp @@ -0,0 +1,53 @@ +// Copyright (C) 2007-2025 EQ2EMulator +// Licensed under GPL v3 + +#include "crypto.h" +#include +#include +#include +#include + +int64 Crypto::RSADecrypt(uchar* text, int16 size) noexcept +{ + int64 ret = 0; + std::array buffer{}; + + for (int i = 7; i >= 0; --i) { + buffer[7 - i] = text[i]; + } + + std::memcpy(&ret, buffer.data(), 8); + return ret; +} + +void Crypto::RC4Decrypt(uchar* text, int32 size) noexcept +{ + const std::lock_guard lock(m_crypto_mutex); + client->Cypher(text, size); +} + +void Crypto::RC4Encrypt(uchar* text, int32 size) noexcept +{ + const std::lock_guard lock(m_crypto_mutex); + server->Cypher(text, size); +} + +void Crypto::setRC4Key(int64 key) noexcept +{ + rc4_key = key; + + if (key > 0) { + encrypted = true; + client = std::make_unique(~key); + server = std::make_unique(key); + + std::array temp{}; + client->Cypher(temp.data(), 20); + server->Cypher(temp.data(), 20); + } else { + encrypted = false; + client.reset(); + server.reset(); + } +} + diff --git a/source/common/crypto/crypto.h b/source/common/crypto/crypto.h new file mode 100644 index 0000000..719f76f --- /dev/null +++ b/source/common/crypto/crypto.h @@ -0,0 +1,35 @@ +// Copyright (C) 2007-2025 EQ2EMulator +// Licensed under GPL v3 + +#pragma once + +#include +#include +#include + +#include "rc4.h" +#include "../types.h" + +class Crypto +{ +public: + Crypto() noexcept = default; + ~Crypto() = default; + + static int64 RSADecrypt(uchar* text, int16 size) noexcept; + void RC4Encrypt(uchar* text, int32 size) noexcept; + void RC4Decrypt(uchar* text, int32 size) noexcept; + + [[nodiscard]] int64 getRC4Key() const noexcept { return rc4_key; } + void setRC4Key(int64 key) noexcept; + + [[nodiscard]] bool isEncrypted() const noexcept { return encrypted; } + void setEncrypted(bool in_val) noexcept { encrypted = in_val; } + +private: + std::unique_ptr server{}; + std::unique_ptr client{}; + bool encrypted{false}; + int64 rc4_key{0}; + std::mutex m_crypto_mutex{}; +}; diff --git a/source/common/crypto/rc4.cpp b/source/common/crypto/rc4.cpp new file mode 100644 index 0000000..76735c1 --- /dev/null +++ b/source/common/crypto/rc4.cpp @@ -0,0 +1,64 @@ +// Copyright (C) 2007-2025 EQ2EMulator +// Licensed under GPL v3 + +#include "rc4.h" +#include +#include +#include + +static constexpr std::array get_init_state() noexcept +{ + std::array state{}; + std::iota(state.begin(), state.end(), 0); + return state; +} + +static const auto g_init_state = get_init_state(); + +RC4::RC4(int64 key) noexcept +{ + Init(key); +} + +void RC4::Init(int64 key) noexcept +{ + m_state = g_init_state; + m_x = 0; + m_y = 0; + + ulong key_index = 0; + ulong state_index = 0; + const auto* key_ptr = reinterpret_cast(&key); + + for (int16 i = 0; i < 256; ++i) { + const ulong temp = m_state[i]; + state_index += key_ptr[key_index] + temp; + state_index &= 0xFF; + m_state[i] = m_state[state_index]; + m_state[state_index] = static_cast(temp); + key_index++; + key_index &= 7; + } +} + +void RC4::Cypher(uchar* data, int32 length) noexcept +{ + uchar key1 = m_x; + uchar key2 = m_y; + + for (int32 i = 0; i < length; ++i) { + ++key1; + const uchar key_val1 = m_state[key1]; + + key2 += key_val1; + const uchar key_val2 = m_state[key2]; + + m_state[key1] = key_val2; + m_state[key2] = key_val1; + + data[i] ^= m_state[(key_val1 + key_val2) & 0xFF]; + } + + m_x = key1; + m_y = key2; +} diff --git a/source/common/crypto/rc4.h b/source/common/crypto/rc4.h new file mode 100644 index 0000000..92e7f77 --- /dev/null +++ b/source/common/crypto/rc4.h @@ -0,0 +1,22 @@ +// Copyright (C) 2007-2025 EQ2EMulator +// Licensed under GPL v3 +#pragma once + +#include "../types.h" +#include + +class RC4 +{ +public: + explicit RC4(int64 key) noexcept; + ~RC4() = default; + + void Init(int64 key) noexcept; + void Cypher(uchar* data, int32 length) noexcept; + +private: + std::array m_state{}; + uchar m_x{}; + uchar m_y{}; +}; +