From 2d094b7a8ca702f79dadc5e336c833a00de7129d Mon Sep 17 00:00:00 2001 From: syeyoung Date: Fri, 5 Aug 2022 19:03:21 +0900 Subject: - Create version field on IDGLoader - Organize Main.java - Make AhUtils / DGTexturePack compatible with new loader - Keep checks consistent in Authenticator --- .../kr/syeyoung/dungeonsguide/launcher/Main.java | 86 ++++++---------------- .../launcher/authentication/Authenticator.java | 75 ++++++++++--------- .../dungeonsguide/launcher/loader/IDGLoader.java | 3 + .../dungeonsguide/launcher/loader/JarLoader.java | 38 ++++++---- .../dungeonsguide/launcher/loader/LocalLoader.java | 8 +- 5 files changed, 93 insertions(+), 117 deletions(-) (limited to 'loader') diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/Main.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/Main.java index c279d812..1df59f83 100755 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/Main.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/Main.java @@ -30,7 +30,6 @@ import kr.syeyoung.dungeonsguide.launcher.loader.IDGLoader; import kr.syeyoung.dungeonsguide.launcher.loader.JarLoader; import kr.syeyoung.dungeonsguide.launcher.loader.LocalLoader; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiErrorScreen; import net.minecraft.client.gui.GuiMainMenu; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.resources.IReloadableResourceManager; @@ -95,8 +94,7 @@ public class Main } } catch (Exception e) { e.printStackTrace(); - lastError = e; - tryOpenError(); + setLastFatalError(e); } } } @@ -114,11 +112,8 @@ public class Main } currentLoader = null; } - public void load(IDGLoader newLoader) throws ClassNotFoundException, InstantiationException, IllegalAccessException { - if (dgInterface != null) throw new IllegalStateException("DG is loaded"); - newLoader.loadJar(authenticator); - dgInterface = newLoader.getInstance(); - currentLoader = newLoader; + private void load(IDGLoader newLoader) throws ClassNotFoundException, InstantiationException, IllegalAccessException { + partialLoad(newLoader); dgInterface.init(configDir); @@ -138,11 +133,11 @@ public class Main unload(); load(newLoader); } catch (Exception e) { - e.printStackTrace(); - lastError = e; dgInterface = null; currentLoader = null; - tryOpenError(); + + e.printStackTrace(); + setLastFatalError(e); } } @@ -209,83 +204,44 @@ public class Main @EventHandler public void preInit(FMLPreInitializationEvent preInitializationEvent) { + // setup static variables main = this; configDir = preInitializationEvent.getModConfigurationDirectory(); - ProgressManager.ProgressBar bar = null; + + // setup preinit progress bar for well, progress bar! + ProgressManager.ProgressBar bar = ProgressManager.push("DungeonsGuide", 2); try { - bar = ProgressManager.push("DungeonsGuide",2); + // Try authenticate bar.step("Authenticating..."); authenticator.repeatAuthenticate(5); + + // If authentication succeeds, obtain loader and partially load dungeons guide File f = new File(preInitializationEvent.getModConfigurationDirectory(), "loader.cfg"); Configuration configuration = new Configuration(f); - bar.step("Instantiating..."); + bar.step("Instantiating..."); partialLoad(obtainLoader(configuration)); + // Save config because... well to generate it configuration.save(); } catch (Throwable t) { - t.printStackTrace(); - lastError = t; dgInterface = null; currentLoader = null; - tryOpenError(); + + t.printStackTrace(); + setLastFatalError(t); } finally { - if (bar != null) { - while(bar.getStep() < bar.getSteps()) bar.step(""); - ProgressManager.pop(bar); - } + while(bar.getStep() < bar.getSteps()) bar.step(""); + ProgressManager.pop(bar); } ((IReloadableResourceManager) Minecraft.getMinecraft().getResourceManager()).registerReloadListener(a -> { if (dgInterface != null) dgInterface.onResourceReload(a); }); -// try { -// token = authenticator.authenticateAndDownload(this.getClass().getResourceAsStream("/kr/syeyoung/dungeonsguide/DungeonsGuide.class") == null ? System.getProperty("dg.version") == null ? "nlatest" : System.getProperty("dg.version") : null); -// if (token != null) { -// main = this; -// URL.setURLStreamHandlerFactory(new DGStreamHandlerFactory(authenticator)); -// LaunchClassLoader classLoader = (LaunchClassLoader) Main.class.getClassLoader(); -// classLoader.addURL(new URL("z:///")); -// -// try { -// progressBar.step("Initializing"); -// this.dgInterface = new DungeonsGuide(authenticator); -// this.dgInterface.pre(preInitializationEvent); -// while (progressBar.getStep() < progressBar.getSteps()) -// progressBar.step("random-"+progressBar.getStep()); -// ProgressManager.pop(progressBar); -// isLoaded = true; -// } catch (Throwable e) { -// cause = e; -// ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); -// PrintStream printStream = new PrintStream(byteArrayOutputStream); -// e.printStackTrace(printStream); -// stacktrace = new String(byteArrayOutputStream.toByteArray()); -// -// while (progressBar.getStep() < progressBar.getSteps()) -// progressBar.step("random-"+progressBar.getStep()); -// ProgressManager.pop(progressBar); -// -// e.printStackTrace(); -// } -// } -// } catch (IOException | AuthenticationException | NoSuchAlgorithmException | CertificateException | KeyStoreException | KeyManagementException | InvalidKeySpecException | SignatureException e) { -// cause = e; -// ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); -// PrintStream printStream = new PrintStream(byteArrayOutputStream); -// e.printStackTrace(printStream); -// stacktrace = new String(byteArrayOutputStream.toByteArray()); -// -// while (progressBar.getStep() < progressBar.getSteps()) -// progressBar.step("random-"+progressBar.getStep()); -// ProgressManager.pop(progressBar); -// -// e.printStackTrace(); -// } } - public void setLastError(Throwable t) { + public void setLastFatalError(Throwable t) { lastError = t; tryOpenError(); } diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/authentication/Authenticator.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/authentication/Authenticator.java index 627a058f..d06e3a39 100755 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/authentication/Authenticator.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/authentication/Authenticator.java @@ -33,7 +33,6 @@ import org.json.JSONObject; import sun.reflect.Reflection; import javax.crypto.*; -import javax.net.ssl.*; import java.io.*; import java.math.BigInteger; import java.net.*; @@ -46,7 +45,7 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Authenticator { - private String token; + private String dgAccessToken; private Instant validThru; @Getter private TokenStatus tokenStatus = TokenStatus.UNAUTHENTICATED; @@ -60,21 +59,21 @@ public class Authenticator { } public String getRawToken() { - return token; + return dgAccessToken; } public String getUnexpiredToken() { if (tokenStatus != TokenStatus.AUTHENTICATED) throw new IllegalStateException("Token is not available"); - long expiry = getJwtPayload(token).getLong("exp"); + long expiry = getJwtPayload(dgAccessToken).getLong("exp"); if (System.currentTimeMillis() >= expiry-2000 || tokenStatus == TokenStatus.EXPIRED) { tokenStatus = TokenStatus.EXPIRED; try { repeatAuthenticate(5); } catch (Throwable t) { - Main.getMain().setLastError(t); + Main.getMain().setLastFatalError(t); throw new TokenExpiredException(t); } } - return token; + return dgAccessToken; } @@ -99,25 +98,24 @@ public class Authenticator { } cnt++; } - return token; + return dgAccessToken; } public String reauthenticate() throws IOException, AuthenticationException, NoSuchAlgorithmException { try { authenticationLock.lock(); - MinecraftSessionService yggdrasilMinecraftSessionService = Minecraft.getMinecraft().getSessionService(); - Session session = Minecraft.getMinecraft().getSession(); - tokenStatus = TokenStatus.UNAUTHENTICATED; - token = null; - token = requestAuth(session.getProfile().getId(), session.getProfile().getName()); - JSONObject d = getJwtPayload(token); + dgAccessToken = null; - byte[] sharedSecret = generateSharedSecret(); + MinecraftSessionService yggdrasilMinecraftSessionService = Minecraft.getMinecraft().getSessionService(); + Session SECURE_USER_SESSION = Minecraft.getMinecraft().getSession(); + dgAccessToken = requestAuth(SECURE_USER_SESSION.getProfile().getId(), SECURE_USER_SESSION.getProfile().getName()); // id: uuid, name: username - String hash = calculateServerHash(sharedSecret, - Base64.decodeBase64(d.getString("publicKey"))); + JSONObject d = getJwtPayload(dgAccessToken); + byte[] sharedSecret = generateSharedSecret(); // Notice.... shared secret is generated on the client side unlike dg 3.0. Yep, I was a stupid when making 3.0. + + String hash = calculateServerHash(sharedSecret, Base64.decodeBase64(d.getString("publicKey"))); // Public Key here is server's public key. byte[] encodedSharedSecret; try { @@ -128,17 +126,19 @@ public class Authenticator { InvalidKeySpecException | InvalidKeyException e) { throw new RuntimeException(e); - } + } // Server connection is SSL but I still encrypt it using publicKey. Additional layer of security considering the request goes through cloudflare. (it's not like I don't trust cloudflare, but idk) - yggdrasilMinecraftSessionService.joinServer(session.getProfile(), session.getToken(), hash); // Sent to "MOJANG" Server. - JSONObject furtherStuff = verifyAuth(token, encodedSharedSecret); - token = furtherStuff.getString("jwt"); + yggdrasilMinecraftSessionService.joinServer(SECURE_USER_SESSION.getProfile(), SECURE_USER_SESSION.getToken(), hash); // Sent to "MOJANG" Server. + + JSONObject furtherStuff = verifyAuth(dgAccessToken, encodedSharedSecret); + + dgAccessToken = furtherStuff.getString("jwt"); if ("TOS_PRIVACY_POLICY_ACCEPT_REQUIRED".equals(furtherStuff.getString("result"))) { tokenStatus = TokenStatus.PP_REQUIRED; throw new PrivacyPolicyRequiredException(); } tokenStatus = TokenStatus.AUTHENTICATED; - return this.token; + return this.dgAccessToken; } finally { authenticationLock.unlock(); } @@ -148,14 +148,14 @@ public class Authenticator { try { authenticationLock.lock(); if (tokenStatus != TokenStatus.PP_REQUIRED) throw new IllegalStateException("Already accepted TOS"); - JSONObject furtherStuff = acceptPrivacyPolicy(token); - token = furtherStuff.getString("jwt"); + JSONObject furtherStuff = acceptPrivacyPolicy(dgAccessToken); + dgAccessToken = furtherStuff.getString("jwt"); if ("TOS_PRIVACY_POLICY_ACCEPT_REQUIRED".equals(furtherStuff.getString("result"))) { tokenStatus = TokenStatus.PP_REQUIRED; throw new PrivacyPolicyRequiredException(); } tokenStatus = TokenStatus.AUTHENTICATED; - return this.token; + return this.dgAccessToken; } finally { authenticationLock.unlock(); } @@ -186,20 +186,22 @@ public class Authenticator { } } } - private JSONObject verifyAuth(String tempToken, byte[] secret) throws IOException { + private JSONObject verifyAuth(String tempToken, byte[] encryptedSecret) throws IOException { HttpURLConnection urlConnection = request("POST", "/auth/v2/authenticate"); - urlConnection.getOutputStream().write(("{\"jwt\":\""+tempToken+"\",\"sharedSecret\":\""+Base64.encodeBase64URLSafeString(secret)+"}").getBytes()); + urlConnection.getOutputStream().write(("{\"jwt\":\""+tempToken+"\",\"sharedSecret\":\""+Base64.encodeBase64URLSafeString(encryptedSecret)+"}").getBytes()); try (InputStream is = obtainInputStream(urlConnection)) { String payload = String.join("\n", IOUtils.readLines(is)); if (urlConnection.getResponseCode() != 200) System.out.println("/auth/authenticate :: Received " + urlConnection.getResponseCode() + " along with\n" + payload); - JSONObject jsonObject = new JSONObject(payload); - if (!"Success".equals(jsonObject.getString("status"))) { - throw new AuthServerException(jsonObject); + JSONObject json = new JSONObject(payload); + + if ("Success".equals(json.getString("status"))) { + return json.getJSONObject("data"); + } else { + throw new AuthServerException(json); } - return jsonObject.getJSONObject("data"); } } private JSONObject acceptPrivacyPolicy(String tempToken) throws IOException { @@ -211,11 +213,13 @@ public class Authenticator { if (urlConnection.getResponseCode() != 200) System.out.println("/auth/authenticate :: Received " + urlConnection.getResponseCode() + " along with\n" + payload); - JSONObject jsonObject = new JSONObject(payload); - if (!"Success".equals(jsonObject.getString("status"))) { - throw new AuthServerException(jsonObject); + JSONObject json = new JSONObject(payload); + + if ("Success".equals(json.getString("status"))) { + return json.getJSONObject("data"); + } else { + throw new AuthServerException(json); } - return jsonObject.getJSONObject("data"); } } @@ -238,9 +242,10 @@ public class Authenticator { } return inputStream; } + public HttpURLConnection request(String method, String url) throws IOException { HttpURLConnection urlConnection = (HttpURLConnection) new URL(Main.DOMAIN+url).openConnection(); - urlConnection.setRequestMethod(method); + urlConnection.setRequestMethod(method); // TODO: setup SSL certificate here, because.... SOME PEOPLE HAVE THAT ISSUE, I HAVE NO IDEA WHY THEY DONT HAVE CLOUDFLARE CERTS INSTALLED ON THEM urlConnection.setRequestProperty("User-Agent", "DungeonsGuide/1.0"); urlConnection.setDoInput(true); urlConnection.setDoOutput(true); diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/IDGLoader.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/IDGLoader.java index ceff437e..53d9b70d 100644 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/IDGLoader.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/IDGLoader.java @@ -23,6 +23,8 @@ import kr.syeyoung.dungeonsguide.launcher.authentication.Authenticator; import kr.syeyoung.dungeonsguide.launcher.exceptions.ReferenceLeakedException; import net.minecraftforge.common.config.Configuration; +import java.io.InputStream; + public interface IDGLoader { void loadJar(Authenticator authenticator) throws InstantiationException, IllegalAccessException, ClassNotFoundException; DGInterface getInstance(); @@ -33,4 +35,5 @@ public interface IDGLoader { boolean isLoaded(); String strategyName(); + String version(); } diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/JarLoader.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/JarLoader.java index 2012412a..0b6cf124 100644 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/JarLoader.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/JarLoader.java @@ -23,6 +23,7 @@ import kr.syeyoung.dungeonsguide.launcher.Main; import kr.syeyoung.dungeonsguide.launcher.authentication.Authenticator; import kr.syeyoung.dungeonsguide.launcher.exceptions.ReferenceLeakedException; +import java.io.InputStream; import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; @@ -46,30 +47,27 @@ public class JarLoader implements IDGLoader { protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { - // First, check if the class has already been loaded Class c = findLoadedClass(name); if (c == null) { - long t0 = System.nanoTime(); - if (c == null) { - // If still not found, then invoke findClass in order - // to find the class. - long t1 = System.nanoTime(); - c = findClass(name); - - // this is the defining class loader; record the stats - sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); - sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); - sun.misc.PerfCounter.getFindClasses().increment(); - } try { - if (getParent() != null && c == null) { - c = getParent().loadClass(name); + if (c == null) { + long t0 = System.nanoTime(); + c = findClass(name); + + sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t0); + sun.misc.PerfCounter.getFindClasses().increment(); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } + if (getParent() != null && c == null) { + long t0 = System.nanoTime(); + c = getParent().loadClass(name); + long t1 = System.nanoTime(); + sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); + } } if (resolve) { resolveClass(c); @@ -83,11 +81,13 @@ public class JarLoader implements IDGLoader { } } + private JarClassLoader classLoader; + @Override public void loadJar(Authenticator authenticator) throws ClassNotFoundException, InstantiationException, IllegalAccessException { if (dgInterface != null) throw new IllegalStateException("Already loaded"); - JarClassLoader classLoader = new JarClassLoader(new URL[] { + classLoader = new JarClassLoader(new URL[] { Main.class.getResource("/mod.jar") }, this.getClass().getClassLoader()); @@ -102,6 +102,7 @@ public class JarLoader implements IDGLoader { @Override public void unloadJar() throws ReferenceLeakedException { + classLoader = null; dgInterface.unload(); dgInterface = null; System.gc();// pls do @@ -125,4 +126,9 @@ public class JarLoader implements IDGLoader { public String strategyName() { return "jar"; } + + @Override + public String version() { + return "unknown"; // maybe read the thing... + } } diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/LocalLoader.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/LocalLoader.java index 67c43ad5..1338138d 100644 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/LocalLoader.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/loader/LocalLoader.java @@ -22,6 +22,8 @@ import kr.syeyoung.dungeonsguide.launcher.DGInterface; import kr.syeyoung.dungeonsguide.launcher.authentication.Authenticator; import kr.syeyoung.dungeonsguide.launcher.exceptions.ReferenceLeakedException; +import java.io.InputStream; + public class LocalLoader implements IDGLoader { private DGInterface dgInterface; @@ -40,7 +42,6 @@ public class LocalLoader implements IDGLoader { public void unloadJar() throws ReferenceLeakedException { throw new UnsupportedOperationException(); } - @Override public boolean isUnloadable() { return false; @@ -55,4 +56,9 @@ public class LocalLoader implements IDGLoader { public String strategyName() { return "local"; } + + @Override + public String version() { + return "unknown"; + } } -- cgit