2016-03-02 17:11:15 -07:00
|
|
|
|
using System;
|
2018-05-03 23:51:56 +02:00
|
|
|
|
using System.Collections.Generic;
|
2020-03-01 19:59:12 +01:00
|
|
|
|
using System.Text.RegularExpressions;
|
2018-05-03 23:51:56 +02:00
|
|
|
|
using System.IO;
|
2022-08-27 23:01:28 +08:00
|
|
|
|
using System.Threading.Tasks;
|
2016-03-02 17:11:15 -07:00
|
|
|
|
|
2018-05-25 20:27:31 +02:00
|
|
|
|
namespace MinecraftClient.Protocol.Session
|
2016-03-02 17:11:15 -07:00
|
|
|
|
{
|
|
|
|
|
|
[Serializable]
|
|
|
|
|
|
public class SessionToken
|
|
|
|
|
|
{
|
2020-03-01 19:59:12 +01:00
|
|
|
|
private static readonly Regex JwtRegex = new Regex("^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+$");
|
|
|
|
|
|
|
2016-03-05 19:10:13 +01:00
|
|
|
|
public string ID { get; set; }
|
|
|
|
|
|
public string PlayerName { get; set; }
|
|
|
|
|
|
public string PlayerID { get; set; }
|
|
|
|
|
|
public string ClientID { get; set; }
|
2021-12-16 15:53:31 +08:00
|
|
|
|
public string RefreshToken { get; set; }
|
2022-08-27 23:01:28 +08:00
|
|
|
|
public string ServerIDhash { get; set; }
|
|
|
|
|
|
public byte[]? ServerPublicKey { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
public Task<bool>? SessionPreCheckTask = null;
|
2016-03-05 19:10:13 +01:00
|
|
|
|
|
|
|
|
|
|
public SessionToken()
|
|
|
|
|
|
{
|
|
|
|
|
|
ID = String.Empty;
|
|
|
|
|
|
PlayerName = String.Empty;
|
|
|
|
|
|
PlayerID = String.Empty;
|
|
|
|
|
|
ClientID = String.Empty;
|
2021-12-16 15:53:31 +08:00
|
|
|
|
RefreshToken = String.Empty;
|
2022-08-27 23:01:28 +08:00
|
|
|
|
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;
|
2016-03-05 19:10:13 +01:00
|
|
|
|
}
|
2018-05-03 23:51:56 +02:00
|
|
|
|
|
|
|
|
|
|
public override string ToString()
|
|
|
|
|
|
{
|
2022-08-27 23:01:28 +08:00
|
|
|
|
return String.Join(",", ID, PlayerName, PlayerID, ClientID, RefreshToken, ServerIDhash,
|
|
|
|
|
|
(ServerPublicKey == null) ? String.Empty : Convert.ToBase64String(ServerPublicKey));
|
2018-05-03 23:51:56 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static SessionToken FromString(string tokenString)
|
|
|
|
|
|
{
|
|
|
|
|
|
string[] fields = tokenString.Split(',');
|
|
|
|
|
|
if (fields.Length < 4)
|
|
|
|
|
|
throw new InvalidDataException("Invalid string format");
|
|
|
|
|
|
|
|
|
|
|
|
SessionToken session = new SessionToken();
|
|
|
|
|
|
session.ID = fields[0];
|
|
|
|
|
|
session.PlayerName = fields[1];
|
|
|
|
|
|
session.PlayerID = fields[2];
|
|
|
|
|
|
session.ClientID = fields[3];
|
2021-12-17 09:29:24 +08:00
|
|
|
|
// Backward compatible with old session file without refresh token field
|
|
|
|
|
|
if (fields.Length > 4)
|
|
|
|
|
|
session.RefreshToken = fields[4];
|
|
|
|
|
|
else
|
|
|
|
|
|
session.RefreshToken = String.Empty;
|
2022-08-27 23:01:28 +08:00
|
|
|
|
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;
|
2018-05-03 23:51:56 +02:00
|
|
|
|
|
|
|
|
|
|
Guid temp;
|
2020-03-01 19:59:12 +01:00
|
|
|
|
if (!JwtRegex.IsMatch(session.ID))
|
2018-05-03 23:51:56 +02:00
|
|
|
|
throw new InvalidDataException("Invalid session ID");
|
|
|
|
|
|
if (!ChatBot.IsValidName(session.PlayerName))
|
|
|
|
|
|
throw new InvalidDataException("Invalid player name");
|
|
|
|
|
|
if (!Guid.TryParseExact(session.PlayerID, "N", out temp))
|
|
|
|
|
|
throw new InvalidDataException("Invalid player ID");
|
|
|
|
|
|
if (!Guid.TryParseExact(session.ClientID, "N", out temp))
|
|
|
|
|
|
throw new InvalidDataException("Invalid client ID");
|
2021-12-16 15:53:31 +08:00
|
|
|
|
// No validation on refresh token because it is custom format token (not Jwt)
|
2018-05-03 23:51:56 +02:00
|
|
|
|
|
|
|
|
|
|
return session;
|
|
|
|
|
|
}
|
2016-03-02 17:11:15 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|