PolarSSL is fully used for 1.3.2 protocol encryption.
This commit is contained in:
@@ -28,13 +28,14 @@
|
||||
#pragma warning(disable:4702)
|
||||
#endif
|
||||
|
||||
#include "cryptopp/randpool.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define HANDLE_PACKET_READ(Proc, Type, Var) \
|
||||
Type Var; \
|
||||
{ \
|
||||
@@ -49,17 +50,6 @@
|
||||
|
||||
|
||||
|
||||
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...
|
||||
|
||||
@@ -93,81 +83,6 @@ enum
|
||||
|
||||
|
||||
|
||||
// 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:
|
||||
|
||||
@@ -197,11 +112,11 @@ void cProtocol132::DataReceived(const char * a_Data, int a_Size)
|
||||
{
|
||||
if (m_IsEncrypted)
|
||||
{
|
||||
byte Decrypted[512];
|
||||
Byte Decrypted[512];
|
||||
while (a_Size > 0)
|
||||
{
|
||||
int NumBytes = (a_Size > (int)sizeof(Decrypted)) ? (int)sizeof(Decrypted) : a_Size;
|
||||
m_Decryptor.ProcessData(Decrypted, (byte *)a_Data, NumBytes);
|
||||
m_Decryptor.ProcessData(Decrypted, (Byte *)a_Data, NumBytes);
|
||||
super::DataReceived((const char *)Decrypted, NumBytes);
|
||||
a_Size -= NumBytes;
|
||||
a_Data += NumBytes;
|
||||
@@ -582,9 +497,7 @@ int cProtocol132::ParseHandshake(void)
|
||||
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);
|
||||
// Send a 0xfd Encryption Key Request http://wiki.vg/Protocol#0xFD
|
||||
SendEncryptionKeyRequest();
|
||||
|
||||
return PARSE_OK;
|
||||
@@ -596,7 +509,7 @@ int cProtocol132::ParseHandshake(void)
|
||||
|
||||
int cProtocol132::ParseClientStatuses(void)
|
||||
{
|
||||
HANDLE_PACKET_READ(ReadByte, byte, Status);
|
||||
HANDLE_PACKET_READ(ReadByte, Byte, Status);
|
||||
if ((Status & 1) == 0)
|
||||
{
|
||||
m_Client->HandleLogin(39, m_Username);
|
||||
@@ -714,11 +627,11 @@ void cProtocol132::Flush(void)
|
||||
int a_Size = m_DataToSend.size();
|
||||
if (m_IsEncrypted)
|
||||
{
|
||||
byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks)
|
||||
Byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks)
|
||||
while (a_Size > 0)
|
||||
{
|
||||
int NumBytes = (a_Size > (int)sizeof(Encrypted)) ? (int)sizeof(Encrypted) : a_Size;
|
||||
m_Encryptor.ProcessData(Encrypted, (byte *)a_Data, NumBytes);
|
||||
m_Encryptor.ProcessData(Encrypted, (Byte *)a_Data, NumBytes);
|
||||
super::SendData((const char *)Encrypted, NumBytes);
|
||||
a_Size -= NumBytes;
|
||||
a_Data += NumBytes;
|
||||
@@ -880,8 +793,8 @@ void cProtocol132::SendEncryptionKeyRequest(void)
|
||||
cCSLock Lock(m_CSPacket);
|
||||
WriteByte(0xfd);
|
||||
WriteString(cRoot::Get()->GetServer()->GetServerID());
|
||||
WriteShort((short)m_ServerPublicKey.size());
|
||||
SendData(m_ServerPublicKey.data(), m_ServerPublicKey.size());
|
||||
WriteShort((short)(cRoot::Get()->GetServer()->GetPublicKeyDER().size()));
|
||||
SendData(cRoot::Get()->GetServer()->GetPublicKeyDER().data(), cRoot::Get()->GetServer()->GetPublicKeyDER().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();
|
||||
@@ -894,13 +807,11 @@ void cProtocol132::SendEncryptionKeyRequest(void)
|
||||
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));
|
||||
cRSAPrivateKey & rsaDecryptor = cRoot::Get()->GetServer()->GetPrivateKey();
|
||||
|
||||
Int32 DecryptedNonce[MAX_ENC_LEN / sizeof(Int32)];
|
||||
DecodingResult res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncNonce.data(), a_EncNonce.size(), (byte *)DecryptedNonce);
|
||||
if (!res.isValidCoding || (res.messageLength != 4))
|
||||
int res = rsaDecryptor.Decrypt((const Byte *)a_EncNonce.data(), a_EncNonce.size(), (Byte *)DecryptedNonce, sizeof(DecryptedNonce));
|
||||
if (res != 4)
|
||||
{
|
||||
LOGD("Bad nonce length");
|
||||
m_Client->Kick("Hacked client");
|
||||
@@ -914,9 +825,9 @@ void cProtocol132::HandleEncryptionKeyResponse(const AString & a_EncKey, const A
|
||||
}
|
||||
|
||||
// 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))
|
||||
Byte DecryptedKey[MAX_ENC_LEN];
|
||||
res = rsaDecryptor.Decrypt((const Byte *)a_EncKey.data(), a_EncKey.size(), DecryptedKey, sizeof(DecryptedKey));
|
||||
if (res != 16)
|
||||
{
|
||||
LOGD("Bad key length");
|
||||
m_Client->Kick("Hacked client");
|
||||
@@ -932,6 +843,12 @@ void cProtocol132::HandleEncryptionKeyResponse(const AString & a_EncKey, const A
|
||||
Flush();
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
AString DecryptedKeyHex;
|
||||
CreateHexDump(DecryptedKeyHex, DecryptedKey, res, 16);
|
||||
LOGD("Received encryption key, %d bytes:\n%s", res, DecryptedKeyHex.c_str());
|
||||
#endif
|
||||
|
||||
StartEncryption(DecryptedKey);
|
||||
return;
|
||||
}
|
||||
@@ -940,21 +857,21 @@ void cProtocol132::HandleEncryptionKeyResponse(const AString & a_EncKey, const A
|
||||
|
||||
|
||||
|
||||
void cProtocol132::StartEncryption(const byte * a_Key)
|
||||
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_Encryptor.Init(a_Key, a_Key);
|
||||
m_Decryptor.Init(a_Key, a_Key);
|
||||
m_IsEncrypted = true;
|
||||
|
||||
// Prepare the m_AuthServerID:
|
||||
CryptoPP::SHA1 Checksum;
|
||||
cSHA1Checksum Checksum;
|
||||
AString ServerID = cRoot::Get()->GetServer()->GetServerID();
|
||||
Checksum.Update((const byte *)ServerID.c_str(), ServerID.length());
|
||||
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);
|
||||
Checksum.Update((const Byte *)cRoot::Get()->GetServer()->GetPublicKeyDER().data(), cRoot::Get()->GetServer()->GetPublicKeyDER().size());
|
||||
Byte Digest[20];
|
||||
Checksum.Finalize(Digest);
|
||||
cSHA1Checksum::DigestToJava(Digest, m_AuthServerID);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user