From 64072d84d8bcfb50591eed03cb0ed8599d0ee5a8 Mon Sep 17 00:00:00 2001 From: Kamilkime Date: Sun, 8 Nov 2020 03:52:39 +0100 Subject: [PATCH 1/3] Update encryption to handle MC 1.16.4, fixes #412 --- .../protocollib/VerifyResponseTask.java | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java index f46aec68..2bda7152 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java @@ -13,23 +13,24 @@ import com.github.games647.craftapi.model.skin.SkinProperty; import com.github.games647.craftapi.resolver.MojangResolver; import com.github.games647.fastlogin.bukkit.BukkitLoginSession; import com.github.games647.fastlogin.bukkit.FastLoginBukkit; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import javax.crypto.Cipher; +import javax.crypto.SecretKey; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.InetAddress; import java.net.InetSocketAddress; import java.security.GeneralSecurityException; +import java.security.Key; import java.security.KeyPair; import java.security.PrivateKey; import java.util.Arrays; import java.util.Optional; import java.util.UUID; -import javax.crypto.SecretKey; - -import org.bukkit.entity.Player; - import static com.comphenix.protocol.PacketType.Login.Client.START; import static com.comphenix.protocol.PacketType.Login.Server.DISCONNECT; @@ -175,13 +176,31 @@ public class VerifyResponseTask implements Runnable { //get the NMS connection handle of this player Object networkManager = getNetworkManager(); - //try to detect the method by parameters - Method encryptMethod = FuzzyReflection - .fromObject(networkManager).getMethodByParameters("a", SecretKey.class); + try { + //try to detect the method by parameters + Method encryptMethod = FuzzyReflection + .fromObject(networkManager).getMethodByParameters("a", SecretKey.class); - //encrypt/decrypt following packets - //the client expects this behaviour - encryptMethod.invoke(networkManager, loginKey); + //encrypt/decrypt following packets + //the client expects this behaviour + encryptMethod.invoke(networkManager, loginKey); + } catch (Exception ex) { + //try to detect the method by parameters + Method encryptMethod = FuzzyReflection + .fromObject(networkManager).getMethodByParameters("a", Cipher.class, Cipher.class); + + //change Key instance into two Cipher instances + String ver = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]; + Class encryptionClass = Class.forName("net.minecraft.server." + ver + ".MinecraftEncryption"); + Method cipherMethod = encryptionClass.getMethod("a", int.class, Key.class); + + Object cipher = cipherMethod.invoke(null, 2, loginKey); + Object cipher1 = cipherMethod.invoke(null, 1, loginKey); + + //encrypt/decrypt following packets + //the client expects this behaviour + encryptMethod.invoke(networkManager, cipher, cipher1); + } } catch (Exception ex) { disconnect("error-kick", false, "Couldn't enable encryption", ex); return false; From 71d84d17485d6b62c05f003c8f2c27af27f3fca4 Mon Sep 17 00:00:00 2001 From: Kamilkime Date: Sun, 8 Nov 2020 17:15:26 +0100 Subject: [PATCH 2/3] Make encryption method reflections static --- .../protocollib/VerifyResponseTask.java | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java index 2bda7152..b54d843c 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java @@ -6,6 +6,7 @@ import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.injector.server.TemporaryPlayerFactory; import com.comphenix.protocol.reflect.FieldUtils; import com.comphenix.protocol.reflect.FuzzyReflection; +import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.wrappers.WrappedChatComponent; import com.comphenix.protocol.wrappers.WrappedGameProfile; import com.github.games647.craftapi.model.auth.Verification; @@ -13,7 +14,6 @@ import com.github.games647.craftapi.model.skin.SkinProperty; import com.github.games647.craftapi.resolver.MojangResolver; import com.github.games647.fastlogin.bukkit.BukkitLoginSession; import com.github.games647.fastlogin.bukkit.FastLoginBukkit; -import org.bukkit.Bukkit; import org.bukkit.entity.Player; import javax.crypto.Cipher; @@ -44,6 +44,9 @@ public class VerifyResponseTask implements Runnable { private final byte[] sharedSecret; + private static Method encryptMethod; + private static Method cipherMethod; + public VerifyResponseTask(FastLoginBukkit plugin, PacketEvent packetEvent, Player player, byte[] sharedSecret, KeyPair keyPair) { this.plugin = plugin; @@ -172,33 +175,31 @@ public class VerifyResponseTask implements Runnable { } private boolean enableEncryption(SecretKey loginKey) throws IllegalArgumentException { - try { - //get the NMS connection handle of this player - Object networkManager = getNetworkManager(); + if (encryptMethod == null) { + Class networkManagerClass = MinecraftReflection.getNetworkManagerClass(); try { - //try to detect the method by parameters - Method encryptMethod = FuzzyReflection - .fromObject(networkManager).getMethodByParameters("a", SecretKey.class); + encryptMethod = FuzzyReflection.fromClass(networkManagerClass) + .getMethodByParameters("a", SecretKey.class); + } catch (IllegalArgumentException exception) { + encryptMethod = FuzzyReflection.fromClass(networkManagerClass) + .getMethodByParameters("a", Cipher.class, Cipher.class); - //encrypt/decrypt following packets - //the client expects this behaviour + Class encryptionClass = MinecraftReflection.getMinecraftClass("MinecraftEncryption"); + cipherMethod = FuzzyReflection.fromClass(encryptionClass) + .getMethodByParameters("a", int.class, Key.class); + } + } + + try { + Object networkManager = this.getNetworkManager(); + + if (cipherMethod == null) { encryptMethod.invoke(networkManager, loginKey); - } catch (Exception ex) { - //try to detect the method by parameters - Method encryptMethod = FuzzyReflection - .fromObject(networkManager).getMethodByParameters("a", Cipher.class, Cipher.class); - - //change Key instance into two Cipher instances - String ver = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]; - Class encryptionClass = Class.forName("net.minecraft.server." + ver + ".MinecraftEncryption"); - Method cipherMethod = encryptionClass.getMethod("a", int.class, Key.class); - + } else { Object cipher = cipherMethod.invoke(null, 2, loginKey); Object cipher1 = cipherMethod.invoke(null, 1, loginKey); - //encrypt/decrypt following packets - //the client expects this behaviour encryptMethod.invoke(networkManager, cipher, cipher1); } } catch (Exception ex) { From 1f63f91f1741c429d11a524cce2aac7332e392ed Mon Sep 17 00:00:00 2001 From: Kamilkime Date: Mon, 9 Nov 2020 13:25:16 +0100 Subject: [PATCH 3/3] Add comments, make cipher variable names clearer --- .../listener/protocollib/VerifyResponseTask.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java index b54d843c..3013a488 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java @@ -175,16 +175,20 @@ public class VerifyResponseTask implements Runnable { } private boolean enableEncryption(SecretKey loginKey) throws IllegalArgumentException { + // Initialize method reflections if (encryptMethod == null) { Class networkManagerClass = MinecraftReflection.getNetworkManagerClass(); try { + // Try to get the old (pre MC 1.16.4) encryption method encryptMethod = FuzzyReflection.fromClass(networkManagerClass) .getMethodByParameters("a", SecretKey.class); } catch (IllegalArgumentException exception) { + // Get the new encryption method encryptMethod = FuzzyReflection.fromClass(networkManagerClass) .getMethodByParameters("a", Cipher.class, Cipher.class); + // Get the needed Cipher helper method (used to generate ciphers from login key) Class encryptionClass = MinecraftReflection.getMinecraftClass("MinecraftEncryption"); cipherMethod = FuzzyReflection.fromClass(encryptionClass) .getMethodByParameters("a", int.class, Key.class); @@ -194,13 +198,17 @@ public class VerifyResponseTask implements Runnable { try { Object networkManager = this.getNetworkManager(); + // If cipherMethod is null - use old encryption (pre MC 1.16.4), otherwise use the new cipher one if (cipherMethod == null) { + // Encrypt/decrypt packet flow, this behaviour is expected by the client encryptMethod.invoke(networkManager, loginKey); } else { - Object cipher = cipherMethod.invoke(null, 2, loginKey); - Object cipher1 = cipherMethod.invoke(null, 1, loginKey); + // Create ciphers from login key + Object decryptionCipher = cipherMethod.invoke(null, Cipher.DECRYPT_MODE, loginKey); + Object encryptionCipher = cipherMethod.invoke(null, Cipher.ENCRYPT_MODE, loginKey); - encryptMethod.invoke(networkManager, cipher, cipher1); + // Encrypt/decrypt packet flow, this behaviour is expected by the client + encryptMethod.invoke(networkManager, decryptionCipher, encryptionCipher); } } catch (Exception ex) { disconnect("error-kick", false, "Couldn't enable encryption", ex);