[FEATURE] 5.3.0 features

This commit is contained in:
Gravita 2022-07-24 16:24:25 +07:00
parent dbdc1b4d6a
commit c8768326ea
44 changed files with 446 additions and 346 deletions

View file

@ -0,0 +1,11 @@
package pro.gravit.launchserver.auth.core.interfaces.session;
import java.security.PrivateKey;
import java.security.PublicKey;
public interface UserSessionSupportKeys {
ClientProfileKeys getClientProfileKeys();
record ClientProfileKeys(PublicKey publicKey, PrivateKey privateKey, byte[] signature /* V2 */, long expiresAt, long refreshedAfter) {
}
}

View file

@ -122,7 +122,7 @@ public String createHardwareToken(String username, UserHardware hardware) {
return Jwts.builder()
.setIssuer("LaunchServer")
.setSubject(username)
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 8))
.setExpiration(new Date(System.currentTimeMillis() + 1000 * server.config.netty.security.hardwareTokenExpire))
.claim("hardware", hardware.getId())
.signWith(server.keyAgreementManager.ecdsaPrivateKey)
.compact();
@ -132,7 +132,7 @@ public String createPublicKeyToken(String username, byte[] publicKey) {
return Jwts.builder()
.setIssuer("LaunchServer")
.setSubject(username)
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 8))
.setExpiration(new Date(System.currentTimeMillis() + 1000 * server.config.netty.security.publicKeyTokenExpire))
.claim("publicKey", Base64.getEncoder().encodeToString(publicKey))
.signWith(server.keyAgreementManager.ecdsaPrivateKey)
.compact();

View file

