From 5f34c78091a7c884a9c27c0f0691cd1f03174dfe Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Thu, 23 Jan 2014 23:35:23 +0100 Subject: PolarSSL is fully used for 1.3.2 protocol encryption. --- src/CMakeLists.txt | 1 + src/Crypto.cpp | 401 ++++++++++++++++++++++++++++++++++++ src/Crypto.h | 164 +++++++++++++++ src/Defines.h | 2 - src/Globals.h | 3 + src/Protocol/Protocol132.cpp | 149 +++----------- src/Protocol/Protocol132.h | 16 +- src/Protocol/Protocol14x.cpp | 2 - src/Protocol/Protocol17x.cpp | 8 +- src/Protocol/Protocol17x.h | 13 +- src/Protocol/ProtocolRecognizer.cpp | 2 +- src/Server.cpp | 12 +- src/Server.h | 14 +- 13 files changed, 630 insertions(+), 157 deletions(-) create mode 100644 src/Crypto.cpp create mode 100644 src/Crypto.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ba642f7e6..ab3b2786e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,6 +4,7 @@ project (MCServer) include_directories (SYSTEM "${PROJECT_SOURCE_DIR}/../lib/") include_directories (SYSTEM "${PROJECT_SOURCE_DIR}/../lib/jsoncpp/include") +include_directories (SYSTEM "${PROJECT_SOURCE_DIR}/../lib/polarssl/include") set(FOLDERS OSSupport HTTPServer Bindings Items Blocks Protocol Generating) set(FOLDERS ${FOLDERS} WorldStorage Mobs Entities Simulator UI BlockEntities) diff --git a/src/Crypto.cpp b/src/Crypto.cpp new file mode 100644 index 000000000..5ad866f34 --- /dev/null +++ b/src/Crypto.cpp @@ -0,0 +1,401 @@ + +// Crypto.cpp + +// Implements classes that wrap the cryptographic code library + +#include "Globals.h" +#include "Crypto.h" + +#include "polarssl/pk.h" + + + + + +/* +// 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]; + cSHA1Checksum Checksum; + Checksum.Update((const Byte *)"Notch", 5); + Checksum.Finalize(Digest); + cSHA1Checksum::DigestToJava(Digest, DigestNotch); + Checksum.Restart(); + Checksum.Update((const Byte *)"jeb_", 4); + Checksum.Finalize(Digest); + cSHA1Checksum::DigestToJava(Digest, DigestJeb); + Checksum.Restart(); + Checksum.Update((const Byte *)"simon", 5); + Checksum.Finalize(Digest); + cSHA1Checksum::DigestToJava(Digest, DigestSimon); + printf("Notch: \"%s\"\n", DigestNotch.c_str()); + printf("jeb_: \"%s\"\n", DigestJeb.c_str()); + printf("simon: \"%s\"\n", DigestSimon.c_str()); + assert(DigestNotch == "4ed1f46bbe04bc756bcb17c0c7ce3e4632f06a48"); + assert(DigestJeb == "-7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1"); + assert(DigestSimon == "88e16a1019277b15d58faf0541e11910eb756f6"); + } +} test; +*/ + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cRSAPrivateKey: + +cRSAPrivateKey::cRSAPrivateKey(void) +{ + rsa_init(&m_Rsa, RSA_PKCS_V15, 0); + InitRnd(); +} + + + + + +cRSAPrivateKey::cRSAPrivateKey(const cRSAPrivateKey & a_Other) +{ + rsa_init(&m_Rsa, RSA_PKCS_V15, 0); + rsa_copy(&m_Rsa, &a_Other.m_Rsa); + InitRnd(); +} + + + + + +cRSAPrivateKey::~cRSAPrivateKey() +{ + entropy_free(&m_Entropy); + rsa_free(&m_Rsa); +} + + + + + +void cRSAPrivateKey::InitRnd(void) +{ + entropy_init(&m_Entropy); + const unsigned char pers[] = "rsa_genkey"; + ctr_drbg_init(&m_Ctr_drbg, entropy_func, &m_Entropy, pers, sizeof(pers) - 1); +} + + + + + +bool cRSAPrivateKey::Generate(unsigned a_KeySizeBits) +{ + if (rsa_gen_key(&m_Rsa, ctr_drbg_random, &m_Ctr_drbg, a_KeySizeBits, 65537) != 0) + { + // Key generation failed + return false; + } + + return true; +} + + + + + +AString cRSAPrivateKey::GetPubKeyDER(void) +{ + class cPubKey + { + public: + cPubKey(rsa_context * a_Rsa) : + m_IsValid(false) + { + pk_init(&m_Key); + if (pk_init_ctx(&m_Key, pk_info_from_type(POLARSSL_PK_RSA)) != 0) + { + ASSERT(!"Cannot init PrivKey context"); + return; + } + if (rsa_copy(pk_rsa(m_Key), a_Rsa) != 0) + { + ASSERT(!"Cannot copy PrivKey to PK context"); + return; + } + m_IsValid = true; + } + + ~cPubKey() + { + if (m_IsValid) + { + pk_free(&m_Key); + } + } + + operator pk_context * (void) { return &m_Key; } + + protected: + bool m_IsValid; + pk_context m_Key; + } PkCtx(&m_Rsa); + + unsigned char buf[3000]; + int res = pk_write_pubkey_der(PkCtx, buf, sizeof(buf)); + if (res < 0) + { + return AString(); + } + return AString((const char *)(buf + sizeof(buf) - res), (size_t)res); +} + + + + + +int cRSAPrivateKey::Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength) +{ + if (a_EncryptedLength < m_Rsa.len) + { + LOGD("%s: Invalid a_EncryptedLength: got %u, exp at least %u", + __FUNCTION__, (unsigned)a_EncryptedLength, (unsigned)(m_Rsa.len) + ); + ASSERT(!"Invalid a_DecryptedMaxLength!"); + return -1; + } + if (a_DecryptedMaxLength < m_Rsa.len) + { + LOGD("%s: Invalid a_DecryptedMaxLength: got %u, exp at least %u", + __FUNCTION__, (unsigned)a_EncryptedLength, (unsigned)(m_Rsa.len) + ); + ASSERT(!"Invalid a_DecryptedMaxLength!"); + return -1; + } + size_t DecryptedLength; + int res = rsa_pkcs1_decrypt( + &m_Rsa, ctr_drbg_random, &m_Ctr_drbg, RSA_PRIVATE, &DecryptedLength, + a_EncryptedData, a_DecryptedData, a_DecryptedMaxLength + ); + if (res != 0) + { + return -1; + } + return (int)DecryptedLength; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cAESCFBDecryptor: + +cAESCFBDecryptor::cAESCFBDecryptor(void) : + m_IsValid(false), + m_IVOffset(0) +{ +} + + + + + +cAESCFBDecryptor::~cAESCFBDecryptor() +{ + // Clear the leftover in-memory data, so that they can't be accessed by a backdoor + memset(&m_Aes, 0, sizeof(m_Aes)); +} + + + + + +void cAESCFBDecryptor::Init(const Byte a_Key[16], const Byte a_IV[16]) +{ + ASSERT(!IsValid()); // Cannot Init twice + + memcpy(m_IV, a_IV, 16); + aes_setkey_enc(&m_Aes, a_Key, 128); + m_IsValid = true; +} + + + + + +void cAESCFBDecryptor::ProcessData(Byte * a_DecryptedOut, const Byte * a_EncryptedIn, size_t a_Length) +{ + ASSERT(IsValid()); // Must Init() first + + // PolarSSL doesn't support AES-CFB8, need to implement it manually: + for (size_t i = 0; i < a_Length; i++) + { + Byte Buffer[sizeof(m_IV)]; + aes_crypt_ecb(&m_Aes, AES_ENCRYPT, m_IV, Buffer); + for (size_t idx = 0; idx < sizeof(m_IV) - 1; idx++) + { + m_IV[idx] = m_IV[idx + 1]; + } + m_IV[sizeof(m_IV) - 1] = a_EncryptedIn[i]; + a_DecryptedOut[i] = a_EncryptedIn[i] ^ Buffer[0]; + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cAESCFBEncryptor: + +cAESCFBEncryptor::cAESCFBEncryptor(void) : + m_IsValid(false), + m_IVOffset(0) +{ +} + + + + + +cAESCFBEncryptor::~cAESCFBEncryptor() +{ + // Clear the leftover in-memory data, so that they can't be accessed by a backdoor + memset(&m_Aes, 0, sizeof(m_Aes)); +} + + + + + +void cAESCFBEncryptor::Init(const Byte a_Key[16], const Byte a_IV[16]) +{ + ASSERT(!IsValid()); // Cannot Init twice + ASSERT(m_IVOffset == 0); + + memcpy(m_IV, a_IV, 16); + aes_setkey_enc(&m_Aes, a_Key, 128); + m_IsValid = true; +} + + + + + +void cAESCFBEncryptor::ProcessData(Byte * a_EncryptedOut, const Byte * a_PlainIn, size_t a_Length) +{ + ASSERT(IsValid()); // Must Init() first + + // PolarSSL doesn't do AES-CFB8, so we need to implement it ourselves: + for (size_t i = 0; i < a_Length; i++) + { + Byte Buffer[sizeof(m_IV)]; + aes_crypt_ecb(&m_Aes, AES_ENCRYPT, m_IV, Buffer); + for (size_t idx = 0; idx < sizeof(m_IV) - 1; idx++) + { + m_IV[idx] = m_IV[idx + 1]; + } + a_EncryptedOut[i] = a_PlainIn[i] ^ Buffer[0]; + m_IV[sizeof(m_IV) - 1] = a_EncryptedOut[i]; + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSHA1Checksum: + +cSHA1Checksum::cSHA1Checksum(void) : + m_DoesAcceptInput(true) +{ + sha1_starts(&m_Sha1); +} + + + + + +void cSHA1Checksum::Update(const Byte * a_Data, size_t a_Length) +{ + ASSERT(m_DoesAcceptInput); // Not Finalize()-d yet, or Restart()-ed + + sha1_update(&m_Sha1, a_Data, a_Length); +} + + + + + +void cSHA1Checksum::Finalize(cSHA1Checksum::Checksum & a_Output) +{ + ASSERT(m_DoesAcceptInput); // Not Finalize()-d yet, or Restart()-ed + + sha1_finish(&m_Sha1, a_Output); + m_DoesAcceptInput = false; +} + + + + + +void cSHA1Checksum::DigestToJava(const Checksum & a_Digest, AString & a_Out) +{ + Checksum Digest; + memcpy(Digest, a_Digest, sizeof(Digest)); + + bool IsNegative = (Digest[0] >= 0x80); + if (IsNegative) + { + // Two's complement: + bool carry = true; // Add one to the whole number + for (int i = 19; i >= 0; i--) + { + Digest[i] = ~Digest[i]; + if (carry) + { + carry = (Digest[i] == 0xff); + Digest[i]++; + } + } + } + a_Out.clear(); + a_Out.reserve(40); + for (int i = 0; i < 20; i++) + { + AppendPrintf(a_Out, "%02x", Digest[i]); + } + while ((a_Out.length() > 0) && (a_Out[0] == '0')) + { + a_Out.erase(0, 1); + } + if (IsNegative) + { + a_Out.insert(0, "-"); + } +} + + + + + + +void cSHA1Checksum::Restart(void) +{ + sha1_starts(&m_Sha1); + m_DoesAcceptInput = true; +} + + + + diff --git a/src/Crypto.h b/src/Crypto.h new file mode 100644 index 000000000..6b576f55b --- /dev/null +++ b/src/Crypto.h @@ -0,0 +1,164 @@ + +// Crypto.h + +// Declares classes that wrap the cryptographic code library + + + + + +#pragma once + +#include "polarssl/rsa.h" +#include "polarssl/aes.h" +#include "polarssl/entropy.h" +#include "polarssl/ctr_drbg.h" +#include "polarssl/sha1.h" + + + + + +/** Encapsulates an RSA private key used in PKI cryptography */ +class cRSAPrivateKey +{ +public: + /** Creates a new empty object, the key is not assigned */ + cRSAPrivateKey(void); + + /** Deep-copies the key from a_Other */ + cRSAPrivateKey(const cRSAPrivateKey & a_Other); + + ~cRSAPrivateKey(); + + /** Generates a new key within this object, with the specified size in bits. + Returns true on success, false on failure. */ + bool Generate(unsigned a_KeySizeBits = 1024); + + /** Returns the public key part encoded in ASN1 DER encoding */ + AString GetPubKeyDER(void); + + /** Decrypts the data using RSAES-PKCS#1 algorithm. + Both a_EncryptedData and a_DecryptedData must be at least bytes large. + Returns the number of bytes decrypted, or negative number for error. */ + int Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength); + +protected: + rsa_context m_Rsa; + entropy_context m_Entropy; + ctr_drbg_context m_Ctr_drbg; + + /** Initializes the m_Entropy and m_Ctr_drbg contexts + Common part of this object's construction, called from all constructors. */ + void InitRnd(void); +} ; + + + + + +/** Decrypts data using the AES / CFB (128) algorithm */ +class cAESCFBDecryptor +{ +public: + Byte test; + + cAESCFBDecryptor(void); + ~cAESCFBDecryptor(); + + /** Initializes the decryptor with the specified Key / IV */ + void Init(const Byte a_Key[16], const Byte a_IV[16]); + + /** Decrypts a_Length bytes of the encrypted data; produces a_Length output bytes */ + void ProcessData(Byte * a_DecryptedOut, const Byte * a_EncryptedIn, size_t a_Length); + + /** Returns true if the object has been initialized with the Key / IV */ + bool IsValid(void) const { return m_IsValid; } + +protected: + aes_context m_Aes; + + /** The InitialVector, used by the CFB mode decryption */ + Byte m_IV[16]; + + /** Current offset in the m_IV, used by the CFB mode decryption */ + size_t m_IVOffset; + + /** Indicates whether the object has been initialized with the Key / IV */ + bool m_IsValid; +} ; + + + + + +/** Encrypts data using the AES / CFB (128) algorithm */ +class cAESCFBEncryptor +{ +public: + Byte test; + + cAESCFBEncryptor(void); + ~cAESCFBEncryptor(); + + /** Initializes the decryptor with the specified Key / IV */ + void Init(const Byte a_Key[16], const Byte a_IV[16]); + + /** Encrypts a_Length bytes of the plain data; produces a_Length output bytes */ + void ProcessData(Byte * a_EncryptedOut, const Byte * a_PlainIn, size_t a_Length); + + /** Returns true if the object has been initialized with the Key / IV */ + bool IsValid(void) const { return m_IsValid; } + +protected: + aes_context m_Aes; + + /** The InitialVector, used by the CFB mode encryption */ + Byte m_IV[16]; + + /** Current offset in the m_IV, used by the CFB mode encryption */ + size_t m_IVOffset; + + /** Indicates whether the object has been initialized with the Key / IV */ + bool m_IsValid; +} ; + + + + + +/** Calculates a SHA1 checksum for data stream */ +class cSHA1Checksum +{ +public: + typedef Byte Checksum[20]; // The type used for storing the checksum + + cSHA1Checksum(void); + + /** Adds the specified data to the checksum */ + void Update(const Byte * a_Data, size_t a_Length); + + /** Calculates and returns the final checksum */ + void Finalize(Checksum & a_Output); + + /** Returns true if the object is accepts more input data, false if Finalize()-d (need to Restart()) */ + bool DoesAcceptInput(void) const { return m_DoesAcceptInput; } + + /** 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(const Checksum & a_Digest, AString & a_JavaOut); + + /** Clears the current context and start a new checksum calculation */ + void Restart(void); + +protected: + /** True if the object is accepts more input data, false if Finalize()-d (need to Restart()) */ + bool m_DoesAcceptInput; + + sha1_context m_Sha1; +} ; + + + + diff --git a/src/Defines.h b/src/Defines.h index 7a86f499e..0dcc3214a 100644 --- a/src/Defines.h +++ b/src/Defines.h @@ -5,8 +5,6 @@ -typedef unsigned char Byte; - /// List of slot numbers, used for inventory-painting typedef std::vector cSlotNums; diff --git a/src/Globals.h b/src/Globals.h index d2080b8eb..7e53da80e 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -91,6 +91,9 @@ typedef unsigned long long UInt64; typedef unsigned int UInt32; typedef unsigned short UInt16; +typedef unsigned char Byte; + + diff --git a/src/Protocol/Protocol132.cpp b/src/Protocol/Protocol132.cpp index b4ca37d37..f5fd95c7e 100644 --- a/src/Protocol/Protocol132.cpp +++ b/src/Protocol/Protocol132.cpp @@ -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::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); } diff --git a/src/Protocol/Protocol132.h b/src/Protocol/Protocol132.h index 80fc8740a..89f4636f5 100644 --- a/src/Protocol/Protocol132.h +++ b/src/Protocol/Protocol132.h @@ -20,13 +20,12 @@ #pragma warning(disable:4702) #endif -#include "cryptopp/modes.h" -#include "cryptopp/aes.h" - #ifdef _MSC_VER #pragma warning(pop) #endif +#include "../Crypto.h" + @@ -79,16 +78,15 @@ public: protected: bool m_IsEncrypted; - CryptoPP::CFB_Mode::Decryption m_Decryptor; - CryptoPP::CFB_Mode::Encryption m_Encryptor; + + cAESCFBDecryptor m_Decryptor; + cAESCFBEncryptor m_Encryptor; + AString m_DataToSend; /// The ServerID used for session authentication; set in StartEncryption(), used in GetAuthServerID() AString m_AuthServerID; - /// The server's public key, as used by SendEncryptionKeyRequest() and StartEncryption() - AString m_ServerPublicKey; - virtual void SendData(const char * a_Data, int a_Size) override; // DEBUG: @@ -108,7 +106,7 @@ protected: void HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce); /// Starts the symmetric encryption with the specified key; also sets m_AuthServerID - void StartEncryption(const byte * a_Key); + void StartEncryption(const Byte * a_Key); } ; diff --git a/src/Protocol/Protocol14x.cpp b/src/Protocol/Protocol14x.cpp index 127ce9d4b..f82e6de45 100644 --- a/src/Protocol/Protocol14x.cpp +++ b/src/Protocol/Protocol14x.cpp @@ -33,8 +33,6 @@ Implements the 1.4.x protocol classes representing these protocols: #pragma warning(disable:4702) #endif -#include "cryptopp/randpool.h" - #ifdef _MSC_VER #pragma warning(pop) #endif diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 9506332dc..bcc638796 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -73,11 +73,11 @@ void cProtocol172::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 > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size; - m_Decryptor.ProcessData(Decrypted, (byte *)a_Data, NumBytes); + m_Decryptor.ProcessData(Decrypted, (Byte *)a_Data, NumBytes); AddReceivedData((const char *)Decrypted, NumBytes); a_Size -= NumBytes; a_Data += NumBytes; @@ -1664,11 +1664,11 @@ void cProtocol172::SendData(const char * a_Data, int a_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 > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Size; - m_Encryptor.ProcessData(Encrypted, (byte *)a_Data, NumBytes); + m_Encryptor.ProcessData(Encrypted, (Byte *)a_Data, NumBytes); m_Client->SendData((const char *)Encrypted, NumBytes); a_Size -= NumBytes; a_Data += NumBytes; diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h index 3f440f313..0617b6c00 100644 --- a/src/Protocol/Protocol17x.h +++ b/src/Protocol/Protocol17x.h @@ -26,21 +26,20 @@ Declares the 1.7.x protocol classes: #pragma warning(disable:4702) #endif -#include "cryptopp/modes.h" -#include "cryptopp/aes.h" - #ifdef _MSC_VER #pragma warning(pop) #endif +#include "../Crypto.h" + class cProtocol172 : - public cProtocol // TODO + public cProtocol { - typedef cProtocol super; // TODO + typedef cProtocol super; public: @@ -220,9 +219,9 @@ protected: cByteBuffer m_OutPacketLenBuffer; bool m_IsEncrypted; - CryptoPP::CFB_Mode::Decryption m_Decryptor; - CryptoPP::CFB_Mode::Encryption m_Encryptor; + cAESCFBDecryptor m_Decryptor; + cAESCFBEncryptor m_Encryptor; /// Adds the received (unencrypted) data to m_ReceivedData, parses complete packets void AddReceivedData(const char * a_Data, int a_Size); diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index de5dd3fb9..32409c2aa 100644 --- a/src/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -965,7 +965,7 @@ void cProtocolRecognizer::SendLengthlessServerPing(void) m_Buffer.ResetRead(); if (m_Buffer.CanReadBytes(2)) { - byte val; + Byte val; m_Buffer.ReadByte(val); // Packet type - Serverlist ping m_Buffer.ReadByte(val); // 0x01 magic value ASSERT(val == 0x01); diff --git a/src/Server.cpp b/src/Server.cpp index 34ee066cb..f64af62eb 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -284,17 +284,9 @@ int cServer::GetNumPlayers(void) 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? - LOGD("Generating protocol encryption keypair..."); - - time_t CurTime = time(NULL); - CryptoPP::RandomPool rng; - rng.Put((const byte *)&CurTime, sizeof(CurTime)); - m_PrivateKey.GenerateRandomWithKeySize(rng, 1024); - CryptoPP::RSA::PublicKey pk(m_PrivateKey); - m_PublicKey = pk; + VERIFY(m_PrivateKey.Generate(1024)); + m_PublicKeyDER = m_PrivateKey.GetPubKeyDER(); } diff --git a/src/Server.h b/src/Server.h index bb55e81b6..15eafc00a 100644 --- a/src/Server.h +++ b/src/Server.h @@ -23,8 +23,7 @@ #pragma warning(disable:4702) #endif -#include "cryptopp/rsa.h" -#include "cryptopp/randpool.h" +#include "Crypto.h" #ifdef _MSC_VER #pragma warning(pop) @@ -110,8 +109,8 @@ public: // tolua_export /** Returns base64 encoded favicon data (obtained from favicon.png) */ const AString & GetFaviconData(void) const { return m_FaviconData; } - CryptoPP::RSA::PrivateKey & GetPrivateKey(void) { return m_PrivateKey; } - CryptoPP::RSA::PublicKey & GetPublicKey (void) { return m_PublicKey; } + cRSAPrivateKey & GetPrivateKey(void) { return m_PrivateKey; } + const AString & GetPublicKeyDER(void) const { return m_PublicKeyDER; } private: @@ -180,8 +179,11 @@ private: bool m_bRestarting; - CryptoPP::RSA::PrivateKey m_PrivateKey; - CryptoPP::RSA::PublicKey m_PublicKey; + /** The private key used for the assymetric encryption start in the protocols */ + cRSAPrivateKey m_PrivateKey; + + /** Public key for m_PrivateKey, ASN1-DER-encoded */ + AString m_PublicKeyDER; cRCONServer m_RCONServer; -- cgit v1.2.3