Skip to content

Commit

Permalink
Added RawSkins, Toppings and renamed the Floodgate plugin name
Browse files Browse the repository at this point in the history
  • Loading branch information
Tim203 committed Sep 19, 2020
1 parent bb20b14 commit 7fbc401
Show file tree
Hide file tree
Showing 16 changed files with 260 additions and 103 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public class GeyserBungeeConfiguration extends GeyserJacksonConfiguration {
private Path floodgateKey;

public void loadFloodgate(GeyserBungeePlugin plugin, Configuration configuration) {
Plugin floodgate = plugin.getProxy().getPluginManager().getPlugin("floodgate-bungee");
Plugin floodgate = plugin.getProxy().getPluginManager().getPlugin("floodgate");
floodgateKey = FloodgateKeyLoader.getKey(plugin.getGeyserLogger(), this, Paths.get(plugin.getDataFolder().toString(), configuration.getString("floodgate-key-file"), "public-key.pem"), floodgate, floodgate != null ? floodgate.getDataFolder().toPath() : null);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public void onEnable() {
this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);

if (geyserConfig.getRemote().getAuthType().equals("floodgate") && getProxy().getPluginManager().getPlugin("floodgate-bungee") == null) {
if (geyserConfig.getRemote().getAuthType().equals("floodgate") && getProxy().getPluginManager().getPlugin("floodgate") == null) {
geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.disabling"));
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public class GeyserSpigotConfiguration extends GeyserJacksonConfiguration {
private Path floodgateKey;

public void loadFloodgate(GeyserSpigotPlugin plugin) {
Plugin floodgate = Bukkit.getPluginManager().getPlugin("floodgate-spigot");
Plugin floodgate = Bukkit.getPluginManager().getPlugin("floodgate");
floodgateKey = FloodgateKeyLoader.getKey(plugin.getGeyserLogger(), this, Paths.get(plugin.getDataFolder().toString(), plugin.getConfig().getString("floodgate-key-file", "public-key.pem")), floodgate, floodgate != null ? floodgate.getDataFolder().toPath() : null);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public void onEnable() {
this.geyserLogger = new GeyserSpigotLogger(getLogger(), geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);

if (geyserConfig.getRemote().getAuthType().equals("floodgate") && Bukkit.getPluginManager().getPlugin("floodgate-spigot") == null) {
if (geyserConfig.getRemote().getAuthType().equals("floodgate") && Bukkit.getPluginManager().getPlugin("floodgate") == null) {
geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.disabling"));
this.getPluginLoader().disablePlugin(this);
return;
Expand Down
49 changes: 38 additions & 11 deletions common/src/main/java/org/geysermc/floodgate/crypto/AesCipher.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

package org.geysermc.floodgate.crypto;

import org.geysermc.floodgate.util.InvalidHeaderException;
import lombok.RequiredArgsConstructor;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
Expand All @@ -35,12 +35,14 @@
import java.security.Key;
import java.security.SecureRandom;

@RequiredArgsConstructor
public final class AesCipher implements FloodgateCipher {
private static final int IV_LENGTH = 12;
public static final int IV_LENGTH = 12;
private static final int TAG_BIT_LENGTH = 128;
private static final String CIPHER_NAME = "AES/GCM/NoPadding";

private final SecureRandom secureRandom = new SecureRandom();
private final Topping topping;
private SecretKey secretKey;

public void init(Key key) {
Expand All @@ -62,32 +64,57 @@ public byte[] encrypt(byte[] data) throws Exception {
cipher.init(Cipher.ENCRYPT_MODE, secretKey, spec);
byte[] cipherText = cipher.doFinal(data);

return ByteBuffer.allocate(iv.length + cipherText.length + HEADER_LENGTH)
.put(IDENTIFIER).put(VERSION) // header
if (topping != null) {
iv = topping.encode(iv);
cipherText = topping.encode(cipherText);
}

return ByteBuffer.allocate(iv.length + cipherText.length + HEADER_LENGTH + 1)
.put(IDENTIFIER) // header
.put(iv)
.put((byte) 0x21)
.put(cipherText)
.array();
}

public byte[] decrypt(byte[] cipherTextWithIv) throws Exception {
HeaderResult pair = checkHeader(cipherTextWithIv);
if (pair.getVersion() != VERSION) {
throw new InvalidHeaderException(
"Expected version " + VERSION + ", got " + pair.getVersion()
);
}
checkHeader(cipherTextWithIv);

Cipher cipher = Cipher.getInstance(CIPHER_NAME);

int bufferLength = cipherTextWithIv.length - HEADER_LENGTH;
ByteBuffer buffer = ByteBuffer.wrap(cipherTextWithIv, HEADER_LENGTH, bufferLength);

byte[] iv = new byte[IV_LENGTH];
int ivLength = IV_LENGTH;

if (topping != null) {
int mark = buffer.position();

// we need the first index, the second is for the optional RawSkin
boolean found = false;
while (buffer.hasRemaining() && !found) {
if (buffer.get() == 0x21) {
found = true;
}
}

ivLength = buffer.position() - mark - 1; // don't include the splitter itself
buffer.position(mark); // reset to the pre-while index
}

byte[] iv = new byte[ivLength];
buffer.get(iv);

buffer.position(buffer.position() + 1); // skip splitter

byte[] cipherText = new byte[buffer.remaining()];
buffer.get(cipherText);

if (topping != null) {
iv = topping.decode(iv);
cipherText = topping.decode(cipherText);
}

GCMParameterSpec spec = new GCMParameterSpec(TAG_BIT_LENGTH, iv);
cipher.init(Cipher.DECRYPT_MODE, secretKey, spec);
return cipher.doFinal(cipherText);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/

package org.geysermc.floodgate.crypto;

import java.util.Base64;

public final class Base64Topping implements Topping {
@Override
public byte[] encode(byte[] data) {
return Base64.getEncoder().encode(data);
}

@Override
public byte[] decode(byte[] data) {
return Base64.getDecoder().decode(data);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

import lombok.AllArgsConstructor;
import lombok.Data;
import org.geysermc.floodgate.util.InvalidHeaderException;
import org.geysermc.floodgate.util.InvalidFormatException;

import java.nio.charset.StandardCharsets;
import java.security.Key;
Expand All @@ -38,9 +38,7 @@
*/
public interface FloodgateCipher {
byte[] IDENTIFIER = "Floodgate".getBytes(StandardCharsets.UTF_8);
byte VERSION = 2;

int HEADER_LENGTH = IDENTIFIER.length + 1; // one byte for version
int HEADER_LENGTH = IDENTIFIER.length;

/**
* Initializes the instance by giving it the key it needs to encrypt or decrypt data
Expand Down Expand Up @@ -110,19 +108,20 @@ default byte[] decryptFromString(String data) throws Exception {
}

/**
* Checks if the header is valid and return a IntPair containing the header version
* and the index to start reading the actual encrypted data from.
* Checks if the header is valid.
* This method will throw an InvalidFormatException when the header is invalid.
*
* @param data the data to check
* @return IntPair. x = version number, y = the index to start reading from.
* @throws InvalidHeaderException when the header is invalid
* @throws InvalidFormatException when the header is invalid
*/
default HeaderResult checkHeader(byte[] data) throws InvalidHeaderException {
default void checkHeader(byte[] data) throws InvalidFormatException {
final int identifierLength = IDENTIFIER.length;

if (data.length <= HEADER_LENGTH) {
throw new InvalidHeaderException("Data length is smaller then header." +
"Needed " + HEADER_LENGTH + ", got " + data.length);
throw new InvalidFormatException("Data length is smaller then header." +
"Needed " + HEADER_LENGTH + ", got " + data.length,
true
);
}

for (int i = 0; i < identifierLength; i++) {
Expand All @@ -132,15 +131,15 @@ default HeaderResult checkHeader(byte[] data) throws InvalidHeaderException {
receivedIdentifier.append(b);
}

throw new InvalidHeaderException(String.format(
"Expected identifier %s, got %s",
new String(IDENTIFIER, StandardCharsets.UTF_8),
receivedIdentifier.toString()
));
throw new InvalidFormatException(
String.format("Expected identifier %s, got %s",
new String(IDENTIFIER, StandardCharsets.UTF_8),
receivedIdentifier.toString()
),
true
);
}
}

return new HeaderResult(data[identifierLength], HEADER_LENGTH);
}

static boolean hasHeader(String data) {
Expand Down
31 changes: 31 additions & 0 deletions common/src/main/java/org/geysermc/floodgate/crypto/Topping.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/

package org.geysermc.floodgate.crypto;

public interface Topping {
byte[] encode(byte[] data);
byte[] decode(byte[] data);
}
20 changes: 7 additions & 13 deletions common/src/main/java/org/geysermc/floodgate/util/BedrockData.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,25 +50,23 @@ public final class BedrockData {
private final LinkedPlayer linkedPlayer;
private final int dataLength;

private RawSkin skin;

public BedrockData(String version, String username, String xuid, int deviceOs,
String languageCode, int uiProfile, int inputMode, String ip,
LinkedPlayer linkedPlayer, RawSkin skin) {
LinkedPlayer linkedPlayer) {
this(version, username, xuid, deviceOs, languageCode,
inputMode, uiProfile, ip, linkedPlayer, EXPECTED_LENGTH, skin);
inputMode, uiProfile, ip, linkedPlayer, EXPECTED_LENGTH);
}

public BedrockData(String version, String username, String xuid, int deviceOs,
String languageCode, int uiProfile, int inputMode, String ip, RawSkin skin) {
this(version, username, xuid, deviceOs, languageCode, uiProfile, inputMode, ip, null, skin);
String languageCode, int uiProfile, int inputMode, String ip) {
this(version, username, xuid, deviceOs, languageCode, uiProfile, inputMode, ip, null);
}

public boolean hasPlayerLink() {
return linkedPlayer != null;
}

public static BedrockData fromString(String data, String skin) {
public static BedrockData fromString(String data) {
String[] split = data.split("\0");
if (split.length != EXPECTED_LENGTH) {
return emptyData(split.length);
Expand All @@ -79,14 +77,10 @@ public static BedrockData fromString(String data, String skin) {
return new BedrockData(
split[0], split[1], split[2], Integer.parseInt(split[3]), split[4],
Integer.parseInt(split[5]), Integer.parseInt(split[6]), split[7],
linkedPlayer, split.length, RawSkin.parse(skin)
linkedPlayer, split.length
);
}

public static BedrockData fromRawData(byte[] data, String skin) {
return fromString(new String(data), skin);
}

@Override
public String toString() {
// The format is the same as the order of the fields in this class
Expand All @@ -96,6 +90,6 @@ public String toString() {
}

private static BedrockData emptyData(int dataLength) {
return new BedrockData(null, null, null, -1, null, -1, -1, null, null, dataLength, null);
return new BedrockData(null, null, null, -1, null, -1, -1, null, null, dataLength);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,26 @@

package org.geysermc.floodgate.util;

public class InvalidHeaderException extends Exception {
public InvalidHeaderException() {
import lombok.Getter;

@Getter
public class InvalidFormatException extends Exception {
private boolean header = false;

public InvalidFormatException() {
super();
}

public InvalidHeaderException(String message) {
public InvalidFormatException(String message) {
super(message);
}

public InvalidFormatException(String message, boolean header) {
super(message);
this.header = header;
}

public InvalidHeaderException(String message, Throwable cause) {
public InvalidFormatException(String message, Throwable cause) {
super(message, cause);
}
}
Loading

0 comments on commit 7fbc401

Please sign in to comment.