summaryrefslogtreecommitdiffstats
path: root/source/Protocol132.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--source/Protocol132.cpp200
1 files changed, 196 insertions, 4 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;
+}
+
+
+
+