@ -71,7 +71,7 @@ public void invoke(String... args) throws IOException, CommandException {
if (version.compareTo(ClientProfile.Version.MC164) <= 0) {
logger.warn("Minecraft 1.6.4 and below not supported. Use at your own risk");
}
MakeProfileHelper.MakeProfileOption[] options = MakeProfileHelper.getMakeProfileOptionsFromDir(clientDir, version);
MakeProfileHelper.MakeProfileOption[] options = MakeProfileHelper.getMakeProfileOptionsFromDir(clientDir, version, Files.exists(server.updatesDir.resolve("assets")));
for (MakeProfileHelper.MakeProfileOption option : options) {
logger.debug("Detected option {}", option.getClass().getSimpleName());
}

View file

@ -10,6 +10,7 @@
import pro.gravit.utils.helper.IOHelper;
import java.io.Writer;
import java.nio.file.Files;
public class MakeProfileCommand extends Command {
private transient final Logger logger = LogManager.getLogger();
@ -32,7 +33,7 @@ public String getUsageDescription() {
public void invoke(String... args) throws Exception {
verifyArgs(args, 3);
ClientProfile.Version version = ClientProfile.Version.byName(args[1]);
MakeProfileHelper.MakeProfileOption[] options = MakeProfileHelper.getMakeProfileOptionsFromDir(server.updatesDir.resolve(args[2]), version);
MakeProfileHelper.MakeProfileOption[] options = MakeProfileHelper.getMakeProfileOptionsFromDir(server.updatesDir.resolve(args[2]), version, Files.exists(server.updatesDir.resolve("assets")));
for (MakeProfileHelper.MakeProfileOption option : options) {
logger.info("Detected option {}", option);
}

View file

@ -47,7 +47,7 @@ public static LaunchServerConfig getDefault(LaunchServer.LaunchServerEnv env) {
LaunchServerConfig newConfig = new LaunchServerConfig();
newConfig.mirrors = new String[]{"https://mirror.gravit.pro/5.2.x/", "https://gravit-launcher-mirror.storage.googleapis.com/"};
newConfig.launch4j = new LaunchServerConfig.ExeConf();
newConfig.launch4j.enabled = true;
newConfig.launch4j.enabled = false;
newConfig.launch4j.copyright = "© GravitLauncher Team";
newConfig.launch4j.fileDesc = "GravitLauncher ".concat(Version.getVersion().getVersionString());
newConfig.launch4j.fileVer = Version.getVersion().getVersionString().concat(".").concat(String.valueOf(Version.getVersion().patch));
@ -289,6 +289,8 @@ public static class NettyConfig {
public String address;
public Map<String, LaunchServerConfig.NettyUpdatesBind> bindings = new HashMap<>();
public NettyPerformanceConfig performance;
public NettySecurityConfig security = new NettySecurityConfig();
public NettyBindAddress[] binds;
public LogLevel logLevel = LogLevel.DEBUG;
}
@ -298,7 +300,6 @@ public static class NettyPerformanceConfig {
public int bossThread;
public int workerThread;
public int schedulerThread;
public long sessionLifetimeMs = 24 * 60 * 60 * 1000;
public int maxWebSocketRequestBytes = 1024 * 1024;
}
@ -311,4 +312,11 @@ public NettyBindAddress(String address, int port) {
this.port = port;
}
}
public static class NettySecurityConfig {
public long hardwareTokenExpire = 60 * 60 * 8;
public long publicKeyTokenExpire = 60 * 60 * 8;
public long launcherTokenExpire = 60 * 60 * 8;
}
}

View file

@ -18,7 +18,11 @@ public static ClientProfile makeProfile(ClientProfile.Version version, String ti
ClientProfileBuilder builder = new ClientProfileBuilder();
builder.setVersion(version.name);
builder.setDir(title);
if(findOption(options, MakeProfileOptionGlobalAssets.class).isPresent()) {
builder.setAssetDir("assets");
} else {
builder.setAssetDir("asset" + version.name);
}
builder.setAssetIndex(version.name);
builder.setInfo("Информация о сервере");
builder.setTitle(title);
@ -42,11 +46,16 @@ public static ClientProfile makeProfile(ClientProfile.Version version, String ti
Set<OptionalFile> optionals = new HashSet<>();
jvmArgs.add("-XX:+DisableAttachMechanism");
// Official Mojang launcher java arguments
if(version.compareTo(ClientProfile.Version.MC112) <= 0) {
jvmArgs.add("-XX:+UseConcMarkSweepGC");
jvmArgs.add("-XX:+CMSIncrementalMode");
} else if(version.compareTo(ClientProfile.Version.MC118) <= 0) { // 1.13 - 1.16.5
jvmArgs.add("-XX:+UseG1GC");
jvmArgs.add("-XX:+UnlockExperimentalVMOptions");
jvmArgs.add("-XX:G1NewSizePercent=20");
jvmArgs.add("-XX:MaxGCPauseMillis=50");
jvmArgs.add("-XX:G1HeapRegionSize=32M");
} else { // 1.18+
jvmArgs.add("-XX:+UseShenandoahGC");
jvmArgs.add("-XX:+UnlockExperimentalVMOptions");
}
// -----------
Optional<MakeProfileOptionForge> forge = findOption(options, MakeProfileOptionForge.class);
Optional<MakeProfileOptionFabric> fabric = findOption(options, MakeProfileOptionFabric.class);
@ -192,7 +201,7 @@ private static String getLog4jVersion(Path dir) throws IOException {
return null;
}
public static MakeProfileOption[] getMakeProfileOptionsFromDir(Path dir, ClientProfile.Version version) throws IOException {
public static MakeProfileOption[] getMakeProfileOptionsFromDir(Path dir, ClientProfile.Version version, boolean globalAssets) throws IOException {
List<MakeProfileOption> options = new ArrayList<>(2);
if (Files.exists(dir.resolve("forge.jar"))) {
options.add(new MakeProfileOptionForge());
@ -302,6 +311,10 @@ public static class MakeProfileOptionLaunchWrapper implements MakeProfileOption
}
public static class MakeProfileOptionGlobalAssets implements MakeProfileOption {
}
public static class MakeProfileOptionFabric implements MakeProfileOption {
public String jimfsPath;
public String guavaPath;

View file

@ -16,6 +16,7 @@
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
import pro.gravit.launchserver.auth.core.User;
import pro.gravit.launchserver.auth.core.UserSession;
import pro.gravit.launchserver.auth.core.interfaces.session.UserSessionSupportKeys;
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportProperties;
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportTextures;
import pro.gravit.launchserver.auth.texture.TextureProvider;
@ -26,7 +27,13 @@
import pro.gravit.utils.helper.SecurityHelper;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.security.KeyPair;
import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
public class AuthManager {
@ -180,6 +187,10 @@ public void internalAuth(Client client, AuthResponse.ConnectTypes authType, Auth
client.uuid = uuid;
}
public UserSessionSupportKeys.ClientProfileKeys createClientProfileKeys(UUID playerUUID) {
throw new UnsupportedOperationException("Minecraft 1.19.1 signature"); // TODO
}
public CheckServerReport checkServer(Client client, String username, String serverID) throws IOException {
if (client.auth == null) return null;
User user = client.auth.core.checkServer(client, username, serverID);

View file

@ -20,6 +20,7 @@
import pro.gravit.launchserver.socket.response.WebSocketServerResponse;
import pro.gravit.launchserver.socket.response.auth.*;
import pro.gravit.launchserver.socket.response.management.FeaturesResponse;
import pro.gravit.launchserver.socket.response.management.GetPublicKeyResponse;
import pro.gravit.launchserver.socket.response.management.ServerStatusResponse;
import pro.gravit.launchserver.socket.response.profile.BatchProfileByUsername;
import pro.gravit.launchserver.socket.response.profile.ProfileByUUIDResponse;
@ -88,6 +89,8 @@ public static void registerResponses() {
providers.register("refreshToken", RefreshTokenResponse.class);
providers.register("restore", RestoreResponse.class);
providers.register("additionalData", AdditionalDataResponse.class);
providers.register("clientProfileKey", FetchClientProfileKeyResponse.class);
providers.register("getPublicKey", GetPublicKeyResponse.class);
}
public void forEachActiveChannels(BiConsumer<Channel, WebSocketFrameHandler> callback) {

View file

@ -0,0 +1,32 @@
package pro.gravit.launchserver.socket.response.auth;
import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.events.request.FetchClientProfileKeyRequestEvent;
import pro.gravit.launchserver.auth.core.User;
import pro.gravit.launchserver.auth.core.UserSession;
import pro.gravit.launchserver.auth.core.interfaces.session.UserSessionSupportKeys;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.SimpleResponse;
public class FetchClientProfileKeyResponse extends SimpleResponse {
@Override
public String getType() {
return "clientProfileKey";
}
@Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
if (!client.isAuth || client.type != AuthResponse.ConnectTypes.CLIENT) {
sendError("Permissions denied");
return;
}
UserSession session = client.sessionObject;
UserSessionSupportKeys.ClientProfileKeys keys;
if(session instanceof UserSessionSupportKeys support) {
keys = support.getClientProfileKeys();
} else {
keys = server.authManager.createClientProfileKeys(client.uuid);
}
sendResult(new FetchClientProfileKeyRequestEvent(keys.publicKey(), keys.privateKey(), keys.signature(), keys.expiresAt(), keys.refreshedAfter()));
}
}

View file

@ -0,0 +1,18 @@
package pro.gravit.launchserver.socket.response.management;
import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.events.request.GetPublicKeyRequestEvent;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.SimpleResponse;
public class GetPublicKeyResponse extends SimpleResponse {
@Override
public String getType() {
return "getPublicKey";
}
@Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
sendResult(new GetPublicKeyRequestEvent(server.keyAgreementManager.rsaPublicKey, server.keyAgreementManager.ecdsaPublicKey));
}
}

View file

@ -71,7 +71,7 @@ public String createLauncherExtendedToken() {
return Jwts.builder()
.setIssuer("LaunchServer")
.claim("checkSign", true)
.setExpiration(Date.from(LocalDateTime.now().plusHours(8).toInstant(ZoneOffset.UTC)))
.setExpiration(Date.from(LocalDateTime.now().plusSeconds(server.config.netty.security.launcherTokenExpire).toInstant(ZoneOffset.UTC)))
.signWith(server.keyAgreementManager.ecdsaPrivateKey, SignatureAlgorithm.ES256)
.compact();
}

View file

@ -42,7 +42,6 @@ task javadocJar(type: Jar) {
shadowJar {
duplicatesStrategy = 'EXCLUDE'
archiveClassifier.set(null)
relocate 'org.objectweb.asm', 'pro.gravit.repackage.org.objectweb.asm'
relocate 'io.netty', 'pro.gravit.repackage.io.netty'
configurations = [project.configurations.pack]
exclude 'module-info.class'
@ -52,7 +51,6 @@ task javadocJar(type: Jar) {
pack project(':LauncherAPI')
bundle group: 'com.github.oshi', name: 'oshi-core', version: rootProject['verOshiCore']
pack group: 'io.netty', name: 'netty-codec-http', version: rootProject['verNetty']
pack group: 'org.ow2.asm', name: 'asm-tree', version: rootProject['verAsm']
}
task genRuntimeJS(type: Zip) {

View file

@ -1,6 +1,5 @@
package pro.gravit.launcher;
import pro.gravit.launcher.patches.FMLPatcher;
import pro.gravit.launcher.utils.NativeJVMHalt;
import pro.gravit.utils.helper.LogHelper;
@ -30,7 +29,6 @@ public static void premain(String agentArgument, Instrumentation instrumentation
checkAgentStacktrace();
inst = instrumentation;
NativeJVMHalt.initFunc();
FMLPatcher.apply();
isAgentStarted = true;
}

View file

@ -5,10 +5,10 @@
import pro.gravit.launcher.client.events.ClientExitPhase;
import pro.gravit.launcher.client.events.ClientPreGuiPhase;
import pro.gravit.launcher.console.GetPublicKeyCommand;
import pro.gravit.launcher.console.ModulesCommand;
import pro.gravit.launcher.console.SignDataCommand;
import pro.gravit.launcher.events.request.*;
import pro.gravit.launcher.guard.LauncherGuardInterface;
import pro.gravit.launcher.guard.LauncherGuardManager;
import pro.gravit.launcher.guard.LauncherGuard;
import pro.gravit.launcher.guard.LauncherNoGuard;
import pro.gravit.launcher.guard.LauncherWrapperGuard;
import pro.gravit.launcher.gui.NoRuntimeProvider;
@ -28,7 +28,6 @@
import pro.gravit.launcher.request.secure.GetSecureLevelInfoRequest;
import pro.gravit.launcher.request.secure.SecurityReportRequest;
import pro.gravit.launcher.request.update.LauncherRequest;
import pro.gravit.launcher.request.websockets.ClientWebSocketService;
import pro.gravit.launcher.request.websockets.OfflineRequestService;
import pro.gravit.launcher.request.websockets.StdWebSocketService;
import pro.gravit.launcher.utils.NativeJVMHalt;
@ -50,7 +49,7 @@
public class LauncherEngine {
public static ClientLauncherProcess.ClientParams clientParams;
public static LauncherGuardInterface guard;
public static LauncherGuard guard;
public static ClientModuleManager modulesManager;
public final boolean clientInstance;
// Instance
@ -86,7 +85,10 @@ public static void checkClass(Class<?> clazz) throws SecurityException {
}
public static void exitLauncher(int code) {
try {
modulesManager.invokeEvent(new ClientExitPhase(code));
} catch (Throwable ignored) {
}
try {
System.exit(code);
} catch (Throwable e) //Forge Security Manager?
@ -143,7 +145,7 @@ public static void verifyNoAgent() {
throw new SecurityException("JavaAgent found");
}
public static LauncherGuardInterface tryGetStdGuard() {
public static LauncherGuard tryGetStdGuard() {
switch (Launcher.getConfig().guardType) {
case "no":
return new LauncherNoGuard();
@ -264,7 +266,6 @@ public void start(String... args) throws Throwable {
registerCommands();
LauncherEngine.modulesManager.invokeEvent(new ClientEngineInitPhase(this));
runtimeProvider.preLoad();
LauncherGuardManager.initGuard(clientInstance);
LogHelper.debug("Dir: %s", DirBridge.dir);
runtimeProvider.run(args);
}
@ -272,5 +273,6 @@ public void start(String... args) throws Throwable {
private void registerCommands() {
ConsoleManager.handler.registerCommand("getpublickey", new GetPublicKeyCommand(this));
ConsoleManager.handler.registerCommand("signdata", new SignDataCommand(this));
ConsoleManager.handler.registerCommand("modules", new ModulesCommand());
}
}

View file

@ -1,5 +1,7 @@
package pro.gravit.launcher.api;
import pro.gravit.launcher.utils.ApiBridgeService;
import java.lang.instrument.Instrumentation;
import java.net.URL;
@ -12,4 +14,8 @@ public class ClientService {
public static ClassLoader getClassLoader() {
return classLoader;
}
public static String findLibrary(String name) {
return ApiBridgeService.findLibrary(classLoader, name);
}
}

View file

@ -3,10 +3,10 @@
import pro.gravit.launcher.*;
import pro.gravit.launcher.api.AuthService;
import pro.gravit.launcher.api.ClientService;
import pro.gravit.launcher.api.KeyService;
import pro.gravit.launcher.client.events.client.*;
import pro.gravit.launcher.events.request.ProfileByUUIDRequestEvent;
import pro.gravit.launcher.events.request.ProfileByUsernameRequestEvent;
import pro.gravit.launcher.guard.LauncherGuardManager;
import pro.gravit.launcher.hasher.FileNameMatcher;
import pro.gravit.launcher.hasher.HashedDir;
import pro.gravit.launcher.hasher.HashedEntry;
@ -15,7 +15,6 @@
import pro.gravit.launcher.modules.LauncherModulesManager;
import pro.gravit.launcher.modules.events.OfflineModeEvent;
import pro.gravit.launcher.modules.events.PreConfigPhase;
import pro.gravit.launcher.patches.FMLPatcher;
import pro.gravit.launcher.profiles.ClientProfile;
import pro.gravit.launcher.profiles.optional.actions.OptionalAction;
import pro.gravit.launcher.profiles.optional.actions.OptionalActionClassPath;
@ -26,7 +25,6 @@
import pro.gravit.launcher.request.RequestService;
import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.launcher.request.auth.GetAvailabilityAuthRequest;
import pro.gravit.launcher.request.uuid.BatchProfileByUsernameRequest;
import pro.gravit.launcher.request.uuid.ProfileByUUIDRequest;
import pro.gravit.launcher.request.uuid.ProfileByUsernameRequest;
import pro.gravit.launcher.request.websockets.OfflineRequestService;
@ -87,7 +85,6 @@ public static void main(String[] args) throws Throwable {
ConsoleManager.initConsole();
LauncherEngine.modulesManager.invokeEvent(new PreConfigPhase());
engine.readKeys();
LauncherGuardManager.initGuard(true);
LogHelper.debug("Reading ClientLauncher params");
ClientLauncherProcess.ClientParams params = readParams(new InetSocketAddress("127.0.0.1", Launcher.getConfig().clientPort));
if (params.profile.getClassLoaderConfig() != ClientProfile.ClassLoaderConfig.AGENT) {
@ -143,13 +140,14 @@ public static void main(String[] args) throws Throwable {
}
};
}
LogHelper.debug("Natives dir %s", params.nativesDir);
ClientProfile.ClassLoaderConfig classLoaderConfig = profile.getClassLoaderConfig();
if (classLoaderConfig == ClientProfile.ClassLoaderConfig.LAUNCHER) {
ClientClassLoader classLoader = new ClientClassLoader(classpathURLs.toArray(new URL[0]), ClassLoader.getSystemClassLoader());
System.setProperty("java.class.path", classpath.stream().map(Path::toString).collect(Collectors.joining(File.pathSeparator)));
ClientLauncherEntryPoint.classLoader = classLoader;
Thread.currentThread().setContextClassLoader(classLoader);
classLoader.nativePath = clientDir.resolve("natives").toString();
classLoader.nativePath = params.nativesDir;
LauncherEngine.modulesManager.invokeEvent(new ClientProcessClassLoaderEvent(engine, classLoader, profile));
ClientService.classLoader = classLoader;
ClientService.nativePath = classLoader.nativePath;
@ -162,7 +160,7 @@ public static void main(String[] args) throws Throwable {
LauncherAgent.addJVMClassPath(Paths.get(url.toURI()));
}
ClientService.instrumentation = LauncherAgent.inst;
ClientService.nativePath = clientDir.resolve("natives").toString();
ClientService.nativePath = params.nativesDir;
LauncherEngine.modulesManager.invokeEvent(new ClientProcessClassLoaderEvent(engine, classLoader, profile));
ClientService.classLoader = classLoader;
ClientService.baseURLs = classpathURLs.toArray(new URL[0]);
@ -170,9 +168,11 @@ public static void main(String[] args) throws Throwable {
ClientLauncherEntryPoint.classLoader = ClassLoader.getSystemClassLoader();
ClientService.classLoader = ClassLoader.getSystemClassLoader();
ClientService.baseURLs = classpathURLs.toArray(new URL[0]);
ClientService.nativePath = params.nativesDir;
}
AuthService.username = params.playerProfile.username;
AuthService.uuid = params.playerProfile.uuid;
KeyService.serverRsaPublicKey = Launcher.getConfig().rsaPublicKey;
if (params.profile.getRuntimeInClientConfig() != ClientProfile.RuntimeInClientConfig.NONE) {
CommonHelper.newThread("Client Launcher Thread", true, () -> {
try {
@ -251,7 +251,6 @@ public static void verifyHDir(Path dir, HashedDir hdir, FileNameMatcher matcher,
HashedDir currentHDir = new HashedDir(dir, matcher, true, digest);
HashedDir.Diff diff = hdir.diff(currentHDir, matcher);
if (!diff.isSame()) {
if (LogHelper.isDebugEnabled()) {
diff.extra.walk(File.separator, (e, k, v) -> {
if (v.getType().equals(HashedEntry.Type.FILE)) {
LogHelper.error("Extra file %s", e);
@ -264,15 +263,14 @@ public static void verifyHDir(Path dir, HashedDir hdir, FileNameMatcher matcher,
} else LogHelper.error("Mismatch %s", e);
return HashedDir.WalkAction.CONTINUE;
});
}
throw new SecurityException(String.format("Forbidden modification: '%s'", IOHelper.getFileName(dir)));
}
}
public static boolean checkJVMBitsAndVersion(int minVersion, int recommendVersion, int maxVersion, boolean showMessage) {
boolean ok = true;
if (JVMHelper.JVM_BITS != JVMHelper.OS_BITS) {
String error = String.format("У Вас установлена Java %d, но Ваша система определена как %d. Установите Java правильной разрядности", JVMHelper.JVM_BITS, JVMHelper.OS_BITS);
if (JVMHelper.JVM_BITS == 64 && JVMHelper.ARCH_TYPE == JVMHelper.ARCH.X86) {
String error = "У Вас установлена Java x64, но Ваша система определена как x32. Установите Java правильной разрядности";
LogHelper.error(error);
if (showMessage)
JOptionPane.showMessageDialog(null, error);
@ -282,7 +280,7 @@ public static boolean checkJVMBitsAndVersion(int minVersion, int recommendVersio
LogHelper.info(jvmVersion);
int version = JVMHelper.getVersion();
if (version < minVersion || version > maxVersion) {
String error = String.format("У Вас установлена Java %s. Для правильной работы необходима Java %d", JVMHelper.RUNTIME_MXBEAN.getVmVersion(), recommendVersion);
String error = String.format("У Вас установлена Java %d, но этот клиент требует Java %d", JVMHelper.getVersion(), recommendVersion);
LogHelper.error(error);
if (showMessage)
JOptionPane.showMessageDialog(null, error);
@ -347,7 +345,6 @@ private static void launch(ClientProfile profile, ClientLauncherProcess.ClientPa
LogHelper.dev("ClassLoader URL: %s", u.toString());
}
}
FMLPatcher.apply();
LauncherEngine.modulesManager.invokeEvent(new ClientProcessPreInvokeMainClassEvent(params, profile, args));
// Invoke main method
try {

View file

@ -1,6 +1,7 @@
package pro.gravit.launcher.client;
import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.LauncherConfig;
import pro.gravit.launcher.LauncherEngine;
import pro.gravit.launcher.LauncherNetworkAPI;
import pro.gravit.launcher.client.events.client.ClientProcessBuilderCreateEvent;
@ -25,6 +26,7 @@
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
@ -42,42 +44,45 @@ public class ClientLauncherProcess {
private final transient Boolean[] waitWriteParams = new Boolean[]{false};
public Path executeFile;
public Path workDir;
public Path javaDir;
public int bits;
public JavaHelper.JavaVersion javaVersion;
public boolean useLegacyJavaClassPathProperty;
public boolean isStarted;
public JavaHelper.JavaVersion javaVersion;
private transient Process process;
public ClientLauncherProcess(Path executeFile, Path workDir, Path javaDir, String mainClass) {
public ClientLauncherProcess(Path executeFile, Path workDir, JavaHelper.JavaVersion javaVersion, String mainClass) {
this.executeFile = executeFile;
this.workDir = workDir;
this.javaDir = javaDir;
this.javaVersion = javaVersion;
this.mainClass = mainClass;
}
public ClientLauncherProcess(Path clientDir, Path assetDir, Path javaDir,
public ClientLauncherProcess(Path clientDir, Path assetDir, JavaHelper.JavaVersion javaVersion,
ClientProfile profile, PlayerProfile playerProfile, String accessToken,
HashedDir clientHDir, HashedDir assetHDir, HashedDir jvmHDir) {
this(clientDir, assetDir, javaDir, clientDir.resolve("resourcepacks"), profile, playerProfile, null, accessToken, clientHDir, assetHDir, jvmHDir);
this(clientDir, assetDir, javaVersion, clientDir.resolve("resourcepacks"), profile, playerProfile, null, accessToken, clientHDir, assetHDir, jvmHDir);
}
public ClientLauncherProcess(Path clientDir, Path assetDir,
ClientProfile profile, PlayerProfile playerProfile, String accessToken,
HashedDir clientHDir, HashedDir assetHDir, HashedDir jvmHDir) {
this(clientDir, assetDir, Paths.get(System.getProperty("java.home")), clientDir.resolve("resourcepacks"), profile, playerProfile, null, accessToken, clientHDir, assetHDir, jvmHDir);
this(clientDir, assetDir, JavaHelper.JavaVersion.getCurrentJavaVersion(), clientDir.resolve("resourcepacks"), profile, playerProfile, null, accessToken, clientHDir, assetHDir, jvmHDir);
}
public ClientLauncherProcess(Path clientDir, Path assetDir, Path javaDir, Path resourcePackDir,
public ClientLauncherProcess(Path clientDir, Path assetDir, JavaHelper.JavaVersion javaVersion, Path resourcePackDir,
ClientProfile profile, PlayerProfile playerProfile, OptionalView view, String accessToken,
HashedDir clientHDir, HashedDir assetHDir, HashedDir jvmHDir) {
this.javaVersion = javaVersion;
this.workDir = clientDir.toAbsolutePath();
this.javaDir = javaDir;
this.executeFile = IOHelper.resolveJavaBin(this.javaDir);
this.executeFile = IOHelper.resolveJavaBin(this.javaVersion.jvmDir);
this.mainClass = ClientLauncherEntryPoint.class.getName();
this.params.clientDir = this.workDir.toString();
this.params.resourcePackDir = resourcePackDir.toAbsolutePath().toString();
this.params.assetDir = assetDir.toAbsolutePath().toString();
Path nativesPath = workDir.resolve("natives").resolve(JVMHelper.OS_TYPE.name).resolve(javaVersion.arch.name);
if(!Files.isDirectory(nativesPath)) {
nativesPath = workDir.resolve("natives");
}
this.params.nativesDir = nativesPath.toString();
this.params.profile = profile;
this.params.playerProfile = playerProfile;
this.params.accessToken = accessToken;
@ -87,16 +92,6 @@ public ClientLauncherProcess(Path clientDir, Path assetDir, Path javaDir, Path r
if (view != null) {
this.params.actions = view.getEnabledActions();
}
try {
javaVersion = JavaHelper.JavaVersion.getByPath(javaDir);
} catch (IOException e) {
LogHelper.error(e);
javaVersion = null;
}
if (javaVersion == null) {
javaVersion = JavaHelper.JavaVersion.getCurrentJavaVersion();
}
this.bits = JVMHelper.JVM_BITS;
applyClientProfile();
}
@ -115,7 +110,7 @@ private void applyClientProfile() {
this.jvmArgs.addAll(((OptionalActionJvmArgs) a).args);
}
}
this.systemEnv.put("JAVA_HOME", javaDir.toString());
this.systemEnv.put("JAVA_HOME", javaVersion.jvmDir.toString());
Collections.addAll(this.systemClassPath, this.params.profile.getAlternativeClassPath());
if (params.ram > 0) {
this.jvmArgs.add("-Xmx" + params.ram + 'M');
@ -154,11 +149,17 @@ public void start(boolean pipeOutput) throws IOException, InterruptedException {
applyJava9Params(processArgs);
}
//ADD CLASSPATH
processArgs.add(JVMHelper.jvmProperty("java.library.path", this.params.nativesDir));
if (params.profile.getClassLoaderConfig() == ClientProfile.ClassLoaderConfig.AGENT) {
processArgs.add("-javaagent:".concat(IOHelper.getCodeSource(ClientLauncherEntryPoint.class).toAbsolutePath().toString()));
} else if (params.profile.getClassLoaderConfig() == ClientProfile.ClassLoaderConfig.SYSTEM_ARGS) {
systemClassPath.addAll(ClientLauncherEntryPoint.resolveClassPath(workDir, params.actions, params.profile).map(Path::toString).collect(Collectors.toList()));
}
if(Launcher.getConfig().environment != LauncherConfig.LauncherEnvironment.PROD) {
processArgs.add(JVMHelper.jvmProperty(LogHelper.DEV_PROPERTY, String.valueOf(LogHelper.isDevEnabled())));
processArgs.add(JVMHelper.jvmProperty(LogHelper.DEBUG_PROPERTY, String.valueOf(LogHelper.isDebugEnabled())));
processArgs.add(JVMHelper.jvmProperty(LogHelper.STACKTRACE_PROPERTY, String.valueOf(LogHelper.isStacktraceEnabled())));
}
if (useLegacyJavaClassPathProperty) {
processArgs.add("-Djava.class.path=".concat(String.join(getPathSeparator(), systemClassPath)));
} else {
@ -176,7 +177,7 @@ public void start(boolean pipeOutput) throws IOException, InterruptedException {
LogHelper.debug("Commandline: %s", Arrays.toString(processArgs.toArray()));
ProcessBuilder processBuilder = new ProcessBuilder(processArgs);
EnvHelper.addEnv(processBuilder);
processBuilder.environment().put("JAVA_HOME", javaDir.toAbsolutePath().toString());
processBuilder.environment().put("JAVA_HOME", javaVersion.jvmDir.toAbsolutePath().toString());
processBuilder.environment().putAll(systemEnv);
processBuilder.directory(workDir.toFile());
processBuilder.inheritIO();
@ -256,6 +257,8 @@ public static class ClientParams {
public String resourcePackDir;
public String nativesDir;
// Client params
public PlayerProfile playerProfile;

View file

@ -7,6 +7,8 @@
import pro.gravit.launcher.modules.impl.SimpleModuleManager;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
public final class ClientModuleManager extends SimpleModuleManager {
public ClientModuleManager() {
@ -28,6 +30,10 @@ public LauncherModule loadModule(LauncherModule module) {
return super.loadModule(module);
}
public List<LauncherModule> getModules() {
return Collections.unmodifiableList(modules);
}
@Override
public final boolean verifyClassCheckResult(LauncherTrustManager.CheckClassResult result) {
return result.type == LauncherTrustManager.CheckClassResultType.SUCCESS;

View file

@ -0,0 +1,49 @@
package pro.gravit.launcher.console;
import pro.gravit.launcher.LauncherEngine;
import pro.gravit.launcher.LauncherTrustManager;
import pro.gravit.launcher.managers.ConsoleManager;
import pro.gravit.launcher.modules.LauncherModule;
import pro.gravit.launcher.modules.LauncherModuleInfo;
import pro.gravit.utils.command.Command;
import pro.gravit.utils.helper.LogHelper;
import java.security.cert.X509Certificate;
import java.util.Arrays;
public class ModulesCommand extends Command {
@Override
public String getArgsDescription() {
return "[]";
}
@Override
public String getUsageDescription() {
return "show modules";
}
@Override
public void invoke(String... args) throws Exception {
for(LauncherModule module : LauncherEngine.modulesManager.getModules()) {
LauncherModuleInfo info = module.getModuleInfo();
LauncherTrustManager.CheckClassResult checkStatus = module.getCheckResult();
if(!ConsoleManager.isConsoleUnlock) {
LogHelper.info("[MODULE] %s v: %s", info.name, info.version.getVersionString());
} else {
LogHelper.info("[MODULE] %s v: %s p: %d deps: %s sig: %s", info.name, info.version.getVersionString(), info.priority, Arrays.toString(info.dependencies), checkStatus == null ? "null" : checkStatus.type);
printCheckStatusInfo(checkStatus);
}
}
}
private void printCheckStatusInfo(LauncherTrustManager.CheckClassResult checkStatus) {
if (checkStatus != null && checkStatus.endCertificate != null) {
X509Certificate cert = checkStatus.endCertificate;
LogHelper.info("[MODULE CERT] Module signer: %s", cert.getSubjectX500Principal().getName());
}
if (checkStatus != null && checkStatus.rootCertificate != null) {
X509Certificate cert = checkStatus.rootCertificate;
LogHelper.info("[MODULE CERT] Module signer CA: %s", cert.getSubjectX500Principal().getName());
}
}
}

View file

@ -2,7 +2,7 @@
import pro.gravit.launcher.client.ClientLauncherProcess;
public interface LauncherGuardInterface {
public interface LauncherGuard {
String getName();
void applyGuardParams(ClientLauncherProcess process);

View file

@ -1,8 +0,0 @@
package pro.gravit.launcher.guard;
public class LauncherGuardManager {
public static LauncherGuardInterface guard;
public static void initGuard(boolean clientInstance) {
}
}

View file

@ -2,7 +2,7 @@
import pro.gravit.launcher.client.ClientLauncherProcess;
public class LauncherNoGuard implements LauncherGuardInterface {
public class LauncherNoGuard implements LauncherGuard {
@Override
public String getName() {
return "noGuard";

View file

@ -8,7 +8,7 @@
import java.io.IOException;
public class LauncherWrapperGuard implements LauncherGuardInterface {
public class LauncherWrapperGuard implements LauncherGuard {
public LauncherWrapperGuard() {
try {

View file

@ -1,107 +0,0 @@
package pro.gravit.launcher.patches;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import pro.gravit.utils.helper.SecurityHelper;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.nio.ByteBuffer;
import java.util.Locale;
import java.util.Random;
import java.util.Vector;
public class FMLPatcher extends ClassLoader implements Opcodes {
public static final MethodType EXITMH = MethodType.methodType(void.class, int.class);
public static final String[] PACKAGES = new String[]{"cpw.mods.fml.", "net.minecraftforge.fml.", "cpw.mods."};
public static final Vector<MethodHandle> MHS = new Vector<>();
public static volatile FMLPatcher INSTANCE = null;
public FMLPatcher(final ClassLoader cl) {
super(cl);
}
public static void apply() {
INSTANCE = new FMLPatcher(null); // Never cause ClassFormatError (fuck forge 1.14!!!)
for (String s : PACKAGES) {
String rMethod = randomStr(16);
try {
MHS.add(MethodHandles.publicLookup().findStatic(INSTANCE.def(s + randomStr(16), rMethod), rMethod,
EXITMH));
} catch (NoSuchMethodException | IllegalAccessException e) {
// Simple ignore - other Forge
}
}
}
public static void exit(final int code) {
for (MethodHandle mh : MHS)
try {
mh.invoke(code);
} catch (Throwable ignored) {
}
}
private static byte[] gen(final String name, final String exName) { // "cpw/mods/fml/SafeExitJVMLegacy", "exit"
final ClassWriter classWriter = new ClassWriter(0);
MethodVisitor methodVisitor;
classWriter.visit(V1_8, ACC_PUBLIC | ACC_SUPER, name, null, "java/lang/Object", null);
{
methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
methodVisitor.visitCode();
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(1, 1);
methodVisitor.visitEnd();
}
{
methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, exName, "(I)V", null, null);
methodVisitor.visitCode();
final Label label0 = new Label();
final Label label1 = new Label();
final Label label2 = new Label();
methodVisitor.visitTryCatchBlock(label0, label1, label2, "java/lang/Throwable");
methodVisitor.visitLabel(label0);
methodVisitor.visitMethodInsn(INVOKESTATIC, "java/lang/Runtime", "getRuntime", "()Ljava/lang/Runtime;",
false);
methodVisitor.visitVarInsn(ILOAD, 0);
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Runtime", "halt", "(I)V", false);
methodVisitor.visitLabel(label1);
final Label label3 = new Label();
methodVisitor.visitJumpInsn(GOTO, label3);
methodVisitor.visitLabel(label2);
methodVisitor.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]{"java/lang/Throwable"});
methodVisitor.visitVarInsn(ASTORE, 1);
methodVisitor.visitVarInsn(ILOAD, 0);
methodVisitor.visitMethodInsn(INVOKESTATIC, "java/lang/System", "exit", "(I)V", false);
methodVisitor.visitLabel(label3);
methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(2, 2);
methodVisitor.visitEnd();
}
classWriter.visitEnd();
return classWriter.toByteArray();
}
public static String randomStr(final int lenght) {
String alphabet = "abcdefghijklmnopqrstuvwxyz";
alphabet += alphabet.toUpperCase(Locale.US);
final StringBuilder sb = new StringBuilder(lenght);
final Random random = SecurityHelper.newRandom();
for (int i = 0; i < lenght; i++)
sb.append(alphabet.charAt(random.nextInt(26)));
return sb.toString();
}
public Class<?> def(final String name, final String exName) {
return super.defineClass(name, ByteBuffer.wrap(gen(name.replace('.', '/'), exName)), null);
}
}

View file

@ -2,6 +2,7 @@
import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.LauncherTrustManager;
import pro.gravit.launcher.client.ClientClassLoader;
import java.security.cert.X509Certificate;
@ -15,4 +16,12 @@ public static void checkCertificatesSuccess(X509Certificate[] certs) throws Exce
LauncherTrustManager trustManager = Launcher.getConfig().trustManager;
trustManager.checkCertificatesSuccess(certs, trustManager::stdCertificateChecker);
}
public static String findLibrary(ClassLoader classLoader, String library) {
if(classLoader instanceof ClientClassLoader) {
ClientClassLoader clientClassLoader = (ClientClassLoader) classLoader;
return clientClassLoader.findLibrary(library);
}
return null;
}
}

View file

@ -1,5 +1,6 @@
package pro.gravit.launcher.utils;
import pro.gravit.launcher.LauncherEngine;
import pro.gravit.launcher.hasher.FileNameMatcher;
import pro.gravit.launcher.hasher.HashedDir;
import pro.gravit.launcher.hasher.HashedEntry;
@ -46,7 +47,7 @@ public DirWatcher(Path dir, HashedDir hdir, FileNameMatcher matcher, boolean dig
private static void handleError(Throwable e) {
LogHelper.error(e);
NativeJVMHalt.haltA(-123);
LauncherEngine.exitLauncher(-123);
}
private static Deque<String> toPath(Iterable<Path> path) {

View file

@ -1,11 +1,11 @@
package pro.gravit.launcher.utils;
import pro.gravit.launcher.patches.FMLPatcher;
import pro.gravit.utils.helper.JVMHelper;
import pro.gravit.utils.helper.LogHelper;
import javax.swing.*;
import java.awt.event.WindowEvent;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;
public final class NativeJVMHalt {
public final int haltCode;
@ -15,51 +15,24 @@ public NativeJVMHalt(int haltCode) {
System.out.println("JVM exit code " + haltCode);
}
public static void initFunc() {
}
public static void haltA(int code) {
Throwable[] th = new Throwable[3];
NativeJVMHalt halt = new NativeJVMHalt(code);
try {
JVMHelper.RUNTIME.exit(code);
} catch (Throwable exitExc) {
th[0] = exitExc;
try {
new WindowShutdown();
} catch (Throwable windowExc) {
th[1] = windowExc;
}
}
try {
FMLPatcher.exit(code);
} catch (Throwable fmlExc) {
th[2] = fmlExc;
}
for (Throwable t : th) {
if (t != null) LogHelper.error(t);
}
boolean a = halt.aaabBooleanC_D();
System.out.println(a);
halt.aaabbb38C_D();
}
public static boolean initFunc() {
return true;
}
public native void aaabbb38C_D();
@SuppressWarnings("null")
private boolean aaabBooleanC_D() {
return (boolean) (Boolean) null;
}
public static class WindowShutdown extends JFrame {
private static final long serialVersionUID = 6321323663070818367L;
public WindowShutdown() {
super();
super.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
super.processWindowEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
LogHelper.dev("Try invoke Shutdown.exit");
Class<?> clazz = Class.forName("java.lang.Shutdown", true, ClassLoader.getSystemClassLoader());
Method exitMethod = clazz.getDeclaredMethod("exit", int.class);
exitMethod.setAccessible(true);
exitMethod.invoke(null, code);
} catch (Throwable e) {
th[1] = e;
if(LogHelper.isDevEnabled()) {
LogHelper.error(e);
}
}
}
}

View file

@ -0,0 +1,7 @@
package pro.gravit.launcher.api;
import java.security.interfaces.RSAPublicKey;
public class KeyService {
public static RSAPublicKey serverRsaPublicKey;
}

View file

@ -0,0 +1,35 @@
package pro.gravit.launcher.events.request;
import pro.gravit.launcher.events.RequestEvent;
import java.security.PrivateKey;
import java.security.PublicKey;
public class FetchClientProfileKeyRequestEvent extends RequestEvent {
public byte[] publicKey;
public byte[] privateKey;
public byte[] signature /* V2 */;
public long expiresAt;
public long refreshedAfter;
public FetchClientProfileKeyRequestEvent(byte[] publicKey, byte[] privateKey, byte[] signature, long expiresAt, long refreshedAfter) {
this.publicKey = publicKey;
this.privateKey = privateKey;
this.signature = signature;
this.expiresAt = expiresAt;
this.refreshedAfter = refreshedAfter;
}
public FetchClientProfileKeyRequestEvent(PublicKey publicKey, PrivateKey privateKey, byte[] signature, long expiresAt, long refreshedAfter) {
this.publicKey = publicKey.getEncoded();
this.privateKey = privateKey.getEncoded();
this.signature = signature;
this.expiresAt = expiresAt;
this.refreshedAfter = refreshedAfter;
}
@Override
public String getType() {
return "clientProfileKey";
}
}

View file

@ -0,0 +1,26 @@
package pro.gravit.launcher.events.request;
import pro.gravit.launcher.events.RequestEvent;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
public class GetPublicKeyRequestEvent extends RequestEvent {
public byte[] rsaPublicKey;
public byte[] ecdsaPublicKey;
public GetPublicKeyRequestEvent(byte[] rsaPublicKey, byte[] ecdsaPublicKey) {
this.rsaPublicKey = rsaPublicKey;
this.ecdsaPublicKey = ecdsaPublicKey;
}
public GetPublicKeyRequestEvent(RSAPublicKey rsaPublicKey, ECPublicKey ecdsaPublicKey) {
this.rsaPublicKey = rsaPublicKey.getEncoded();
this.ecdsaPublicKey = ecdsaPublicKey.getEncoded();
}
@Override
public String getType() {
return "getPublicKey";
}
}

View file

@ -0,0 +1,14 @@
package pro.gravit.launcher.request.auth;
import pro.gravit.launcher.events.request.FetchClientProfileKeyRequestEvent;
import pro.gravit.launcher.request.Request;
public class FetchClientProfileKeyRequest extends Request<FetchClientProfileKeyRequestEvent> {
public FetchClientProfileKeyRequest() {
}
@Override
public String getType() {
return "clientProfileKey";
}
}

View file

@ -0,0 +1,14 @@
package pro.gravit.launcher.request.auth;
import pro.gravit.launcher.events.request.GetPublicKeyRequestEvent;
import pro.gravit.launcher.request.Request;
public class GetPublicKeyRequest extends Request<GetPublicKeyRequestEvent> {
public GetPublicKeyRequest() {
}
@Override
public String getType() {
return "getPublicKey";
}
}

View file

@ -108,6 +108,8 @@ public void registerResults() {
results.register("refreshToken", RefreshTokenRequestEvent.class);
results.register("restore", RestoreRequestEvent.class);
results.register("additionalData", AdditionalDataRequestEvent.class);
results.register("clientProfileKey", FetchClientProfileKeyRequestEvent.class);
results.register("getPublicKey", GetPublicKeyRequestEvent.class);
resultsRegistered = true;
}
}

View file

@ -5,7 +5,7 @@ public final class CommandException extends Exception {
public CommandException(String message) {
super(message);
super(message, null, false, false);
}

View file

@ -16,27 +16,9 @@
public final class CommonHelper {
private static ScriptEngineFactory nashornFactory;
static {
try {
ScriptEngineManager scriptManager = new ScriptEngineManager();
nashornFactory = getEngineFactories(scriptManager);
} catch (Throwable e) {
nashornFactory = null;
}
}
private CommonHelper() {
}
private static ScriptEngineFactory getEngineFactories(ScriptEngineManager manager) {
// Метод похож на костыль но таковым не является, ибо единоразовое получение фактории быстрее, чем её переполучение на ходу.
for (ScriptEngineFactory fact : manager.getEngineFactories())
if (fact.getNames().contains("nashorn") || fact.getNames().contains("Nashorn")) return fact;
return null;
}
public static String low(String s) {
return s.toLowerCase(Locale.US);
}
@ -57,12 +39,10 @@ public static String multiReplace(Pattern[] pattern, String from, String replace
return tmp != null ? tmp : from;
}
@Deprecated
public static ScriptEngine newScriptEngine() {
if (nashornFactory == null) {
throw new UnsupportedOperationException("ScriptEngine not supported");
}
return nashornFactory.getScriptEngine();
}
public static Thread newThread(String name, boolean daemon, Runnable runnable) {
Thread thread = new Thread(runnable);

View file

@ -1,66 +0,0 @@
package pro.gravit.utils.helper;
import java.util.Objects;
import java.util.function.LongSupplier;
public final class CryptoHelper {
private CryptoHelper() {
}
public static byte[] encode(byte[] txt, String pKey) {
Objects.requireNonNull(txt);
Objects.requireNonNull(pKey);
byte[] key = SecurityHelper.fromHex(pKey);
byte[] res = new byte[txt.length];
for (int i = 0; i < txt.length; i++)
res[i] = (byte) (txt[i] ^ key[i % key.length]);
return res;
}
public static byte[] decode(byte[] pText, String pKey) {
Objects.requireNonNull(pText);
Objects.requireNonNull(pKey);
byte[] res = new byte[pText.length];
byte[] key = SecurityHelper.fromHex(pKey);
for (int i = 0; i < pText.length; i++)
res[i] = (byte) (pText[i] ^ key[i % key.length]);
return res;
}
public static void encodeOrig(byte[] txt, String pKey) {
Objects.requireNonNull(txt);
Objects.requireNonNull(pKey);
byte[] key = SecurityHelper.fromHex(pKey);
for (int i = 0; i < txt.length; i++)
txt[i] = (byte) (txt[i] ^ key[i % key.length]);
}
public static void decodeOrig(byte[] pText, String pKey) {
Objects.requireNonNull(pText);
Objects.requireNonNull(pKey);
byte[] key = SecurityHelper.fromHex(pKey);
for (int i = 0; i < pText.length; i++)
pText[i] = (byte) (pText[i] ^ key[i % key.length]);
}
public static String randomToken(int depth) {
VerifyHelper.verifyInt(depth, VerifyHelper.POSITIVE, "Depth must be positive");
return SecurityHelper.toHex(SecurityHelper.randomBytes(SecurityHelper.TOKEN_LENGTH * depth));
}
public static class StaticRandom implements LongSupplier {
private volatile long rnd;
public StaticRandom(long rnd) {
this.rnd = rnd;
}
@Override
public long getAsLong() {
this.rnd ^= (this.rnd << 21);
this.rnd ^= (this.rnd >>> 35);
this.rnd ^= (this.rnd << 4);
return this.rnd;
}
}
}

View file

@ -22,7 +22,11 @@ public final class JVMHelper {
public static final OS OS_TYPE = OS.byName(OPERATING_SYSTEM_MXBEAN.getName());
// System properties
public static final String OS_VERSION = OPERATING_SYSTEM_MXBEAN.getVersion();
@Deprecated
public static final int OS_BITS = getCorrectOSArch();
public static final ARCH ARCH_TYPE = getArch(System.getProperty("os.arch"));
public static final int JVM_BITS = Integer.parseInt(System.getProperty("sun.arch.data.model"));
public static final SecurityManager SECURITY_MANAGER = System.getSecurityManager();
// Public static fields
@ -42,6 +46,24 @@ public final class JVMHelper {
private JVMHelper() {
}
public enum ARCH {
X86("x86"), X86_64("x86-64"), ARM64("arm64"), ARM32("arm32");
public final String name;
ARCH(String name) {
this.name = name;
}
}
public static ARCH getArch(String arch) {
if(arch.equals("amd64") || arch.equals("x86-64") || arch.equals("x86_64")) return ARCH.X86_64;
if(arch.equals("i386") || arch.equals("i686") || arch.equals("x86")) return ARCH.X86;
if(arch.startsWith("armv8") || arch.startsWith("aarch64")) return ARCH.ARM64;
if(arch.startsWith("arm") || arch.startsWith("aarch32")) return ARCH.ARM32;
throw new InternalError(String.format("Unsupported arch '%s'", arch));
}
public static int getVersion() {
String version = System.getProperty("java.version");
if (version.startsWith("1.")) {
@ -132,6 +154,7 @@ public static void checkStackTrace(Class<?> mainClass) {
}
}
@Deprecated
private static int getCorrectOSArch() {
// As always, mustdie must die
if (OS_TYPE == OS.MUSTDIE)
@ -147,6 +170,7 @@ public static String getEnvPropertyCaseSensitive(String name) {
}
@Deprecated
public static boolean isJVMMatchesSystemArch() {
return JVM_BITS == OS_BITS;
}
@ -178,10 +202,6 @@ public static void verifySystemProperties(Class<?> mainClass, boolean requireSys
// Verify system and java architecture
LogHelper.debug("Verifying JVM architecture");
if (!isJVMMatchesSystemArch()) {
LogHelper.warning("Java and OS architecture mismatch");
LogHelper.warning("It's recommended to download %d-bit JRE", OS_BITS);
}
}
public enum OS {

View file

@ -185,14 +185,14 @@ public static class JavaVersion {
public final Path jvmDir;
public final int version;
public final int build;
public final int bitness;
public final JVMHelper.ARCH arch;
public boolean enabledJavaFX;
public JavaVersion(Path jvmDir, int version) {
this.jvmDir = jvmDir;
this.version = version;
this.build = 0;
this.bitness = JVMHelper.OS_BITS;
this.arch = JVMHelper.ARCH_TYPE;
this.enabledJavaFX = true;
}
@ -200,20 +200,20 @@ public JavaVersion(Path jvmDir, int version, int build, boolean enabledJavaFX) {
this.jvmDir = jvmDir;
this.version = version;
this.build = build;
this.bitness = JVMHelper.OS_BITS;
this.arch = JVMHelper.ARCH_TYPE;
this.enabledJavaFX = enabledJavaFX;
}
public JavaVersion(Path jvmDir, int version, int build, int bitness, boolean enabledJavaFX) {
public JavaVersion(Path jvmDir, int version, int build, JVMHelper.ARCH arch, boolean enabledJavaFX) {
this.jvmDir = jvmDir;
this.version = version;
this.build = build;
this.bitness = bitness;
this.arch = arch;
this.enabledJavaFX = enabledJavaFX;
}
public static JavaVersion getCurrentJavaVersion() {
return new JavaVersion(Paths.get(System.getProperty("java.home")), JVMHelper.getVersion(), JVMHelper.JVM_BUILD, JVMHelper.JVM_BITS, isCurrentJavaSupportJavaFX());
return new JavaVersion(Paths.get(System.getProperty("java.home")), JVMHelper.getVersion(), JVMHelper.JVM_BUILD, JVMHelper.ARCH_TYPE, isCurrentJavaSupportJavaFX());
}
private static boolean isCurrentJavaSupportJavaFX() {
@ -238,21 +238,20 @@ public static JavaVersion getByPath(Path jvmDir) throws IOException {
}
Path releaseFile = jvmDir.resolve("release");
JavaVersionAndBuild versionAndBuild;
int bitness = JVMHelper.OS_BITS;
JVMHelper.ARCH arch = JVMHelper.ARCH_TYPE;
if (IOHelper.isFile(releaseFile)) {
Properties properties = new Properties();
properties.load(IOHelper.newReader(releaseFile));
versionAndBuild = getJavaVersion(properties.getProperty("JAVA_VERSION").replaceAll("\"", ""));
String arch = properties.getProperty("JAVA_VERSION").replaceAll("\"", "");
if (arch.contains("x86_64")) {
bitness = 64;
} else if (arch.contains("x86") || arch.contains("x32")) {
bitness = 32;
try {
arch = JVMHelper.getArch(properties.getProperty("OS_ARCH").replaceAll("\"", ""));
} catch (Throwable ignored) {
arch = null;
}
} else {
versionAndBuild = new JavaVersionAndBuild(isExistExtJavaLibrary(jvmDir, "jfxrt") ? 8 : 9, 0);
}
JavaVersion resultJavaVersion = new JavaVersion(jvmDir, versionAndBuild.version, versionAndBuild.build, bitness, false);
JavaVersion resultJavaVersion = new JavaVersion(jvmDir, versionAndBuild.version, versionAndBuild.build, arch, false);
if (versionAndBuild.version <= 8) {
resultJavaVersion.enabledJavaFX = isExistExtJavaLibrary(jvmDir, "jfxrt");
} else {

View file

@ -387,6 +387,16 @@ public static byte[] sign(byte[] bytes, ECPrivateKey privateKey) {
}
}
public static byte[] sign(byte[] bytes, RSAPrivateKey privateKey) {
Signature signature = newRSASignSignature(privateKey);
try {
signature.update(bytes);
return signature.sign();
} catch (SignatureException e) {
throw new InternalError(e);
}
}
public static String toHex(byte[] bytes) {
if (bytes == null) {

View file

@ -22,7 +22,12 @@ public final class JVMHelper {
public static final OS OS_TYPE = OS.byName(OPERATING_SYSTEM_MXBEAN.getName());
// System properties
public static final String OS_VERSION = OPERATING_SYSTEM_MXBEAN.getVersion();
@Deprecated
public static final int OS_BITS = getCorrectOSArch();
public static final ARCH ARCH_TYPE = getArch(System.getProperty("os.arch"));
public static final int JVM_BITS = Integer.parseInt(System.getProperty("sun.arch.data.model"));
// Public static fields
public static final Runtime RUNTIME = Runtime.getRuntime();
@ -41,6 +46,24 @@ public final class JVMHelper {
private JVMHelper() {
}
public enum ARCH {
X86("x86"), X86_64("x86-64"), ARM64("arm64"), ARM32("arm32");
public final String name;
ARCH(String name) {
this.name = name;
}
}
public static ARCH getArch(String arch) {
if(arch.equals("amd64") || arch.equals("x86-64") || arch.equals("x86_64")) return ARCH.X86_64;
if(arch.equals("i386") || arch.equals("i686") || arch.equals("x86")) return ARCH.X86;
if(arch.startsWith("armv8") || arch.startsWith("aarch64")) return ARCH.ARM64;
if(arch.startsWith("arm") || arch.startsWith("aarch32")) return ARCH.ARM32;
throw new InternalError(String.format("Unsupported arch '%s'", arch));
}
public static int getVersion() {
//System.out.println("[DEBUG] JVMHelper 11 version");
return Runtime.version().feature();
@ -108,6 +131,7 @@ public static void checkStackTrace(Class<?> mainClass) {
}
}
@Deprecated
private static int getCorrectOSArch() {
// As always, mustdie must die
if (OS_TYPE == OS.MUSTDIE)
@ -123,6 +147,7 @@ public static String getEnvPropertyCaseSensitive(String name) {
}
@Deprecated
public static boolean isJVMMatchesSystemArch() {
return JVM_BITS == OS_BITS;
}
@ -154,10 +179,6 @@ public static void verifySystemProperties(Class<?> mainClass, boolean requireSys
// Verify system and java architecture
LogHelper.debug("Verifying JVM architecture");
if (!isJVMMatchesSystemArch()) {
LogHelper.warning("Java and OS architecture mismatch");
LogHelper.warning("It's recommended to download %d-bit JRE", OS_BITS);
}
}
public enum OS {

View file

@ -3,6 +3,7 @@
import pro.gravit.launcher.ClientPermissions;
import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.LauncherConfig;
import pro.gravit.launcher.api.KeyService;
import pro.gravit.launcher.config.JsonConfigurable;
import pro.gravit.launcher.events.request.AuthRequestEvent;
import pro.gravit.launcher.events.request.ProfilesRequestEvent;
@ -24,6 +25,7 @@
import pro.gravit.utils.PublicURLClassLoader;
import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.LogHelper;
import pro.gravit.utils.helper.SecurityHelper;
import java.lang.reflect.Type;
import java.nio.file.Path;
@ -134,6 +136,9 @@ public void run(String... args) throws Throwable {
restore();
getProfiles();
}
if(config.encodedServerRsaPublicKey != null) {
KeyService.serverRsaPublicKey = SecurityHelper.toPublicRSAKey(config.encodedServerRsaPublicKey);
}
String classname = (config.mainclass == null || config.mainclass.isEmpty()) ? args[0] : config.mainclass;
if (classname.length() == 0) {
LogHelper.error("MainClass not found. Please set MainClass for ServerWrapper.json or first commandline argument");
@ -231,6 +236,10 @@ public static final class Config {
public Map<String, String> extendedTokens;
public LauncherConfig.LauncherEnvironment env;
public ModuleConf moduleConf = new ModuleConf();
public byte[] encodedServerRsaPublicKey;
public byte[] encodedServerEcPublicKey;
}
public static final class ModuleConf {

View file

@ -1,7 +1,9 @@
package pro.gravit.launcher.server.setup;
import pro.gravit.launcher.events.request.GetPublicKeyRequestEvent;
import pro.gravit.launcher.profiles.ClientProfile;
import pro.gravit.launcher.request.Request;
import pro.gravit.launcher.request.auth.GetPublicKeyRequest;
import pro.gravit.launcher.request.websockets.StdWebSocketService;
import pro.gravit.launcher.server.ServerWrapper;
import pro.gravit.utils.PublicURLClassLoader;
@ -79,6 +81,9 @@ public void run() throws Exception {
try {
wrapper.restore();
wrapper.getProfiles();
GetPublicKeyRequestEvent publicKeyRequestEvent = new GetPublicKeyRequest().request();
wrapper.config.encodedServerRsaPublicKey = publicKeyRequestEvent.rsaPublicKey;
wrapper.config.encodedServerEcPublicKey = publicKeyRequestEvent.ecdsaPublicKey;
break;
} catch (Throwable e) {
LogHelper.error(e);

View file

@ -5,7 +5,7 @@
id 'org.openjfx.javafxplugin' version '0.0.10' apply false
}
group = 'pro.gravit.launcher'
version = '5.2.13'
version = '5.3.0-SNAPSHOT'
apply from: 'props.gradle'

@ -1 +1 @@
Subproject commit d3722bf136b8c8c8da3dea879d5d2015e84b0f8c
Subproject commit 54630405c075ff3c988fdeb8d03be8f77fbbe6d0