Skip to content

Commit

Permalink
switch to transparent streams if no private key is provided
Browse files Browse the repository at this point in the history
  • Loading branch information
caunt committed Sep 30, 2024
1 parent 6d5747a commit cbfbd8f
Show file tree
Hide file tree
Showing 18 changed files with 210 additions and 58 deletions.
9 changes: 9 additions & 0 deletions src/API/Events/Encryption/SearchServerPrivateKey.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Void.Proxy.API.Servers;

namespace Void.Proxy.API.Events.Encryption;

public class SearchServerPrivateKey : IEventWithResult<byte[]>
{
public required IServer Server { get; init; }
public byte[]? Result { get; set; }
}
16 changes: 10 additions & 6 deletions src/API/Network/IO/Channels/IMinecraftChannel.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -14,11 +15,14 @@ public interface IMinecraftChannel : IDisposable, IAsyncDisposable
public bool IsPaused { get; }
public bool IsRedirectionSupported { get; }

public void Add<T>() where T : IMinecraftStream, new();
public void Add<T>(T stream) where T : IMinecraftStream;
public void AddBefore<TBefore, TValue>() where TBefore : IMinecraftStream where TValue : IMinecraftStream, new();
public void AddBefore<TBefore, TValue>(TValue stream) where TBefore : IMinecraftStream where TValue : IMinecraftStream;
public T Get<T>() where T : IMinecraftStreamBase;
public void Add<T>() where T : class, IMinecraftStream, new();
public void Add<T>(T stream) where T : class, IMinecraftStream;
public void AddBefore<TBefore, TValue>() where TBefore : class, IMinecraftStream where TValue : class, IMinecraftStream, new();
public void AddBefore<TBefore, TValue>(TValue stream) where TBefore : class, IMinecraftStream where TValue : class, IMinecraftStream;
public void Remove<T>() where T : class, IMinecraftStream, new();
public void Remove<T>(T stream) where T : class, IMinecraftStream;
public T Get<T>() where T : class, IMinecraftStreamBase;
public bool Search<T>([MaybeNullWhen(false)] out T result) where T : class, IMinecraftStreamBase;
public void PrependBuffer(Memory<byte> memory);
public ValueTask<IMinecraftMessage> ReadMessageAsync(CancellationToken cancellationToken = default);
public ValueTask WriteMessageAsync(IMinecraftMessage message, CancellationToken cancellationToken = default);
Expand Down
2 changes: 1 addition & 1 deletion src/API/Network/Protocol/ProtocolVersion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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(<version>) instead");
throw new InvalidOperationException($"ProtocolVersion {version} already registered, use Search(<version>) instead");
}

public static ProtocolVersion Latest => Mapping.MaxBy(kv => kv.Key).Value;
Expand Down
66 changes: 56 additions & 10 deletions src/Common/Network/IO/Channels/SimpleChannel.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<T>() where T : IMinecraftStream, new()
public void Add<T>() where T : class, IMinecraftStream, new()
{
Add(new T());
}

public void Add<T>(T stream) where T : IMinecraftStream
public void Add<T>(T stream) where T : class, IMinecraftStream
{
stream.BaseStream = head;
head = stream;
}

public void AddBefore<TBefore, TValue>() where TBefore : IMinecraftStream where TValue : IMinecraftStream, new()
public void AddBefore<TBefore, TValue>() where TBefore : class, IMinecraftStream where TValue : class, IMinecraftStream, new()
{
AddBefore<TBefore, TValue>(new TValue());
}

public void AddBefore<TBefore, TValue>(TValue stream) where TBefore : IMinecraftStream where TValue : IMinecraftStream
public void AddBefore<TBefore, TValue>(TValue stream) where TBefore : class, IMinecraftStream where TValue : class, IMinecraftStream
{
var before = Get<TBefore>();
var beforeBaseStream = before.BaseStream;
Expand All @@ -47,9 +48,51 @@ public void AddBefore<TBefore, TValue>(TValue stream) where TBefore : IMinecraft
before.BaseStream = stream;
}

public T Get<T>() where T : IMinecraftStreamBase
public void Remove<T>() where T : class, IMinecraftStream, new()
{
return Get<T>(head);
Remove(Get<T>());
}

