From e9cf576268e0e5b1f2565bbfd958a2fc92b02371 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Tue, 17 Sep 2024 01:26:42 +0200 Subject: [PATCH 1/6] New HWID system prep --- Robust.Client/ClientIoC.cs | 2 + .../Console/Commands/LauncherAuthCommand.cs | 5 +- Robust.Client/HWId/BasicHWid.cs | 85 +++++++++++++++++++ Robust.Client/Utility/UserDataDir.cs | 10 ++- Robust.Server/ServerIoC.cs | 1 + Robust.Shared/Network/HWId.cs | 46 ---------- Robust.Shared/Network/IHWId.cs | 67 +++++++++++++++ .../Network/NetManager.ClientConnect.cs | 7 +- .../Network/NetManager.ServerAuth.cs | 12 ++- Robust.Shared/Network/NetManager.cs | 1 + Robust.Shared/Network/NetUserData.cs | 17 ++++ Robust.Shared/Robust.Shared.csproj | 1 + Robust.Shared/Utility/Base64Helpers.cs | 12 +++ .../RobustIntegrationTest.NetManager.cs | 3 +- 14 files changed, 212 insertions(+), 57 deletions(-) create mode 100644 Robust.Client/HWId/BasicHWid.cs delete mode 100644 Robust.Shared/Network/HWId.cs create mode 100644 Robust.Shared/Network/IHWId.cs diff --git a/Robust.Client/ClientIoC.cs b/Robust.Client/ClientIoC.cs index b444bd09ed2..596996637b5 100644 --- a/Robust.Client/ClientIoC.cs +++ b/Robust.Client/ClientIoC.cs @@ -8,6 +8,7 @@ using Robust.Client.GameStates; using Robust.Client.Graphics; using Robust.Client.Graphics.Clyde; +using Robust.Client.HWId; using Robust.Client.Input; using Robust.Client.Map; using Robust.Client.Placement; @@ -158,6 +159,7 @@ public static void RegisterIoC(GameController.DisplayMode mode, IDependencyColle deps.Register(); deps.Register(); + deps.Register(); } } } diff --git a/Robust.Client/Console/Commands/LauncherAuthCommand.cs b/Robust.Client/Console/Commands/LauncherAuthCommand.cs index 27d1469aad8..5fa5c519255 100644 --- a/Robust.Client/Console/Commands/LauncherAuthCommand.cs +++ b/Robust.Client/Console/Commands/LauncherAuthCommand.cs @@ -20,8 +20,9 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args) { var wantName = args.Length > 0 ? args[0] : null; - var basePath = Path.GetDirectoryName(UserDataDir.GetUserDataDir(_gameController))!; - var dbPath = Path.Combine(basePath, "launcher", "settings.db"); + var basePath = UserDataDir.GetRootUserDataDir(_gameController); + var launcherDirName = Environment.GetEnvironmentVariable("SS14_LAUNCHER_APPDATA_NAME") ?? "launcher"; + var dbPath = Path.Combine(basePath, launcherDirName, "settings.db"); #if USE_SYSTEM_SQLITE SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_sqlite3()); diff --git a/Robust.Client/HWId/BasicHWid.cs b/Robust.Client/HWId/BasicHWid.cs new file mode 100644 index 00000000000..858756301a4 --- /dev/null +++ b/Robust.Client/HWId/BasicHWid.cs @@ -0,0 +1,85 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using Microsoft.Win32; +using Robust.Client.Utility; +using Robust.Shared.IoC; +using Robust.Shared.Network; + +namespace Robust.Client.HWId +{ + internal sealed class BasicHWid : IHWId + { + [Dependency] private readonly IGameControllerInternal _gameController = default!; + + public const int LengthHwid = 32; + + public byte[] GetLegacy() + { + if (OperatingSystem.IsWindows()) + return GetWindowsHWid("Hwid"); + + return []; + } + + public byte[] GetModern() + { + byte[] raw; + + // if (OperatingSystem.IsWindows()) + // raw = GetWindowsHWid("Hwid2"); + // else + raw = GetFileHWid(); + + return [0, ..raw]; + } + + private static byte[] GetWindowsHWid(string keyName) + { + var regKey = Registry.GetValue(@"HKEY_CURRENT_USER\SOFTWARE\Space Wizards\Robust", keyName, null); + if (regKey is byte[] { Length: LengthHwid } bytes) + return bytes; + + var newId = new byte[LengthHwid]; + RandomNumberGenerator.Fill(newId); + Registry.SetValue( + @"HKEY_CURRENT_USER\SOFTWARE\Space Wizards\Robust", + keyName, + newId, + RegistryValueKind.Binary); + + return newId; + } + + private byte[] GetFileHWid() + { + var path = UserDataDir.GetRootUserDataDir(_gameController); + var hwidPath = Path.Combine(path, ".hwid"); + + var value = ReadHWidFile(hwidPath); + if (value != null) + return value; + + value = RandomNumberGenerator.GetBytes(LengthHwid); + File.WriteAllBytes(hwidPath, value); + + return value; + } + + private static byte[]? ReadHWidFile(string path) + { + try + { + var value = File.ReadAllBytes(path); + if (value.Length == LengthHwid) + return value; + } + catch (FileNotFoundException) + { + // First time the file won't exist. + } + + return null; + } + } +} diff --git a/Robust.Client/Utility/UserDataDir.cs b/Robust.Client/Utility/UserDataDir.cs index 19a507bc0e7..9ebcaad46c7 100644 --- a/Robust.Client/Utility/UserDataDir.cs +++ b/Robust.Client/Utility/UserDataDir.cs @@ -1,7 +1,6 @@ using System; using System.IO; using JetBrains.Annotations; -using Robust.Shared.IoC; namespace Robust.Client.Utility { @@ -9,6 +8,12 @@ internal static class UserDataDir { [Pure] public static string GetUserDataDir(IGameControllerInternal gameController) + { + return Path.Combine(GetRootUserDataDir(gameController), "data"); + } + + [Pure] + public static string GetRootUserDataDir(IGameControllerInternal gameController) { string appDataDir; @@ -30,8 +35,7 @@ public static string GetUserDataDir(IGameControllerInternal gameController) appDataDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); #endif - return Path.Combine(appDataDir, gameController.Options.UserDataDirectoryName, "data"); + return Path.Combine(appDataDir, gameController.Options.UserDataDirectoryName); } - } } diff --git a/Robust.Server/ServerIoC.cs b/Robust.Server/ServerIoC.cs index d69eb0f6eb5..49fd258e2d2 100644 --- a/Robust.Server/ServerIoC.cs +++ b/Robust.Server/ServerIoC.cs @@ -97,6 +97,7 @@ internal static void RegisterIoC(IDependencyCollection deps) deps.Register(); deps.Register(); deps.Register(); + deps.Register(); } } } diff --git a/Robust.Shared/Network/HWId.cs b/Robust.Shared/Network/HWId.cs deleted file mode 100644 index ad936c0310b..00000000000 --- a/Robust.Shared/Network/HWId.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Security.Cryptography; -using Microsoft.Win32; -using Robust.Shared.Console; - -namespace Robust.Shared.Network -{ - internal static class HWId - { - public const int LengthHwid = 32; - - public static byte[] Calc() - { - if (OperatingSystem.IsWindows()) - { - var regKey = Registry.GetValue(@"HKEY_CURRENT_USER\SOFTWARE\Space Wizards\Robust", "Hwid", null); - if (regKey is byte[] { Length: LengthHwid } bytes) - return bytes; - - var newId = new byte[LengthHwid]; - RandomNumberGenerator.Fill(newId); - Registry.SetValue( - @"HKEY_CURRENT_USER\SOFTWARE\Space Wizards\Robust", - "Hwid", - newId, - RegistryValueKind.Binary); - - return newId; - } - - return Array.Empty(); - } - } - -#if DEBUG - internal sealed class HwidCommand : LocalizedCommands - { - public override string Command => "hwid"; - - public override void Execute(IConsoleShell shell, string argStr, string[] args) - { - shell.WriteLine(Convert.ToBase64String(HWId.Calc(), Base64FormattingOptions.None)); - } - } -#endif -} diff --git a/Robust.Shared/Network/IHWId.cs b/Robust.Shared/Network/IHWId.cs new file mode 100644 index 00000000000..4bb60701f4a --- /dev/null +++ b/Robust.Shared/Network/IHWId.cs @@ -0,0 +1,67 @@ +using System; +using Robust.Shared.Console; +using Robust.Shared.IoC; +using Robust.Shared.Utility; + +namespace Robust.Shared.Network; + +/// +/// Fetches HWID (hardware ID) unique identifiers for the local system. +/// +internal interface IHWId +{ + /// + /// Gets the "legacy" HWID. + /// + /// + /// These are directly sent to servers and therefore susceptible to malicious spoofing. + /// They should not be relied on for the future. + /// + /// + /// An opaque value that gets sent to the server to identify this computer, + /// or an empty array if legacy HWID is not supported on this platform. + /// + byte[] GetLegacy(); + + /// + /// Gets the "modern" HWID. + /// + /// + /// An opaque value that gets sent to the auth server to identify this computer, + /// or null if modern HWID is not supported on this platform. + /// + byte[]? GetModern(); +} + +/// +/// Implementation of that does nothing, always returning an empty result. +/// +internal sealed class DummyHWid : IHWId +{ + public byte[] GetLegacy() + { + return []; + } + + public byte[] GetModern() + { + return []; + } +} + +#if DEBUG +internal sealed class HwidCommand : LocalizedCommands +{ + [Dependency] private readonly IHWId _hwId = default!; + + public override string Command => "hwid"; + + public override void Execute(IConsoleShell shell, string argStr, string[] args) + { + shell.WriteLine($""" + legacy: {Convert.ToBase64String(_hwId.GetLegacy(), Base64FormattingOptions.None)} + modern: {Base64Helpers.ToBase64Nullable(_hwId.GetModern())} + """); + } +} +#endif diff --git a/Robust.Shared/Network/NetManager.ClientConnect.cs b/Robust.Shared/Network/NetManager.ClientConnect.cs index 1a0e0cfdafc..c8d28b2fc58 100644 --- a/Robust.Shared/Network/NetManager.ClientConnect.cs +++ b/Robust.Shared/Network/NetManager.ClientConnect.cs @@ -132,7 +132,7 @@ private async Task CCDoHandshake(NetPeerData peer, NetConnection connection, str var hasPubKey = !string.IsNullOrEmpty(pubKey); var authenticate = !string.IsNullOrEmpty(authToken); - var hwId = ImmutableArray.Create(HWId.Calc()); + var hwId = ImmutableArray.Create(_hwId.GetLegacy()); var msgLogin = new MsgLoginStart { UserName = userNameRequest, @@ -190,8 +190,9 @@ private async Task CCDoHandshake(NetPeerData peer, NetConnection connection, str var authHashBytes = MakeAuthHash(sharedSecret, keyBytes); var authHash = Convert.ToBase64String(authHashBytes); + var modernHwid = _hwId.GetModern(); - var joinReq = new JoinRequest(authHash); + var joinReq = new JoinRequest(authHash, Base64Helpers.ToBase64Nullable(modernHwid)); var request = new HttpRequestMessage(HttpMethod.Post, authServer + "api/session/join"); request.Content = JsonContent.Create(joinReq); request.Headers.Authorization = new AuthenticationHeaderValue("SS14Auth", authToken); @@ -520,6 +521,6 @@ private Task AwaitData(NetConnection connection, } } - private sealed record JoinRequest(string Hash); + private sealed record JoinRequest(string Hash, string? Hwid); } } diff --git a/Robust.Shared/Network/NetManager.ServerAuth.cs b/Robust.Shared/Network/NetManager.ServerAuth.cs index 8296c446dda..4f7254d8376 100644 --- a/Robust.Shared/Network/NetManager.ServerAuth.cs +++ b/Robust.Shared/Network/NetManager.ServerAuth.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Immutable; using System.Linq; using System.Net; using System.Net.Http; @@ -156,7 +157,13 @@ private async void HandleHandshake(NetPeerData peer, NetConnection connection) userData = new NetUserData(userId, joinedRespJson.UserData.UserName) { PatronTier = joinedRespJson.UserData.PatronTier, - HWId = msgLogin.HWId + HWId = msgLogin.HWId, + ModernHWIds = + [ + ..joinedRespJson.ConnectionData!.Hwids + .Select(h => ImmutableArray.Create(Convert.FromBase64String(h))) + ], + Trust = joinedRespJson.ConnectionData!.Trust }; padSuccessMessage = false; type = LoginType.LoggedIn; @@ -359,8 +366,9 @@ private async void HandleApproval(NetIncomingMessage message) } // ReSharper disable ClassNeverInstantiated.Local - private sealed record HasJoinedResponse(bool IsValid, HasJoinedUserData? UserData); + private sealed record HasJoinedResponse(bool IsValid, HasJoinedUserData? UserData, HasJoinedConnectionData? ConnectionData); private sealed record HasJoinedUserData(string UserName, Guid UserId, string? PatronTier); + private sealed record HasJoinedConnectionData(string[] Hwids, float Trust); // ReSharper restore ClassNeverInstantiated.Local } } diff --git a/Robust.Shared/Network/NetManager.cs b/Robust.Shared/Network/NetManager.cs index 76ec09748e3..f418e0b1926 100644 --- a/Robust.Shared/Network/NetManager.cs +++ b/Robust.Shared/Network/NetManager.cs @@ -111,6 +111,7 @@ public sealed partial class NetManager : IClientNetManager, IServerNetManager, I [Dependency] private readonly ILogManager _logMan = default!; [Dependency] private readonly ProfManager _prof = default!; [Dependency] private readonly HttpClientHolder _http = default!; + [Dependency] private readonly IHWId _hwId = default!; /// /// Holds lookup table for NetMessage.Id -> NetMessage.Type diff --git a/Robust.Shared/Network/NetUserData.cs b/Robust.Shared/Network/NetUserData.cs index b5ee8c33c90..428ea3d96ae 100644 --- a/Robust.Shared/Network/NetUserData.cs +++ b/Robust.Shared/Network/NetUserData.cs @@ -20,6 +20,23 @@ public sealed record NetUserData public ImmutableArray HWId { get; init; } + /// + /// Unique identifiers for a client's computer, account and connection. + /// + /// + /// If any of these values match between two connections, + /// it means the auth server believes them to be the same user. + /// + public ImmutableArray> ModernHWIds { get; init; } + + /// + /// A trust value that reports the auth server's estimate of how likely this user is to be a malicious/suspicious account. + /// + /// + /// A value of 0.5 can be considered "neutral", 1 being "fully trusted". + /// + public float Trust { get; init; } + public NetUserData(NetUserId userId, string userName) { UserId = userId; diff --git a/Robust.Shared/Robust.Shared.csproj b/Robust.Shared/Robust.Shared.csproj index 333cbd5a936..9af5f625f37 100644 --- a/Robust.Shared/Robust.Shared.csproj +++ b/Robust.Shared/Robust.Shared.csproj @@ -50,6 +50,7 @@ RobustMappedStringSerializer.cs + diff --git a/Robust.Shared/Utility/Base64Helpers.cs b/Robust.Shared/Utility/Base64Helpers.cs index 21c17be6b42..6daeffaec56 100644 --- a/Robust.Shared/Utility/Base64Helpers.cs +++ b/Robust.Shared/Utility/Base64Helpers.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Text; namespace Robust.Shared.Utility @@ -50,5 +51,16 @@ public static byte[] ConvertFromBase64Url(string s) return Convert.FromBase64String(s); } + /// + /// Convert a byte array to base64. Returns null if the input byte array is null. + /// + [return: NotNullIfNotNull(nameof(data))] + public static string? ToBase64Nullable(byte[]? data) + { + if (data == null) + return null; + + return Convert.ToBase64String(data, Base64FormattingOptions.None); + } } } diff --git a/Robust.UnitTesting/RobustIntegrationTest.NetManager.cs b/Robust.UnitTesting/RobustIntegrationTest.NetManager.cs index 7553aa27a08..598a498c90f 100644 --- a/Robust.UnitTesting/RobustIntegrationTest.NetManager.cs +++ b/Robust.UnitTesting/RobustIntegrationTest.NetManager.cs @@ -132,7 +132,8 @@ async Task DoConnect() var sessionId = new NetUserId(userId); var userData = new NetUserData(sessionId, userName) { - HWId = ImmutableArray.Empty + HWId = ImmutableArray.Empty, + ModernHWIds = [] }; var args = await OnConnecting( From ff783bac2866a84266d4393dc7a9a70509e986cd Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Tue, 17 Sep 2024 02:40:07 +0200 Subject: [PATCH 2/6] Allow HWID to be disabled. Both client and server can now request HWID to be disabled. On the server via CVar, if disabled the client won't send it. On the client via env var, if disabled it won't be sent to the client. This involved moving legacy HWID to be sent in MsgEncryptionResponse instead of MsgLoginStart. This means the legacy HWID won't be available anymore if the connection isn't authenticated. --- Robust.Shared/CVars.cs | 12 +++++++++ Robust.Shared/Network/AuthManager.cs | 11 ++++++++ .../Handshake/MsgEncryptionRequest.cs | 3 +++ .../Handshake/MsgEncryptionResponse.cs | 5 ++++ .../Messages/Handshake/MsgLoginStart.cs | 5 ---- .../Network/NetManager.ClientConnect.cs | 17 ++++++++---- .../Network/NetManager.ServerAuth.cs | 27 +++++++++++++------ 7 files changed, 62 insertions(+), 18 deletions(-) diff --git a/Robust.Shared/CVars.cs b/Robust.Shared/CVars.cs index 6404bd1bfb4..b950b4414a5 100644 --- a/Robust.Shared/CVars.cs +++ b/Robust.Shared/CVars.cs @@ -394,6 +394,18 @@ protected CVars() public static readonly CVarDef NetEncryptionThreadChannelSize = CVarDef.Create("net.encryption_thread_channel_size", 16); + /// + /// Whether the server should request HWID system for client identification. + /// + /// + /// + /// Note that modern HWIDs are only available if the connection is authenticated. + /// + /// + public static readonly CVarDef NetHwid = + CVarDef.Create("net.hwid", true, CVar.SERVERONLY); + + /** * SUS */ diff --git a/Robust.Shared/Network/AuthManager.cs b/Robust.Shared/Network/AuthManager.cs index f3ed56e8f40..ea2250e891f 100644 --- a/Robust.Shared/Network/AuthManager.cs +++ b/Robust.Shared/Network/AuthManager.cs @@ -15,6 +15,11 @@ internal interface IAuthManager string? Token { get; set; } string? PubKey { get; set; } + /// + /// If true, the user allows HWID information to be provided to servers. + /// + bool AllowHwid { get; set; } + void LoadFromEnv(); } @@ -26,6 +31,7 @@ internal sealed class AuthManager : IAuthManager public string? Server { get; set; } = DefaultAuthServer; public string? Token { get; set; } public string? PubKey { get; set; } + public bool AllowHwid { get; set; } = true; public void LoadFromEnv() { @@ -49,6 +55,11 @@ public void LoadFromEnv() Token = token; } + if (TryGetVar("ROBUST_AUTH_ALLOW_HWID", out var allowHwid)) + { + AllowHwid = allowHwid.Trim() == "1"; + } + static bool TryGetVar(string var, [NotNullWhen(true)] out string? val) { val = Environment.GetEnvironmentVariable(var); diff --git a/Robust.Shared/Network/Messages/Handshake/MsgEncryptionRequest.cs b/Robust.Shared/Network/Messages/Handshake/MsgEncryptionRequest.cs index 1c281453b98..8d263136606 100644 --- a/Robust.Shared/Network/Messages/Handshake/MsgEncryptionRequest.cs +++ b/Robust.Shared/Network/Messages/Handshake/MsgEncryptionRequest.cs @@ -13,6 +13,7 @@ internal sealed class MsgEncryptionRequest : NetMessage public byte[] VerifyToken; public byte[] PublicKey; + public bool WantHwid; public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer) { @@ -20,6 +21,7 @@ public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer VerifyToken = buffer.ReadBytes(tokenLength); var keyLength = buffer.ReadVariableInt32(); PublicKey = buffer.ReadBytes(keyLength); + WantHwid = buffer.ReadBoolean(); } public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer) @@ -28,6 +30,7 @@ public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer buffer.Write(VerifyToken); buffer.WriteVariableInt32(PublicKey.Length); buffer.Write(PublicKey); + buffer.Write(WantHwid); } } } diff --git a/Robust.Shared/Network/Messages/Handshake/MsgEncryptionResponse.cs b/Robust.Shared/Network/Messages/Handshake/MsgEncryptionResponse.cs index 0442a16f0df..658a3990b16 100644 --- a/Robust.Shared/Network/Messages/Handshake/MsgEncryptionResponse.cs +++ b/Robust.Shared/Network/Messages/Handshake/MsgEncryptionResponse.cs @@ -14,12 +14,15 @@ internal sealed class MsgEncryptionResponse : NetMessage public Guid UserId; public byte[] SealedData; + public byte[] LegacyHwid; public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer) { UserId = buffer.ReadGuid(); var keyLength = buffer.ReadVariableInt32(); SealedData = buffer.ReadBytes(keyLength); + var legacyHwidLength = buffer.ReadVariableInt32(); + LegacyHwid = buffer.ReadBytes(legacyHwidLength); } public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer) @@ -27,6 +30,8 @@ public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer buffer.Write(UserId); buffer.WriteVariableInt32(SealedData.Length); buffer.Write(SealedData); + buffer.WriteVariableInt32(LegacyHwid.Length); + buffer.Write(LegacyHwid); } } } diff --git a/Robust.Shared/Network/Messages/Handshake/MsgLoginStart.cs b/Robust.Shared/Network/Messages/Handshake/MsgLoginStart.cs index 21e2f790e3d..ca8d39755fc 100644 --- a/Robust.Shared/Network/Messages/Handshake/MsgLoginStart.cs +++ b/Robust.Shared/Network/Messages/Handshake/MsgLoginStart.cs @@ -16,7 +16,6 @@ internal sealed class MsgLoginStart : NetMessage public override MsgGroups MsgGroup => MsgGroups.Core; public string UserName; - public ImmutableArray HWId; public bool CanAuth; public bool NeedPubKey; public bool Encrypt; @@ -24,8 +23,6 @@ internal sealed class MsgLoginStart : NetMessage public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer) { UserName = buffer.ReadString(); - var length = buffer.ReadByte(); - HWId = ImmutableArray.Create(buffer.ReadBytes(length)); CanAuth = buffer.ReadBoolean(); NeedPubKey = buffer.ReadBoolean(); Encrypt = buffer.ReadBoolean(); @@ -34,8 +31,6 @@ public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer) { buffer.Write(UserName); - buffer.Write((byte) HWId.Length); - buffer.Write(HWId.AsSpan()); buffer.Write(CanAuth); buffer.Write(NeedPubKey); buffer.Write(Encrypt); diff --git a/Robust.Shared/Network/NetManager.ClientConnect.cs b/Robust.Shared/Network/NetManager.ClientConnect.cs index c8d28b2fc58..7e8a4d3c9cc 100644 --- a/Robust.Shared/Network/NetManager.ClientConnect.cs +++ b/Robust.Shared/Network/NetManager.ClientConnect.cs @@ -132,13 +132,13 @@ private async Task CCDoHandshake(NetPeerData peer, NetConnection connection, str var hasPubKey = !string.IsNullOrEmpty(pubKey); var authenticate = !string.IsNullOrEmpty(authToken); - var hwId = ImmutableArray.Create(_hwId.GetLegacy()); + byte[] legacyHwid = []; + var msgLogin = new MsgLoginStart { UserName = userNameRequest, CanAuth = authenticate, NeedPubKey = !hasPubKey, - HWId = hwId, Encrypt = encrypt }; @@ -190,7 +190,13 @@ private async Task CCDoHandshake(NetPeerData peer, NetConnection connection, str var authHashBytes = MakeAuthHash(sharedSecret, keyBytes); var authHash = Convert.ToBase64String(authHashBytes); - var modernHwid = _hwId.GetModern(); + + byte[]? modernHwid = null; + if (_authManager.AllowHwid && encRequest.WantHwid) + { + legacyHwid = _hwId.GetLegacy(); + modernHwid = _hwId.GetModern(); + } var joinReq = new JoinRequest(authHash, Base64Helpers.ToBase64Nullable(modernHwid)); var request = new HttpRequestMessage(HttpMethod.Post, authServer + "api/session/join"); @@ -203,7 +209,8 @@ private async Task CCDoHandshake(NetPeerData peer, NetConnection connection, str var encryptionResponse = new MsgEncryptionResponse { SealedData = sealedData, - UserId = userId!.Value.UserId + UserId = userId!.Value.UserId, + LegacyHwid = legacyHwid }; var outEncRespMsg = peer.Peer.CreateMessage(); @@ -218,7 +225,7 @@ private async Task CCDoHandshake(NetPeerData peer, NetConnection connection, str var msgSuc = new MsgLoginSuccess(); msgSuc.ReadFromBuffer(response, _serializer); - var channel = new NetChannel(this, connection, msgSuc.UserData with { HWId = hwId }, msgSuc.Type); + var channel = new NetChannel(this, connection, msgSuc.UserData with { HWId = [..legacyHwid] }, msgSuc.Type); _channels.Add(connection, channel); peer.AddChannel(channel); diff --git a/Robust.Shared/Network/NetManager.ServerAuth.cs b/Robust.Shared/Network/NetManager.ServerAuth.cs index 4f7254d8376..1fc70d8bd60 100644 --- a/Robust.Shared/Network/NetManager.ServerAuth.cs +++ b/Robust.Shared/Network/NetManager.ServerAuth.cs @@ -80,10 +80,12 @@ private async void HandleHandshake(NetPeerData peer, NetConnection connection) var verifyToken = new byte[4]; RandomNumberGenerator.Fill(verifyToken); + var wantHwid = _config.GetCVar(CVars.NetHwid); var msgEncReq = new MsgEncryptionRequest { PublicKey = needPk ? CryptoPublicKey : Array.Empty(), - VerifyToken = verifyToken + VerifyToken = verifyToken, + WantHwid = wantHwid }; var outMsgEncReq = peer.Peer.CreateMessage(); @@ -154,15 +156,23 @@ private async void HandleHandshake(NetPeerData peer, NetConnection connection) $"Patron: {joinedRespJson.UserData.PatronTier}"); var userId = new NetUserId(joinedRespJson.UserData!.UserId); + ImmutableArray> modernHWIds = [ + ..joinedRespJson.ConnectionData!.Hwids + .Select(h => ImmutableArray.Create(Convert.FromBase64String(h))) + ]; + ImmutableArray legacyHwid = [..msgEncResponse.LegacyHwid]; + if (!wantHwid) + { + // If the client somehow sends a HWID even if we didn't ask for one, ignore it. + modernHWIds = []; + legacyHwid = []; + } + userData = new NetUserData(userId, joinedRespJson.UserData.UserName) { PatronTier = joinedRespJson.UserData.PatronTier, - HWId = msgLogin.HWId, - ModernHWIds = - [ - ..joinedRespJson.ConnectionData!.Hwids - .Select(h => ImmutableArray.Create(Convert.FromBase64String(h))) - ], + HWId = legacyHwid, + ModernHWIds = modernHWIds, Trust = joinedRespJson.ConnectionData!.Trust }; padSuccessMessage = false; @@ -206,7 +216,8 @@ private async void HandleHandshake(NetPeerData peer, NetConnection connection) userData = new NetUserData(userId, name) { - HWId = msgLogin.HWId + HWId = [], + ModernHWIds = [] }; } From 54d4beb36d3c0f5f8b365463e57bb4967fccf71d Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Tue, 17 Sep 2024 02:43:52 +0200 Subject: [PATCH 3/6] Fix tests --- Robust.UnitTesting/Server/RobustServerSimulation.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Robust.UnitTesting/Server/RobustServerSimulation.cs b/Robust.UnitTesting/Server/RobustServerSimulation.cs index 55232b2e510..441aa636e98 100644 --- a/Robust.UnitTesting/Server/RobustServerSimulation.cs +++ b/Robust.UnitTesting/Server/RobustServerSimulation.cs @@ -3,6 +3,7 @@ using System.Reflection; using JetBrains.Annotations; using Moq; +using Robust.Client.HWId; using Robust.Server; using Robust.Server.Configuration; using Robust.Server.Console; @@ -198,6 +199,7 @@ public ISimulation InitializeInstance() container.RegisterInstance(new Mock().Object); container.Register(); container.Register(); + container.Register(); var realReflection = new ServerReflectionManager(); realReflection.LoadAssemblies(new List(2) From 90a364ba42a1d576681b27817b57dd0695479580 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Tue, 17 Sep 2024 15:24:24 +0200 Subject: [PATCH 4/6] Fix another test --- Robust.UnitTesting/Shared/GameObjects/EntityState_Tests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Robust.UnitTesting/Shared/GameObjects/EntityState_Tests.cs b/Robust.UnitTesting/Shared/GameObjects/EntityState_Tests.cs index 73a02377708..4f99277b58b 100644 --- a/Robust.UnitTesting/Shared/GameObjects/EntityState_Tests.cs +++ b/Robust.UnitTesting/Shared/GameObjects/EntityState_Tests.cs @@ -36,6 +36,7 @@ public void ComponentChangedSerialized() container.Register(); container.Register(); container.Register(); + container.Register(); container.Register(); container.Register(); container.Register(); From 348f91383339bda9823ece05f0137226c41e53fa Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Wed, 18 Sep 2024 15:19:29 +0200 Subject: [PATCH 5/6] Review --- Robust.Client/ClientIoC.cs | 2 +- Robust.Client/HWId/BasicHWId.cs | 86 +++++++++++++++++++ Robust.Client/HWId/BasicHWid.cs | 85 ------------------ Robust.Server/ServerIoC.cs | 2 +- Robust.Shared/CVars.cs | 2 +- Robust.Shared/Network/IHWId.cs | 2 +- .../Network/NetManager.ServerAuth.cs | 2 +- .../Server/RobustServerSimulation.cs | 2 +- .../Shared/GameObjects/EntityState_Tests.cs | 2 +- 9 files changed, 93 insertions(+), 92 deletions(-) create mode 100644 Robust.Client/HWId/BasicHWId.cs delete mode 100644 Robust.Client/HWId/BasicHWid.cs diff --git a/Robust.Client/ClientIoC.cs b/Robust.Client/ClientIoC.cs index 596996637b5..e197f433fb7 100644 --- a/Robust.Client/ClientIoC.cs +++ b/Robust.Client/ClientIoC.cs @@ -159,7 +159,7 @@ public static void RegisterIoC(GameController.DisplayMode mode, IDependencyColle deps.Register(); deps.Register(); - deps.Register(); + deps.Register(); } } } diff --git a/Robust.Client/HWId/BasicHWId.cs b/Robust.Client/HWId/BasicHWId.cs new file mode 100644 index 00000000000..9dc4d7960a9 --- /dev/null +++ b/Robust.Client/HWId/BasicHWId.cs @@ -0,0 +1,86 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using Microsoft.Win32; +using Robust.Client.Utility; +using Robust.Shared.IoC; +using Robust.Shared.Network; + +namespace Robust.Client.HWId; + +internal sealed class BasicHWId : IHWId +{ + [Dependency] private readonly IGameControllerInternal _gameController = default!; + + public const int LengthHwid = 32; + + public byte[] GetLegacy() + { + if (OperatingSystem.IsWindows()) + return GetWindowsHWid("Hwid"); + + return []; + } + + public byte[] GetModern() + { + byte[] raw; + + if (OperatingSystem.IsWindows()) + raw = GetWindowsHWid("Hwid2"); + else + raw = GetFileHWid(); + + return [0, ..raw]; + } + + private static byte[] GetWindowsHWid(string keyName) + { + const string keyPath = @"HKEY_CURRENT_USER\SOFTWARE\Space Wizards\Robust"; + + var regKey = Registry.GetValue(keyPath, keyName, null); + if (regKey is byte[] { Length: LengthHwid } bytes) + return bytes; + + var newId = new byte[LengthHwid]; + RandomNumberGenerator.Fill(newId); + Registry.SetValue( + keyPath, + keyName, + newId, + RegistryValueKind.Binary); + + return newId; + } + + private byte[] GetFileHWid() + { + var path = UserDataDir.GetRootUserDataDir(_gameController); + var hwidPath = Path.Combine(path, ".hwid"); + + var value = ReadHWidFile(hwidPath); + if (value != null) + return value; + + value = RandomNumberGenerator.GetBytes(LengthHwid); + File.WriteAllBytes(hwidPath, value); + + return value; + } + + private static byte[]? ReadHWidFile(string path) + { + try + { + var value = File.ReadAllBytes(path); + if (value.Length == LengthHwid) + return value; + } + catch (FileNotFoundException) + { + // First time the file won't exist. + } + + return null; + } +} diff --git a/Robust.Client/HWId/BasicHWid.cs b/Robust.Client/HWId/BasicHWid.cs deleted file mode 100644 index 858756301a4..00000000000 --- a/Robust.Client/HWId/BasicHWid.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System; -using System.IO; -using System.Security.Cryptography; -using Microsoft.Win32; -using Robust.Client.Utility; -using Robust.Shared.IoC; -using Robust.Shared.Network; - -namespace Robust.Client.HWId -{ - internal sealed class BasicHWid : IHWId - { - [Dependency] private readonly IGameControllerInternal _gameController = default!; - - public const int LengthHwid = 32; - - public byte[] GetLegacy() - { - if (OperatingSystem.IsWindows()) - return GetWindowsHWid("Hwid"); - - return []; - } - - public byte[] GetModern() - { - byte[] raw; - - // if (OperatingSystem.IsWindows()) - // raw = GetWindowsHWid("Hwid2"); - // else - raw = GetFileHWid(); - - return [0, ..raw]; - } - - private static byte[] GetWindowsHWid(string keyName) - { - var regKey = Registry.GetValue(@"HKEY_CURRENT_USER\SOFTWARE\Space Wizards\Robust", keyName, null); - if (regKey is byte[] { Length: LengthHwid } bytes) - return bytes; - - var newId = new byte[LengthHwid]; - RandomNumberGenerator.Fill(newId); - Registry.SetValue( - @"HKEY_CURRENT_USER\SOFTWARE\Space Wizards\Robust", - keyName, - newId, - RegistryValueKind.Binary); - - return newId; - } - - private byte[] GetFileHWid() - { - var path = UserDataDir.GetRootUserDataDir(_gameController); - var hwidPath = Path.Combine(path, ".hwid"); - - var value = ReadHWidFile(hwidPath); - if (value != null) - return value; - - value = RandomNumberGenerator.GetBytes(LengthHwid); - File.WriteAllBytes(hwidPath, value); - - return value; - } - - private static byte[]? ReadHWidFile(string path) - { - try - { - var value = File.ReadAllBytes(path); - if (value.Length == LengthHwid) - return value; - } - catch (FileNotFoundException) - { - // First time the file won't exist. - } - - return null; - } - } -} diff --git a/Robust.Server/ServerIoC.cs b/Robust.Server/ServerIoC.cs index 49fd258e2d2..fafe7d3bd5b 100644 --- a/Robust.Server/ServerIoC.cs +++ b/Robust.Server/ServerIoC.cs @@ -97,7 +97,7 @@ internal static void RegisterIoC(IDependencyCollection deps) deps.Register(); deps.Register(); deps.Register(); - deps.Register(); + deps.Register(); } } } diff --git a/Robust.Shared/CVars.cs b/Robust.Shared/CVars.cs index b950b4414a5..01359245cef 100644 --- a/Robust.Shared/CVars.cs +++ b/Robust.Shared/CVars.cs @@ -402,7 +402,7 @@ protected CVars() /// Note that modern HWIDs are only available if the connection is authenticated. /// /// - public static readonly CVarDef NetHwid = + public static readonly CVarDef NetHWId = CVarDef.Create("net.hwid", true, CVar.SERVERONLY); diff --git a/Robust.Shared/Network/IHWId.cs b/Robust.Shared/Network/IHWId.cs index 4bb60701f4a..2d5f272032a 100644 --- a/Robust.Shared/Network/IHWId.cs +++ b/Robust.Shared/Network/IHWId.cs @@ -36,7 +36,7 @@ internal interface IHWId /// /// Implementation of that does nothing, always returning an empty result. /// -internal sealed class DummyHWid : IHWId +internal sealed class DummyHWId : IHWId { public byte[] GetLegacy() { diff --git a/Robust.Shared/Network/NetManager.ServerAuth.cs b/Robust.Shared/Network/NetManager.ServerAuth.cs index 1fc70d8bd60..a8eda20dc86 100644 --- a/Robust.Shared/Network/NetManager.ServerAuth.cs +++ b/Robust.Shared/Network/NetManager.ServerAuth.cs @@ -80,7 +80,7 @@ private async void HandleHandshake(NetPeerData peer, NetConnection connection) var verifyToken = new byte[4]; RandomNumberGenerator.Fill(verifyToken); - var wantHwid = _config.GetCVar(CVars.NetHwid); + var wantHwid = _config.GetCVar(CVars.NetHWId); var msgEncReq = new MsgEncryptionRequest { PublicKey = needPk ? CryptoPublicKey : Array.Empty(), diff --git a/Robust.UnitTesting/Server/RobustServerSimulation.cs b/Robust.UnitTesting/Server/RobustServerSimulation.cs index 441aa636e98..806ac4f3535 100644 --- a/Robust.UnitTesting/Server/RobustServerSimulation.cs +++ b/Robust.UnitTesting/Server/RobustServerSimulation.cs @@ -199,7 +199,7 @@ public ISimulation InitializeInstance() container.RegisterInstance(new Mock().Object); container.Register(); container.Register(); - container.Register(); + container.Register(); var realReflection = new ServerReflectionManager(); realReflection.LoadAssemblies(new List(2) diff --git a/Robust.UnitTesting/Shared/GameObjects/EntityState_Tests.cs b/Robust.UnitTesting/Shared/GameObjects/EntityState_Tests.cs index 4f99277b58b..e042a1aa59a 100644 --- a/Robust.UnitTesting/Shared/GameObjects/EntityState_Tests.cs +++ b/Robust.UnitTesting/Shared/GameObjects/EntityState_Tests.cs @@ -36,7 +36,7 @@ public void ComponentChangedSerialized() container.Register(); container.Register(); container.Register(); - container.Register(); + container.Register(); container.Register(); container.Register(); container.Register(); From 9d8f83d86f725c029938b5e6627a042a0e0e414b Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Thu, 19 Sep 2024 10:00:42 +0200 Subject: [PATCH 6/6] Thanks Rider --- Robust.Shared/Robust.Shared.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/Robust.Shared/Robust.Shared.csproj b/Robust.Shared/Robust.Shared.csproj index 9af5f625f37..333cbd5a936 100644 --- a/Robust.Shared/Robust.Shared.csproj +++ b/Robust.Shared/Robust.Shared.csproj @@ -50,7 +50,6 @@ RobustMappedStringSerializer.cs -