Paper/patches/server/1017-Use-Velocity-compression-and-cipher-natives.patch
Owen 89d51d5f29
Allow enabling sand duping (#10191)
Because this exploit has been widely known for years and has not been fixed by Mojang, we decided that it was worth allowing people to toggle it on/off due to how easy it is to make it configurable.

It should be noted that this decision does not promise all future exploits will be configurable.
2024-03-03 17:05:34 -05:00

361 lines
19 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 b5f31e210f2ca4bdfe9957d44780d89240da6269..241808d8619e17c0681f79acbbc98af5bf52dd89 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -40,6 +40,11 @@ dependencies {
runtimeOnly("org.xerial:sqlite-jdbc:3.42.0.1")
runtimeOnly("com.mysql:mysql-connector-j:8.2.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 - Use Velocity cipher
runtimeOnly("org.apache.maven:maven-resolver-provider:3.9.6")
runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.18")
diff --git a/src/main/java/net/minecraft/network/CipherDecoder.java b/src/main/java/net/minecraft/network/CipherDecoder.java
index 778beb445eac5769b9e4e07b4d1294c50ae2602b..7b895b6a626da6297f07582e96d98ecfb6c8c951 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 - Use Velocity cipher
- public CipherDecoder(Cipher cipher) {
- this.cipher = new CipherBase(cipher);
+ public CipherDecoder(com.velocitypowered.natives.encryption.VelocityCipher cipher) { // Paper - Use Velocity cipher
+ this.cipher = cipher; // Paper - Use Velocity cipher
}
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
- list.add(this.cipher.decipher(channelHandlerContext, byteBuf));
+ // Paper start - Use Velocity cipher
+ 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 - Use Velocity cipher
}
+
+ // Paper start - Use Velocity cipher
+ @Override
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
+ cipher.close();
+ }
+ // Paper end - Use Velocity cipher
}
diff --git a/src/main/java/net/minecraft/network/CipherEncoder.java b/src/main/java/net/minecraft/network/CipherEncoder.java
index 0f3d502a9680006bcdcd7d272240a2e5c3b46790..ffa1c48585fbbc1d30826d435043527f6183a3ee 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 - Use Velocity cipher; change superclass
+ private final com.velocitypowered.natives.encryption.VelocityCipher cipher; // Paper - Use Velocity cipher
- public CipherEncoder(Cipher cipher) {
- this.cipher = new CipherBase(cipher);
+ public CipherEncoder(com.velocitypowered.natives.encryption.VelocityCipher cipher) { // Paper - Use Velocity cipher
+ this.cipher = cipher; // Paper - Use Velocity cipher
}
- 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 - Use Velocity cipher
+ 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 - Use Velocity cipher
}
+
+ // Paper start - Use Velocity cipher
+ @Override
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
+ cipher.close();
+ }
+ // Paper end - Use Velocity cipher
}
diff --git a/src/main/java/net/minecraft/network/CompressionDecoder.java b/src/main/java/net/minecraft/network/CompressionDecoder.java
index 2758c257cb4e2b0497bd9243c635f8fe3dbc414c..2763052eb2d5b8eb432022ab00a417976f4b2927 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 - Use Velocity cipher
private int threshold;
private boolean validateDecompressed;
+ // Paper start - Use Velocity cipher
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 - Use Velocity cipher
}
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 cipher; fallback to vanilla inflater
this.setupInflaterInput(byteBuf);
ByteBuf byteBuf2 = this.inflate(channelHandlerContext, i);
this.inflater.reset();
list.add(byteBuf2);
+ return; // Paper - Use Velocity cipher
+ } // Paper - use velocity compression
+
+ // Paper start - Use Velocity cipher
+ 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 cipher
}
}
}
+ // Paper start - Use Velocity cipher
+ @Override
+ public void handlerRemoved0(ChannelHandlerContext ctx) throws Exception {
+ if (this.compressor != null) {
+ this.compressor.close();
+ }
+ }
+ // Paper end - Use Velocity cipher
+
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 859af8c845bae9781a62fa4acae56c6e2d449e10..f67f59f287d9a5cdd685b6b56ed1daf3f091e099 100644
--- a/src/main/java/net/minecraft/network/CompressionEncoder.java
+++ b/src/main/java/net/minecraft/network/CompressionEncoder.java
@@ -6,21 +6,36 @@ 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 byte[] encodeBuf; // Paper - Use Velocity cipher
private final Deflater deflater;
+ private final com.velocitypowered.natives.compression.VelocityCompressor compressor; // Paper - Use Velocity cipher
private int threshold;
+ // Paper start - Use Velocity cipher
public CompressionEncoder(int compressionThreshold) {
+ this(null, compressionThreshold);
+ }
+ public CompressionEncoder(com.velocitypowered.natives.compression.VelocityCompressor compressor, int compressionThreshold) {
this.threshold = compressionThreshold;
- this.deflater = new Deflater();
+ if (compressor == null) {
+ this.encodeBuf = new byte[8192];
+ this.deflater = new Deflater();
+ } else {
+ this.encodeBuf = null;
+ this.deflater = null;
+ }
+ this.compressor = compressor;
+ // Paper end - Use Velocity cipher
}
- protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, ByteBuf byteBuf2) {
+ protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, ByteBuf byteBuf2) throws Exception { // Paper - Use Velocity cipher
int i = byteBuf.readableBytes();
if (i < this.threshold) {
VarInt.write(byteBuf2, 0);
byteBuf2.writeBytes(byteBuf);
} else {
+ // Paper start - Use Velocity cipher
+ if (this.deflater != null) {
byte[] bs = new byte[i];
byteBuf.readBytes(bs);
VarInt.write(byteBuf2, bs.length);
@@ -33,10 +48,48 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
}
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 - Use Velocity cipher
}
}
+ // Paper start - Use Velocity cipher
+ @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 - Use Velocity cipher
+
public int getThreshold() {
return this.threshold;
}
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
index 7f2aa5e17fe675f3404d67b1794d2ca68b188eb9..fff375fd50fa1a804636a92ded1ae55cff42977d 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
@@ -734,11 +734,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 - Use Velocity cipher
+// 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 - Use Velocity cipher
public boolean isEncrypted() {
return this.encrypted;
@@ -771,16 +788,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 - Use Velocity cipher
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 - Use Velocity cipher
}
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 - Use Velocity cipher
}
this.channel.pipeline().fireUserEventTriggered(io.papermc.paper.network.ConnectionEvent.COMPRESSION_THRESHOLD_SET); // Paper - Add Channel initialization listeners
} else {
diff --git a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
index a4a29a7ea0035ecf4c61ee8547a9eb24acb667d0..586521a2cbb1d4dcfb912029f65e4363ec7674a7 100644
--- a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
+++ b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
@@ -106,6 +106,11 @@ public class ServerConnectionListener {
ServerConnectionListener.LOGGER.info("Using default channel type");
}
+ // Paper start - Use Velocity cipher
+ 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 - Use Velocity cipher
+
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 b8d8f14c30786321949901ca5184c43bee716355..c5fa9f4d28f9a7f64a50a902ee5e631bfc00119c 100644
--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
@@ -235,12 +235,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 - Use Velocity cipher
+// Cipher cipher = Crypt.getCipher(2, secretkey);
+// Cipher cipher1 = Crypt.getCipher(1, secretkey);
+ // Paper end - Use Velocity cipher
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 - Use Velocity cipher
} catch (CryptException cryptographyexception) {
throw new IllegalStateException("Protocol error", cryptographyexception);
}