First upload

This commit is contained in:
games647
2015-09-04 19:56:58 +02:00
parent 8c8f51ca9c
commit 800f077be0
9 changed files with 619 additions and 10 deletions

View File

@@ -0,0 +1,93 @@
package com.github.games647.fastlogin;
import com.github.games647.fastlogin.listener.PlayerListener;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.github.games647.fastlogin.listener.EncryptionPacketListener;
import com.github.games647.fastlogin.listener.StartPacketListener;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.TimeUnit;
import org.bukkit.plugin.java.JavaPlugin;
public class FastLogin extends JavaPlugin {
private final KeyPair keyPair = generateKey();
private final Cache<String, PlayerData> session = CacheBuilder.newBuilder()
.expireAfterWrite(2, TimeUnit.MINUTES)
.build();
@Override
public void onEnable() {
if (!isEnabled()) {
return;
}
if (!getServer().getPluginManager().isPluginEnabled("AuthMe")
&& !getServer().getPluginManager().isPluginEnabled("xAuth")) {
getLogger().warning("No support offline Auth plugin found. ");
getLogger().warning("Disabling this plugin...");
setEnabled(false);
return;
}
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
protocolManager.addPacketListener(new EncryptionPacketListener(this, protocolManager));
protocolManager.addPacketListener(new StartPacketListener(this, protocolManager));
getServer().getPluginManager().registerEvents(new PlayerListener(this), this);
}
@Override
public void onLoad() {
//online mode is only changeable aftter a restart
if (getServer().getOnlineMode()) {
getLogger().severe("Server have to be in offline mode");
setEnabled(false);
}
generateKey();
}
private KeyPair generateKey() {
try {
KeyPairGenerator keypairgenerator = KeyPairGenerator.getInstance("RSA");
keypairgenerator.initialize(1024);
return keypairgenerator.generateKeyPair();
} catch (NoSuchAlgorithmException noSuchAlgorithmException) {
//Should be default existing in every vm
}
return null;
}
public Cache<String, PlayerData> getSession() {
return session;
}
public KeyPair getKeyPair() {
return keyPair;
}
public HttpURLConnection getConnection(String url) throws IOException {
final HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setConnectTimeout(15000);
connection.setReadTimeout(15000);
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("User-Agent", "Premium-Checker");
return connection;
}
}

View File

@@ -0,0 +1,20 @@
package com.github.games647.fastlogin;
public class PlayerData {
private final byte[] verifyToken;
private final String username;
public PlayerData(byte[] verifyToken, String username) {
this.username = username;
this.verifyToken = verifyToken;
}
public byte[] getVerifyToken() {
return verifyToken;
}
public String getUsername() {
return username;
}
}

View File

@@ -0,0 +1,145 @@
package com.github.games647.fastlogin.listener;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.injector.server.SocketInjector;
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.github.games647.fastlogin.FastLogin;
import com.github.games647.fastlogin.PlayerData;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.security.PrivateKey;
import java.util.Arrays;
import java.util.logging.Level;
import javax.crypto.SecretKey;
import net.minecraft.server.v1_8_R3.MinecraftEncryption;
import net.minecraft.server.v1_8_R3.NetworkManager;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
public class EncryptionPacketListener extends PacketAdapter {
private static final String HAS_JOINED_URL = "https://sessionserver.mojang.com/session/minecraft/hasJoined?";
private final ProtocolManager protocolManager;
private final FastLogin fastLogin;
public EncryptionPacketListener(FastLogin plugin, ProtocolManager protocolManger) {
super(params(plugin, PacketType.Login.Client.ENCRYPTION_BEGIN).optionAsync());
this.fastLogin = plugin;
this.protocolManager = protocolManger;
}
/*
* C->S : Handshake State=2
* C->S : Login Start
* S->C : Encryption Key Request
* (Client Auth)
* C->S : Encryption Key Response
* (Server Auth, Both enable encryption)
* S->C : Login Success (*)
*/
@Override
public void onPacketReceiving(PacketEvent event) {
PacketContainer packet = event.getPacket();
Player player = event.getPlayer();
final byte[] sharedSecret = packet.getByteArrays().read(0);
byte[] clientVerify = packet.getByteArrays().read(1);
PrivateKey privateKey = fastLogin.getKeyPair().getPrivate();
String addressString = player.getAddress().toString();
PlayerData cachedEntry = fastLogin.getSession().asMap().get(addressString);
byte[] serverVerify = cachedEntry.getVerifyToken();
if (!Arrays.equals(serverVerify, MinecraftEncryption.b(privateKey, clientVerify))) {
player.kickPlayer("Invalid token");
event.setCancelled(true);
return;
}
//encrypt all following packets
NetworkManager networkManager = getNetworkManager(event);
SecretKey loginKey = MinecraftEncryption.a(privateKey, sharedSecret);
networkManager.a(loginKey);
String serverId = (new BigInteger(MinecraftEncryption.a("", fastLogin.getKeyPair().getPublic(), loginKey)))
.toString(16);
String username = cachedEntry.getUsername();
if (!hasJoinedServer(username, serverId)) {
//user tried to fake a authentification
player.kickPlayer("Invalid session");
event.setCancelled(true);
return;
}
//fake a new login packet
PacketContainer startPacket = protocolManager.createPacket(PacketType.Login.Client.START, true);
WrappedGameProfile fakeProfile = WrappedGameProfile.fromOfflinePlayer(Bukkit.getOfflinePlayer(username));
startPacket.getGameProfiles().write(0, fakeProfile);
try {
protocolManager.recieveClientPacket(event.getPlayer(), startPacket, false);
} catch (InvocationTargetException | IllegalAccessException ex) {
plugin.getLogger().log(Level.WARNING, null, ex);
}
event.setCancelled(true);
}
private NetworkManager getNetworkManager(PacketEvent event) throws IllegalArgumentException {
SocketInjector injector = TemporaryPlayerFactory.getInjectorFromPlayer(event.getPlayer());
NetworkManager networkManager = null;
try {
Field declaredField = injector.getClass().getDeclaredField("injector");
declaredField.setAccessible(true);
Object rawInjector = declaredField.get(injector);
declaredField = rawInjector.getClass().getDeclaredField("networkManager");
declaredField.setAccessible(true);
networkManager = (NetworkManager) declaredField.get(rawInjector);
} catch (IllegalAccessException | NoSuchFieldException ex) {
plugin.getLogger().log(Level.WARNING, null, ex);
}
return networkManager;
}
private boolean hasJoinedServer(String username, String serverId) {
try {
String url = HAS_JOINED_URL + "username=" + username + "&serverId=" + serverId;
HttpURLConnection conn = fastLogin.getConnection(url);
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = reader.readLine();
if (!line.equals("null")) {
JSONObject object = (JSONObject) JSONValue.parse(line);
String uuid = (String) object.get("id");
String name = (String) object.get("name");
return true;
}
} catch (IOException ex) {
plugin.getLogger().log(Level.WARNING, null, ex);
}
return false;
}
}

