Fix Linux crypto + chat crash + handshake init

This commit is contained in:
DeveloperNyx committed 2026-03-16 18:09:02 +00:00
1 parent 4252422a93
commit c4baa42a23
3 files changed
+56 -66

No files matched your search

+24 -39
View File
@@ -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 -9
View File
@@ -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.
+17 -18
View File
@@ -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)
{