using Lidgren.Network; using NUnit.Framework; using Robust.Shared.Network; namespace Robust.Shared.Tests.Networking; public sealed class NetEncryptionDoSTest { private const ulong Magic = 0x13377777_77777777; [Test] [Description("A control test that ensures connecting in a test works.")] public void ConnectionWorks() { var (client, server) = MakeConnectionPair(); var message = client.CreateMessage(); message.WriteVariableUInt64(Magic); client.SendMessage(message, NetDeliveryMethod.ReliableOrdered); var packet = Receive(server); Assert.That(packet.ReadVariableUInt64(), Is.EqualTo(Magic)); server.Shutdown(null); } [Test] [Description("A control test that just ensures encryption works as other tests expect.")] public void EncryptionWorks() { var (clientEnc, serverEnc) = MakeEncryptionPair(); var (client, server) = MakeConnectionPair(); var message = client.CreateMessage(); message.WriteVariableUInt64(Magic); clientEnc.Encrypt(message); client.SendMessage(message, NetDeliveryMethod.ReliableOrdered); var packet = Receive(server); Assert.That(serverEnc.TryDecrypt(packet), Is.True); server.Shutdown(null); } [Test] [Description("Attempt to decrypt a packet that is using the wrong encryption keys, ensuring it doesn't throw.")] public void WrongKeyFailureDoesNotThrow() { var (clientEnc, serverEnc) = MakeEncryptionPair(disjointKey: true); var (client, server) = MakeConnectionPair(); var message = client.CreateMessage(); message.WriteVariableUInt64(Magic); clientEnc.Encrypt(message); client.SendMessage(message, NetDeliveryMethod.ReliableOrdered); var packet = Receive(server); Assert.That(serverEnc.TryDecrypt(packet), Is.False); server.Shutdown(null); } private static int[] _badMessages = [ 5, 1, 4, 16, 1024, ]; [Test] [Description("Attempt to decrypt a packet that is bogus, ensuring it doesn't throw.")] [TestCaseSource(nameof(_badMessages))] public void BadMessageDoesNotThrow(int badMessageLength) { var badMessage = new byte[badMessageLength]; System.Random.Shared.NextBytes(badMessage); var (_, serverEnc) = MakeEncryptionPair(disjointKey: true); var (client, server) = MakeConnectionPair(); var message = client.CreateMessage(); message.Write(badMessage); // Don't encrypt at all. client.SendMessage(message, NetDeliveryMethod.ReliableOrdered); var packet = Receive(server); Assert.That(packet.LengthBytes, Is.EqualTo(badMessageLength)); Assert.That(serverEnc.TryDecrypt(packet), Is.False); server.Shutdown(null); } // TODO: Generalize all this for other low level network tests. private (NetClient client, NetServer server) MakeConnectionPair() { const string id = "test"; var client = new NetClient(new NetPeerConfiguration(id)); var server = new NetServer( new NetPeerConfiguration(id)); client.Start(); // Lidgren has no facilities for mocking this nicely. // So we just use an actual socket. server.Start(); client.Connect("localhost", server.Port); var ready = false; while (!ready) { switch (server.WaitMessage(1000)) { case { MessageType: NetIncomingMessageType.StatusChanged } msg: { // hello there. var status = (NetConnectionStatus)msg.ReadByte(); if (status == NetConnectionStatus.Connected) ready = true; break; } } } return (client, server); } private NetIncomingMessage Receive(NetPeer peer) { NetIncomingMessage? found = null; while (found == null) { switch (peer.WaitMessage(1000)) { case { MessageType: NetIncomingMessageType.Data } msg: { found = msg; break; } } } return found; } private (NetEncryption client, NetEncryption server) MakeEncryptionPair(bool disjointKey = false) { var serverKey = new byte[32]; System.Random.Shared.NextBytes(serverKey.AsSpan()); var clientKey = (byte[])serverKey.Clone(); if (disjointKey) System.Random.Shared.NextBytes(clientKey.AsSpan()); return (new NetEncryption(clientKey, false), new NetEncryption(serverKey, true)); } }