From 4757c4be533586579088c2cb8095ff039c9ccae6 Mon Sep 17 00:00:00 2001 From: BruceChen Date: Sun, 28 Aug 2022 13:18:07 +0800 Subject: [PATCH] Trim --- .../Crypto/{Streams => }/AesCfb8Stream.cs | 115 ++++++++++++------ MinecraftClient/Crypto/AesContext.cs | 22 ++-- MinecraftClient/Crypto/CryptoHandler.cs | 13 -- MinecraftClient/Crypto/IAesStream.cs | 18 --- .../Crypto/Streams/RegularAesStream.cs | 106 ---------------- .../Protocol/Handlers/Protocol16.cs | 4 +- .../Protocol/Handlers/Protocol18Terrain.cs | 4 + .../Protocol/Handlers/SocketWrapper.cs | 4 +- 8 files changed, 97 insertions(+), 189 deletions(-) rename MinecraftClient/Crypto/{Streams => }/AesCfb8Stream.cs (54%) delete mode 100644 MinecraftClient/Crypto/IAesStream.cs delete mode 100644 MinecraftClient/Crypto/Streams/RegularAesStream.cs diff --git a/MinecraftClient/Crypto/Streams/AesCfb8Stream.cs b/MinecraftClient/Crypto/AesCfb8Stream.cs similarity index 54% rename from MinecraftClient/Crypto/Streams/AesCfb8Stream.cs rename to MinecraftClient/Crypto/AesCfb8Stream.cs index 1b1a80a7..a08e4bc7 100644 --- a/MinecraftClient/Crypto/Streams/AesCfb8Stream.cs +++ b/MinecraftClient/Crypto/AesCfb8Stream.cs @@ -8,31 +8,37 @@ using System.IO; using System.Collections.Concurrent; using System.Runtime.CompilerServices; -namespace MinecraftClient.Crypto.Streams +namespace MinecraftClient.Crypto { - internal class AesCfb8Stream : Stream, IAesStream + public class AesCfb8Stream : Stream { public static readonly int blockSize = 16; private readonly Aes? Aes = null; - - private readonly AesContext? FastAes = null; - - public System.IO.Stream BaseStream { get; set; } + private readonly FastAes? FastAes = null; + + public Stream BaseStream { get; set; } private bool inStreamEnded = false; private byte[] ReadStreamIV = new byte[16]; private byte[] WriteStreamIV = new byte[16]; - public AesCfb8Stream(System.IO.Stream stream, byte[] key) + public AesCfb8Stream(Stream stream, byte[] key) { BaseStream = stream; - if (System.Runtime.Intrinsics.X86.Sse2.IsSupported && System.Runtime.Intrinsics.X86.Aes.IsSupported) - FastAes = new AesContext(key); + if (FastAes.IsSupport()) + FastAes = new FastAes(key); else - Aes = GenerateAES(key); + { + Aes = Aes.Create(); + Aes.BlockSize = 128; + Aes.KeySize = 128; + Aes.Key = key; + Aes.Mode = CipherMode.ECB; + Aes.Padding = PaddingMode.None; + } Array.Copy(key, ReadStreamIV, 16); Array.Copy(key, WriteStreamIV, 16); @@ -77,17 +83,37 @@ namespace MinecraftClient.Crypto.Streams public override int ReadByte() { - byte[] temp = new byte[1]; - Read(temp, 0, 1); - return temp[0]; + if (inStreamEnded) + return -1; + + int inputBuf = BaseStream.ReadByte(); + if (inputBuf == -1) + { + inStreamEnded = true; + return -1; + } + + Span blockOutput = stackalloc byte[blockSize]; + if (FastAes != null) + FastAes.EncryptEcb(ReadStreamIV, blockOutput); + else + Aes!.EncryptEcb(ReadStreamIV, blockOutput, PaddingMode.None); + + // Shift left + Array.Copy(ReadStreamIV, 1, ReadStreamIV, 0, blockSize - 1); + ReadStreamIV[blockSize - 1] = (byte)inputBuf; + + return (byte)(blockOutput[0] ^ inputBuf); } [MethodImpl(MethodImplOptions.AggressiveOptimization)] public override int Read(byte[] buffer, int outOffset, int required) { - if (this.inStreamEnded) + if (inStreamEnded) return 0; + Span blockOutput = FastAes != null ? stackalloc byte[blockSize] : null; + byte[] inputBuf = new byte[blockSize + required]; Array.Copy(ReadStreamIV, inputBuf, blockSize); @@ -96,25 +122,35 @@ namespace MinecraftClient.Crypto.Streams curRead = BaseStream.Read(inputBuf, blockSize + readed, required - readed); if (curRead == 0) { - this.inStreamEnded = true; + inStreamEnded = true; return readed; } - OrderablePartitioner> rangePartitioner = (curRead <= 256) ? - Partitioner.Create(readed, readed + curRead, 32) : Partitioner.Create(readed, readed + curRead); - Parallel.ForEach(rangePartitioner, (range, loopState) => + int processEnd = readed + curRead; + if (FastAes != null) { - Span blockOutput = stackalloc byte[blockSize]; - for (int idx = range.Item1; idx < range.Item2; idx++) + for (int idx = readed; idx < processEnd; idx++) { ReadOnlySpan blockInput = new(inputBuf, idx, blockSize); - if (FastAes != null) - FastAes.EncryptEcb(blockInput, blockOutput); - else - Aes!.EncryptEcb(blockInput, blockOutput, PaddingMode.None); + FastAes.EncryptEcb(blockInput, blockOutput); buffer[outOffset + idx] = (byte)(blockOutput[0] ^ inputBuf[idx + blockSize]); } - }); + } + else + { + OrderablePartitioner> rangePartitioner = curRead <= 256 ? + Partitioner.Create(readed, processEnd, 32) : Partitioner.Create(readed, processEnd); + Parallel.ForEach(rangePartitioner, (range, loopState) => + { + Span blockOutput = stackalloc byte[blockSize]; + for (int idx = range.Item1; idx < range.Item2; idx++) + { + ReadOnlySpan blockInput = new(inputBuf, idx, blockSize); + Aes!.EncryptEcb(blockInput, blockOutput, PaddingMode.None); + buffer[outOffset + idx] = (byte)(blockOutput[0] ^ inputBuf[idx + blockSize]); + } + }); + } } Array.Copy(inputBuf, required, ReadStreamIV, 0, blockSize); @@ -122,7 +158,7 @@ namespace MinecraftClient.Crypto.Streams return required; } - public override long Seek(long offset, System.IO.SeekOrigin origin) + public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } @@ -134,9 +170,23 @@ namespace MinecraftClient.Crypto.Streams public override void WriteByte(byte b) { - Write(new byte[] { b }, 0, 1); + Span blockOutput = stackalloc byte[blockSize]; + + if (FastAes != null) + FastAes.EncryptEcb(WriteStreamIV, blockOutput); + else + Aes!.EncryptEcb(WriteStreamIV, blockOutput, PaddingMode.None); + + byte outputBuf = (byte)(blockOutput[0] ^ b); + + BaseStream.WriteByte(outputBuf); + + // Shift left + Array.Copy(WriteStreamIV, 1, WriteStreamIV, 0, blockSize - 1); + WriteStreamIV[blockSize - 1] = outputBuf; } + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public override void Write(byte[] input, int offset, int required) { byte[] outputBuf = new byte[blockSize + required]; @@ -156,16 +206,5 @@ namespace MinecraftClient.Crypto.Streams Array.Copy(outputBuf, required, WriteStreamIV, 0, blockSize); } - - private static Aes GenerateAES(byte[] key) - { - Aes aes = Aes.Create(); - aes.BlockSize = 128; - aes.KeySize = 128; - aes.Key = key; - aes.Mode = CipherMode.ECB; - aes.Padding = PaddingMode.None; - return aes; - } } } diff --git a/MinecraftClient/Crypto/AesContext.cs b/MinecraftClient/Crypto/AesContext.cs index bbaab330..fb6fd7bc 100644 --- a/MinecraftClient/Crypto/AesContext.cs +++ b/MinecraftClient/Crypto/AesContext.cs @@ -8,17 +8,24 @@ namespace MinecraftClient.Crypto { // Using the AES-NI instruction set // https://gist.github.com/Thealexbarney/9f75883786a9f3100408ff795fb95d85 - public class AesContext + public class FastAes { private Vector128[] RoundKeys { get; } - public byte[] Iv { get; } = new byte[0x10]; - - public AesContext(Span key) + public FastAes(Span key) { RoundKeys = KeyExpansion(key); } + /// + /// Detects if the required instruction set is supported + /// + /// Is it supported + public static bool IsSupport() + { + return Sse2.IsSupported && Aes.IsSupported; + } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] public void EncryptEcb(ReadOnlySpan plaintext, Span destination) { @@ -88,10 +95,5 @@ namespace MinecraftClient.Crypto keys[i] = Sse2.Xor(s, t); } - - public void SetIv(Span iv) - { - iv.Slice(0, 0x10).CopyTo(Iv); - } } -} \ No newline at end of file +} diff --git a/MinecraftClient/Crypto/CryptoHandler.cs b/MinecraftClient/Crypto/CryptoHandler.cs index e65f231b..5728835a 100644 --- a/MinecraftClient/Crypto/CryptoHandler.cs +++ b/MinecraftClient/Crypto/CryptoHandler.cs @@ -193,18 +193,5 @@ namespace MinecraftClient.Crypto } return p; } - - /// - /// Get a new AES-encrypted stream for wrapping a non-encrypted stream. - /// - /// Stream to encrypt - /// Key to use - /// Return an appropriate stream depending on the framework being used - - public static IAesStream getAesStream(Stream underlyingStream, byte[] AesKey) - { - // return new Streams.RegularAesStream(underlyingStream, AesKey); - return new Streams.AesCfb8Stream(underlyingStream, AesKey); - } } } diff --git a/MinecraftClient/Crypto/IAesStream.cs b/MinecraftClient/Crypto/IAesStream.cs deleted file mode 100644 index 23f4a83b..00000000 --- a/MinecraftClient/Crypto/IAesStream.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace MinecraftClient.Crypto -{ - /// - /// Interface for AES stream - /// Allows to use a different implementation depending on the framework being used. - /// - - public interface IAesStream - { - int Read(byte[] buffer, int offset, int count); - void Write(byte[] buffer, int offset, int count); - } -} diff --git a/MinecraftClient/Crypto/Streams/RegularAesStream.cs b/MinecraftClient/Crypto/Streams/RegularAesStream.cs deleted file mode 100644 index 1c825c3b..00000000 --- a/MinecraftClient/Crypto/Streams/RegularAesStream.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Security.Cryptography; -using System.IO; - -namespace MinecraftClient.Crypto.Streams -{ - /// - /// An encrypted stream using AES, used for encrypting network data on the fly using AES. - /// This is the regular AesStream class used with the regular .NET framework from Microsoft. - /// - - public class RegularAesStream : Stream, IAesStream - { - CryptoStream enc; - CryptoStream dec; - public RegularAesStream(Stream stream, byte[] key) - { - BaseStream = stream; - enc = new CryptoStream(stream, GenerateAES(key).CreateEncryptor(), CryptoStreamMode.Write); - dec = new CryptoStream(stream, GenerateAES(key).CreateDecryptor(), CryptoStreamMode.Read); - } - public System.IO.Stream BaseStream { get; set; } - - public override bool CanRead - { - get { return true; } - } - - public override bool CanSeek - { - get { return false; } - } - - public override bool CanWrite - { - get { return true; } - } - - public override void Flush() - { - BaseStream.Flush(); - } - - public override long Length - { - get { throw new NotSupportedException(); } - } - - public override long Position - { - get - { - throw new NotSupportedException(); - } - set - { - throw new NotSupportedException(); - } - } - - public override int ReadByte() - { - return dec.ReadByte(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - return dec.Read(buffer, offset, count); - } - - public override long Seek(long offset, System.IO.SeekOrigin origin) - { - throw new NotSupportedException(); - } - - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override void WriteByte(byte b) - { - enc.WriteByte(b); - } - - public override void Write(byte[] buffer, int offset, int count) - { - enc.Write(buffer, offset, count); - } - - private static Aes GenerateAES(byte[] key) - { - Aes aes = Aes.Create(); - aes.Mode = CipherMode.CFB; - aes.Padding = PaddingMode.None; - aes.KeySize = 128; - aes.FeedbackSize = 8; - aes.Key = key; - aes.IV = key; - return aes; - } - } -} diff --git a/MinecraftClient/Protocol/Handlers/Protocol16.cs b/MinecraftClient/Protocol/Handlers/Protocol16.cs index 89768131..a819b3a1 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol16.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol16.cs @@ -26,7 +26,7 @@ namespace MinecraftClient.Protocol.Handlers private bool encrypted = false; private int protocolversion; private Tuple? netRead = null; - Crypto.IAesStream s; + Crypto.AesCfb8Stream s; TcpClient c; public Protocol16Handler(TcpClient Client, int ProtocolVersion, IMinecraftComHandler Handler) @@ -568,7 +568,7 @@ namespace MinecraftClient.Protocol.Handlers if (pid[0] == 0xFC) { readData(4); - s = CryptoHandler.getAesStream(c.GetStream(), secretKey); + s = new AesCfb8Stream(c.GetStream(), secretKey); encrypted = true; return true; } diff --git a/MinecraftClient/Protocol/Handlers/Protocol18Terrain.cs b/MinecraftClient/Protocol/Handlers/Protocol18Terrain.cs index e1283daa..62459c0f 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18Terrain.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18Terrain.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Numerics; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; //using System.Linq; @@ -34,6 +35,7 @@ namespace MinecraftClient.Protocol.Handlers /// Reading the "Block states" field: consists of 4096 entries, representing all the blocks in the chunk section. /// /// Cache for reading data + [MethodImpl(MethodImplOptions.AggressiveOptimization)] private Chunk? ReadBlockStatesField(Queue cache) { // read Block states (Type: Paletted Container) @@ -142,6 +144,7 @@ namespace MinecraftClient.Protocol.Handlers /// Cache for reading chunk data /// token to cancel the task /// true if successfully loaded + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public bool ProcessChunkColumnData(int chunkX, int chunkZ, ulong[]? verticalStripBitmask, Queue cache, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) @@ -243,6 +246,7 @@ namespace MinecraftClient.Protocol.Handlers /// Cache for reading chunk data /// token to cancel the task /// true if successfully loaded + [MethodImpl(MethodImplOptions.AggressiveOptimization)] public bool ProcessChunkColumnData(int chunkX, int chunkZ, ushort chunkMask, ushort chunkMask2, bool hasSkyLight, bool chunksContinuous, int currentDimension, Queue cache, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) diff --git a/MinecraftClient/Protocol/Handlers/SocketWrapper.cs b/MinecraftClient/Protocol/Handlers/SocketWrapper.cs index 0147ab5f..084b6cf0 100644 --- a/MinecraftClient/Protocol/Handlers/SocketWrapper.cs +++ b/MinecraftClient/Protocol/Handlers/SocketWrapper.cs @@ -10,7 +10,7 @@ namespace MinecraftClient.Protocol.Handlers class SocketWrapper { TcpClient c; - IAesStream s; + AesCfb8Stream s; bool encrypted = false; /// @@ -49,7 +49,7 @@ namespace MinecraftClient.Protocol.Handlers { if (encrypted) throw new InvalidOperationException("Stream is already encrypted!?"); - this.s = CryptoHandler.getAesStream(c.GetStream(), secretKey); + this.s = new AesCfb8Stream(c.GetStream(), secretKey); this.encrypted = true; }