mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-02 13:07:06 +01:00
419 lines
20 KiB
Diff
419 lines
20 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Andrew Steinborn <git@steinborn.me>
|
|
Date: Mon, 26 Jul 2021 02:15:17 -0400
|
|
Subject: [PATCH] Use Velocity compression and cipher natives
|
|
|
|
|
|
diff --git a/build.gradle.kts b/build.gradle.kts
|
|
index b81b99a7767e3b0d3549e49529e98efdb7334101..7df1b44674ba5e826ad7c96c9d242865a3282307 100644
|
|
--- a/build.gradle.kts
|
|
+++ b/build.gradle.kts
|
|
@@ -41,6 +41,11 @@ dependencies {
|
|
runtimeOnly("org.xerial:sqlite-jdbc:3.43.0.0")
|
|
runtimeOnly("com.mysql:mysql-connector-j:8.1.0")
|
|
runtimeOnly("com.lmax:disruptor:3.4.4") // Paper
|
|
+ // Paper start - Use Velocity cipher
|
|
+ implementation("com.velocitypowered:velocity-native:3.1.2-SNAPSHOT") {
|
|
+ isTransitive = false
|
|
+ }
|
|
+ // Paper end
|
|
|
|
runtimeOnly("org.apache.maven:maven-resolver-provider:3.8.5")
|
|
runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.7.3")
|
|
diff --git a/src/main/java/net/minecraft/network/CipherDecoder.java b/src/main/java/net/minecraft/network/CipherDecoder.java
|
|
index c76d406dd0ec9a65579b8cde293cff9306ce1c9d..937c47db17472f34ec6e783a81afad76ac0c252c 100644
|
|
--- a/src/main/java/net/minecraft/network/CipherDecoder.java
|
|
+++ b/src/main/java/net/minecraft/network/CipherDecoder.java
|
|
@@ -7,13 +7,29 @@ import java.util.List;
|
|
import javax.crypto.Cipher;
|
|
|
|
public class CipherDecoder extends MessageToMessageDecoder<ByteBuf> {
|
|
- private final CipherBase cipher;
|
|
+ private final com.velocitypowered.natives.encryption.VelocityCipher cipher; // Paper
|
|
|
|
- public CipherDecoder(Cipher cipher) {
|
|
- this.cipher = new CipherBase(cipher);
|
|
+ public CipherDecoder(com.velocitypowered.natives.encryption.VelocityCipher cipher) { // Paper
|
|
+ this.cipher = cipher; // Paper
|
|
}
|
|
|
|
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
|
|
- list.add(this.cipher.decipher(channelHandlerContext, byteBuf));
|
|
+ // Paper start
|
|
+ ByteBuf compatible = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(channelHandlerContext.alloc(), cipher, byteBuf);
|
|
+ try {
|
|
+ cipher.process(compatible);
|
|
+ list.add(compatible);
|
|
+ } catch (Exception e) {
|
|
+ compatible.release(); // compatible will never be used if we throw an exception
|
|
+ throw e;
|
|
+ }
|
|
+ // Paper end
|
|
}
|
|
+
|
|
+ // Paper start
|
|
+ @Override
|
|
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
|
|
+ cipher.close();
|
|
+ }
|
|
+ // Paper end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/network/CipherEncoder.java b/src/main/java/net/minecraft/network/CipherEncoder.java
|
|
index fcfbf35a76f92875bc2e2a3b2d900420d91945a8..7ed0d37ecbf79e8642e8b730d8e81abeeed653ea 100644
|
|
--- a/src/main/java/net/minecraft/network/CipherEncoder.java
|
|
+++ b/src/main/java/net/minecraft/network/CipherEncoder.java
|
|
@@ -4,15 +4,32 @@ import io.netty.buffer.ByteBuf;
|
|
import io.netty.channel.ChannelHandlerContext;
|
|
import io.netty.handler.codec.MessageToByteEncoder;
|
|
import javax.crypto.Cipher;
|
|
+import java.util.List;
|
|
|
|
-public class CipherEncoder extends MessageToByteEncoder<ByteBuf> {
|
|
- private final CipherBase cipher;
|
|
+public class CipherEncoder extends io.netty.handler.codec.MessageToMessageEncoder<ByteBuf> { // Paper - change superclass
|
|
+ private final com.velocitypowered.natives.encryption.VelocityCipher cipher; // Paper
|
|
|
|
- public CipherEncoder(Cipher cipher) {
|
|
- this.cipher = new CipherBase(cipher);
|
|
+ public CipherEncoder(com.velocitypowered.natives.encryption.VelocityCipher cipher) { // Paper
|
|
+ this.cipher = cipher; // Paper
|
|
}
|
|
|
|
- protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, ByteBuf byteBuf2) throws Exception {
|
|
- this.cipher.encipher(byteBuf, byteBuf2);
|
|
+ protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
|
|
+ // Paper start
|
|
+ ByteBuf compatible = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(channelHandlerContext.alloc(), cipher, byteBuf);
|
|
+ try {
|
|
+ cipher.process(compatible);
|
|
+ list.add(compatible);
|
|
+ } catch (Exception e) {
|
|
+ compatible.release(); // compatible will never be used if we throw an exception
|
|
+ throw e;
|
|
+ }
|
|
+ // Paper end
|
|
}
|
|
+
|
|
+ // Paper start
|
|
+ @Override
|
|
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
|
|
+ cipher.close();
|
|
+ }
|
|
+ // Paper end
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/network/CompressionDecoder.java b/src/main/java/net/minecraft/network/CompressionDecoder.java
|
|
index 64767b4e542a78e91eafb83a40ef34fb9272598f..0d519b4e51909e62e9aae24f9ee98910d0e449aa 100644
|
|
--- a/src/main/java/net/minecraft/network/CompressionDecoder.java
|
|
+++ b/src/main/java/net/minecraft/network/CompressionDecoder.java
|
|
@@ -13,13 +13,20 @@ public class CompressionDecoder extends ByteToMessageDecoder {
|
|
public static final int MAXIMUM_COMPRESSED_LENGTH = 2097152;
|
|
public static final int MAXIMUM_UNCOMPRESSED_LENGTH = 8388608;
|
|
private final Inflater inflater;
|
|
+ private final com.velocitypowered.natives.compression.VelocityCompressor compressor; // Paper
|
|
private int threshold;
|
|
private boolean validateDecompressed;
|
|
|
|
+ // Paper start
|
|
public CompressionDecoder(int compressionThreshold, boolean rejectsBadPackets) {
|
|
+ this(null, compressionThreshold, rejectsBadPackets);
|
|
+ }
|
|
+ public CompressionDecoder(com.velocitypowered.natives.compression.VelocityCompressor compressor, int compressionThreshold, boolean rejectsBadPackets) {
|
|
this.threshold = compressionThreshold;
|
|
this.validateDecompressed = rejectsBadPackets;
|
|
- this.inflater = new Inflater();
|
|
+ this.inflater = compressor == null ? new Inflater() : null;
|
|
+ this.compressor = compressor;
|
|
+ // Paper end
|
|
}
|
|
|
|
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
|
|
@@ -38,14 +45,42 @@ public class CompressionDecoder extends ByteToMessageDecoder {
|
|
}
|
|
}
|
|
|
|
+ if (inflater != null) { // Paper - use velocity compression - fallback to vanilla inflater
|
|
this.setupInflaterInput(byteBuf);
|
|
ByteBuf byteBuf2 = this.inflate(channelHandlerContext, i);
|
|
this.inflater.reset();
|
|
list.add(byteBuf2);
|
|
+ return; // Paper - use velocity compression
|
|
+ } // Paper - use velocity compression
|
|
+
|
|
+ // Paper start - use velocity compression
|
|
+ int claimedUncompressedSize = i; // OBFHELPER
|
|
+ ByteBuf compatibleIn = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(channelHandlerContext.alloc(), this.compressor, byteBuf);
|
|
+ ByteBuf uncompressed = com.velocitypowered.natives.util.MoreByteBufUtils.preferredBuffer(channelHandlerContext.alloc(), this.compressor, claimedUncompressedSize);
|
|
+ try {
|
|
+ this.compressor.inflate(compatibleIn, uncompressed, claimedUncompressedSize);
|
|
+ list.add(uncompressed);
|
|
+ byteBuf.clear();
|
|
+ } catch (Exception e) {
|
|
+ uncompressed.release();
|
|
+ throw e;
|
|
+ } finally {
|
|
+ compatibleIn.release();
|
|
+ }
|
|
+ // Paper end - use velocity compression
|
|
}
|
|
}
|
|
}
|
|
|
|
+ // Paper start
|
|
+ @Override
|
|
+ public void handlerRemoved0(ChannelHandlerContext ctx) throws Exception {
|
|
+ if (this.compressor != null) {
|
|
+ this.compressor.close();
|
|
+ }
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
private void setupInflaterInput(ByteBuf buf) {
|
|
ByteBuffer byteBuffer;
|
|
if (buf.nioBufferCount() > 0) {
|
|
diff --git a/src/main/java/net/minecraft/network/CompressionEncoder.java b/src/main/java/net/minecraft/network/CompressionEncoder.java
|
|
index 0a8a00b2fcaf8ac171a5cce60a50ffd37de87b57..ec7239fd6a2ecf732d2843f9426e4cb69d166ce6 100644
|
|
--- a/src/main/java/net/minecraft/network/CompressionEncoder.java
|
|
+++ b/src/main/java/net/minecraft/network/CompressionEncoder.java
|
|
@@ -1,47 +1,100 @@
|
|
-package net.minecraft.network;
|
|
-
|
|
-import io.netty.buffer.ByteBuf;
|
|
-import io.netty.channel.ChannelHandlerContext;
|
|
-import io.netty.handler.codec.MessageToByteEncoder;
|
|
-import java.util.zip.Deflater;
|
|
-
|
|
-public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
|
|
- private final byte[] encodeBuf = new byte[8192];
|
|
- private final Deflater deflater;
|
|
- private int threshold;
|
|
-
|
|
- public CompressionEncoder(int compressionThreshold) {
|
|
- this.threshold = compressionThreshold;
|
|
- this.deflater = new Deflater();
|
|
- }
|
|
-
|
|
- protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, ByteBuf byteBuf2) {
|
|
- int i = byteBuf.readableBytes();
|
|
- if (i < this.threshold) {
|
|
- VarInt.write(byteBuf2, 0);
|
|
- byteBuf2.writeBytes(byteBuf);
|
|
- } else {
|
|
- byte[] bs = new byte[i];
|
|
- byteBuf.readBytes(bs);
|
|
- VarInt.write(byteBuf2, bs.length);
|
|
- this.deflater.setInput(bs, 0, i);
|
|
- this.deflater.finish();
|
|
-
|
|
- while(!this.deflater.finished()) {
|
|
- int j = this.deflater.deflate(this.encodeBuf);
|
|
- byteBuf2.writeBytes(this.encodeBuf, 0, j);
|
|
- }
|
|
-
|
|
- this.deflater.reset();
|
|
- }
|
|
-
|
|
- }
|
|
-
|
|
- public int getThreshold() {
|
|
- return this.threshold;
|
|
- }
|
|
-
|
|
- public void setThreshold(int compressionThreshold) {
|
|
- this.threshold = compressionThreshold;
|
|
- }
|
|
-}
|
|
+package net.minecraft.network;
|
|
+
|
|
+import io.netty.buffer.ByteBuf;
|
|
+import io.netty.channel.ChannelHandlerContext;
|
|
+import io.netty.handler.codec.MessageToByteEncoder;
|
|
+import java.util.zip.Deflater;
|
|
+
|
|
+public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
|
|
+ private final byte[] encodeBuf; // Paper
|
|
+ private final Deflater deflater;
|
|
+ private final com.velocitypowered.natives.compression.VelocityCompressor compressor; // Paper
|
|
+ private int threshold;
|
|
+
|
|
+ // Paper start
|
|
+ public CompressionEncoder(int compressionThreshold) {
|
|
+ this(null, compressionThreshold);
|
|
+ }
|
|
+ public CompressionEncoder(com.velocitypowered.natives.compression.VelocityCompressor compressor, int compressionThreshold) {
|
|
+ this.threshold = compressionThreshold;
|
|
+ if (compressor == null) {
|
|
+ this.encodeBuf = new byte[8192];
|
|
+ this.deflater = new Deflater();
|
|
+ } else {
|
|
+ this.encodeBuf = null;
|
|
+ this.deflater = null;
|
|
+ }
|
|
+ this.compressor = compressor;
|
|
+ // Paper end
|
|
+ }
|
|
+
|
|
+ protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, ByteBuf byteBuf2) throws Exception { // Paper
|
|
+ int i = byteBuf.readableBytes();
|
|
+ if (i < this.threshold) {
|
|
+ VarInt.write(byteBuf2, 0);
|
|
+ byteBuf2.writeBytes(byteBuf);
|
|
+ } else {
|
|
+ // Paper start
|
|
+ if (this.deflater != null) {
|
|
+ byte[] bs = new byte[i];
|
|
+ byteBuf.readBytes(bs);
|
|
+ VarInt.write(byteBuf2, bs.length);
|
|
+ this.deflater.setInput(bs, 0, i);
|
|
+ this.deflater.finish();
|
|
+
|
|
+ while(!this.deflater.finished()) {
|
|
+ int j = this.deflater.deflate(this.encodeBuf);
|
|
+ byteBuf2.writeBytes(this.encodeBuf, 0, j);
|
|
+ }
|
|
+
|
|
+ this.deflater.reset();
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ VarInt.write(byteBuf2, i);
|
|
+ ByteBuf compatibleIn = com.velocitypowered.natives.util.MoreByteBufUtils.ensureCompatible(channelHandlerContext.alloc(), this.compressor, byteBuf);
|
|
+ try {
|
|
+ this.compressor.deflate(compatibleIn, byteBuf2);
|
|
+ } finally {
|
|
+ compatibleIn.release();
|
|
+ }
|
|
+ // Paper end
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ // Paper start
|
|
+ @Override
|
|
+ protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect) throws Exception{
|
|
+ if (this.compressor != null) {
|
|
+ // We allocate bytes to be compressed plus 1 byte. This covers two cases:
|
|
+ //
|
|
+ // - Compression
|
|
+ // According to https://github.com/ebiggers/libdeflate/blob/master/libdeflate.h#L103,
|
|
+ // if the data compresses well (and we do not have some pathological case) then the maximum
|
|
+ // size the compressed size will ever be is the input size minus one.
|
|
+ // - Uncompressed
|
|
+ // This is fairly obvious - we will then have one more than the uncompressed size.
|
|
+ int initialBufferSize = msg.readableBytes() + 1;
|
|
+ return com.velocitypowered.natives.util.MoreByteBufUtils.preferredBuffer(ctx.alloc(), this.compressor, initialBufferSize);
|
|
+ }
|
|
+
|
|
+ return super.allocateBuffer(ctx, msg, preferDirect);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
|
|
+ if (this.compressor != null) {
|
|
+ this.compressor.close();
|
|
+ }
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
+ public int getThreshold() {
|
|
+ return this.threshold;
|
|
+ }
|
|
+
|
|
+ public void setThreshold(int compressionThreshold) {
|
|
+ this.threshold = compressionThreshold;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
|
|
index 07fc88f8d057c59070e5de97b91c5bf5760ff09a..8ce56c24c0d8016b03ec626341609080e7cef232 100644
|
|
--- a/src/main/java/net/minecraft/network/Connection.java
|
|
+++ b/src/main/java/net/minecraft/network/Connection.java
|
|
@@ -573,11 +573,28 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
|
return networkmanager;
|
|
}
|
|
|
|
- public void setEncryptionKey(Cipher decryptionCipher, Cipher encryptionCipher) {
|
|
- this.encrypted = true;
|
|
- this.channel.pipeline().addBefore("splitter", "decrypt", new CipherDecoder(decryptionCipher));
|
|
- this.channel.pipeline().addBefore("prepender", "encrypt", new CipherEncoder(encryptionCipher));
|
|
+ // Paper start
|
|
+// public void setEncryptionKey(Cipher decryptionCipher, Cipher encryptionCipher) {
|
|
+// this.encrypted = true;
|
|
+// this.channel.pipeline().addBefore("splitter", "decrypt", new CipherDecoder(decryptionCipher));
|
|
+// this.channel.pipeline().addBefore("prepender", "encrypt", new CipherEncoder(encryptionCipher));
|
|
+// }
|
|
+
|
|
+ public void setupEncryption(javax.crypto.SecretKey key) throws net.minecraft.util.CryptException {
|
|
+ if (!this.encrypted) {
|
|
+ try {
|
|
+ com.velocitypowered.natives.encryption.VelocityCipher decryption = com.velocitypowered.natives.util.Natives.cipher.get().forDecryption(key);
|
|
+ com.velocitypowered.natives.encryption.VelocityCipher encryption = com.velocitypowered.natives.util.Natives.cipher.get().forEncryption(key);
|
|
+
|
|
+ this.encrypted = true;
|
|
+ this.channel.pipeline().addBefore("splitter", "decrypt", new CipherDecoder(decryption));
|
|
+ this.channel.pipeline().addBefore("prepender", "encrypt", new CipherEncoder(encryption));
|
|
+ } catch (java.security.GeneralSecurityException e) {
|
|
+ throw new net.minecraft.util.CryptException(e);
|
|
+ }
|
|
+ }
|
|
}
|
|
+ // Paper end
|
|
|
|
public boolean isEncrypted() {
|
|
return this.encrypted;
|
|
@@ -610,16 +627,17 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
|
|
|
public void setupCompression(int compressionThreshold, boolean rejectsBadPackets) {
|
|
if (compressionThreshold >= 0) {
|
|
+ com.velocitypowered.natives.compression.VelocityCompressor compressor = com.velocitypowered.natives.util.Natives.compress.get().create(io.papermc.paper.configuration.GlobalConfiguration.get().misc.compressionLevel.or(-1)); // Paper
|
|
if (this.channel.pipeline().get("decompress") instanceof CompressionDecoder) {
|
|
((CompressionDecoder) this.channel.pipeline().get("decompress")).setThreshold(compressionThreshold, rejectsBadPackets);
|
|
} else {
|
|
- this.channel.pipeline().addBefore("decoder", "decompress", new CompressionDecoder(compressionThreshold, rejectsBadPackets));
|
|
+ this.channel.pipeline().addBefore("decoder", "decompress", new CompressionDecoder(compressor, compressionThreshold, rejectsBadPackets)); // Paper
|
|
}
|
|
|
|
if (this.channel.pipeline().get("compress") instanceof CompressionEncoder) {
|
|
((CompressionEncoder) this.channel.pipeline().get("compress")).setThreshold(compressionThreshold);
|
|
} else {
|
|
- this.channel.pipeline().addBefore("encoder", "compress", new CompressionEncoder(compressionThreshold));
|
|
+ this.channel.pipeline().addBefore("encoder", "compress", new CompressionEncoder(compressor, compressionThreshold)); // Paper
|
|
}
|
|
this.channel.pipeline().fireUserEventTriggered(io.papermc.paper.network.ConnectionEvent.COMPRESSION_THRESHOLD_SET); // Paper
|
|
} else {
|
|
diff --git a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
|
index 2e7540b0e100347cd91624c9d61a5c4688a52b26..dbefe5450b9d8d9c32c30a06afd614725f890d00 100644
|
|
--- a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
|
+++ b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
|
@@ -104,6 +104,11 @@ public class ServerConnectionListener {
|
|
ServerConnectionListener.LOGGER.info("Using default channel type");
|
|
}
|
|
|
|
+ // Paper start - indicate Velocity natives in use
|
|
+ ServerConnectionListener.LOGGER.info("Paper: Using " + com.velocitypowered.natives.util.Natives.compress.getLoadedVariant() + " compression from Velocity.");
|
|
+ ServerConnectionListener.LOGGER.info("Paper: Using " + com.velocitypowered.natives.util.Natives.cipher.getLoadedVariant() + " cipher from Velocity.");
|
|
+ // Paper end
|
|
+
|
|
this.channels.add(((ServerBootstrap) ((ServerBootstrap) (new ServerBootstrap()).channel(oclass)).childHandler(new ChannelInitializer<Channel>() {
|
|
protected void initChannel(Channel channel) {
|
|
Connection.setInitialProtocolAttributes(channel);
|
|
diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
|
index e0070317d7c51e239cef45c248ab7bd7544fe56c..f10647075a55630d91673ef3ccf91f9079f69d37 100644
|
|
--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
|
+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
|
@@ -222,12 +222,14 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
|
|
}
|
|
|
|
SecretKey secretkey = packet.getSecretKey(privatekey);
|
|
- Cipher cipher = Crypt.getCipher(2, secretkey);
|
|
- Cipher cipher1 = Crypt.getCipher(1, secretkey);
|
|
+ // Paper start
|
|
+// Cipher cipher = Crypt.getCipher(2, secretkey);
|
|
+// Cipher cipher1 = Crypt.getCipher(1, secretkey);
|
|
+ // Paper end
|
|
|
|
s = (new BigInteger(Crypt.digestData("", this.server.getKeyPair().getPublic(), secretkey))).toString(16);
|
|
this.state = ServerLoginPacketListenerImpl.State.AUTHENTICATING;
|
|
- this.connection.setEncryptionKey(cipher, cipher1);
|
|
+ this.connection.setupEncryption(secretkey); // Paper
|
|
} catch (CryptException cryptographyexception) {
|
|
throw new IllegalStateException("Protocol error", cryptographyexception);
|
|
}
|