aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0736-Use-Velocity-compression-and-cipher-natives.patch
blob: a37ddabfc883fb2cd73703ee6c9e058a377d39d7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
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 a02f53c6ee0111e07d78a718a6ca0ec708f70cfc..fb6bfd4967b4ec113463cfaa77e621183f93e441 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -29,6 +29,11 @@ dependencies {
     implementation("org.spongepowered:configurate-yaml:4.1.2") // Paper - config files
     implementation("commons-lang:commons-lang:2.6")
     implementation("net.fabricmc:mapping-io:0.3.0") // Paper - needed to read mappings for stacktrace deobfuscation
+    // Paper start - Use Velocity cipher
+    implementation("com.velocitypowered:velocity-native:1.1.0-SNAPSHOT") {
+        isTransitive = false
+    }
+    // Paper end
     runtimeOnly("org.xerial:sqlite-jdbc:3.36.0.3")
     runtimeOnly("mysql:mysql-connector-java:8.0.29")
     runtimeOnly("com.lmax:disruptor:3.4.4") // Paper
diff --git a/src/main/java/net/minecraft/network/CipherDecoder.java b/src/main/java/net/minecraft/network/CipherDecoder.java
index 778beb445eac5769b9e4e07b4d1294c50ae2602b..c712fb8193115e1ab71b5e40fb0ccb9413062b03 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 0f3d502a9680006bcdcd7d272240a2e5c3b46790..5dd7be70603e8754d2625bb9d16900cb01b9c730 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 b62be99c57b0a5bba0dc29809557d4d247698b13..f4d4ad983baf24d889441541d5a84dc1f49ea4d4 100644
--- a/src/main/java/net/minecraft/network/CompressionDecoder.java
+++ b/src/main/java/net/minecraft/network/CompressionDecoder.java
@@ -12,13 +12,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,6 +45,8 @@ public class CompressionDecoder extends ByteToMessageDecoder {
                     }
                 }
 
+                // Paper start
+                if (this.inflater != null) {
                 byte[] bs = new byte[friendlyByteBuf.readableBytes()];
                 friendlyByteBuf.readBytes(bs);
                 this.inflater.setInput(bs);
@@ -45,10 +54,36 @@ public class CompressionDecoder extends ByteToMessageDecoder {
                 this.inflater.inflate(cs);
                 list.add(Unpooled.wrappedBuffer(cs));
                 this.inflater.reset();
+                    return;
+                }
+
+                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
             }
         }
     }
 
+    // Paper start
+    @Override
+    public void handlerRemoved0(ChannelHandlerContext ctx) throws Exception {
+        if (this.compressor != null) {
+            this.compressor.close();
+        }
+    }
+    // Paper end
+
     public void setThreshold(int compressionThreshold, boolean rejectsBadPackets) {
         this.threshold = compressionThreshold;
         this.validateDecompressed = rejectsBadPackets;
diff --git a/src/main/java/net/minecraft/network/CompressionEncoder.java b/src/main/java/net/minecraft/network/CompressionEncoder.java
index 792883afe53d2b7989c25a81c2f9a639d5e21d20..c04379ca8a4db0f4de46ad2b3b3384310eebddf3 100644
--- a/src/main/java/net/minecraft/network/CompressionEncoder.java
+++ b/src/main/java/net/minecraft/network/CompressionEncoder.java
@@ -6,22 +6,37 @@ 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
     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;
-        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
     }
 
-    protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, ByteBuf byteBuf2) {
+    protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, ByteBuf byteBuf2) throws Exception { // Paper
         int i = byteBuf.readableBytes();
         FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(byteBuf2);
         if (i < this.threshold) {
             friendlyByteBuf.writeVarInt(0);
             friendlyByteBuf.writeBytes(byteBuf);
         } else {
+            // Paper start
+            if (this.deflater != null) {
             byte[] bs = new byte[i];
             byteBuf.readBytes(bs);
             friendlyByteBuf.writeVarInt(bs.length);
@@ -34,10 +49,48 @@ public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
             }
 
             this.deflater.reset();
+                return;
+            }
+
+            friendlyByteBuf.writeVarInt(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;
     }
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
index 957fc22a6c79fd480fefc3cce9cc374d25bc8cf9..6967c90c50ea75fb9dd5da808b2c8c8ea046ecec 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
@@ -681,11 +681,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;
@@ -714,16 +731,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(-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 b80aedd2002959b4026c27ce76b3ed17f0acfb5b..2985271132c9ae822dcb0d7a7e6f0c268d1736cc 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 - 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) {
                     try {
diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
index 76d6572429548d90b5efa3029f33923a81f54c3f..26781be1a01b0683f4d0f3cf565c3a623c987606 100644
--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
@@ -305,12 +305,14 @@ public class ServerLoginPacketListenerImpl implements TickablePacketListener, Se
             }
 
             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);
         }