public void Remove<T>(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<T>() where T : class, IMinecraftStreamBase
{
if (Search<T>(out var stream))
return stream;

throw new InvalidOperationException($"{typeof(T)} not found in channel");
}

public bool Search<T>([MaybeNullWhen(false)] out T result) where T : class, IMinecraftStreamBase
{
return Get(head, out result);
}

public void PrependBuffer(Memory<byte> memory)
Expand Down Expand Up @@ -131,19 +174,22 @@ public void Flush()
head.Flush();
}

private T Get<T>(IMinecraftStreamBase? baseStream) where T : IMinecraftStreamBase
private bool Get<T>(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;
}
}
}
2 changes: 1 addition & 1 deletion src/Platform/Platform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class Platform(ILogger<Platform> 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();
Expand Down
4 changes: 4 additions & 0 deletions src/Platform/Void.Proxy.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<CopyToOutputDirectory>false</CopyToOutputDirectory>
</ProjectReference>
<ProjectReference Include="..\Plugins\ExamplePlugin\Void.Proxy.Plugins.ExamplePlugin.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<CopyToOutputDirectory>false</CopyToOutputDirectory>
</ProjectReference>
<ProjectReference Include="..\Plugins\ModsSupport\Forge\Void.Proxy.Plugins.ModsSupport.Forge.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<CopyToOutputDirectory>false</CopyToOutputDirectory>
Expand Down
1 change: 1 addition & 0 deletions src/Plugins/ExamplePlugin/ExamplePlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,6 @@ public void OnPluginLoad(PluginLoadEvent @event)
return;

events.RegisterListeners<TraceService>();
events.RegisterListeners<DebugService>();
}
}
16 changes: 16 additions & 0 deletions src/Plugins/ExamplePlugin/Services/DebugService.cs
Original file line number Diff line number Diff line change
@@ -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");
}
}
2 changes: 1 addition & 1 deletion src/Plugins/ModsSupport/Forge/ForgeMarker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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)
{
Expand All @@ -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<MinecraftPacketMessageStream>(out _))
@event.Link.PlayerChannel.Resume();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -34,7 +34,5 @@ public void OnMessageSent(MessageSentEvent @event, CancellationToken cancellatio

var zlibStream = @event.Link.PlayerChannel.Get<SharpZipLibCompressionMessageStream>();
zlibStream.CompressionThreshold = setCompression.Threshold;

@event.Link.PlayerChannel.Resume();
}
}
Original file line number Diff line number Diff line change
@@ -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<MinecraftPacketMessageStream>();
@event.Link.PlayerChannel.Remove<MinecraftPacketMessageStream>();

@event.Link.ServerChannel.Add<MinecraftTransparentMessageStream>();
@event.Link.PlayerChannel.Add<MinecraftTransparentMessageStream>();
}
else
{
var secret = Decrypt(privateKey, encryptionResponse.SharedSecret);

@event.Link.ServerChannel.AddBefore<MinecraftPacketMessageStream, AesCfb8BufferedStream>(new AesCfb8BufferedStream(secret));
@event.Link.PlayerChannel.AddBefore<MinecraftPacketMessageStream, AesCfb8BufferedStream>(new AesCfb8BufferedStream(secret));
@event.Link.ServerChannel.AddBefore<MinecraftPacketMessageStream, AesCfb8BufferedStream>(new AesCfb8BufferedStream(secret));
@event.Link.PlayerChannel.AddBefore<MinecraftPacketMessageStream, AesCfb8BufferedStream>(new AesCfb8BufferedStream(secret));
}
}

private static byte[] Decrypt(byte[] key, byte[] data)
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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)
{
Expand All @@ -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<MinecraftPacketMessageStream>(out _))
@event.Link.PlayerChannel.Resume();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -34,7 +34,5 @@ public void OnMessageSent(MessageSentEvent @event, CancellationToken cancellatio

var zlibStream = @event.Link.PlayerChannel.Get<SharpZipLibCompressionMessageStream>();
zlibStream.CompressionThreshold = setCompression.Threshold;

@event.Link.PlayerChannel.Resume();
}
}
Loading

0 comments on commit cbfbd8f

Please sign in to comment.