diff --git a/bungee/src/main/resources/plugin.yml b/bungee/src/main/resources/plugin.yml index e5edac3c..8068498d 100644 --- a/bungee/src/main/resources/plugin.yml +++ b/bungee/src/main/resources/plugin.yml @@ -1,4 +1,4 @@ -name: ${outputName} +name: ${project.parent.version} description: ${project.description} version: ${project.version} author: ${project.organization.name} diff --git a/common/src/main/java/org/geysermc/floodgate/FloodgatePlayerImpl.java b/common/src/main/java/org/geysermc/floodgate/FloodgatePlayerImpl.java index dcc28cad..a0c16ed2 100644 --- a/common/src/main/java/org/geysermc/floodgate/FloodgatePlayerImpl.java +++ b/common/src/main/java/org/geysermc/floodgate/FloodgatePlayerImpl.java @@ -60,7 +60,7 @@ public final class FloodgatePlayerImpl implements FloodgatePlayer { @Setter private boolean login = true; - FloodgatePlayerImpl(BedrockData data, String prefix, boolean replaceSpaces) { + FloodgatePlayerImpl(BedrockData data, RawSkin skin, String prefix, boolean replaceSpaces) { FloodgateApi api = FloodgateApi.getInstance(); version = data.getVersion(); username = data.getUsername(); diff --git a/common/src/main/java/org/geysermc/floodgate/HandshakeHandler.java b/common/src/main/java/org/geysermc/floodgate/HandshakeHandler.java index ba13d1fe..e1dcfda2 100644 --- a/common/src/main/java/org/geysermc/floodgate/HandshakeHandler.java +++ b/common/src/main/java/org/geysermc/floodgate/HandshakeHandler.java @@ -26,16 +26,17 @@ package org.geysermc.floodgate; +import com.google.common.base.Charsets; import com.google.inject.Inject; import lombok.*; import org.geysermc.floodgate.api.SimpleFloodgateApi; import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.config.FloodgateConfig; +import org.geysermc.floodgate.crypto.AesCipher; import org.geysermc.floodgate.crypto.FloodgateCipher; import org.geysermc.floodgate.util.BedrockData; -import org.geysermc.floodgate.util.InvalidHeaderException; - -import java.util.Base64; +import org.geysermc.floodgate.util.InvalidFormatException; +import org.geysermc.floodgate.util.RawSkin; import static org.geysermc.floodgate.util.BedrockData.EXPECTED_LENGTH; @@ -57,35 +58,67 @@ public final class HandshakeHandler { public HandshakeResult handle(@NonNull String handshakeData) { try { - String[] data = handshakeData.split("\0"); + String[] dataArray = handshakeData.split("\0"); - boolean isBungeeData = data.length == 5; + boolean isBungeeData = dataArray.length == 5; // this can be Bungee data (without skin) or Floodgate data - if (data.length == 4) { - isBungeeData = FloodgateCipher.hasHeader(data[3]); + if (dataArray.length == 4) { + isBungeeData = FloodgateCipher.hasHeader(dataArray[3]); } - if (proxy && isBungeeData || !isBungeeData && data.length != 2) { + if (proxy && isBungeeData || !isBungeeData && dataArray.length != 2) { return ResultType.NOT_FLOODGATE_DATA.getCachedResult(); } - String decrypted = cipher.decryptToString(Base64.getDecoder().decode(data[1])); + // calculate the expected Base64 encoded IV length. + int expectedIvLength = 4 * ((AesCipher.IV_LENGTH + 2) / 3); + int lastSplitIndex = dataArray[1].lastIndexOf(0x21); + + byte[] floodgateData; + byte[] rawSkinData = null; + + // if it has a RawSkin + if (lastSplitIndex - expectedIvLength != 0) { + floodgateData = dataArray[1].substring(0, lastSplitIndex).getBytes(Charsets.UTF_8); + rawSkinData = dataArray[1].substring(lastSplitIndex + 1).getBytes(Charsets.UTF_8); + } else { + floodgateData = dataArray[1].getBytes(Charsets.UTF_8); + } + + // actual decryption + String decrypted = cipher.decryptToString(floodgateData); BedrockData bedrockData = BedrockData.fromString(decrypted); if (bedrockData.getDataLength() != EXPECTED_LENGTH) { return ResultType.INVALID_DATA_LENGTH.getCachedResult(); } + RawSkin rawSkin = null; + // only decompile the skin after knowing that the floodgateData is legit + // note that we don't store a hash or anything in the BedrockData, + // so a mitm can change skins + if (rawSkinData != null) { + rawSkin = RawSkin.decode(rawSkinData); + } + + System.out.println(rawSkin); + FloodgatePlayer player = - new FloodgatePlayerImpl(bedrockData, usernamePrefix, replaceSpaces); + new FloodgatePlayerImpl(bedrockData, rawSkin, usernamePrefix, replaceSpaces); api.addPlayer(player.getJavaUniqueId(), player); - return new HandshakeResult(ResultType.SUCCESS, data, bedrockData, player); - } catch (InvalidHeaderException headerException) { - return ResultType.NOT_FLOODGATE_DATA.getCachedResult(); + return new HandshakeResult(ResultType.SUCCESS, dataArray, bedrockData, player); + } catch (InvalidFormatException formatException) { + // only header exceptions should return 'not floodgate data', + // all the other format exceptions are because of invalid/tempered Floodgate data + if (formatException.isHeader()) { + return ResultType.NOT_FLOODGATE_DATA.getCachedResult(); + } + + formatException.printStackTrace(); + return ResultType.EXCEPTION.getCachedResult(); } catch (Exception exception) { exception.printStackTrace(); - System.out.println(handshakeData); return ResultType.EXCEPTION.getCachedResult(); } } diff --git a/common/src/main/java/org/geysermc/floodgate/module/CommonModule.java b/common/src/main/java/org/geysermc/floodgate/module/CommonModule.java index 08af75eb..ddc3e9b7 100644 --- a/common/src/main/java/org/geysermc/floodgate/module/CommonModule.java +++ b/common/src/main/java/org/geysermc/floodgate/module/CommonModule.java @@ -42,10 +42,7 @@ import org.geysermc.floodgate.config.FloodgateConfig; import org.geysermc.floodgate.config.loader.ConfigLoader; import org.geysermc.floodgate.config.updater.ConfigFileUpdater; import org.geysermc.floodgate.config.updater.ConfigUpdater; -import org.geysermc.floodgate.crypto.AesCipher; -import org.geysermc.floodgate.crypto.AesKeyProducer; -import org.geysermc.floodgate.crypto.FloodgateCipher; -import org.geysermc.floodgate.crypto.KeyProducer; +import org.geysermc.floodgate.crypto.*; import org.geysermc.floodgate.inject.CommonPlatformInjector; import org.geysermc.floodgate.link.PlayerLinkLoader; @@ -70,7 +67,7 @@ public final class CommonModule extends AbstractModule { @Provides @Singleton public FloodgateCipher cipher() { - return new AesCipher(); + return new AesCipher(new Base64Topping()); } @Provides diff --git a/pom.xml b/pom.xml index f892bb3a..5ca6af6f 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ https://github.com/GeyserMC/Floodgate - 1.0.0 + 1.1.0 1.8.8-R0.1-SNAPSHOT 1.15-SNAPSHOT 1.1.0-SNAPSHOT diff --git a/spigot/src/main/resources/plugin.yml b/spigot/src/main/resources/plugin.yml index 36dc6876..aa216a9d 100644 --- a/spigot/src/main/resources/plugin.yml +++ b/spigot/src/main/resources/plugin.yml @@ -1,4 +1,4 @@ -name: ${outputName} +name: ${project.parent.name} description: ${project.description} version: ${project.version} author: ${project.organization.name} diff --git a/velocity/src/main/resources/velocity-plugin.json b/velocity/src/main/resources/velocity-plugin.json index 872b82e7..bf949f5e 100644 --- a/velocity/src/main/resources/velocity-plugin.json +++ b/velocity/src/main/resources/velocity-plugin.json @@ -1 +1 @@ -{"id": "${project.parent.name}", "name": "${outputName}", "version": "${project.version}", "description": "${project.description}", "url": "${project.url}", "authors": ["${project.organization.name}"], "main": "org.geysermc.floodgate.VelocityPlugin"} \ No newline at end of file +{"id": "${project.parent.name}", "name": "${project.parent.name}", "version": "${project.version}", "description": "${project.description}", "url": "${project.url}", "authors": ["${project.organization.name}"], "main": "org.geysermc.floodgate.VelocityPlugin"} \ No newline at end of file