mirror of
https://git.revela.dev/itsRevela/LCE-Revelations.git
synced 2026-05-21 19:24:55 +00:00
Comprehensive security system to protect against packet-sniffing attacks, XUID harvesting, privilege escalation, bot flooding, and XUID impersonation. - Stream cipher: per-session XOR cipher with 4-message handshake via CustomPayloadPacket (MC|CKey, MC|CAck, MC|COn). Negotiated per-connection, backwards compatible (old clients/servers fall back to plaintext). - Security gate: buffers all game data until cipher handshake completes, preventing unsecured clients from receiving any XUIDs or game state. - Cipher handshake enforcer: kicks clients that don't complete the handshake within 5 seconds (configurable via require-secure-client). - Identity tokens: persistent per-XUID tokens in identity-tokens.json, issued over the encrypted channel, verified on reconnect. Prevents XUID replay attacks. Client stores server-specific tokens. - PROXY protocol v1: parses real client IPs from playit.gg tunnel headers so rate limiting, IP bans, and XUID spoof detection work per-player. - Rate limiting: per-IP sliding window (default 5 connections/30s) with pending connection cap (default 10). - Privilege hardening: OP requires ops.json, live checks on every command and privilege packet. Host-only server settings changes. - XUID stripping: PreLoginPacket response sends INVALID_XUID placeholders. - Packet validation: readUtf global string cap, reduced max packet size, stream desync protection on oversized strings. - OpManager: persistent ops.json with XUID-based OP list. - Whitelist improvements: whitelist add accepts player names with ambiguity detection, XUID cache from login attempts. - revoketoken command: revoke identity tokens for players who lost theirs. - server.log: persistent log file written alongside console output with flush-per-write to survive crashes. - CLI security logging: consolidated per-join security summary with cipher status, token status, XUID, and real IP. Security warnings for kicks, spoofing, and unauthorized commands.
98 lines
3.1 KiB
C++
98 lines
3.1 KiB
C++
#pragma once
|
|
|
|
#include "StreamCipher.h"
|
|
|
|
#ifdef _WINDOWS64
|
|
#include <Windows.h>
|
|
#endif
|
|
|
|
namespace ServerRuntime
|
|
{
|
|
namespace Security
|
|
{
|
|
/**
|
|
* Per-connection cipher registry for the dedicated server.
|
|
*
|
|
* Handshake protocol (4-message, via CustomPayloadPacket):
|
|
* 1. Server calls PrepareKey(smallId) -> sends MC|CKey with key to client
|
|
* 2. Client stores key, sends MC|CAck, activates send cipher
|
|
* 3. Server recv thread detects MC|CAck -> calls SendCOnAndCommit which
|
|
* atomically sends MC|COn plaintext then calls CommitCipher(smallId)
|
|
* 4. Client recv thread detects MC|COn -> activates recv cipher
|
|
*
|
|
* Backwards compatible: old clients ignore MC|CKey, server never gets ack,
|
|
* cipher stays inactive. Old servers never send MC|CKey, client stays plaintext.
|
|
*/
|
|
class ConnectionCipherRegistry
|
|
{
|
|
public:
|
|
ConnectionCipherRegistry();
|
|
~ConnectionCipherRegistry();
|
|
|
|
ConnectionCipherRegistry(const ConnectionCipherRegistry &) = delete;
|
|
ConnectionCipherRegistry &operator=(const ConnectionCipherRegistry &) = delete;
|
|
ConnectionCipherRegistry(ConnectionCipherRegistry &&) = delete;
|
|
ConnectionCipherRegistry &operator=(ConnectionCipherRegistry &&) = delete;
|
|
|
|
/**
|
|
* Generate a random key and store it in pending state for the given smallId.
|
|
* Does NOT activate the cipher. Call CommitCipher() after the client acks.
|
|
* Returns the generated key in outKey.
|
|
*/
|
|
bool PrepareKey(unsigned char smallId, uint8_t outKey[StreamCipher::KEY_SIZE]);
|
|
|
|
/**
|
|
* Activate a previously prepared cipher. Called from the recv thread
|
|
* when the client's MC|CAck is detected by raw byte matching.
|
|
* Returns false if no key was pending for this smallId.
|
|
*/
|
|
bool CommitCipher(unsigned char smallId);
|
|
|
|
/**
|
|
* Cancel a pending key (e.g., client disconnected before ack).
|
|
*/
|
|
void CancelPending(unsigned char smallId);
|
|
|
|
/**
|
|
* Check if a key is pending for the given smallId (no side effects).
|
|
*/
|
|
bool HasPendingKey(unsigned char smallId) const;
|
|
|
|
/**
|
|
* Deactivate the cipher and cancel any pending key for a disconnected connection.
|
|
*/
|
|
void DeactivateCipher(unsigned char smallId);
|
|
|
|
/**
|
|
* Atomically check if cipher is active and encrypt outgoing data.
|
|
* Returns true if data was encrypted, false if cipher is inactive (data untouched).
|
|
*/
|
|
bool TryEncryptOutgoing(unsigned char smallId, uint8_t *data, int length);
|
|
|
|
/**
|
|
* Check if the cipher is active (handshake completed) for a given smallId.
|
|
* Thread-safe, read-only query.
|
|
*/
|
|
bool IsCipherActive(unsigned char smallId) const;
|
|
|
|
/**
|
|
* Decrypt incoming data from a specific connection.
|
|
* No-op if the cipher is not active for this connection.
|
|
*/
|
|
void DecryptIncoming(unsigned char smallId, uint8_t *data, int length);
|
|
|
|
private:
|
|
static const int MAX_CONNECTIONS = 256;
|
|
StreamCipher m_ciphers[MAX_CONNECTIONS];
|
|
bool m_pending[MAX_CONNECTIONS];
|
|
uint8_t m_pendingKeys[MAX_CONNECTIONS][StreamCipher::KEY_SIZE];
|
|
mutable CRITICAL_SECTION m_lock;
|
|
};
|
|
|
|
/**
|
|
* Global cipher registry singleton.
|
|
*/
|
|
ConnectionCipherRegistry &GetCipherRegistry();
|
|
}
|
|
}
|