1
0
cuberite-2a/source/Protocol/Protocol132.cpp
madmaxoft e9f18f8b4f Tab completion packet is handled and sent.
This only handles the network comm and the overall design logic, the actual completion is not yet implemented, only dummy values are returned for now.
2013-07-30 22:48:59 +02:00

941 lines
21 KiB
C++

// Protocol132.cpp
// Implements the cProtocol132 class representing the release 1.3.2 protocol (#39)
#include "Globals.h"
#include "Protocol132.h"
#include "../Root.h"
#include "../Server.h"
#include "../ClientHandle.h"
#include "../../CryptoPP/randpool.h"
#include "../Item.h"
#include "ChunkDataSerializer.h"
#include "../Player.h"
#include "../Mobs/Monster.h"
#include "../UI/Window.h"
#include "../Pickup.h"
#include "../WorldStorage/FastNBT.h"
#include "../StringCompression.h"
#define HANDLE_PACKET_READ(Proc, Type, Var) \
Type Var; \
{ \
if (!m_ReceivedData.Proc(Var)) \
{ \
m_ReceivedData.CheckValid(); \
return PARSE_INCOMPLETE; \
} \
m_ReceivedData.CheckValid(); \
}
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...
enum
{
PACKET_KEEP_ALIVE = 0x00,
PACKET_LOGIN = 0x01,
PACKET_ENTITY_EQUIPMENT = 0x05,
PACKET_COMPASS = 0x06,
PACKET_PLAYER_SPAWN = 0x14,
PACKET_COLLECT_PICKUP = 0x16,
PACKET_SPAWN_MOB = 0x18,
PACKET_DESTROY_ENTITIES = 0x1d,
PACKET_CHUNK_DATA = 0x33,
PACKET_BLOCK_CHANGE = 0x35,
PACKET_BLOCK_ACTION = 0x36,
PACKET_BLOCK_BREAK_ANIM = 0x37,
PACKET_SOUND_EFFECT = 0x3e,
PACKET_SOUND_PARTICLE_EFFECT = 0x3d,
PACKET_TAB_COMPLETION = 0xcb,
PACKET_LOCALE_VIEW_DISTANCE = 0xcc,
PACKET_CLIENT_STATUSES = 0xcd,
PACKET_ENCRYPTION_KEY_RESP = 0xfc,
} ;
// Converts a raw 160-bit SHA1 digest into a Java Hex representation
// According to http://wiki.vg/wiki/index.php?title=Protocol_Encryption&oldid=2802
static void DigestToJava(byte a_Digest[20], AString & a_Out)
{
bool IsNegative = (a_Digest[0] >= 0x80);
if (IsNegative)
{
// Two's complement:
bool carry = true; // Add one to the whole number
for (int i = 19; i >= 0; i--)
{
a_Digest[i] = ~a_Digest[i];
if (carry)
{
carry = (a_Digest[i] == 0xff);
a_Digest[i]++;
}
}
}
a_Out.clear();
a_Out.reserve(40);
for (int i = 0; i < 20; i++)
{
AppendPrintf(a_Out, "%02x", a_Digest[i]);
}
while ((a_Out.length() > 0) && (a_Out[0] == '0'))
{
a_Out.erase(0, 1);
}
if (IsNegative)
{
a_Out.insert(0, "-");
}
}
/*
// Self-test the hash formatting for known values:
// sha1(Notch) : 4ed1f46bbe04bc756bcb17c0c7ce3e4632f06a48
// sha1(jeb_) : -7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1
// sha1(simon) : 88e16a1019277b15d58faf0541e11910eb756f6
class Test
{
public:
Test(void)
{
AString DigestNotch, DigestJeb, DigestSimon;
byte Digest[20];
CryptoPP::SHA1 Checksum;
Checksum.Update((const byte *)"Notch", 5);
Checksum.Final(Digest);
DigestToJava(Digest, DigestNotch);
Checksum.Restart();
Checksum.Update((const byte *)"jeb_", 4);
Checksum.Final(Digest);
DigestToJava(Digest, DigestJeb);
Checksum.Restart();
Checksum.Update((const byte *)"simon", 5);
Checksum.Final(Digest);
DigestToJava(Digest, DigestSimon);
printf("Notch: \"%s\"", DigestNotch.c_str());
printf("jeb_: \"%s\"", DigestJeb.c_str());
printf("simon: \"%s\"", DigestSimon.c_str());
}
} test;
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cProtocol132:
cProtocol132::cProtocol132(cClientHandle * a_Client) :
super(a_Client),
m_IsEncrypted(false)
{
}
cProtocol132::~cProtocol132()
{
if (!m_DataToSend.empty())
{
LOGD("There are %d unsent bytes while deleting cProtocol132", m_DataToSend.size());
}
}
void cProtocol132::DataReceived(const char * a_Data, int 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);
}
}
void cProtocol132::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
{
cCSLock Lock(m_CSPacket);
WriteByte (PACKET_BLOCK_ACTION);
WriteInt (a_BlockX);
WriteShort((short)a_BlockY);
WriteInt (a_BlockZ);
WriteByte (a_Byte1);
WriteByte (a_Byte2);
WriteShort(a_BlockType);
Flush();
}
void cProtocol132::SendBlockBreakAnim(int a_entityID, int a_BlockX, int a_BlockY, int a_BlockZ, char stage)
{
cCSLock Lock(m_CSPacket);
WriteByte (PACKET_BLOCK_BREAK_ANIM);
WriteInt (a_entityID);
WriteInt (a_BlockX);
WriteInt (a_BlockY);
WriteInt (a_BlockZ);
WriteByte (stage);
Flush();
}
void cProtocol132::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
cCSLock Lock(m_CSPacket);
WriteByte (PACKET_BLOCK_CHANGE);
WriteInt (a_BlockX);
WriteByte ((unsigned char)a_BlockY);
WriteInt (a_BlockZ);
WriteShort(a_BlockType);
WriteByte (a_BlockMeta);
Flush();
}
void cProtocol132::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
{
cCSLock Lock(m_CSPacket);
// Pre-chunk not used in 1.3.2. Finally.
// Send the chunk data:
AString Serialized = a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_3_2);
WriteByte(PACKET_CHUNK_DATA);
WriteInt (a_ChunkX);
WriteInt (a_ChunkZ);
SendData(Serialized.data(), Serialized.size());
Flush();
}
void cProtocol132::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player)
{
cCSLock Lock(m_CSPacket);
WriteByte(PACKET_COLLECT_PICKUP);
WriteInt (a_Pickup.GetUniqueID());
WriteInt (a_Player.GetUniqueID());
Flush();
// Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;)
SendSoundEffect(
"random.pop",
(int)(a_Pickup.GetPosX() * 8), (int)(a_Pickup.GetPosY() * 8), (int)(a_Pickup.GetPosZ() * 8),
0.5, (float)(0.75 + ((float)((a_Pickup.GetUniqueID() * 23) % 32)) / 64)
);
}
void cProtocol132::SendDestroyEntity(const cEntity & a_Entity)
{
if (a_Entity.GetUniqueID() == m_Client->GetPlayer()->GetUniqueID())
{
// Do not send "destroy self" to the client, the client would crash (FS #254)
return;
}
cCSLock Lock(m_CSPacket);
WriteByte(PACKET_DESTROY_ENTITIES);
WriteByte(1); // entity count
WriteInt (a_Entity.GetUniqueID());
Flush();
}
void cProtocol132::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item)
{
cCSLock Lock(m_CSPacket);
WriteByte (PACKET_ENTITY_EQUIPMENT);
WriteInt (a_Entity.GetUniqueID());
WriteShort(a_SlotNum);
WriteItem (a_Item);
Flush();
}
void cProtocol132::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
{
cCSLock Lock(m_CSPacket);
WriteByte (PACKET_LOGIN);
WriteInt (a_Player.GetUniqueID()); // EntityID of the player
WriteString("default"); // Level type
WriteByte ((int)a_Player.GetGameMode());
WriteByte ((Byte)(a_World.GetDimension()));
WriteByte (2); // TODO: Difficulty
WriteByte (0); // Unused, used to be world height
WriteByte (8); // Client list width or something
Flush();
SendCompass(a_World);
// Send the initial position (so that confirmation works, FS #245):
SendPlayerMoveLook();
}
void cProtocol132::SendPlayerSpawn(const cPlayer & a_Player)
{
const cItem & HeldItem = a_Player.GetEquippedItem();
cCSLock Lock(m_CSPacket);
WriteByte (PACKET_PLAYER_SPAWN);
WriteInt (a_Player.GetUniqueID());
WriteString(a_Player.GetName());
WriteInt ((int)(a_Player.GetPosX() * 32));
WriteInt ((int)(a_Player.GetPosY() * 32));
WriteInt ((int)(a_Player.GetPosZ() * 32));
WriteByte ((char)((a_Player.GetRot().x / 360.f) * 256));
WriteByte ((char)((a_Player.GetRot().y / 360.f) * 256));
WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType);
// Player metadata: just use a default metadata value, since the client doesn't like starting without any metadata:
WriteByte (0); // Index 0, byte (flags)
WriteByte (0); // Flags, empty
WriteByte (0x7f); // End of metadata
Flush();
}
void cProtocol132::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch)
{
cCSLock Lock(m_CSPacket);
WriteByte (PACKET_SOUND_EFFECT);
WriteString (a_SoundName);
WriteInt (a_SrcX);
WriteInt (a_SrcY);
WriteInt (a_SrcZ);
WriteFloat (a_Volume);
WriteByte ((char)(a_Pitch * 63.0f));
Flush();
}
void cProtocol132::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
{
cCSLock Lock(m_CSPacket);
WriteByte(PACKET_SOUND_PARTICLE_EFFECT);
WriteInt (a_EffectID);
WriteInt (a_SrcX / 8);
WriteByte(a_SrcY / 8);
WriteInt (a_SrcZ / 8);
WriteInt (a_Data);
Flush();
}
void cProtocol132::SendSpawnMob(const cMonster & a_Mob)
{
cCSLock Lock(m_CSPacket);
WriteByte (PACKET_SPAWN_MOB);
WriteInt (a_Mob.GetUniqueID());
WriteByte (a_Mob.GetMobType());
WriteVectorI((Vector3i)(a_Mob.GetPosition() * 32));
WriteByte ((Byte)((a_Mob.GetRotation() / 360.f) * 256));
WriteByte ((Byte)((a_Mob.GetPitch() / 360.f) * 256));
WriteByte ((Byte)((a_Mob.GetHeadYaw() / 360.f) * 256));
WriteShort ((short)(a_Mob.GetSpeedX() * 400));
WriteShort ((short)(a_Mob.GetSpeedY() * 400));
WriteShort ((short)(a_Mob.GetSpeedZ() * 400));
AString MetaData = GetEntityMetaData(a_Mob);
SendData (MetaData.data(), MetaData.size());
Flush();
}
void cProtocol132::SendTabCompletionResults(const AStringVector & a_Results)
{
if (a_Results.empty())
{
// No results to send
return;
}
AString Serialized(a_Results[0]);
for (AStringVector::const_iterator itr = a_Results.begin() + 1, end = a_Results.end(); itr != end; ++itr)
{
Serialized.push_back(0);
Serialized.append(*itr);
} // for itr - a_Results[]
cCSLock Lock(m_CSPacket);
WriteByte(PACKET_TAB_COMPLETION);
WriteString(Serialized);
Flush();
}
void cProtocol132::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
{
// Not used in 1.3.2
// Does it unload chunks on its own?
}
void cProtocol132::SendWholeInventory(const cWindow & a_Window)
{
// 1.3.2 requires player inventory slots to be sent as SetSlot packets,
// otherwise it sometimes fails to update the window
// Send the entire window:
super::SendWholeInventory(a_Window);
// Send the player inventory and hotbar:
const cInventory & Inventory = m_Client->GetPlayer()->GetInventory();
int BaseOffset = a_Window.GetNumSlots() - (cInventory::invNumSlots - cInventory::invInventoryOffset); // Number of non-inventory slots
char WindowID = a_Window.GetWindowID();
for (int i = 0; i < cInventory::invInventoryCount; i++)
{
SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetInventorySlot(i));
} // for i - Inventory[]
BaseOffset += cInventory::invInventoryCount;
for (int i = 0; i < cInventory::invHotbarCount; i++)
{
SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetHotbarSlot(i));
} // for i - Hotbar[]
// Send even the item being dragged:
SendInventorySlot(-1, -1, m_Client->GetPlayer()->GetDraggingItem());
}
AString cProtocol132::GetAuthServerID(void)
{
// http://wiki.vg/wiki/index.php?title=Session&oldid=2615
// Server uses SHA1 to mix ServerID, Client secret and server public key together
// The mixing is done in StartEncryption, the result is in m_AuthServerID
return m_AuthServerID;
}
int cProtocol132::ParsePacket(unsigned char a_PacketType)
{
switch (a_PacketType)
{
default: return super::ParsePacket(a_PacketType); // off-load previously known packets into cProtocol125
case PACKET_CLIENT_STATUSES: return ParseClientStatuses();
case PACKET_ENCRYPTION_KEY_RESP: return ParseEncryptionKeyResponse();
case PACKET_LOCALE_VIEW_DISTANCE: return ParseLocaleViewDistance();
case PACKET_TAB_COMPLETION: return ParseTabCompletion();
}
}
int cProtocol132::ParseBlockPlace(void)
{
HANDLE_PACKET_READ(ReadBEInt, int, PosX);
HANDLE_PACKET_READ(ReadByte, Byte, PosY);
HANDLE_PACKET_READ(ReadBEInt, int, PosZ);
HANDLE_PACKET_READ(ReadChar, char, BlockFace);
cItem HeldItem;
int res = ParseItem(HeldItem);
if (res < 0)
{
return res;
}
HANDLE_PACKET_READ(ReadChar, char, CursorX);
HANDLE_PACKET_READ(ReadChar, char, CursorY);
HANDLE_PACKET_READ(ReadChar, char, CursorZ);
m_Client->HandleRightClick(PosX, PosY, PosZ, BlockFace, CursorX, CursorY, CursorZ, HeldItem);
return PARSE_OK;
}
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);
m_Username = Username;
if (!m_Client->HandleHandshake( m_Username ))
{
return PARSE_OK; // Player is not allowed into the server
}
// Send a 0xFD Encryption Key Request http://wiki.vg/Protocol#0xFD
CryptoPP::StringSink sink(m_ServerPublicKey); // GCC won't allow inline instantiation in the following line, damned temporary refs
cRoot::Get()->GetServer()->GetPublicKey().Save(sink);
SendEncryptionKeyRequest();
return PARSE_OK;
}
int cProtocol132::ParseClientStatuses(void)
{
HANDLE_PACKET_READ(ReadByte, byte, Status);
if ((Status & 1) == 0)
{
m_Client->HandleLogin(39, m_Username);
}
else
{
m_Client->HandleRespawn();
}
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;
}
int cProtocol132::ParseLocaleViewDistance(void)
{
HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Locale);
HANDLE_PACKET_READ(ReadChar, char, ViewDistance);
HANDLE_PACKET_READ(ReadChar, char, ChatFlags);
HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty);
// TODO: m_Client->HandleLocale(Locale);
// TODO: m_Client->HandleViewDistance(ViewDistance);
// TODO: m_Client->HandleChatFlags(ChatFlags);
// Ignoring client difficulty
return PARSE_OK;
}
int cProtocol132::ParseLogin(void)
{
// Login packet not used in 1.3.2
return PARSE_ERROR;
}
int cProtocol132::ParsePlayerAbilities(void)
{
HANDLE_PACKET_READ(ReadBool, bool, Flags);
HANDLE_PACKET_READ(ReadChar, char, FlyingSpeed);
HANDLE_PACKET_READ(ReadChar, char, WalkingSpeed);
// TODO: m_Client->HandlePlayerAbilities(...);
return PARSE_OK;
}
int cProtocol132::ParseTabCompletion(void)
{
HANDLE_PACKET_READ(ReadBEUTF16String16, AString, Text);
m_Client->HandleTabCompletion(Text);
return PARSE_OK;
}
void cProtocol132::SendData(const char * a_Data, int a_Size)
{
m_DataToSend.append(a_Data, a_Size);
}
void cProtocol132::Flush(void)
{
ASSERT(m_CSPacket.IsLockedByCurrentThread()); // Did all packets lock the CS properly?
if (m_DataToSend.empty())
{
LOGD("Flushing empty");
return;
}
const char * a_Data = m_DataToSend.data();
int a_Size = m_DataToSend.size();
if (m_IsEncrypted)
{
byte Encrypted[8192]; // 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);
}
m_DataToSend.clear();
}
void cProtocol132::WriteItem(const cItem & a_Item)
{
short ItemType = a_Item.m_ItemType;
ASSERT(ItemType >= -1); // Check validity of packets in debug runtime
if (ItemType <= 0)
{
// Fix, to make sure no invalid values are sent.
ItemType = -1;
}
if (a_Item.IsEmpty())
{
WriteShort(-1);
return;
}
WriteShort(ItemType);
WriteByte (a_Item.m_ItemCount);
WriteShort(a_Item.m_ItemDamage);
if (a_Item.m_Enchantments.IsEmpty())
{
WriteShort(-1);
return;
}
// Send the enchantments:
cFastNBTWriter Writer;
const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench";
a_Item.m_Enchantments.WriteToNBTCompound(Writer, TagName);
Writer.Finish();
AString Compressed;
CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed);
WriteShort(Compressed.size());
SendData(Compressed.data(), Compressed.size());
}
int cProtocol132::ParseItem(cItem & a_Item)
{
HANDLE_PACKET_READ(ReadBEShort, short, ItemType);
if (ItemType <= -1)
{
a_Item.Empty();
return PARSE_OK;
}
a_Item.m_ItemType = ItemType;
HANDLE_PACKET_READ(ReadChar, char, ItemCount);
HANDLE_PACKET_READ(ReadBEShort, short, ItemDamage);
a_Item.m_ItemCount = ItemCount;
a_Item.m_ItemDamage = ItemDamage;
if (ItemCount <= 0)
{
a_Item.Empty();
}
HANDLE_PACKET_READ(ReadBEShort, short, MetadataLength);
if (MetadataLength <= 0)
{
return PARSE_OK;
}
// Read the metadata
AString Metadata;
Metadata.resize(MetadataLength);
if (!m_ReceivedData.ReadBuf((void *)Metadata.data(), MetadataLength))
{
return PARSE_INCOMPLETE;
}
return ParseItemMetadata(a_Item, Metadata);
}
int cProtocol132::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata)
{
// Uncompress the GZIPped data:
AString Uncompressed;
if (UncompressStringGZIP(a_Metadata.data(), a_Metadata.size(), Uncompressed) != Z_OK)
{
AString HexDump;
CreateHexDump(HexDump, a_Metadata.data(), a_Metadata.size(), 16);
LOG("Cannot unGZIP item metadata:\n%s", HexDump.c_str());
return PARSE_ERROR;
}
// Parse into NBT:
cParsedNBT NBT(Uncompressed.data(), Uncompressed.size());
if (!NBT.IsValid())
{
AString HexDump;
CreateHexDump(HexDump, Uncompressed.data(), Uncompressed.size(), 16);
LOG("Cannot parse NBT item metadata:\n%s", HexDump.c_str());
return PARSE_ERROR;
}
// Load enchantments from the NBT:
for (int tag = NBT.GetFirstChild(NBT.GetRoot()); tag >= 0; tag = NBT.GetNextSibling(tag))
{
if (
(NBT.GetType(tag) == TAG_List) &&
(
(NBT.GetName(tag) == "ench") ||
(NBT.GetName(tag) == "StoredEnchantments")
)
)
{
a_Item.m_Enchantments.ParseFromNBT(NBT, tag);
}
}
return PARSE_OK;
}
void cProtocol132::SendCompass(const cWorld & a_World)
{
cCSLock Lock(m_CSPacket);
WriteByte(PACKET_COMPASS);
WriteInt((int)(a_World.GetSpawnX()));
WriteInt((int)(a_World.GetSpawnY()));
WriteInt((int)(a_World.GetSpawnZ()));
Flush();
}
void cProtocol132::SendEncryptionKeyRequest(void)
{
cCSLock Lock(m_CSPacket);
WriteByte((char)0xfd);
WriteString(cRoot::Get()->GetServer()->GetServerID());
WriteShort((short)m_ServerPublicKey.size());
SendData(m_ServerPublicKey.data(), m_ServerPublicKey.size());
WriteShort(4);
WriteInt((int)(intptr_t)this); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :)
Flush();
}
void cProtocol132::HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce)
{
// Decrypt EncNonce using privkey
RSAES<PKCS1v15>::Decryptor rsaDecryptor(cRoot::Get()->GetServer()->GetPrivateKey());
time_t CurTime = time(NULL);
CryptoPP::RandomPool rng;
rng.Put((const byte *)&CurTime, sizeof(CurTime));
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)(uintptr_t)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:
cCSLock Lock(m_CSPacket);
WriteByte((char)0xfc);
WriteShort(0);
WriteShort(0);
Flush();
}
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;
// Prepare the m_AuthServerID:
CryptoPP::SHA1 Checksum;
AString ServerID = cRoot::Get()->GetServer()->GetServerID();
Checksum.Update((const byte *)ServerID.c_str(), ServerID.length());
Checksum.Update(a_Key, 16);
Checksum.Update((const byte *)m_ServerPublicKey.c_str(), m_ServerPublicKey.length());
byte Digest[20];
Checksum.Final(Digest);
DigestToJava(Digest, m_AuthServerID);
}