From 0ba2be666f68dda7a76d28e7cd8c74812c501d9f Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Thu, 6 Sep 2012 17:36:59 +0000 Subject: Added protocol-specific authentication, now works for both 1.2.5 and 1.3.2 git-svn-id: http://mc-server.googlecode.com/svn/trunk@841 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- source/Protocol.h | 3 ++ source/Protocol125.cpp | 11 +++++ source/Protocol125.h | 2 + source/Protocol132.cpp | 112 ++++++++++++++++++++++++++++++++++++++---- source/Protocol132.h | 12 ++++- source/ProtocolRecognizer.cpp | 10 ++++ source/ProtocolRecognizer.h | 2 + source/cAuthenticator.cpp | 6 +-- source/cAuthenticator.h | 13 +++-- source/cClientHandle.cpp | 2 +- 10 files changed, 154 insertions(+), 19 deletions(-) diff --git a/source/Protocol.h b/source/Protocol.h index e01afa9cc..44ef2cafc 100644 --- a/source/Protocol.h +++ b/source/Protocol.h @@ -81,6 +81,9 @@ public: virtual void SendWholeInventory (const cWindow & a_Window) = 0; virtual void SendWindowClose (char a_WindowID) = 0; virtual void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) = 0; + + /// Returns the ServerID used for authentication through session.minecraft.net + virtual AString GetAuthServerID(void) = 0; protected: cClientHandle * m_Client; diff --git a/source/Protocol125.cpp b/source/Protocol125.cpp index 7047f1f30..1927013da 100644 --- a/source/Protocol125.cpp +++ b/source/Protocol125.cpp @@ -756,6 +756,17 @@ void cProtocol125::SendWindowOpen(char a_WindowID, char a_WindowType, const AStr +AString cProtocol125::GetAuthServerID(void) +{ + // http://wiki.vg/wiki/index.php?title=Session&oldid=2262 + // The server generates a random hash and that is used for all clients, unmodified + return cRoot::Get()->GetServer()->GetServerID(); +} + + + + + void cProtocol125::SendData(const char * a_Data, int a_Size) { m_Client->SendData(a_Data, a_Size); diff --git a/source/Protocol125.h b/source/Protocol125.h index e787f462f..2e94cdfbe 100644 --- a/source/Protocol125.h +++ b/source/Protocol125.h @@ -67,6 +67,8 @@ public: virtual void SendWindowClose (char a_WindowID) override; virtual void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) override; + virtual AString GetAuthServerID(void) override; + protected: /// Results of packet-parsing: enum { diff --git a/source/Protocol132.cpp b/source/Protocol132.cpp index f22bbda94..e291c0c69 100644 --- a/source/Protocol132.cpp +++ b/source/Protocol132.cpp @@ -65,6 +65,81 @@ 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: @@ -267,6 +342,19 @@ void cProtocol132::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) +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) @@ -322,10 +410,9 @@ int cProtocol132::ParseHandshake(void) } // Send a 0xFD Encryption Key Request http://wiki.vg/Protocol#0xFD - AString key; - CryptoPP::StringSink sink(key); // GCC won't allow inline instantiation in the following line, damned temporary refs + 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(key); + SendEncryptionKeyRequest(); return PARSE_OK; } @@ -537,16 +624,13 @@ void cProtocol132::SendCompass(const cWorld & a_World) -void cProtocol132::SendEncryptionKeyRequest(const AString & a_Key) +void cProtocol132::SendEncryptionKeyRequest(void) { - // DEBUG: - LOGD("ServerID: \"%s\"", cRoot::Get()->GetServer()->GetServerID().c_str()); - cCSLock Lock(m_CSPacket); WriteByte((char)0xfd); WriteString(cRoot::Get()->GetServer()->GetServerID()); - WriteShort((short)a_Key.size()); - SendData(a_Key.data(), a_Key.size()); + 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(); @@ -610,6 +694,16 @@ 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); } diff --git a/source/Protocol132.h b/source/Protocol132.h index 3117af777..cca91c44b 100644 --- a/source/Protocol132.h +++ b/source/Protocol132.h @@ -40,6 +40,8 @@ public: virtual void SendPlayerSpawn (const cPlayer & a_Player) override; virtual void SendSpawnMob (const cMonster & a_Mob) override; virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; + + virtual AString GetAuthServerID(void) override; // DEBUG: virtual void SendKeepAlive (int a_PingID) override; @@ -64,6 +66,12 @@ protected: CryptoPP::CFB_Mode::Encryption 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: @@ -74,12 +82,12 @@ protected: virtual int ParseItem(cItem & a_Item) override; virtual void SendCompass(const cWorld & a_World); - virtual void SendEncryptionKeyRequest(const AString & a_Key); + virtual void SendEncryptionKeyRequest(void); /// 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 + /// Starts the symmetric encryption with the specified key; also sets m_AuthServerID void StartEncryption(const byte * a_Key); } ; diff --git a/source/ProtocolRecognizer.cpp b/source/ProtocolRecognizer.cpp index 69b733c47..669598923 100644 --- a/source/ProtocolRecognizer.cpp +++ b/source/ProtocolRecognizer.cpp @@ -458,6 +458,16 @@ void cProtocolRecognizer::SendWindowOpen(char a_WindowID, char a_WindowType, con +AString cProtocolRecognizer::GetAuthServerID(void) +{ + ASSERT(m_Protocol != NULL); + return m_Protocol->GetAuthServerID(); +} + + + + + void cProtocolRecognizer::SendData(const char * a_Data, int a_Size) { // This is used only when handling the server ping diff --git a/source/ProtocolRecognizer.h b/source/ProtocolRecognizer.h index f773c7ac1..eef89ec2e 100644 --- a/source/ProtocolRecognizer.h +++ b/source/ProtocolRecognizer.h @@ -76,6 +76,8 @@ public: virtual void SendWholeInventory (const cWindow & a_Window) override; virtual void SendWindowClose (char a_WindowID) override; virtual void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) override; + + virtual AString GetAuthServerID(void) override; virtual void SendData(const char * a_Data, int a_Size) override; diff --git a/source/cAuthenticator.cpp b/source/cAuthenticator.cpp index d34758c34..9d06e7f40 100644 --- a/source/cAuthenticator.cpp +++ b/source/cAuthenticator.cpp @@ -127,11 +127,11 @@ void cAuthenticator::Execute(void) } ASSERT(!m_Queue.empty()); - int ClientID = m_Queue.front().mClientID; - AString UserName = m_Queue.front().mName; + int ClientID = m_Queue.front().m_ClientID; + AString UserName = m_Queue.front().m_Name; AString ActualAddress = m_Address; ReplaceString(ActualAddress, "%USERNAME%", UserName); - ReplaceString(ActualAddress, "%SERVERID%", cRoot::Get()->GetServer()->GetServerID()); + ReplaceString(ActualAddress, "%SERVERID%", m_Queue.front().m_ServerID); m_Queue.pop_front(); Lock.Unlock(); diff --git a/source/cAuthenticator.h b/source/cAuthenticator.h index 0d69e930e..4d0bd1bbb 100644 --- a/source/cAuthenticator.h +++ b/source/cAuthenticator.h @@ -50,11 +50,16 @@ private: class cUser { public: - int mClientID; - AString mName; - AString mServerHash; + int m_ClientID; + AString m_Name; + AString m_ServerID; - cUser(int a_ClientID, const AString & a_Name, const AString & a_ServerHash) : mClientID(a_ClientID), mName(a_Name), mServerHash(a_ServerHash) {} + cUser(int a_ClientID, const AString & a_Name, const AString & a_ServerID) : + m_ClientID(a_ClientID), + m_Name(a_Name), + m_ServerID(a_ServerID) + { + } } ; typedef std::deque cUserList; diff --git a/source/cClientHandle.cpp b/source/cClientHandle.cpp index 98ca7e92e..1d539ff79 100644 --- a/source/cClientHandle.cpp +++ b/source/cClientHandle.cpp @@ -440,7 +440,7 @@ bool cClientHandle::HandleLogin(int a_ProtocolVersion, const AString & a_Usernam // Schedule for authentication; until then, let them wait (but do not block) m_State = csAuthenticating; - cRoot::Get()->GetAuthenticator().Authenticate(GetUniqueID(), GetUsername(), cRoot::Get()->GetServer()->GetServerID()); + cRoot::Get()->GetAuthenticator().Authenticate(GetUniqueID(), GetUsername(), m_Protocol->GetAuthServerID()); return true; } -- cgit v1.2.3