Rewrote AES stream & Perform "SessionCheck" in advance

This commit is contained in:
BruceChen 2022-08-27 23:01:28 +08:00
parent a3971f9097
commit 13d1a9856a
28 changed files with 306 additions and 2807 deletions

View file

@ -235,13 +235,13 @@ namespace MinecraftClient.Protocol.Handlers
{
int i = 0;
int j = 0;
int k = 0;
byte b;
while (true)
{
k = socket.ReadDataRAW(1)[0];
i |= (k & 0x7F) << j++ * 7;
b = socket.ReadDataRAW(1)[0];
i |= (b & 0x7F) << j++ * 7;
if (j > 5) throw new OverflowException("VarInt too big");
if ((k & 0x80) != 128) break;
if ((b & 0x80) != 128) break;
}
return i;
}
@ -259,9 +259,9 @@ namespace MinecraftClient.Protocol.Handlers
do
{
b = cache.Dequeue();
i |= (b & 127) << j++ * 7;
i |= (b & 0x7F) << j++ * 7;
if (j > 5) throw new OverflowException("VarInt too big");
} while ((b & 128) == 128);
} while ((b & 0x80) == 128);
return i;
}

View file

@ -10,6 +10,7 @@ using System.Security.Cryptography;
using MinecraftClient.Mapping;
using MinecraftClient.Inventory;
using MinecraftClient.Protocol.Keys;
using MinecraftClient.Protocol.Session;
namespace MinecraftClient.Protocol.Handlers
{
@ -439,7 +440,7 @@ namespace MinecraftClient.Protocol.Handlers
else c.Client.Send(buffer);
}
private bool Handshake(string uuid, string username, string sessionID, string host, int port)
private bool Handshake(string uuid, string username, string sessionID, string host, int port, SessionToken session)
{
//array
byte[] data = new byte[10 + (username.Length + host.Length) * 2];
@ -493,7 +494,7 @@ namespace MinecraftClient.Protocol.Handlers
else if (Settings.DebugMessages)
ConsoleIO.WriteLineFormatted(Translations.Get("mcc.handshake", serverID));
return StartEncryption(uuid, username, sessionID, token, serverID, PublicServerkey);
return StartEncryption(uuid, username, sessionID, token, serverID, PublicServerkey, session);
}
else
{
@ -502,10 +503,10 @@ namespace MinecraftClient.Protocol.Handlers
}
}
private bool StartEncryption(string uuid, string username, string sessionID, byte[] token, string serverIDhash, byte[] serverKey)
private bool StartEncryption(string uuid, string username, string sessionID, byte[] token, string serverIDhash, byte[] serverPublicKey, SessionToken session)
{
System.Security.Cryptography.RSACryptoServiceProvider RSAService = CryptoHandler.DecodeRSAPublicKey(serverKey);
byte[] secretKey = CryptoHandler.GenerateAESPrivateKey();
RSACryptoServiceProvider RSAService = CryptoHandler.DecodeRSAPublicKey(serverPublicKey);
byte[] secretKey = CryptoHandler.ClientAESPrivateKey ?? CryptoHandler.GenerateAESPrivateKey();
if (Settings.DebugMessages)
Translations.WriteLineFormatted("debug.crypto");
@ -513,13 +514,34 @@ namespace MinecraftClient.Protocol.Handlers
if (serverIDhash != "-")
{
Translations.WriteLine("mcc.session");
if (!ProtocolHandler.SessionCheck(uuid, sessionID, CryptoHandler.getServerHash(serverIDhash, serverKey, secretKey)))
string serverHash = CryptoHandler.getServerHash(serverIDhash, serverPublicKey, secretKey);
bool needCheckSession = true;
if (session.ServerPublicKey != null && session.SessionPreCheckTask != null
&& serverIDhash == session.ServerIDhash && Enumerable.SequenceEqual(serverPublicKey, session.ServerPublicKey))
{
handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, Translations.Get("mcc.session_fail"));
return false;
session.SessionPreCheckTask.Wait();
if (session.SessionPreCheckTask.Result) // PreCheck Successed
needCheckSession = false;
}
if (needCheckSession)
{
if (ProtocolHandler.SessionCheck(uuid, sessionID, serverHash))
{
session.ServerIDhash = serverIDhash;
session.ServerPublicKey = serverPublicKey;
SessionCache.Store(Settings.Login.ToLower(), session);
}
else
{
handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, Translations.Get("mcc.session_fail"));
return false;
}
}
}
//Encrypt the data
byte[] key_enc = RSAService.Encrypt(secretKey, false);
byte[] token_enc = RSAService.Encrypt(token, false);
@ -557,9 +579,9 @@ namespace MinecraftClient.Protocol.Handlers
}
}
public bool Login(PlayerKeyPair playerKeyPair)
public bool Login(PlayerKeyPair? playerKeyPair, SessionToken session)
{
if (Handshake(handler.GetUserUUID(), handler.GetUsername(), handler.GetSessionID(), handler.GetServerHost(), handler.GetServerPort()))
if (Handshake(handler.GetUserUUID(), handler.GetUsername(), handler.GetSessionID(), handler.GetServerHost(), handler.GetServerPort(), session))
{
Send(new byte[] { 0xCD, 0 });
try

View file

@ -19,6 +19,7 @@ using MinecraftClient.Logger;
using System.Threading.Tasks;
using MinecraftClient.Protocol.Keys;
using System.Text.RegularExpressions;
using MinecraftClient.Protocol.Session;
namespace MinecraftClient.Protocol.Handlers
{
@ -1534,7 +1535,7 @@ namespace MinecraftClient.Protocol.Handlers
/// Do the Minecraft login.
/// </summary>
/// <returns>True if login successful</returns>
public bool Login(PlayerKeyPair? playerKeyPair)
public bool Login(PlayerKeyPair? playerKeyPair, SessionToken session)
{
byte[] protocol_version = dataTypes.GetVarInt(protocolversion);
string server_address = pForge.GetServerAddress(handler.GetServerHost());
@ -1574,7 +1575,7 @@ namespace MinecraftClient.Protocol.Handlers
string serverID = dataTypes.ReadNextString(packetData);
byte[] serverPublicKey = dataTypes.ReadNextByteArray(packetData);
byte[] token = dataTypes.ReadNextByteArray(packetData);
return StartEncryption(handler.GetUserUUID(), handler.GetSessionID(), token, serverID, serverPublicKey, playerKeyPair);
return StartEncryption(handler.GetUserUUID(), handler.GetSessionID(), token, serverID, serverPublicKey, playerKeyPair, session);
}
else if (packetID == 0x02) //Login successful
{
@ -1598,24 +1599,43 @@ namespace MinecraftClient.Protocol.Handlers
/// Start network encryption. Automatically called by Login() if the server requests encryption.
/// </summary>
/// <returns>True if encryption was successful</returns>
private bool StartEncryption(string uuid, string sessionID, byte[] token, string serverIDhash, byte[] serverPublicKey, PlayerKeyPair? playerKeyPair)
private bool StartEncryption(string uuid, string sessionID, byte[] token, string serverIDhash, byte[] serverPublicKey, PlayerKeyPair? playerKeyPair, SessionToken session)
{
System.Security.Cryptography.RSACryptoServiceProvider RSAService = CryptoHandler.DecodeRSAPublicKey(serverPublicKey);
byte[] secretKey = CryptoHandler.GenerateAESPrivateKey();
RSACryptoServiceProvider RSAService = CryptoHandler.DecodeRSAPublicKey(serverPublicKey);
byte[] secretKey = CryptoHandler.ClientAESPrivateKey ?? CryptoHandler.GenerateAESPrivateKey();
log.Debug(Translations.Get("debug.crypto"));
if (serverIDhash != "-")
{
log.Info(Translations.Get("mcc.session"));
if (!ProtocolHandler.SessionCheck(uuid, sessionID, CryptoHandler.getServerHash(serverIDhash, serverPublicKey, secretKey)))
string serverHash = CryptoHandler.getServerHash(serverIDhash, serverPublicKey, secretKey);
bool needCheckSession = true;
if (session.ServerPublicKey != null && session.SessionPreCheckTask != null
&& serverIDhash == session.ServerIDhash && Enumerable.SequenceEqual(serverPublicKey, session.ServerPublicKey))
{
handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, Translations.Get("mcc.session_fail"));
return false;
session.SessionPreCheckTask.Wait();
if (session.SessionPreCheckTask.Result) // PreCheck Successed
needCheckSession = false;
}
if (needCheckSession)
{
if (ProtocolHandler.SessionCheck(uuid, sessionID, serverHash))
{
session.ServerIDhash = serverIDhash;
session.ServerPublicKey = serverPublicKey;
SessionCache.Store(Settings.Login.ToLower(), session);
}
else
{
handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, Translations.Get("mcc.session_fail"));
return false;
}
}
}
// Encryption Response packet
List<byte> encryptionResponse = new();
encryptionResponse.AddRange(dataTypes.GetArray(RSAService.Encrypt(secretKey, false))); // Shared Secret

View file

@ -22,7 +22,7 @@ namespace MinecraftClient.Protocol
/// Start the login procedure once connected to the server
/// </summary>
/// <returns>True if login was successful</returns>
bool Login(PlayerKeyPair playerKeyPair);
bool Login(PlayerKeyPair? playerKeyPair, Session.SessionToken session);
/// <summary>
/// Disconnect from the server

View file

@ -10,7 +10,7 @@ namespace MinecraftClient.Protocol.Keys
{
private static string certificates = "https://api.minecraftservices.com/player/certificates";
public static PlayerKeyPair? GetKeys(string accessToken)
public static PlayerKeyPair? GetNewProfileKeys(string accessToken)
{
ProxiedWebRequest.Response? response = null;
try

View file

@ -252,7 +252,7 @@ namespace MinecraftClient.Protocol.Session
List<string> sessionCacheLines = new List<string>();
sessionCacheLines.Add("# Generated by MCC v" + Program.Version + " - Keep it secret & Edit at own risk!");
sessionCacheLines.Add("# Login=SessionID,PlayerName,UUID,ClientID");
sessionCacheLines.Add("# Login=SessionID,PlayerName,UUID,ClientID,RefreshToken,ServerIDhash,ServerPublicKey");
foreach (KeyValuePair<string, SessionToken> entry in sessions)
sessionCacheLines.Add(entry.Key + '=' + entry.Value.ToString());

View file

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.IO;
using System.Threading.Tasks;
namespace MinecraftClient.Protocol.Session
{
@ -15,6 +16,10 @@ namespace MinecraftClient.Protocol.Session
public string PlayerID { get; set; }
public string ClientID { get; set; }
public string RefreshToken { get; set; }
public string ServerIDhash { get; set; }
public byte[]? ServerPublicKey { get; set; }
public Task<bool>? SessionPreCheckTask = null;
public SessionToken()
{
@ -23,11 +28,26 @@ namespace MinecraftClient.Protocol.Session
PlayerID = String.Empty;
ClientID = String.Empty;
RefreshToken = String.Empty;
ServerIDhash = String.Empty;
ServerPublicKey = null;
}
public bool SessionPreCheck()
{
if (this.ID == string.Empty || this.PlayerID == String.Empty || this.ServerPublicKey == null)
return false;
if (Crypto.CryptoHandler.ClientAESPrivateKey == null)
Crypto.CryptoHandler.ClientAESPrivateKey = Crypto.CryptoHandler.GenerateAESPrivateKey();
string serverHash = Crypto.CryptoHandler.getServerHash(ServerIDhash, ServerPublicKey, Crypto.CryptoHandler.ClientAESPrivateKey);
if (ProtocolHandler.SessionCheck(PlayerID, ID, serverHash))
return true;
return false;
}
public override string ToString()
{
return String.Join(",", ID, PlayerName, PlayerID, ClientID, RefreshToken);
return String.Join(",", ID, PlayerName, PlayerID, ClientID, RefreshToken, ServerIDhash,
(ServerPublicKey == null) ? String.Empty : Convert.ToBase64String(ServerPublicKey));
}
public static SessionToken FromString(string tokenString)
@ -46,6 +66,23 @@ namespace MinecraftClient.Protocol.Session
session.RefreshToken = fields[4];
else
session.RefreshToken = String.Empty;
if (fields.Length > 5)
session.ServerIDhash = fields[5];
else
session.ServerIDhash = String.Empty;
if (fields.Length > 6)
{
try
{
session.ServerPublicKey = Convert.FromBase64String(fields[6]);
}
catch
{
session.ServerPublicKey = null;
}
}
else
session.ServerPublicKey = null;
Guid temp;
if (!JwtRegex.IsMatch(session.ID))
@ -60,5 +97,7 @@ namespace MinecraftClient.Protocol.Session
return session;
}
}
}