From cbfbd8f1693243a7bb41fc995187c05b83ac41e4 Mon Sep 17 00:00:00 2001 From: Vitalii Kotenko Date: Tue, 1 Oct 2024 01:04:34 +0300 Subject: [PATCH] switch to transparent streams if no private key is provided --- .../Encryption/SearchServerPrivateKey.cs | 9 +++ .../Network/IO/Channels/IMinecraftChannel.cs | 16 +++-- src/API/Network/Protocol/ProtocolVersion.cs | 2 +- .../Network/IO/Channels/SimpleChannel.cs | 66 ++++++++++++++++--- src/Platform/Platform.cs | 2 +- src/Platform/Void.Proxy.csproj | 4 ++ src/Plugins/ExamplePlugin/ExamplePlugin.cs | 1 + .../ExamplePlugin/Services/DebugService.cs | 16 +++++ src/Plugins/ModsSupport/Forge/ForgeMarker.cs | 2 +- .../Services/ChannelCoordinatorService.cs | 16 ++++- .../Services/CompressionService.cs | 4 +- .../Services/EncryptionService.cs | 30 ++++++--- .../Services/ChannelCoordinatorService.cs | 16 ++++- .../Services/CompressionService.cs | 4 +- .../Services/EncryptionService.cs | 30 ++++++--- .../Services/ChannelCoordinatorService.cs | 16 ++++- .../Services/CompressionService.cs | 4 +- .../Services/EncryptionService.cs | 30 ++++++--- 18 files changed, 210 insertions(+), 58 deletions(-) create mode 100644 src/API/Events/Encryption/SearchServerPrivateKey.cs create mode 100644 src/Plugins/ExamplePlugin/Services/DebugService.cs diff --git a/src/API/Events/Encryption/SearchServerPrivateKey.cs b/src/API/Events/Encryption/SearchServerPrivateKey.cs new file mode 100644 index 0000000..1f463ab --- /dev/null +++ b/src/API/Events/Encryption/SearchServerPrivateKey.cs @@ -0,0 +1,9 @@ +using Void.Proxy.API.Servers; + +namespace Void.Proxy.API.Events.Encryption; + +public class SearchServerPrivateKey : IEventWithResult +{ + public required IServer Server { get; init; } + public byte[]? Result { get; set; } +} \ No newline at end of file diff --git a/src/API/Network/IO/Channels/IMinecraftChannel.cs b/src/API/Network/IO/Channels/IMinecraftChannel.cs index 5be38a3..ff68025 100644 --- a/src/API/Network/IO/Channels/IMinecraftChannel.cs +++ b/src/API/Network/IO/Channels/IMinecraftChannel.cs @@ -1,4 +1,5 @@ -using Void.Proxy.API.Network.IO.Messages; +using System.Diagnostics.CodeAnalysis; +using Void.Proxy.API.Network.IO.Messages; using Void.Proxy.API.Network.IO.Streams; namespace Void.Proxy.API.Network.IO.Channels; @@ -14,11 +15,14 @@ public interface IMinecraftChannel : IDisposable, IAsyncDisposable public bool IsPaused { get; } public bool IsRedirectionSupported { get; } - public void Add() where T : IMinecraftStream, new(); - public void Add(T stream) where T : IMinecraftStream; - public void AddBefore() where TBefore : IMinecraftStream where TValue : IMinecraftStream, new(); - public void AddBefore(TValue stream) where TBefore : IMinecraftStream where TValue : IMinecraftStream; - public T Get() where T : IMinecraftStreamBase; + public void Add() where T : class, IMinecraftStream, new(); + public void Add(T stream) where T : class, IMinecraftStream; + public void AddBefore() where TBefore : class, IMinecraftStream where TValue : class, IMinecraftStream, new(); + public void AddBefore(TValue stream) where TBefore : class, IMinecraftStream where TValue : class, IMinecraftStream; + public void Remove() where T : class, IMinecraftStream, new(); + public void Remove(T stream) where T : class, IMinecraftStream; + public T Get() where T : class, IMinecraftStreamBase; + public bool Search([MaybeNullWhen(false)] out T result) where T : class, IMinecraftStreamBase; public void PrependBuffer(Memory memory); public ValueTask ReadMessageAsync(CancellationToken cancellationToken = default); public ValueTask WriteMessageAsync(IMinecraftMessage message, CancellationToken cancellationToken = default); diff --git a/src/API/Network/Protocol/ProtocolVersion.cs b/src/API/Network/Protocol/ProtocolVersion.cs index 4f34bba..5be4a14 100644 --- a/src/API/Network/Protocol/ProtocolVersion.cs +++ b/src/API/Network/Protocol/ProtocolVersion.cs @@ -53,7 +53,7 @@ public ProtocolVersion(int version, params string[] names) Names = names; if (!Mapping.TryAdd(version, this)) - throw new InvalidOperationException($"ProtocolVersion {version} already registered, use Get() instead"); + throw new InvalidOperationException($"ProtocolVersion {version} already registered, use Search() instead"); } public static ProtocolVersion Latest => Mapping.MaxBy(kv => kv.Key).Value; diff --git a/src/Common/Network/IO/Channels/SimpleChannel.cs b/src/Common/Network/IO/Channels/SimpleChannel.cs index e649fa2..82e8e40 100644 --- a/src/Common/Network/IO/Channels/SimpleChannel.cs +++ b/src/Common/Network/IO/Channels/SimpleChannel.cs @@ -1,4 +1,5 @@ -using Void.Proxy.API.Network.IO.Channels; +using System.Diagnostics.CodeAnalysis; +using Void.Proxy.API.Network.IO.Channels; using Void.Proxy.API.Network.IO.Messages; using Void.Proxy.API.Network.IO.Streams; using Void.Proxy.API.Network.IO.Streams.Manual.Binary; @@ -22,23 +23,23 @@ public class SimpleChannel(IMinecraftStreamBase head) : IMinecraftChannel public bool IsPaused => _pause is { Task.IsCompleted: false }; public bool IsRedirectionSupported => false; - public void Add() where T : IMinecraftStream, new() + public void Add() where T : class, IMinecraftStream, new() { Add(new T()); } - public void Add(T stream) where T : IMinecraftStream + public void Add(T stream) where T : class, IMinecraftStream { stream.BaseStream = head; head = stream; } - public void AddBefore() where TBefore : IMinecraftStream where TValue : IMinecraftStream, new() + public void AddBefore() where TBefore : class, IMinecraftStream where TValue : class, IMinecraftStream, new() { AddBefore(new TValue()); } - public void AddBefore(TValue stream) where TBefore : IMinecraftStream where TValue : IMinecraftStream + public void AddBefore(TValue stream) where TBefore : class, IMinecraftStream where TValue : class, IMinecraftStream { var before = Get(); var beforeBaseStream = before.BaseStream; @@ -47,9 +48,51 @@ public void AddBefore(TValue stream) where TBefore : IMinecraft before.BaseStream = stream; } - public T Get() where T : IMinecraftStreamBase + public void Remove() where T : class, IMinecraftStream, new() { - return Get(head); + Remove(Get()); + } + + public void Remove(T value) where T : class, IMinecraftStream + { + if (head != value) + { + var previousStreamBase = head; + var currentStreamBase = previousStreamBase; + + while (currentStreamBase is not null) + { + if (currentStreamBase is not IMinecraftStream currentStream || previousStreamBase is not IMinecraftStream previousStream) + break; + + if (currentStream == value) + previousStream.BaseStream = currentStream.BaseStream; + + previousStreamBase = currentStream; + currentStreamBase = currentStream.BaseStream; + } + } + else if (value.BaseStream is not null) + { + head = value.BaseStream; + } + else + { + throw new InvalidOperationException($"Cannot remove {value} stream with unset BaseStream"); + } + } + + public T Get() where T : class, IMinecraftStreamBase + { + if (Search(out var stream)) + return stream; + + throw new InvalidOperationException($"{typeof(T)} not found in channel"); + } + + public bool Search([MaybeNullWhen(false)] out T result) where T : class, IMinecraftStreamBase + { + return Get(head, out result); } public void PrependBuffer(Memory memory) @@ -131,19 +174,22 @@ public void Flush() head.Flush(); } - private T Get(IMinecraftStreamBase? baseStream) where T : IMinecraftStreamBase + private bool Get(IMinecraftStreamBase? baseStream, [MaybeNullWhen(false)] out T result) where T : class, IMinecraftStreamBase { var current = baseStream ?? head; + while (true) switch (current) { case T found: - return found; + result = found; + return true; case IMinecraftStream stream: current = stream.BaseStream; break; default: - throw new InvalidOperationException($"{typeof(T)} not found in channel"); + result = null; + return false; } } } \ No newline at end of file diff --git a/src/Platform/Platform.cs b/src/Platform/Platform.cs index 45e25c8..fce3a8c 100644 --- a/src/Platform/Platform.cs +++ b/src/Platform/Platform.cs @@ -22,7 +22,7 @@ public class Platform(ILogger logger, ISettings settings, IPluginServi public async Task StartAsync(CancellationToken cancellationToken) { - LoggingLevelSwitch.MinimumLevel = LogEventLevel.Debug; + LoggingLevelSwitch.MinimumLevel = LogEventLevel.Verbose; logger.LogInformation("Starting Void proxy"); var startTime = Stopwatch.GetTimestamp(); diff --git a/src/Platform/Void.Proxy.csproj b/src/Platform/Void.Proxy.csproj index 27e91fb..10d1fed 100644 --- a/src/Platform/Void.Proxy.csproj +++ b/src/Platform/Void.Proxy.csproj @@ -30,6 +30,10 @@ false false + + false + false + false false diff --git a/src/Plugins/ExamplePlugin/ExamplePlugin.cs b/src/Plugins/ExamplePlugin/ExamplePlugin.cs index e4a0921..2b78834 100644 --- a/src/Plugins/ExamplePlugin/ExamplePlugin.cs +++ b/src/Plugins/ExamplePlugin/ExamplePlugin.cs @@ -31,5 +31,6 @@ public void OnPluginLoad(PluginLoadEvent @event) return; events.RegisterListeners(); + events.RegisterListeners(); } } \ No newline at end of file diff --git a/src/Plugins/ExamplePlugin/Services/DebugService.cs b/src/Plugins/ExamplePlugin/Services/DebugService.cs new file mode 100644 index 0000000..db28c3a --- /dev/null +++ b/src/Plugins/ExamplePlugin/Services/DebugService.cs @@ -0,0 +1,16 @@ +using Void.Proxy.API.Events; +using Void.Proxy.API.Events.Encryption; + +namespace Void.Proxy.Plugins.ExamplePlugin.Services; + +public class DebugService : IEventListener +{ + [Subscribe] + public void OnSearchServerPrivateKey(SearchServerPrivateKey @event) + { + if (@event.Server.Name is not "lobby") + return; + + @event.Result = Convert.FromHexString("30820276020100300d06092a864886f70d0101010500048202603082025c02010002818100eb0ab790f61d4d1ee2b39698223d505b4cbda41143e7027688382c56e7aeedda2eb2c38c5ae7287f45c48ed6a7b4c54191d326c64504f27cc761e04b58192f486b2e6fbef903c470ae7b4583c81632558b50dcdd97759f174e23d090f34d7ad116406b11a5fccda4e402fbcfc36a5783f400872f1e9bb53104ba83633d360bd3020301000102818019d449abf27ff1d3ad12134090b2b03bf848f6d8b6df9213b89083bee123061c6df95327ff6d5bb3f0d4d2e59ff46ba0f307834152a0628d77d3b7b44ff02493d8d0fe8111802e6fd087badd7295d39ee6f819c4c1b890ed3adfc8bad7e4108c3196fc3ddff82872a49a823fc22b03452b274ac0783dc06fed0687791ce07451024100ecc10becd82f0547077fef1dc2232d3c29f4d57ef7e977ffd7bd7b131563db70bbc530e61f407c3f001f875a19ad6d346efac7af909896cd480274833e98dbff024100fe2609d121fec3920d15ce131443b3880c70d513da6f68a527982edde3a382c9d91fcf4a045a56d7f4573b81853d9c5123c0caf3d32d1c0e1758235cad28a02d024025d1ccbadfe9daf8f6bcbc10cfe358a584ba44a48cabb1ec9fa4f8151b54a14847e67f223399d47f27e0e1794622cb10162e5f59af4a80c4781d544966e57a3702406b654b94b256e3f1ddd1af0964f0cef6d8bafd6fac4893e1e67f6a9e9d49454562990c916c91784d3d957731de4a10ba40ef8153d393265dd6038abde8f657e9024100bf1940ff91189f4d15cbbe9d243cdd8b29074189906a98e1fa7b82fbe078e00dab30ea2d82ea6cb99855ace25296d6f75b87b392bfbfaec8a7617d16d78b93df"); + } +} \ No newline at end of file diff --git a/src/Plugins/ModsSupport/Forge/ForgeMarker.cs b/src/Plugins/ModsSupport/Forge/ForgeMarker.cs index 1b6309b..b727f21 100644 --- a/src/Plugins/ModsSupport/Forge/ForgeMarker.cs +++ b/src/Plugins/ModsSupport/Forge/ForgeMarker.cs @@ -9,7 +9,7 @@ // else if (addressParts.Length > 1) // Console.WriteLine($"Player {link.Player} had extra marker(s) {string.Join(", ", addressParts[1..])} in handshake, ignoring"); // -// link.SetProtocolVersion(ProtocolVersion.Get(packet.ProtocolVersion)); +// link.SetProtocolVersion(ProtocolVersion.Search(packet.ProtocolVersion)); // link.SwitchState(packet.NextState); // link.SaveHandshake(packet); diff --git a/src/Plugins/ProtocolSupport/Java/v1_13_to_1_20_1/Services/ChannelCoordinatorService.cs b/src/Plugins/ProtocolSupport/Java/v1_13_to_1_20_1/Services/ChannelCoordinatorService.cs index 617acc9..b8aba09 100644 --- a/src/Plugins/ProtocolSupport/Java/v1_13_to_1_20_1/Services/ChannelCoordinatorService.cs +++ b/src/Plugins/ProtocolSupport/Java/v1_13_to_1_20_1/Services/ChannelCoordinatorService.cs @@ -1,5 +1,6 @@ using Void.Proxy.API.Events; using Void.Proxy.API.Events.Network; +using Void.Proxy.Common.Network.IO.Streams.Packet; using Void.Proxy.Common.Services; using Void.Proxy.Plugins.ProtocolSupport.Java.v1_13_to_1_20_1.Packets.Clientbound; using Void.Proxy.Plugins.ProtocolSupport.Java.v1_13_to_1_20_1.Packets.Serverbound; @@ -8,8 +9,8 @@ namespace Void.Proxy.Plugins.ProtocolSupport.Java.v1_13_to_1_20_1.Services; public class ChannelCoordinatorService : IPluginService { - [Subscribe(PostOrder.Last)] - public void OnMessageSent(MessageSentEvent @event, CancellationToken cancellationToken) + [Subscribe] + public void OnMessageSent(MessageSentEvent @event) { switch (@event.Message) { @@ -34,4 +35,15 @@ public void OnMessageSent(MessageSentEvent @event, CancellationToken cancellatio break; } } + + [Subscribe(PostOrder.Last)] + public void OnMessageSentLast(MessageSentEvent @event) + { + if (@event.Message is not EncryptionResponsePacket) + return; + + // if encryption forced channel to remove packet stream, resume reading with what has left + if (!@event.Link.PlayerChannel.Search(out _)) + @event.Link.PlayerChannel.Resume(); + } } \ No newline at end of file diff --git a/src/Plugins/ProtocolSupport/Java/v1_13_to_1_20_1/Services/CompressionService.cs b/src/Plugins/ProtocolSupport/Java/v1_13_to_1_20_1/Services/CompressionService.cs index 098a108..1ee98d7 100644 --- a/src/Plugins/ProtocolSupport/Java/v1_13_to_1_20_1/Services/CompressionService.cs +++ b/src/Plugins/ProtocolSupport/Java/v1_13_to_1_20_1/Services/CompressionService.cs @@ -23,7 +23,7 @@ public void OnMessageReceived(MessageReceivedEvent @event, CancellationToken can zlibStream.CompressionThreshold = setCompression.Threshold; } - [Subscribe] + [Subscribe(PostOrder.First)] public void OnMessageSent(MessageSentEvent @event, CancellationToken cancellationToken) { if (@event.Message is not SetCompressionPacket setCompression) @@ -34,7 +34,5 @@ public void OnMessageSent(MessageSentEvent @event, CancellationToken cancellatio var zlibStream = @event.Link.PlayerChannel.Get(); zlibStream.CompressionThreshold = setCompression.Threshold; - - @event.Link.PlayerChannel.Resume(); } } \ No newline at end of file diff --git a/src/Plugins/ProtocolSupport/Java/v1_13_to_1_20_1/Services/EncryptionService.cs b/src/Plugins/ProtocolSupport/Java/v1_13_to_1_20_1/Services/EncryptionService.cs index 4026a2d..8a30eaa 100644 --- a/src/Plugins/ProtocolSupport/Java/v1_13_to_1_20_1/Services/EncryptionService.cs +++ b/src/Plugins/ProtocolSupport/Java/v1_13_to_1_20_1/Services/EncryptionService.cs @@ -1,27 +1,41 @@ using System.Security.Cryptography; using Void.Proxy.API.Events; +using Void.Proxy.API.Events.Encryption; using Void.Proxy.API.Events.Network; +using Void.Proxy.API.Events.Services; using Void.Proxy.Common.Network.IO.Streams.Encryption; using Void.Proxy.Common.Network.IO.Streams.Packet; +using Void.Proxy.Common.Network.IO.Streams.Transparent; using Void.Proxy.Common.Services; using Void.Proxy.Plugins.ProtocolSupport.Java.v1_13_to_1_20_1.Packets.Serverbound; namespace Void.Proxy.Plugins.ProtocolSupport.Java.v1_13_to_1_20_1.Services; -public class EncryptionService : IPluginService +public class EncryptionService(IEventService events) : IPluginService { - private static readonly byte[] PrivateKey = Convert.FromHexString("30820277020100300d06092a864886f70d0101010500048202613082025d02010002818100e9675bb7efd2f2b75ee5f1ea07399fe38239a4196506fd7f6489961d6de770e7580c69e84fb040fe19721f06e7d8dc634280f0dbb71b79292f783eb2b6c409c5438197c3bd869b17264bb5aa448614fefccd3d841b278f05c202429d075deb2f5d1d2d24d80363f69444974358f23912951b9d3c2a4a86a0e5c13a84a997a04b02030100010281802e0fb94088023be72741bee79e0c67bae8d8c24346b645f1cd9fff71885e7be013f6c331d704241761632daf59b2e8ef67d0f5778edfcb9deea1ced1cb12ce1070f7d5226093bf9f2c7587a38239837849ed4728bbbba8da60b297b0397d6cafae8f427775aa8e22a42e04c7c8208da79a8f940ff2d8c31a22e723c56e136ce1024100ed595c08a640f5a71573ee45c79247bca08d4cb478d658aab90c5133b9bb0187b0e8801066adc6bb8f1a9903ccf548e7c746883443e96d3fde505116ee9466eb024100fbbea1f5dd1b2f63f867597a66fb26b85a0faea928525e5906635d3366e14ef4d02e851015e34ce78652e0a6a22e0cdfe92c6dbebc1da9daae2086d86e44142102401780268db0b073e2444c834623798762d4dec8be81cc6f61100b792acef40635c23d7318aca1fe3069fdef32a223934167c8c309b1c3b60e81db9ffbce49a15b024100a685c02e99567d2f9cc6086b2e299dc03e5ab7474fd3c4731105b345e81ccb94a70cce9a085075b384a7d7d081e102452ec163cad236b0ff654540cd738af6e1024100abd64d321c66d4154a4d3ff1dfb265609d3ec650610bad4f00e461357fadf9542058ecc8458f0acd2744020f96ef1c5f21ea8c82028d837ac2e4a61f2ce35689"); - - [Subscribe] - public void OnMessageSent(MessageSentEvent @event, CancellationToken cancellationToken) + [Subscribe(PostOrder.First)] + public async ValueTask OnMessageSent(MessageSentEvent @event, CancellationToken cancellationToken) { if (@event.Message is not EncryptionResponsePacket encryptionResponse) return; - var secret = Decrypt(PrivateKey, encryptionResponse.SharedSecret); + var privateKey = await events.ThrowWithResultAsync(new SearchServerPrivateKey { Server = @event.Link.Server }, cancellationToken); + + if (privateKey is null) + { + @event.Link.ServerChannel.Remove(); + @event.Link.PlayerChannel.Remove(); + + @event.Link.ServerChannel.Add(); + @event.Link.PlayerChannel.Add(); + } + else + { + var secret = Decrypt(privateKey, encryptionResponse.SharedSecret); - @event.Link.ServerChannel.AddBefore(new AesCfb8BufferedStream(secret)); - @event.Link.PlayerChannel.AddBefore(new AesCfb8BufferedStream(secret)); + @event.Link.ServerChannel.AddBefore(new AesCfb8BufferedStream(secret)); + @event.Link.PlayerChannel.AddBefore(new AesCfb8BufferedStream(secret)); + } } private static byte[] Decrypt(byte[] key, byte[] data) diff --git a/src/Plugins/ProtocolSupport/Java/v1_20_2_to_latest/Services/ChannelCoordinatorService.cs b/src/Plugins/ProtocolSupport/Java/v1_20_2_to_latest/Services/ChannelCoordinatorService.cs index 0b65110..f89c4c2 100644 --- a/src/Plugins/ProtocolSupport/Java/v1_20_2_to_latest/Services/ChannelCoordinatorService.cs +++ b/src/Plugins/ProtocolSupport/Java/v1_20_2_to_latest/Services/ChannelCoordinatorService.cs @@ -1,5 +1,6 @@ using Void.Proxy.API.Events; using Void.Proxy.API.Events.Network; +using Void.Proxy.Common.Network.IO.Streams.Packet; using Void.Proxy.Common.Services; using Void.Proxy.Plugins.ProtocolSupport.Java.v1_20_2_to_latest.Packets.Clientbound; using Void.Proxy.Plugins.ProtocolSupport.Java.v1_20_2_to_latest.Packets.Serverbound; @@ -8,8 +9,8 @@ namespace Void.Proxy.Plugins.ProtocolSupport.Java.v1_20_2_to_latest.Services; public class ChannelCoordinatorService : IPluginService { - [Subscribe(PostOrder.Last)] - public void OnMessageSent(MessageSentEvent @event, CancellationToken cancellationToken) + [Subscribe] + public void OnMessageSent(MessageSentEvent @event) { switch (@event.Message) { @@ -34,4 +35,15 @@ public void OnMessageSent(MessageSentEvent @event, CancellationToken cancellatio break; } } + + [Subscribe(PostOrder.Last)] + public void OnMessageSentLast(MessageSentEvent @event) + { + if (@event.Message is not EncryptionResponsePacket) + return; + + // if encryption forced channel to remove packet stream, resume reading with what has left + if (!@event.Link.PlayerChannel.Search(out _)) + @event.Link.PlayerChannel.Resume(); + } } \ No newline at end of file diff --git a/src/Plugins/ProtocolSupport/Java/v1_20_2_to_latest/Services/CompressionService.cs b/src/Plugins/ProtocolSupport/Java/v1_20_2_to_latest/Services/CompressionService.cs index 379a544..389bea8 100644 --- a/src/Plugins/ProtocolSupport/Java/v1_20_2_to_latest/Services/CompressionService.cs +++ b/src/Plugins/ProtocolSupport/Java/v1_20_2_to_latest/Services/CompressionService.cs @@ -23,7 +23,7 @@ public void OnMessageReceived(MessageReceivedEvent @event, CancellationToken can zlibStream.CompressionThreshold = setCompression.Threshold; } - [Subscribe] + [Subscribe(PostOrder.First)] public void OnMessageSent(MessageSentEvent @event, CancellationToken cancellationToken) { if (@event.Message is not SetCompressionPacket setCompression) @@ -34,7 +34,5 @@ public void OnMessageSent(MessageSentEvent @event, CancellationToken cancellatio var zlibStream = @event.Link.PlayerChannel.Get(); zlibStream.CompressionThreshold = setCompression.Threshold; - - @event.Link.PlayerChannel.Resume(); } } \ No newline at end of file diff --git a/src/Plugins/ProtocolSupport/Java/v1_20_2_to_latest/Services/EncryptionService.cs b/src/Plugins/ProtocolSupport/Java/v1_20_2_to_latest/Services/EncryptionService.cs index 4d2f6fb..5647aad 100644 --- a/src/Plugins/ProtocolSupport/Java/v1_20_2_to_latest/Services/EncryptionService.cs +++ b/src/Plugins/ProtocolSupport/Java/v1_20_2_to_latest/Services/EncryptionService.cs @@ -1,27 +1,41 @@ using System.Security.Cryptography; using Void.Proxy.API.Events; +using Void.Proxy.API.Events.Encryption; using Void.Proxy.API.Events.Network; +using Void.Proxy.API.Events.Services; using Void.Proxy.Common.Network.IO.Streams.Encryption; using Void.Proxy.Common.Network.IO.Streams.Packet; +using Void.Proxy.Common.Network.IO.Streams.Transparent; using Void.Proxy.Common.Services; using Void.Proxy.Plugins.ProtocolSupport.Java.v1_20_2_to_latest.Packets.Serverbound; namespace Void.Proxy.Plugins.ProtocolSupport.Java.v1_20_2_to_latest.Services; -public class EncryptionService : IPluginService +public class EncryptionService(IEventService events) : IPluginService { - private static readonly byte[] PrivateKey = Convert.FromHexString("30820277020100300d06092a864886f70d0101010500048202613082025d02010002818100e9675bb7efd2f2b75ee5f1ea07399fe38239a4196506fd7f6489961d6de770e7580c69e84fb040fe19721f06e7d8dc634280f0dbb71b79292f783eb2b6c409c5438197c3bd869b17264bb5aa448614fefccd3d841b278f05c202429d075deb2f5d1d2d24d80363f69444974358f23912951b9d3c2a4a86a0e5c13a84a997a04b02030100010281802e0fb94088023be72741bee79e0c67bae8d8c24346b645f1cd9fff71885e7be013f6c331d704241761632daf59b2e8ef67d0f5778edfcb9deea1ced1cb12ce1070f7d5226093bf9f2c7587a38239837849ed4728bbbba8da60b297b0397d6cafae8f427775aa8e22a42e04c7c8208da79a8f940ff2d8c31a22e723c56e136ce1024100ed595c08a640f5a71573ee45c79247bca08d4cb478d658aab90c5133b9bb0187b0e8801066adc6bb8f1a9903ccf548e7c746883443e96d3fde505116ee9466eb024100fbbea1f5dd1b2f63f867597a66fb26b85a0faea928525e5906635d3366e14ef4d02e851015e34ce78652e0a6a22e0cdfe92c6dbebc1da9daae2086d86e44142102401780268db0b073e2444c834623798762d4dec8be81cc6f61100b792acef40635c23d7318aca1fe3069fdef32a223934167c8c309b1c3b60e81db9ffbce49a15b024100a685c02e99567d2f9cc6086b2e299dc03e5ab7474fd3c4731105b345e81ccb94a70cce9a085075b384a7d7d081e102452ec163cad236b0ff654540cd738af6e1024100abd64d321c66d4154a4d3ff1dfb265609d3ec650610bad4f00e461357fadf9542058ecc8458f0acd2744020f96ef1c5f21ea8c82028d837ac2e4a61f2ce35689"); - - [Subscribe] - public void OnMessageSent(MessageSentEvent @event, CancellationToken cancellationToken) + [Subscribe(PostOrder.First)] + public async ValueTask OnMessageSent(MessageSentEvent @event, CancellationToken cancellationToken) { if (@event.Message is not EncryptionResponsePacket encryptionResponse) return; - var secret = Decrypt(PrivateKey, encryptionResponse.SharedSecret); + var privateKey = await events.ThrowWithResultAsync(new SearchServerPrivateKey { Server = @event.Link.Server }, cancellationToken); + + if (privateKey is null) + { + @event.Link.ServerChannel.Remove(); + @event.Link.PlayerChannel.Remove(); + + @event.Link.ServerChannel.Add(); + @event.Link.PlayerChannel.Add(); + } + else + { + var secret = Decrypt(privateKey, encryptionResponse.SharedSecret); - @event.Link.ServerChannel.AddBefore(new AesCfb8BufferedStream(secret)); - @event.Link.PlayerChannel.AddBefore(new AesCfb8BufferedStream(secret)); + @event.Link.ServerChannel.AddBefore(new AesCfb8BufferedStream(secret)); + @event.Link.PlayerChannel.AddBefore(new AesCfb8BufferedStream(secret)); + } } private static byte[] Decrypt(byte[] key, byte[] data) diff --git a/src/Plugins/ProtocolSupport/Java/v1_7_2_to_1_12_2/Services/ChannelCoordinatorService.cs b/src/Plugins/ProtocolSupport/Java/v1_7_2_to_1_12_2/Services/ChannelCoordinatorService.cs index 3ad2362..deb422c 100644 --- a/src/Plugins/ProtocolSupport/Java/v1_7_2_to_1_12_2/Services/ChannelCoordinatorService.cs +++ b/src/Plugins/ProtocolSupport/Java/v1_7_2_to_1_12_2/Services/ChannelCoordinatorService.cs @@ -1,5 +1,6 @@ using Void.Proxy.API.Events; using Void.Proxy.API.Events.Network; +using Void.Proxy.Common.Network.IO.Streams.Packet; using Void.Proxy.Common.Services; using Void.Proxy.Plugins.ProtocolSupport.Java.v1_7_2_to_1_12_2.Packets.Clientbound; using Void.Proxy.Plugins.ProtocolSupport.Java.v1_7_2_to_1_12_2.Packets.Serverbound; @@ -8,8 +9,8 @@ namespace Void.Proxy.Plugins.ProtocolSupport.Java.v1_7_2_to_1_12_2.Services; public class ChannelCoordinatorService : IPluginService { - [Subscribe(PostOrder.Last)] - public void OnMessageSent(MessageSentEvent @event, CancellationToken cancellationToken) + [Subscribe] + public void OnMessageSent(MessageSentEvent @event) { switch (@event.Message) { @@ -34,4 +35,15 @@ public void OnMessageSent(MessageSentEvent @event, CancellationToken cancellatio break; } } + + [Subscribe(PostOrder.Last)] + public void OnMessageSentLast(MessageSentEvent @event) + { + if (@event.Message is not EncryptionResponsePacket) + return; + + // if encryption forced channel to remove packet stream, resume reading with what has left + if (!@event.Link.PlayerChannel.Search(out _)) + @event.Link.PlayerChannel.Resume(); + } } \ No newline at end of file diff --git a/src/Plugins/ProtocolSupport/Java/v1_7_2_to_1_12_2/Services/CompressionService.cs b/src/Plugins/ProtocolSupport/Java/v1_7_2_to_1_12_2/Services/CompressionService.cs index fa2d938..c87efb7 100644 --- a/src/Plugins/ProtocolSupport/Java/v1_7_2_to_1_12_2/Services/CompressionService.cs +++ b/src/Plugins/ProtocolSupport/Java/v1_7_2_to_1_12_2/Services/CompressionService.cs @@ -23,7 +23,7 @@ public void OnMessageReceived(MessageReceivedEvent @event, CancellationToken can zlibStream.CompressionThreshold = setCompression.Threshold; } - [Subscribe] + [Subscribe(PostOrder.First)] public void OnMessageSent(MessageSentEvent @event, CancellationToken cancellationToken) { if (@event.Message is not SetCompressionPacket setCompression) @@ -34,7 +34,5 @@ public void OnMessageSent(MessageSentEvent @event, CancellationToken cancellatio var zlibStream = @event.Link.PlayerChannel.Get(); zlibStream.CompressionThreshold = setCompression.Threshold; - - @event.Link.PlayerChannel.Resume(); } } \ No newline at end of file diff --git a/src/Plugins/ProtocolSupport/Java/v1_7_2_to_1_12_2/Services/EncryptionService.cs b/src/Plugins/ProtocolSupport/Java/v1_7_2_to_1_12_2/Services/EncryptionService.cs index 9ddfd18..323b5e8 100644 --- a/src/Plugins/ProtocolSupport/Java/v1_7_2_to_1_12_2/Services/EncryptionService.cs +++ b/src/Plugins/ProtocolSupport/Java/v1_7_2_to_1_12_2/Services/EncryptionService.cs @@ -1,27 +1,41 @@ using System.Security.Cryptography; using Void.Proxy.API.Events; +using Void.Proxy.API.Events.Encryption; using Void.Proxy.API.Events.Network; +using Void.Proxy.API.Events.Services; using Void.Proxy.Common.Network.IO.Streams.Encryption; using Void.Proxy.Common.Network.IO.Streams.Packet; +using Void.Proxy.Common.Network.IO.Streams.Transparent; using Void.Proxy.Common.Services; using Void.Proxy.Plugins.ProtocolSupport.Java.v1_7_2_to_1_12_2.Packets.Serverbound; namespace Void.Proxy.Plugins.ProtocolSupport.Java.v1_7_2_to_1_12_2.Services; -public class EncryptionService : IPluginService +public class EncryptionService(IEventService events) : IPluginService { - private static readonly byte[] PrivateKey = Convert.FromHexString("30820277020100300d06092a864886f70d0101010500048202613082025d02010002818100e9675bb7efd2f2b75ee5f1ea07399fe38239a4196506fd7f6489961d6de770e7580c69e84fb040fe19721f06e7d8dc634280f0dbb71b79292f783eb2b6c409c5438197c3bd869b17264bb5aa448614fefccd3d841b278f05c202429d075deb2f5d1d2d24d80363f69444974358f23912951b9d3c2a4a86a0e5c13a84a997a04b02030100010281802e0fb94088023be72741bee79e0c67bae8d8c24346b645f1cd9fff71885e7be013f6c331d704241761632daf59b2e8ef67d0f5778edfcb9deea1ced1cb12ce1070f7d5226093bf9f2c7587a38239837849ed4728bbbba8da60b297b0397d6cafae8f427775aa8e22a42e04c7c8208da79a8f940ff2d8c31a22e723c56e136ce1024100ed595c08a640f5a71573ee45c79247bca08d4cb478d658aab90c5133b9bb0187b0e8801066adc6bb8f1a9903ccf548e7c746883443e96d3fde505116ee9466eb024100fbbea1f5dd1b2f63f867597a66fb26b85a0faea928525e5906635d3366e14ef4d02e851015e34ce78652e0a6a22e0cdfe92c6dbebc1da9daae2086d86e44142102401780268db0b073e2444c834623798762d4dec8be81cc6f61100b792acef40635c23d7318aca1fe3069fdef32a223934167c8c309b1c3b60e81db9ffbce49a15b024100a685c02e99567d2f9cc6086b2e299dc03e5ab7474fd3c4731105b345e81ccb94a70cce9a085075b384a7d7d081e102452ec163cad236b0ff654540cd738af6e1024100abd64d321c66d4154a4d3ff1dfb265609d3ec650610bad4f00e461357fadf9542058ecc8458f0acd2744020f96ef1c5f21ea8c82028d837ac2e4a61f2ce35689"); - - [Subscribe] - public void OnMessageSent(MessageSentEvent @event, CancellationToken cancellationToken) + [Subscribe(PostOrder.First)] + public async ValueTask OnMessageSent(MessageSentEvent @event, CancellationToken cancellationToken) { if (@event.Message is not EncryptionResponsePacket encryptionResponse) return; - var secret = Decrypt(PrivateKey, encryptionResponse.SharedSecret); + var privateKey = await events.ThrowWithResultAsync(new SearchServerPrivateKey { Server = @event.Link.Server }, cancellationToken); + + if (privateKey is null) + { + @event.Link.ServerChannel.Remove(); + @event.Link.PlayerChannel.Remove(); + + @event.Link.ServerChannel.Add(); + @event.Link.PlayerChannel.Add(); + } + else + { + var secret = Decrypt(privateKey, encryptionResponse.SharedSecret); - @event.Link.ServerChannel.AddBefore(new AesCfb8BufferedStream(secret)); - @event.Link.PlayerChannel.AddBefore(new AesCfb8BufferedStream(secret)); + @event.Link.ServerChannel.AddBefore(new AesCfb8BufferedStream(secret)); + @event.Link.PlayerChannel.AddBefore(new AesCfb8BufferedStream(secret)); + } } private static byte[] Decrypt(byte[] key, byte[] data)