Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions Robust.Shared.Tests/Robust.Shared.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\MSBuild\Robust.Engine.props"/>

<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="YamlDotNet" />
<PackageReference Include="Linguini.Bundle" />
<PackageReference Include="Pidgin" />

<PackageReference Include="JetBrains.Annotations"/>
<PackageReference Include="Microsoft.NET.Test.Sdk"/>
<PackageReference Include="Moq" />
<PackageReference Include="NUnit"/>
<PackageReference Include="NUnit3TestAdapter"/>
<PackageReference Include="NUnit.Analyzers"/>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Lidgren.Network\Lidgren.Network.csproj" />
<ProjectReference Include="..\NetSerializer\NetSerializer\NetSerializer.csproj" />
<ProjectReference Include="..\Robust.Shared.Maths.Tests\Robust.Shared.Maths.Tests.csproj" />
<ProjectReference Include="..\Robust.Shared.Maths\Robust.Shared.Maths.csproj"/>
<ProjectReference Include="..\Robust.Shared.Maths.Testing\Robust.Shared.Maths.Testing.csproj"/>
<ProjectReference Include="..\Robust.Shared\Robust.Shared.csproj"/>
</ItemGroup>

<Import Project="..\MSBuild\Robust.Properties.targets"/>
</Project>
25 changes: 25 additions & 0 deletions Robust.Shared/CVars.cs
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,31 @@ protected CVars()
public static readonly CVarDef<string> NetLidgrenAppIdentifier =
CVarDef.Create("net.lidgren_app_identifier", "RobustToolbox");

/// <summary>
/// Whether to disconnect clients that exceed the decryption failure threshold.
/// </summary>
public static readonly CVarDef<bool> NetDecryptFailKick =
CVarDef.Create("net.dos_fail_kick", true, CVar.SERVERONLY);

/// <summary>
/// Number of decryption failures from a single IP (or /64 subnet for IPv6) before logging a ban warning and optionally disconnecting.
/// </summary>
public static readonly CVarDef<int> NetDecryptFailBanThreshold =
CVarDef.Create("net.dos_fail_ban_threshold", 10, CVar.SERVERONLY);

/// <summary>
/// How often (in minutes) to clean up stale decryption failure records.
/// Records are only removed if they have not been seen for this many minutes.
/// </summary>
public static readonly CVarDef<int> NetDecryptFailCleanupInterval =
CVarDef.Create("net.dos_fail_cleanup_interval", 10, CVar.SERVERONLY);

/// <summary>
/// Maximum number of IPs tracked for decryption failures. Prevents memory exhaustion from botnet attacks.
/// </summary>
public static readonly CVarDef<int> NetDecryptFailMaxTracked =
CVarDef.Create("net.dos_fail_max_tracked", 10000, CVar.SERVERONLY);

/// <summary>
/// Add random fake network loss to all outgoing UDP network packets, as a ratio of how many packets to drop.
/// 0 = no packet loss, 1 = all packets dropped
Expand Down
18 changes: 12 additions & 6 deletions Robust.Shared/Network/NetEncryption.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Threading;
Expand Down Expand Up @@ -84,7 +84,12 @@ public unsafe void Encrypt(NetOutgoingMessage message)
ArrayPool<byte>.Shared.Return(returnPool);
}

public unsafe void Decrypt(NetIncomingMessage message)
/// <summary>
/// Attempts to decrypt an incoming network message, falliably.
/// </summary>
/// <param name="message">The message to decrypt in-place. This will be mutated with the decrypted results.</param>
/// <returns>Whether the operation was successful. If this fails, you likely want to drop the connection.</returns>
public unsafe bool TryDecrypt(NetIncomingMessage message)
{
var nonce = message.ReadUInt64();
var cipherText = message.Data.AsSpan(sizeof(ulong), message.LengthBytes - sizeof(ulong));
Expand All @@ -109,12 +114,13 @@ public unsafe void Decrypt(NetIncomingMessage message)
// key
_key);

message.Position = 0;
message.LengthBytes = messageLength;

ArrayPool<byte>.Shared.Return(buffer);

if (!result)
throw new SodiumException("Decryption operation failed!");
return false;

message.Position = 0;
message.LengthBytes = messageLength;
return true;
}
}
13 changes: 10 additions & 3 deletions Robust.Shared/Network/NetManager.ClientConnect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,20 @@ private async Task CCDoHandshake(

// Expect login success here.
response = await AwaitData(connection, cancel);
encryption?.Decrypt(response);

// Attempt to decrypt the message, only logging if we fail to decrypt and we actually have encryption.
if ((!encryption?.TryDecrypt(response)) ?? false)
{
const string msg = "Failed to decrypt login success.";
connection.Disconnect(msg);
throw new Exception(msg);
}
}

var msgSuc = new MsgLoginSuccess();
msgSuc.ReadFromBuffer(response, _serializer);

var channel = new NetChannel(this, connection, msgSuc.UserData with { HWId = [..legacyHwid] }, msgSuc.Type);
var channel = new NetChannel(this, connection, msgSuc.UserData with { HWId = [.. legacyHwid] }, msgSuc.Type);
_channels.Add(connection, channel);
peer.AddChannel(channel);

Expand Down Expand Up @@ -440,7 +447,7 @@ private Task<NetIncomingMessage> AwaitData(
if (ipAddress.AddressFamily == AddressFamily.InterNetwork
|| ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
{
return new[] {ipAddress};
return new[] { ipAddress };
}

throw new ArgumentException("This method will not currently resolve other than IPv4 or IPv6 addresses");
Expand Down
Loading
Loading