View File

@@ -0,0 +1,59 @@
package com.github.games647.fastlogin.listener;
import com.github.games647.fastlogin.FastLogin;
import de.luricos.bukkit.xAuth.xAuth;
import de.luricos.bukkit.xAuth.xAuthPlayer;
import de.luricos.bukkit.xAuth.xAuthPlayer.Status;
import fr.xephi.authme.api.NewAPI;
import fr.xephi.authme.cache.limbo.LimboCache;
import java.sql.Timestamp;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
public class PlayerListener implements Listener {
private final FastLogin plugin;
public PlayerListener(FastLogin plugin) {
this.plugin = plugin;
}
@EventHandler(ignoreCancelled = true)
public void onJoin(PlayerJoinEvent joinEvent) {
final Player player = joinEvent.getPlayer();
String address = player.getAddress().toString();
if (plugin.getSession().asMap().containsKey(address)) {
Bukkit.getScheduler().runTaskLater(plugin, () -> {
doLogin(player);
}, 1 * 20L);
}
}
private void doLogin(Player player) {
if (Bukkit.getPluginManager().isPluginEnabled("AuthMe")) {
//add cache entry - otherwise loggin wouldn't work
LimboCache.getInstance().addLimboPlayer(player);
//skips registration and login
NewAPI.getInstance().forceLogin(player);
} else if (Bukkit.getPluginManager().isPluginEnabled("xAuth")) {
xAuth xAuthPlugin = xAuth.getPlugin();
xAuthPlayer xAuthPlayer = xAuthPlugin.getPlayerManager().getPlayer(player);
xAuthPlayer.setPremium(true);
xAuthPlugin.getAuthClass(xAuthPlayer).online(xAuthPlayer.getName());
xAuthPlayer.setLoginTime(new Timestamp(System.currentTimeMillis()));
xAuthPlayer.setStatus(Status.AUTHENTICATED);
xAuthPlugin.getPlayerManager().unprotect(xAuthPlayer);
}
}
}

View File

@@ -0,0 +1,90 @@
package com.github.games647.fastlogin.listener;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.github.games647.fastlogin.FastLogin;
import com.github.games647.fastlogin.PlayerData;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.HttpURLConnection;
import java.security.PublicKey;
import java.util.Random;
import java.util.logging.Level;
import org.bukkit.entity.Player;
public class StartPacketListener extends PacketAdapter {
//only premium members have a uuid from there
private static final String UUID_LINK = "https://api.mojang.com/users/profiles/minecraft/";
private final ProtocolManager protocolManager;
private final FastLogin fastLogin;
private final Random random = new Random();
public StartPacketListener(FastLogin plugin, ProtocolManager protocolManger) {
super(params(plugin, PacketType.Login.Client.START).optionAsync());
this.fastLogin = plugin;
this.protocolManager = protocolManger;
}
/*
* C->S : Handshake State=2
* C->S : Login Start
* S->C : Encryption Key Request
* (Client Auth)
* C->S : Encryption Key Response
* (Server Auth, Both enable encryption)
* S->C : Login Success (*)
*/
@Override
public void onPacketReceiving(PacketEvent packetEvent) {
PacketContainer packet = packetEvent.getPacket();
Player player = packetEvent.getPlayer();
String username = packet.getGameProfiles().read(0).getName();
if (isPremium(username)) {
//do premium login process
try {
PacketContainer newPacket = protocolManager.createPacket(PacketType.Login.Server.ENCRYPTION_BEGIN, true);
//constr ServerID=""
//public key=plugin.getPublic
newPacket.getSpecificModifier(PublicKey.class).write(0, fastLogin.getKeyPair().getPublic());
byte[] verifyToken = new byte[4];
random.nextBytes(verifyToken);
newPacket.getByteArrays().write(0, verifyToken);
String addressString = player.getAddress().toString();
fastLogin.getSession().asMap().put(addressString, new PlayerData(verifyToken, username));
protocolManager.sendServerPacket(player, newPacket, false);
} catch (InvocationTargetException ex) {
plugin.getLogger().log(Level.SEVERE, null, ex);
}
//cancel only if the player is premium
packetEvent.setCancelled(true);
}
}
private boolean isPremium(String playerName) {
try {
final HttpURLConnection connection = fastLogin.getConnection(UUID_LINK + playerName);
final int responseCode = connection.getResponseCode();
return responseCode == HttpURLConnection.HTTP_OK;
} catch (IOException ex) {
plugin.getLogger().log(Level.SEVERE, null, ex);
}
return false;
}
}