/*
* Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
* Copyright (C) 2021 cyoung06
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package kr.syeyoung.dungeonsguide;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.exceptions.AuthenticationException;
import com.mojang.authlib.minecraft.MinecraftSessionService;
import net.minecraft.client.Minecraft;
import net.minecraft.util.Session;
import net.minecraftforge.fml.common.ProgressManager;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.*;
import java.io.*;
import java.math.BigInteger;
import java.net.*;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class b {
private KeyPair a;
private String b;
private final ProgressManager.ProgressBar p;
public String c() {
return b;
}
private KeyPair a() {
KeyPairGenerator a = null;
try {
a = KeyPairGenerator.getInstance("RSA");
} catch (NoSuchAlgorithmException b) { }
a.initialize(1024);
this.a = a.generateKeyPair();
return this.a;
}
private PublicKey d;
private PublicKey e() throws NoSuchAlgorithmException, InvalidKeySpecException {
if (d != null) return d;
X509EncodedKeySpec spec = new X509EncodedKeySpec(Base64.decodeBase64("MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxO89qtwG67jNucQ9Y44c" +
"IUs/B+5BeJPs7G+RG2gfs4/2+tzF/c1FLDc33M7yKw8aKk99vsBUY9Oo8gxxiEPB" +
"JitP/qfon2THp94oM77ZTpHlmFoqbZMcKGZVI8yfvEL4laTM8Hw+qh5poQwtpEbK" +
"Xo47AkxygxJasUnykER2+aSTZ6kWU2D4xiNtFA6lzqN+/oA+NaYfPS0amAvyVlHR" +
"n/8IuGkxb5RrlqVssQstFnxsJuv88qdGSEqlcKq2tLeg9hb8eCnl2OFzvXmgbVER" +
"0JaV+4Z02fVG1IlR3Xo1mSit7yIU6++3usRCjx2yfXpnGGJUW5pe6YETjNew3ax+" +
"FAZ4GePWCdmS7FvBnbbABKo5pE06ZTfDUTCjQlAJQiUgoF6ntMJvQAXPu48Vr8q/" +
"mTcuZWVnI6CDgyE7nNq3WNoq3397sBzxRohMxuqzl3T19zkfPKF05iV2Ju1HQMW5" +
"I119bYrmVD240aGESZc20Sx/9g1BFpNzQbM5PGUlWJ0dhLjl2ge4ip2hHciY3OEY" +
"p2Qy2k+xEdenpKdL+WMRimCQoO9gWe2Tp4NmP5dppDXZgPjXqjZpnGs0Uxs+fXqW" +
"cwlg3MbX3rFl9so/fhVf4p9oXZK3ve7z5D6XSSDRYECvsKIa08WAxJ/U6n204E/4" +
"xUF+3ZgFPdzZGn2PU7SsnOsCAwEAAQ=="));
return d = KeyFactory.getInstance("RSA").generatePublic(spec);
}
public b(ProgressManager.ProgressBar p) {
this.p = p;
p.step("Generating KeyPair");
a();
}
public String b(String e) throws IOException, AuthenticationException, NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException, CertificateException, KeyStoreException, KeyManagementException, InvalidKeySpecException, SignatureException {
Session a = Minecraft.getMinecraft().getSession();
String b = a.getToken();
p.step("Authenticating (1/2)");
String c = a(a.getProfile());
MinecraftSessionService yggdrasilMinecraftSessionService = Minecraft.getMinecraft().getSessionService();
JsonObject d = a(c);
String hash = a(Base64.decodeBase64(d.get("sharedSecret").getAsString()),
Base64.decodeBase64(d.get("publicKey").getAsString()));
yggdrasilMinecraftSessionService.joinServer(a.getProfile(), b, hash);
p.step("Authenticating (2/2)");
this.b = a(c, this.a.getPublic());
try {
p.step("Downloading Jar");
if (e != null)
b(this.b, "https://dungeons.guide/resource/version?v=" + e, true);
p.step("Downloading Rooms");
b(this.b, "https://dungeons.guide/resource/roomdata", false);
} catch (Throwable t) {
t.printStackTrace();
}
return this.b;
}
public JsonObject a(String c) {
String a = c.split("\\.")[1].replace("+", "-").replace("/", "_");
String b = new String(Base64.decodeBase64(a)); // padding
return (JsonObject) new JsonParser().parse(b);
}
private String a(GameProfile d) throws IOException, NoSuchAlgorithmException, CertificateException, KeyStoreException, KeyManagementException {
HttpsURLConnection a = (HttpsURLConnection) new URL("https://dungeons.guide/auth/requestAuth").openConnection();
a.setRequestProperty("User-Agent", "DungeonsGuide/1.0");
a.setRequestProperty("Content-Type", "application/json");
a.setRequestMethod("POST");
a.setDoInput(true);
a.setDoOutput(true);
a.getOutputStream().write(("{\"uuid\":\""+d.getId().toString()+"\",\"nickname\":\""+d.getName()+"\"}").getBytes());
InputStreamReader b = new InputStreamReader(a.getInputStream());
JsonObject c = (JsonObject) new JsonParser().parse(b);
if (!"ok".equals(c.get("status").getAsString())) {
return null;
}
return c.get("data").getAsString();
}
private String a(String a, PublicKey b) throws IOException, NoSuchAlgorithmException, CertificateException, KeyStoreException, KeyManagementException {
HttpsURLConnection c = (HttpsURLConnection) new URL("https://dungeons.guide/auth/authenticate").openConnection();
c.setRequestMethod("POST");
c.setRequestProperty("User-Agent", "DungeonsGuide/1.0");
c.setRequestProperty("Content-Type", "application/json");
c.setDoInput(true);
c.setDoOutput(true);
c.getOutputStream().write(("{\"jwt\":\""+a+"\",\"publicKey\":\""+Base64.encodeBase64URLSafeString(b.getEncoded())+"\"}").getBytes());
c.getResponseCode();
InputStreamReader d = new InputStreamReader(c.getInputStream());
JsonObject e = (JsonObject) new JsonParser().parse(d);
if (!"ok".equals(e.get("status").getAsString())) {
return null;
}
return e.get("data").getAsString();
}
private final HashMap c = new HashMap();
public HashMap d() {
return c;
}
private void b(String a, String u, boolean v) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, CertificateException, KeyStoreException, KeyManagementException, SignatureException, InvalidKeySpecException {
HttpsURLConnection b = (HttpsURLConnection) new URL(u).openConnection();
b.setRequestProperty("User-Agent", "DungeonsGuide/1.0");
b.setRequestProperty("Content-Type", "application/json");
b.setRequestMethod("GET");
b.setRequestProperty("Authorization", a);
b.setDoInput(true);
b.setDoOutput(true);
InputStream c = b.getInputStream();
byte[] d = new byte[4];
c.read(d);
int f = ((d[0] & 0xFF) << 24) |
((d[1] & 0xFF) << 16) |
((d[2] & 0xFF) << 8) |
((d[3] & 0xFF));
while (c.available() < f) ;
byte[] e = new byte[f];
c.read(e);
Cipher g = Cipher.getInstance("RSA/ECB/PKCS1Padding");
g.init(Cipher.DECRYPT_MODE, this.a.getPrivate());
byte[] h = g.doFinal(e);
g = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec i = new SecretKeySpec(h, "AES");
IvParameterSpec j = new IvParameterSpec(h);
g.init(Cipher.DECRYPT_MODE, i, j);
CipherInputStream k = new CipherInputStream(c, g);
k.read(d);
f = ((d[0] & 0xFF) << 24) |
((d[1] & 0xFF) << 16) |
((d[2] & 0xFF) << 8) |
((d[3] & 0xFF));
int totalLen = f;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buff = new byte[256];
while (totalLen > 0) {
int len = k.read(buff, 0, Math.min(buff.length, totalLen));
totalLen -= len;
bos.write(buff, 0, len);
}
byte[] payload = bos.toByteArray();
byte[] signed = null;
if (v) {
p.step("Validating Signature");
k.read(d,0 , 4);
f = ((d[0] & 0xFF) << 24) |
((d[1] & 0xFF) << 16) |
((d[2] & 0xFF) << 8) |
((d[3] & 0xFF));
totalLen = f;
bos = new ByteArrayOutputStream();
while (totalLen > 0) {
int len = k.read(buff, 0, Math.min(buff.length, totalLen));
totalLen -= len;
bos.write(buff, 0, len);
}
signed = bos.toByteArray();
Signature sign = Signature.getInstance("SHA512withRSA");
sign.initVerify(e());
sign.update(payload);
boolean truth = sign.verify(signed);
if (!truth) throw new SignatureException("DG SIGNATURE FORGED");
}
ZipInputStream l = new ZipInputStream(new ByteArrayInputStream(payload));
ZipEntry m;
while ((m=l.getNextEntry()) != null) {
byte[] n = new byte[256];
ByteArrayOutputStream o = new ByteArrayOutputStream();
int p = 0;
while((p = l.read(n)) > 0) {
o.write(n, 0, p);
}
this.c.put(m.getName(), o.toByteArray());
}
b.disconnect();
}
public JsonElement d(String u) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, CertificateException, KeyStoreException, KeyManagementException {
HttpsURLConnection b = (HttpsURLConnection) new URL(u).openConnection();
b.setRequestProperty("User-Agent", "DungeonsGuide/1.0");
b.setRequestProperty("Content-Type", "application/json");
b.setRequestMethod("GET");
b.setRequestProperty("Authorization", this.b);
b.setDoInput(true);
b.setDoOutput(true);
InputStream c = b.getInputStream();
byte[] d = new byte[4];
c.read(d);
int f = ((d[0] & 0xFF) << 24) |
((d[1] & 0xFF) << 16) |
((d[2] & 0xFF) << 8) |
((d[3] & 0xFF));
while (c.available() < f) ;
byte[] e = new byte[f];
c.read(e);
Cipher g = Cipher.getInstance("RSA/ECB/PKCS1Padding");
g.init(Cipher.DECRYPT_MODE, this.a.getPrivate());
byte[] h = g.doFinal(e);
g = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec i = new SecretKeySpec(h, "AES");
IvParameterSpec j = new IvParameterSpec(h);
g.init(Cipher.DECRYPT_MODE, i, j);
CipherInputStream k = new CipherInputStream(c, g);
k.read(d);
f = ((d[0] & 0xFF) << 24) |
((d[1] & 0xFF) << 16) |
((d[2] & 0xFF) << 8) |
((d[3] & 0xFF));
JsonElement l = new JsonParser().parse(new InputStreamReader(k));
b.disconnect();
return l;
}
public String a(byte[] a, byte[] b) throws NoSuchAlgorithmException {
MessageDigest c = MessageDigest.getInstance("SHA-1");
c.update("".getBytes());
c.update(a);
c.update(b);
byte[] d = c.digest();
return new BigInteger(d).toString(16);
}
}