summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/Protocol132.cpp200
-rw-r--r--source/Protocol132.h23
-rw-r--r--source/ProtocolRecognizer.cpp3
-rw-r--r--source/cServer.cpp18
-rw-r--r--source/cServer.h10
5 files changed, 248 insertions, 6 deletions
diff --git a/source/Protocol132.cpp b/source/Protocol132.cpp
index faff57004..2a4a830c6 100644
--- a/source/Protocol132.cpp
+++ b/source/Protocol132.cpp
@@ -5,6 +5,10 @@
#include "Globals.h"
#include "Protocol132.h"
+#include "cRoot.h"
+#include "cServer.h"
+#include "cClientHandle.h"
+#include "CryptoPP/osrng.h"
@@ -28,11 +32,24 @@ typedef unsigned char Byte;
+using namespace CryptoPP;
+
+
+
+
+
+const int MAX_ENC_LEN = 512; // Maximum size of the encrypted message; should be 128, but who knows...
+
+
+
+
+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cProtocol132:
cProtocol132::cProtocol132(cClientHandle * a_Client) :
- super(a_Client)
+ super(a_Client),
+ m_IsEncrypted(false)
{
}
@@ -42,8 +59,36 @@ cProtocol132::cProtocol132(cClientHandle * a_Client) :
void cProtocol132::DataReceived(const char * a_Data, int a_Size)
{
- // TODO: Protocol decryption
- super::DataReceived(a_Data, a_Size);
+ if (m_IsEncrypted)
+ {
+ byte Decrypted[512];
+ while (a_Size > 0)
+ {
+ int NumBytes = (a_Size > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size;
+ m_Decryptor.ProcessData(Decrypted, (byte *)a_Data, NumBytes);
+ super::DataReceived((const char *)Decrypted, NumBytes);
+ a_Size -= NumBytes;
+ a_Data += NumBytes;
+ }
+ }
+ else
+ {
+ super::DataReceived(a_Data, a_Size);
+ }
+}
+
+
+
+
+
+int cProtocol132::ParsePacket(unsigned char a_PacketType)
+{
+ switch (a_PacketType)
+ {
+ default: return super::ParsePacket(a_PacketType); // off-load previously known packets into cProtocol125
+ case 0xcd: return ParseClientStatuses();
+ case 0xfc: return ParseEncryptionKeyResponse();
+ }
}
@@ -55,11 +100,158 @@ int cProtocol132::ParseHandshake(void)
HANDLE_PACKET_READ(ReadByte, Byte, ProtocolVersion);
HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Username);
HANDLE_PACKET_READ(ReadBEUTF16String16, AString, ServerHost);
- HANDLE_PACKET_READ(ReadBEInt, int, ServerPort);
+ HANDLE_PACKET_READ(ReadBEInt, int, ServerPort);
m_Username = Username;
+
+ AString key;
+ CryptoPP::StringSink sink(key); // GCC won't allow inline instantiation in the following line, damned temporary refs
+ cRoot::Get()->GetServer()->GetPublicKey().Save(sink);
+
+ // Send a 0xFD Encryption Key Request http://wiki.vg/Protocol#Encryption_Key_Request_.280xFD.29
+ WriteByte((char)0xfd);
+ WriteString("MCServer");
+ WriteShort((short)key.size());
+ SendData(key.data(), key.size());
+ WriteShort(4);
+ WriteInt((int)this); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :)
+ return PARSE_OK;
+}
+
+
+
+
+
+int cProtocol132::ParseLogin(void)
+{
+ // Login packet not used in 1.3.2
+ return PARSE_ERROR;
+}
+
+
+
+
+
+int cProtocol132::ParseClientStatuses(void)
+{
+ HANDLE_PACKET_READ(ReadByte, byte, Status);
+
+ // DEBUG:
+ // Kick the client, we don't have all the packets yet and sending wrong packets makes the client freeze
+ // m_Client->Kick("I don't speak your language (yet)");
+
+ // TODO:
+ // m_Client->HandleLogin(39, m_Username);
+
return PARSE_OK;
}
+
+int cProtocol132::ParseEncryptionKeyResponse(void)
+{
+ HANDLE_PACKET_READ(ReadBEShort, short, EncKeyLength);
+ AString EncKey;
+ if (!m_ReceivedData.ReadString(EncKey, EncKeyLength))
+ {
+ return PARSE_INCOMPLETE;
+ }
+ HANDLE_PACKET_READ(ReadBEShort, short, EncNonceLength);
+ AString EncNonce;
+ if (!m_ReceivedData.ReadString(EncNonce, EncNonceLength))
+ {
+ return PARSE_INCOMPLETE;
+ }
+ if ((EncKeyLength > MAX_ENC_LEN) || (EncNonceLength > MAX_ENC_LEN))
+ {
+ LOGD("Too long encryption");
+ m_Client->Kick("Hacked client");
+ return PARSE_OK;
+ }
+
+ HandleEncryptionKeyResponse(EncKey, EncNonce);
+ return PARSE_OK;
+}
+
+
+
+
+
+void cProtocol132::SendData(const char * a_Data, int a_Size)
+{
+ if (m_IsEncrypted)
+ {
+ byte Encrypted[1024]; // Larger buffer, we may be sending lots of data (chunks)
+ while (a_Size > 0)
+ {
+ int NumBytes = (a_Size > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Size;
+ m_Encryptor.ProcessData(Encrypted, (byte *)a_Data, NumBytes);
+ super::SendData((const char *)Encrypted, NumBytes);
+ a_Size -= NumBytes;
+ a_Data += NumBytes;
+ }
+ }
+ else
+ {
+ super::SendData(a_Data, a_Size);
+ }
+}
+
+
+
+
+
+void cProtocol132::HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce)
+{
+ // Decrypt EncNonce using privkey
+ RSAES<PKCS1v15>::Decryptor rsaDecryptor(cRoot::Get()->GetServer()->GetPrivateKey());
+ AutoSeededRandomPool rng;
+ byte DecryptedNonce[MAX_ENC_LEN];
+ DecodingResult res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncNonce.data(), a_EncNonce.size(), DecryptedNonce);
+ if (!res.isValidCoding || (res.messageLength != 4))
+ {
+ LOGD("Bad nonce length");
+ m_Client->Kick("Hacked client");
+ return;
+ }
+ if (ntohl(*((int *)DecryptedNonce)) != (unsigned)this)
+ {
+ LOGD("Bad nonce value");
+ m_Client->Kick("Hacked client");
+ return;
+ }
+
+ // Decrypt the symmetric encryption key using privkey:
+ byte DecryptedKey[MAX_ENC_LEN];
+ res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncKey.data(), a_EncKey.size(), DecryptedKey);
+ if (!res.isValidCoding || (res.messageLength != 16))
+ {
+ LOGD("Bad key length");
+ m_Client->Kick("Hacked client");
+ return;
+ }
+
+ // Send encryption key response:
+ WriteByte((char)0xfc);
+ WriteShort(0);
+ WriteShort(0);
+
+ StartEncryption(DecryptedKey);
+ return;
+}
+
+
+
+
+
+void cProtocol132::StartEncryption(const byte * a_Key)
+{
+ m_Encryptor.SetKey(a_Key, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(a_Key, 16))(Name::FeedbackSize(), 1));
+ m_Decryptor.SetKey(a_Key, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(a_Key, 16))(Name::FeedbackSize(), 1));
+ m_IsEncrypted = true;
+}
+
+
+
+
diff --git a/source/Protocol132.h b/source/Protocol132.h
index af3e78ecc..a0e16b585 100644
--- a/source/Protocol132.h
+++ b/source/Protocol132.h
@@ -10,6 +10,8 @@
#pragma once
#include "Protocol125.h"
+#include "CryptoPP/modes.h"
+#include "CryptoPP/aes.h"
@@ -26,8 +28,29 @@ public:
/// Called when client sends some data:
virtual void DataReceived(const char * a_Data, int a_Size) override;
+ /// Handling of the additional packets:
+ virtual int ParsePacket(unsigned char a_PacketType) override;
+
// Modified packets:
virtual int ParseHandshake(void) override;
+ virtual int ParseLogin (void) override;
+
+ // New packets:
+ virtual int ParseClientStatuses (void);
+ virtual int ParseEncryptionKeyResponse(void);
+
+ virtual void SendData(const char * a_Data, int a_Size);
+
+protected:
+ bool m_IsEncrypted;
+ CryptoPP::CFB_Mode<CryptoPP::AES>::Decryption m_Decryptor; // ((byte*)sDecryptedSharedSecret.c_str(),(unsigned int)16, IV, 1);
+ CryptoPP::CFB_Mode<CryptoPP::AES>::Encryption m_Encryptor;
+
+ /// Decrypts the key and nonce, checks nonce, starts the symmetric encryption
+ void HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce);
+
+ /// Starts the symmetric encryption with the specified key
+ void StartEncryption(const byte * a_Key);
} ;
diff --git a/source/ProtocolRecognizer.cpp b/source/ProtocolRecognizer.cpp
index af917b7de..d0a3f4637 100644
--- a/source/ProtocolRecognizer.cpp
+++ b/source/ProtocolRecognizer.cpp
@@ -43,7 +43,6 @@ void cProtocolRecognizer::DataReceived(const char * a_Data, int a_Size)
{
return;
}
- LOGD("ProtocolRecognizer at %p recognized protocol %p", this, m_Protocol);
// The protocol has just been recognized, dump the whole m_Buffer contents into it for parsing:
AString Dump;
@@ -491,7 +490,7 @@ bool cProtocolRecognizer::TryRecognizeProtocol(void)
{
return false;
}
- if (ch == 0x39)
+ if (ch == 39)
{
m_Protocol = new cProtocol132(m_Client);
return true;
diff --git a/source/cServer.cpp b/source/cServer.cpp
index 330a69873..157de2388 100644
--- a/source/cServer.cpp
+++ b/source/cServer.cpp
@@ -241,6 +241,8 @@ bool cServer::InitServer( int a_Port )
m_NotifyWriteThread.Start(this);
+ PrepareKeys();
+
return true;
}
@@ -283,6 +285,22 @@ cServer::~cServer()
+void cServer::PrepareKeys(void)
+{
+ // TODO: Save and load key for persistence across sessions
+ // But generating the key takes only a moment, do we even need that?
+
+ LOG("Generating protocol encryption keypair...");
+ CryptoPP::AutoSeededRandomPool rng;
+ m_PrivateKey.GenerateRandomWithKeySize(rng, 1024);
+ CryptoPP::RSA::PublicKey pk(m_PrivateKey);
+ m_PublicKey = pk;
+}
+
+
+
+
+
void cServer::BroadcastChat(const AString & a_Message, const cClientHandle * a_Exclude)
{
cCSLock Lock(m_CSClients);
diff --git a/source/cServer.h b/source/cServer.h
index a97bcbd7a..7baaa0d6a 100644
--- a/source/cServer.h
+++ b/source/cServer.h
@@ -12,6 +12,8 @@
#define CSERVER_H_INCLUDED
#include "cSocketThreads.h"
+#include "CryptoPP/rsa.h"
+#include "CryptoPP/osrng.h"
@@ -66,6 +68,9 @@ public: //tolua_export
void RemoveClient(const cSocket * a_Socket); // Removes the socket from m_SocketThreads
+ CryptoPP::RSA::PrivateKey & GetPrivateKey(void) { return m_PrivateKey; }
+ CryptoPP::RSA::PublicKey & GetPublicKey (void) { return m_PublicKey; }
+
private:
friend class cRoot; // so cRoot can create and destroy cServer
@@ -114,10 +119,15 @@ private:
int m_iServerPort;
bool m_bRestarting;
+
+ CryptoPP::RSA::PrivateKey m_PrivateKey;
+ CryptoPP::RSA::PublicKey m_PublicKey;
cServer();
~cServer();
+ /// Loads, or generates, if missing, RSA keys for protocol encryption
+ void PrepareKeys(void);
}; //tolua_export