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
This commit is contained in:
parent
d75bdabc90
commit
0ba2be666f
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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<CryptoPP::AES>::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);
|
||||
} ;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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<cUser> cUserList;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user