mirror of
https://github.com/D1GQ/BetterAmongUs.git
synced 2026-10-20 01:46:19 +00:00
Fix Linux crypto + chat crash + handshake init
This commit is contained in:
1 parent
4252422a93
commit
c4baa42a23
3 files changed
+56
-66
No files matched your search
@@ -3,58 +3,53 @@ using System.Security.Cryptography;
|
||||
|
||||
namespace BetterAmongUs.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Handles secure key exchange using Elliptic Curve Diffie-Hellman (ECDH) for establishing shared secrets.
|
||||
/// </summary>
|
||||
internal sealed class SharedSecretExchange
|
||||
{
|
||||
private readonly ECDiffieHellman dh;
|
||||
private byte[] publicKey;
|
||||
private int tempKey;
|
||||
private byte[] sharedSecret = [];
|
||||
private bool cryptoDisabled;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the SharedSecretExchange class with a new ECDH key pair.
|
||||
/// </summary>
|
||||
internal SharedSecretExchange()
|
||||
{
|
||||
dh = ECDiffieHellman.Create(ECCurve.NamedCurves.nistP256);
|
||||
publicKey = dh.ExportSubjectPublicKeyInfo();
|
||||
try
|
||||
{
|
||||
dh = ECDiffieHellman.Create(ECCurve.NamedCurves.nistP256);
|
||||
publicKey = dh.ExportSubjectPublicKeyInfo();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger_.Error("ECDH unavailable, disabling secure handshake: " + ex.Message);
|
||||
cryptoDisabled = true;
|
||||
dh = null;
|
||||
publicKey = [];
|
||||
}
|
||||
|
||||
var random = new Random();
|
||||
tempKey = random.Next();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the public key for key exchange.
|
||||
/// </summary>
|
||||
/// <returns>The public key in SubjectPublicKeyInfo format.</returns>
|
||||
internal byte[] GetPublicKey()
|
||||
{
|
||||
if (cryptoDisabled) return [];
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the temporary key used for initial communication.
|
||||
/// </summary>
|
||||
/// <returns>A random integer used as a temporary key.</returns>
|
||||
internal int GetTempKey()
|
||||
{
|
||||
return tempKey;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the established shared secret.
|
||||
/// </summary>
|
||||
/// <returns>The shared secret byte array.</returns>
|
||||
internal byte[] GetSharedSecret() => sharedSecret;
|
||||
internal byte[] GetSharedSecret()
|
||||
{
|
||||
if (cryptoDisabled) return [];
|
||||
return sharedSecret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a shared secret using another party's public key.
|
||||
/// </summary>
|
||||
/// <param name="otherPartyPublicKey">The other party's public key in SubjectPublicKeyInfo format.</param>
|
||||
/// <returns>The shared secret byte array, or an empty array if generation fails.</returns>
|
||||
internal byte[] GenerateSharedSecret(byte[] otherPartyPublicKey)
|
||||
{
|
||||
if (cryptoDisabled) return [];
|
||||
if (sharedSecret.Length > 0) return sharedSecret;
|
||||
|
||||
try
|
||||
@@ -68,19 +63,15 @@ internal sealed class SharedSecretExchange
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger_.Error($"Error generating shared secret: {ex.Message}");
|
||||
Logger_.Error("Error generating shared secret: " + ex.Message);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a numeric hash of the shared secret for verification purposes.
|
||||
/// </summary>
|
||||
/// <returns>A 32-bit integer hash of the shared secret, or 0 if no shared secret exists.</returns>
|
||||
internal int GetSharedSecretHash()
|
||||
{
|
||||
if (sharedSecret.Length == 0)
|
||||
return 0;
|
||||
if (cryptoDisabled) return 0;
|
||||
if (sharedSecret.Length == 0) return 0;
|
||||
|
||||
using SHA256 sha256 = SHA256.Create();
|
||||
byte[] hashBytes = sha256.ComputeHash(sharedSecret);
|
||||
@@ -88,14 +79,8 @@ internal sealed class SharedSecretExchange
|
||||
return Math.Abs(numericHash);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the key exchange data has been cleared for security.
|
||||
/// </summary>
|
||||
internal bool HasBeenCleared { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Clears all sensitive key exchange data for security purposes.
|
||||
/// </summary>
|
||||
internal void ClearData()
|
||||
{
|
||||
if (HasBeenCleared) return;
|
||||
|
||||
@@ -15,17 +15,19 @@ internal sealed class ExtendedPlayerInfo : MonoBehaviour, IMonoExtension<Network
|
||||
{
|
||||
internal ExtendedPlayerInfo()
|
||||
{
|
||||
HandshakeHandler = new(this);
|
||||
try
|
||||
{
|
||||
HandshakeHandler = new(this);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger_.Error("Handshake disabled: " + ex.Message);
|
||||
HandshakeHandler = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the base NetworkedPlayerInfo instance.
|
||||
/// </summary>
|
||||
public NetworkedPlayerInfo? BaseMono { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the NetworkedPlayerInfo instance.
|
||||
/// </summary>
|
||||
internal NetworkedPlayerInfo? _Data => BaseMono;
|
||||
|
||||
private bool hasSet = false;
|
||||
@@ -47,7 +49,11 @@ internal sealed class ExtendedPlayerInfo : MonoBehaviour, IMonoExtension<Network
|
||||
private void Awake()
|
||||
{
|
||||
if (!this.RegisterExtension()) return;
|
||||
HandshakeHandler.WaitSendSecretToPlayer();
|
||||
|
||||
if (HandshakeHandler != null)
|
||||
{
|
||||
HandshakeHandler.WaitSendSecretToPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
@@ -90,7 +96,7 @@ internal sealed class ExtendedPlayerInfo : MonoBehaviour, IMonoExtension<Network
|
||||
/// Gets the handshake handler for this player.
|
||||
/// </summary>
|
||||
[HideFromIl2Cpp]
|
||||
internal HandshakeHandler HandshakeHandler { get; }
|
||||
internal HandshakeHandler? HandshakeHandler { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the player ID.
|
||||
|
||||
@@ -134,28 +134,33 @@ internal static class ChatPatch
|
||||
private static void ChatController_SetChatBubbleName_Postfix(ChatController __instance, ChatBubble bubble, NetworkedPlayerInfo playerInfo, bool isDead, bool didVote)
|
||||
{
|
||||
if (didVote) return;
|
||||
if (bubble == null) return;
|
||||
if (bubble.NameText == null) return;
|
||||
if (playerInfo == null) return;
|
||||
|
||||
var sourcePlayer = playerInfo.Object;
|
||||
if (sourcePlayer == null) return;
|
||||
if (PlayerControl.LocalPlayer == null) return;
|
||||
|
||||
StringBuilder sbTag = new();
|
||||
StringBuilder sbInfo = new();
|
||||
|
||||
var sourcePlayer = playerInfo.Object;
|
||||
string hashPuid = Utils.GetHashPuid(sourcePlayer);
|
||||
string friendCode = playerInfo.FriendCode;
|
||||
string playerName = playerInfo.BetterData()?.RealName ?? "???";
|
||||
|
||||
// Format role display with team color
|
||||
string Role = $"<size=75%><color={sourcePlayer.GetTeamHexColor()}>{sourcePlayer.GetRoleName()}</color></size>+++";
|
||||
|
||||
// In lobby, show player tags instead of roles
|
||||
if (GameState.IsLobby && !GameState.IsFreePlay)
|
||||
{
|
||||
Role = "";
|
||||
|
||||
// Show BAU user tag
|
||||
if (sourcePlayer.IsLocalPlayer() || sourcePlayer.BetterData().IsBetterUser)
|
||||
sbTag.AppendFormat("<color=#0dff00>{1}{0}</color>+++", Translator.GetString("Player.BetterUser"), sourcePlayer.BetterData().IsVerifiedBetterUser || sourcePlayer.IsLocalPlayer() ? "✓ " : "");
|
||||
var betterData = sourcePlayer.BetterData();
|
||||
if (betterData == null) return;
|
||||
|
||||
if (sourcePlayer.IsLocalPlayer() || betterData.IsBetterUser)
|
||||
sbTag.AppendFormat("<color=#0dff00>{1}{0}</color>+++", Translator.GetString("Player.BetterUser"), betterData.IsVerifiedBetterUser || sourcePlayer.IsLocalPlayer() ? "✓ " : "");
|
||||
|
||||
// Show mod-specific tags based on player data
|
||||
if (BetterDataManager.BetterDataFile.SickoData.Any(info => info.CheckPlayerData(sourcePlayer.Data)))
|
||||
sbTag.Append($"<color=#00f583>{Translator.GetString("Player.SickoUser")}</color>+++");
|
||||
else if (BetterDataManager.BetterDataFile.AUMData.Any(info => info.CheckPlayerData(sourcePlayer.Data)))
|
||||
@@ -166,7 +171,6 @@ internal static class ChatPatch
|
||||
sbTag.Append($"<color=#fc0000>{Translator.GetString("Player.KnownCheater")}</color>+++");
|
||||
}
|
||||
|
||||
// Hide roles from alive players (unless same team)
|
||||
if (!sourcePlayer.IsImpostorTeammate())
|
||||
{
|
||||
if (PlayerControl.LocalPlayer.IsAlive() && !sourcePlayer.IsLocalPlayer())
|
||||
@@ -175,23 +179,19 @@ internal static class ChatPatch
|
||||
}
|
||||
}
|
||||
|
||||
// Show role for dead players or if local player is Guardian Angel
|
||||
if (PlayerControl.LocalPlayer.Is(RoleTypes.GuardianAngel) && !sourcePlayer.IsAlive() || !PlayerControl.LocalPlayer.Is(RoleTypes.GuardianAngel))
|
||||
{
|
||||
sbTag.Append(Role);
|
||||
}
|
||||
|
||||
// Format tags with separators
|
||||
sbInfo.Append("<size=75%>");
|
||||
for (int i = 0; i < sbTag.ToString().Split("+++").Length; i++)
|
||||
var parts = sbTag.ToString().Split("+++");
|
||||
for (int i = 0; i < parts.Length; i++)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(sbTag.ToString().Split("+++")[i]))
|
||||
if (!string.IsNullOrEmpty(parts[i]))
|
||||
{
|
||||
if (i < sbTag.ToString().Split("+++").Length)
|
||||
{
|
||||
sbInfo.Append(sbTag.ToString().Split("+++")[i]);
|
||||
}
|
||||
if (i != sbTag.ToString().Split("+++").Length - 2)
|
||||
sbInfo.Append(parts[i]);
|
||||
if (i != parts.Length - 2)
|
||||
{
|
||||
sbInfo.Append(" - ");
|
||||
}
|
||||
@@ -199,7 +199,6 @@ internal static class ChatPatch
|
||||
}
|
||||
sbInfo.Append("</size>");
|
||||
|
||||
// Position tags before local player name, after other players' names
|
||||
bool flag = sourcePlayer.IsLocalPlayer();
|
||||
if (flag)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user