mirror of
https://github.com/GravitLauncher/Launcher
synced 2025-07-06 15:59:47 +03:00
Compare commits
5 commits
2470780591
...
15ed66b3d5
Author | SHA1 | Date | |
---|---|---|---|
|
15ed66b3d5 | ||
|
cacbbfc8ed | ||
|
0a163bb09c | ||
|
f246ab697b | ||
|
a03de56dde |
18 changed files with 420 additions and 128 deletions
|
@ -6,18 +6,14 @@
|
|||
import pro.gravit.launcher.client.*;
|
||||
import pro.gravit.launcher.core.api.LauncherAPI;
|
||||
import pro.gravit.launcher.core.api.LauncherAPIHolder;
|
||||
import pro.gravit.launcher.core.api.features.AuthFeatureAPI;
|
||||
import pro.gravit.launcher.core.api.features.ProfileFeatureAPI;
|
||||
import pro.gravit.launcher.core.api.features.UserFeatureAPI;
|
||||
import pro.gravit.launcher.core.api.features.*;
|
||||
import pro.gravit.launcher.core.backend.LauncherBackendAPIHolder;
|
||||
import pro.gravit.launcher.runtime.backend.LauncherBackendImpl;
|
||||
import pro.gravit.launcher.runtime.client.*;
|
||||
import pro.gravit.launcher.runtime.client.events.ClientEngineInitPhase;
|
||||
import pro.gravit.launcher.client.events.ClientExitPhase;
|
||||
import pro.gravit.launcher.runtime.client.events.ClientPreGuiPhase;
|
||||
import pro.gravit.launcher.runtime.console.GetPublicKeyCommand;
|
||||
import pro.gravit.launcher.runtime.console.ModulesCommand;
|
||||
import pro.gravit.launcher.runtime.console.SignDataCommand;
|
||||
import pro.gravit.launcher.runtime.gui.NoRuntimeProvider;
|
||||
import pro.gravit.launcher.runtime.gui.RuntimeProvider;
|
||||
import pro.gravit.launcher.runtime.managers.ConsoleManager;
|
||||
|
@ -33,15 +29,8 @@
|
|||
import pro.gravit.launcher.start.RuntimeModuleManager;
|
||||
import pro.gravit.utils.helper.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.KeyPair;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
@ -54,8 +43,6 @@ public class LauncherEngine {
|
|||
// Instance
|
||||
private final AtomicBoolean started = new AtomicBoolean(false);
|
||||
public RuntimeProvider runtimeProvider;
|
||||
public ECPublicKey publicKey;
|
||||
public ECPrivateKey privateKey;
|
||||
public Class<? extends RuntimeProvider> basicRuntimeProvider;
|
||||
|
||||
private LauncherEngine(boolean clientInstance, Class<? extends RuntimeProvider> basicRuntimeProvider) {
|
||||
|
@ -182,36 +169,6 @@ public static LauncherEngine newInstance(boolean clientInstance, Class<? extends
|
|||
return new LauncherEngine(clientInstance, basicRuntimeProvider);
|
||||
}
|
||||
|
||||
public ECPublicKey getClientPublicKey() {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
public byte[] sign(byte[] bytes) {
|
||||
return SecurityHelper.sign(bytes, privateKey);
|
||||
}
|
||||
|
||||
public void readKeys() throws IOException, InvalidKeySpecException {
|
||||
if (privateKey != null || publicKey != null) return;
|
||||
Path dir = DirBridge.dir;
|
||||
Path publicKeyFile = dir.resolve("public.key");
|
||||
Path privateKeyFile = dir.resolve("private.key");
|
||||
if (IOHelper.isFile(publicKeyFile) && IOHelper.isFile(privateKeyFile)) {
|
||||
LogHelper.info("Reading EC keypair");
|
||||
publicKey = SecurityHelper.toPublicECDSAKey(IOHelper.read(publicKeyFile));
|
||||
privateKey = SecurityHelper.toPrivateECDSAKey(IOHelper.read(privateKeyFile));
|
||||
} else {
|
||||
LogHelper.info("Generating EC keypair");
|
||||
KeyPair pair = SecurityHelper.genECDSAKeyPair(new SecureRandom());
|
||||
publicKey = (ECPublicKey) pair.getPublic();
|
||||
privateKey = (ECPrivateKey) pair.getPrivate();
|
||||
|
||||
// Write key pair list
|
||||
LogHelper.info("Writing EC keypair list");
|
||||
IOHelper.write(publicKeyFile, publicKey.getEncoded());
|
||||
IOHelper.write(privateKeyFile, privateKey.getEncoded());
|
||||
}
|
||||
}
|
||||
|
||||
public void start(String... args) throws Throwable {
|
||||
//Launcher.modulesManager = new ClientModuleManager(this);
|
||||
ClientPreGuiPhase event = new ClientPreGuiPhase(null);
|
||||
|
@ -256,14 +213,15 @@ public void start(String... args) throws Throwable {
|
|||
return new LauncherAPI(Map.of(
|
||||
AuthFeatureAPI.class, impl,
|
||||
UserFeatureAPI.class, impl,
|
||||
ProfileFeatureAPI.class, impl));
|
||||
ProfileFeatureAPI.class, impl,
|
||||
TextureUploadFeatureAPI.class, impl,
|
||||
HardwareVerificationFeatureAPI.class, impl));
|
||||
});
|
||||
LauncherBackendAPIHolder.setApi(new LauncherBackendImpl());
|
||||
//
|
||||
Objects.requireNonNull(args, "args");
|
||||
if (started.getAndSet(true))
|
||||
throw new IllegalStateException("Launcher has been already started");
|
||||
readKeys();
|
||||
registerCommands();
|
||||
LauncherEngine.modulesManager.invokeEvent(new ClientEngineInitPhase(this));
|
||||
runtimeProvider.preLoad();
|
||||
|
@ -272,8 +230,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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,6 +115,9 @@ CompletableFuture<DownloadedDir> downloadDir(Path targetDir, ProfileFeatureAPI.U
|
|||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
callback.onStage(LauncherBackendAPI.DownloadCallback.STAGE_HASHING);
|
||||
if(!Files.exists(targetDir)) {
|
||||
Files.createDirectories(targetDir);
|
||||
}
|
||||
HashedDir realFiles = new HashedDir(targetDir, matcher, false, true);
|
||||
callback.onStage(LauncherBackendAPI.DownloadCallback.STAGE_DIFF);
|
||||
return updateInfo.getHashedDir().diff(realFiles, matcher);
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package pro.gravit.launcher.runtime.backend;
|
||||
|
||||
import pro.gravit.launcher.runtime.client.DirBridge;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.security.KeyPair;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
|
||||
public class ECKeyHolder {
|
||||
public ECPublicKey publicKey;
|
||||
public ECPrivateKey privateKey;
|
||||
|
||||
public ECPublicKey getClientPublicKey() {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
public byte[] sign(byte[] bytes) {
|
||||
return SecurityHelper.sign(bytes, privateKey);
|
||||
}
|
||||
|
||||
public void readKeys() throws IOException, InvalidKeySpecException {
|
||||
if (privateKey != null || publicKey != null) return;
|
||||
Path dir = DirBridge.dir;
|
||||
Path publicKeyFile = dir.resolve("public.key");
|
||||
Path privateKeyFile = dir.resolve("private.key");
|
||||
if (IOHelper.isFile(publicKeyFile) && IOHelper.isFile(privateKeyFile)) {
|
||||
LogHelper.info("Reading EC keypair");
|
||||
publicKey = SecurityHelper.toPublicECDSAKey(IOHelper.read(publicKeyFile));
|
||||
privateKey = SecurityHelper.toPrivateECDSAKey(IOHelper.read(privateKeyFile));
|
||||
} else {
|
||||
LogHelper.info("Generating EC keypair");
|
||||
KeyPair pair = SecurityHelper.genECDSAKeyPair(new SecureRandom());
|
||||
publicKey = (ECPublicKey) pair.getPublic();
|
||||
privateKey = (ECPrivateKey) pair.getPrivate();
|
||||
|
||||
// Write key pair list
|
||||
LogHelper.info("Writing EC keypair list");
|
||||
IOHelper.write(publicKeyFile, publicKey.getEncoded());
|
||||
IOHelper.write(privateKeyFile, privateKey.getEncoded());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,9 +3,7 @@
|
|||
import pro.gravit.launcher.base.ClientPermissions;
|
||||
import pro.gravit.launcher.base.profiles.ClientProfile;
|
||||
import pro.gravit.launcher.core.api.LauncherAPIHolder;
|
||||
import pro.gravit.launcher.core.api.features.AuthFeatureAPI;
|
||||
import pro.gravit.launcher.core.api.features.CoreFeatureAPI;
|
||||
import pro.gravit.launcher.core.api.features.ProfileFeatureAPI;
|
||||
import pro.gravit.launcher.core.api.features.*;
|
||||
import pro.gravit.launcher.core.api.method.AuthMethod;
|
||||
import pro.gravit.launcher.core.api.method.AuthMethodPassword;
|
||||
import pro.gravit.launcher.core.api.model.SelfUser;
|
||||
|
@ -15,14 +13,18 @@
|
|||
import pro.gravit.launcher.core.backend.UserSettings;
|
||||
import pro.gravit.launcher.core.backend.exceptions.LauncherBackendException;
|
||||
import pro.gravit.launcher.core.backend.extensions.Extension;
|
||||
import pro.gravit.launcher.core.backend.extensions.TextureUploadExtension;
|
||||
import pro.gravit.launcher.runtime.LauncherEngine;
|
||||
import pro.gravit.launcher.runtime.NewLauncherSettings;
|
||||
import pro.gravit.launcher.runtime.client.DirBridge;
|
||||
import pro.gravit.launcher.runtime.client.ServerPinger;
|
||||
import pro.gravit.launcher.runtime.debug.DebugMain;
|
||||
import pro.gravit.launcher.runtime.managers.SettingsManager;
|
||||
import pro.gravit.launcher.runtime.utils.HWIDProvider;
|
||||
import pro.gravit.launcher.runtime.utils.LauncherUpdater;
|
||||
import pro.gravit.utils.helper.JavaHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
@ -39,7 +41,7 @@
|
|||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class LauncherBackendImpl implements LauncherBackendAPI {
|
||||
public class LauncherBackendImpl implements LauncherBackendAPI, TextureUploadExtension {
|
||||
private final ClientDownloadImpl clientDownloadImpl = new ClientDownloadImpl(this);
|
||||
private volatile MainCallback callback;
|
||||
ExecutorService executorService;
|
||||
|
@ -48,12 +50,15 @@ public class LauncherBackendImpl implements LauncherBackendAPI {
|
|||
private SettingsManager settingsManager;
|
||||
private NewLauncherSettings allSettings;
|
||||
private BackendSettings backendSettings;
|
||||
// Hardware
|
||||
private volatile ECKeyHolder ecKeyHolder;
|
||||
// Data
|
||||
private volatile List<ProfileFeatureAPI.ClientProfile> profiles;
|
||||
private volatile UserPermissions permissions;
|
||||
private volatile SelfUser selfUser;
|
||||
private volatile List<Java> availableJavas;
|
||||
private volatile CompletableFuture<List<Java>> availableJavasFuture;
|
||||
private volatile CompletableFuture<Void> processHardwareFuture;
|
||||
private final Map<UUID, CompletableFuture<ServerPingInfo>> pingFutures = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
|
@ -74,6 +79,8 @@ private void doInit() throws Exception {
|
|||
allSettings = settingsManager.getConfig();
|
||||
backendSettings = (BackendSettings) getUserSettings("backend", (k) -> new BackendSettings());
|
||||
permissions = new ClientPermissions();
|
||||
ecKeyHolder = new ECKeyHolder();
|
||||
ecKeyHolder.readKeys();
|
||||
DirBridge.dirUpdates = DirBridge.defaultUpdatesDir;
|
||||
}
|
||||
|
||||
|
@ -157,6 +164,9 @@ private void onAuthorize(SelfUser selfUser) {
|
|||
this.selfUser = selfUser;
|
||||
permissions = selfUser.getPermissions();
|
||||
callback.onAuthorize(selfUser);
|
||||
if(processHardwareFuture == null) {
|
||||
processHardwareFuture = processHardware();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -285,8 +295,14 @@ public boolean isTestMode() {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T extends Extension> T getExtension(Class<T> clazz) {
|
||||
if(clazz == TextureUploadExtension.class) {
|
||||
if(authMethod != null && authMethod.getFeatures().contains(TextureUploadFeatureAPI.FEATURE_NAME)) {
|
||||
return (T) this;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -303,4 +319,43 @@ public void shutdown() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<TextureUploadFeatureAPI.TextureUploadInfo> fetchTextureUploadInfo() {
|
||||
return LauncherAPIHolder.get().get(TextureUploadFeatureAPI.class).fetchInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Texture> uploadTexture(String name, byte[] bytes, TextureUploadFeatureAPI.UploadSettings settings) {
|
||||
return LauncherAPIHolder.get().get(TextureUploadFeatureAPI.class).upload(name, bytes, settings);
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> processHardware() {
|
||||
HardwareVerificationFeatureAPI featureAPI = LauncherAPIHolder.get().get(HardwareVerificationFeatureAPI.class);
|
||||
if(featureAPI == null) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
return featureAPI.getSecurityInfo().thenCompose((response) -> {
|
||||
if(!response.isRequired()) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
byte[] signature = SecurityHelper.sign(response.getSignData(), ecKeyHolder.privateKey);
|
||||
return featureAPI.privateKeyVerification(ecKeyHolder.publicKey, signature);
|
||||
}).thenCompose((response) -> {
|
||||
switch (response.getHardwareCollectLevel()) {
|
||||
case NONE -> {
|
||||
return featureAPI.sendHardwareInfo(null, null);
|
||||
}
|
||||
case ONLY_STATISTIC -> {
|
||||
HWIDProvider hwidProvider = new HWIDProvider();
|
||||
return featureAPI.sendHardwareInfo(hwidProvider.getStatisticData(), null);
|
||||
}
|
||||
case ALL -> {
|
||||
HWIDProvider hwidProvider = new HWIDProvider();
|
||||
return featureAPI.sendHardwareInfo(hwidProvider.getStatisticData(), hwidProvider.getIdentifyData());
|
||||
}
|
||||
}
|
||||
return CompletableFuture.failedFuture(new UnsupportedOperationException());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ public class ProfileSettingsImpl implements LauncherBackendAPI.ClientProfileSett
|
|||
@LauncherNetworkAPI
|
||||
private String saveJavaPath;
|
||||
transient OptionalView view;
|
||||
transient JavaHelper.JavaVersion selectedJava;
|
||||
transient volatile JavaHelper.JavaVersion selectedJava;
|
||||
|
||||
public ProfileSettingsImpl() {
|
||||
}
|
||||
|
@ -203,5 +203,21 @@ public void initAfterGson(ClientProfile profile, LauncherBackendImpl backend) {
|
|||
}
|
||||
enableOptional(opt, (var1, var2) -> {});
|
||||
}
|
||||
if(this.saveJavaPath != null) {
|
||||
backend.getAvailableJava().thenAccept((javas) -> {
|
||||
for(var java : javas) {
|
||||
if(!isCompatible(java)) {
|
||||
continue;
|
||||
}
|
||||
if(java.getPath() == null) {
|
||||
continue;
|
||||
}
|
||||
if(java.getPath().toAbsolutePath().toString().equals(this.saveJavaPath)) {
|
||||
this.selectedJava = (JavaHelper.JavaVersion) java;
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
package pro.gravit.launcher.runtime.console;
|
||||
|
||||
import pro.gravit.launcher.runtime.LauncherEngine;
|
||||
import pro.gravit.utils.command.Command;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
public class GetPublicKeyCommand extends Command {
|
||||
private final LauncherEngine engine;
|
||||
|
||||
public GetPublicKeyCommand(LauncherEngine engine) {
|
||||
this.engine = engine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArgsDescription() {
|
||||
return "[]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsageDescription() {
|
||||
return "print public key in base64 format";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(String... args) {
|
||||
LogHelper.info("PublicKey: %s", Base64.getEncoder().encodeToString(engine.getClientPublicKey().getEncoded()));
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package pro.gravit.launcher.runtime.console;
|
||||
|
||||
import pro.gravit.launcher.runtime.LauncherEngine;
|
||||
import pro.gravit.utils.command.Command;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
public class SignDataCommand extends Command {
|
||||
private final LauncherEngine engine;
|
||||
|
||||
public SignDataCommand(LauncherEngine engine) {
|
||||
this.engine = engine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArgsDescription() {
|
||||
return "[base64 data]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsageDescription() {
|
||||
return "sign any data";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(String... args) throws Exception {
|
||||
verifyArgs(args, 1);
|
||||
byte[] data = Base64.getDecoder().decode(args[0]);
|
||||
byte[] signature = engine.sign(data);
|
||||
String base64 = Base64.getEncoder().encodeToString(signature);
|
||||
LogHelper.info("Signature: %s", base64);
|
||||
}
|
||||
}
|
|
@ -4,6 +4,8 @@
|
|||
import oshi.hardware.*;
|
||||
import oshi.software.os.OperatingSystem;
|
||||
import pro.gravit.launcher.base.request.secure.HardwareReportRequest;
|
||||
import pro.gravit.launcher.core.api.features.HardwareVerificationFeatureAPI;
|
||||
import pro.gravit.utils.helper.JVMHelper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -104,6 +106,27 @@ public String getBaseboardSerialNumber() {
|
|||
return hardware.getComputerSystem().getBaseboard().getSerialNumber();
|
||||
}
|
||||
|
||||
public HardwareVerificationFeatureAPI.HardwareStatisticData getStatisticData() {
|
||||
return new HardwareVerificationFeatureAPI.HardwareStatisticData(
|
||||
JVMHelper.ARCH.toHardwareFeatureArch(JVMHelper.ARCH_TYPE),
|
||||
JVMHelper.OS.toHardwareFeatureOs(JVMHelper.OS_TYPE),
|
||||
getTotalMemory(),
|
||||
getProcessorLogicalCount(),
|
||||
getProcessorPhysicalCount(),
|
||||
getProcessorMaxFreq(),
|
||||
isBattery(),
|
||||
getGraphicCardName()
|
||||
);
|
||||
}
|
||||
|
||||
public HardwareVerificationFeatureAPI.HardwareIdentifyData getIdentifyData() {
|
||||
return new HardwareVerificationFeatureAPI.HardwareIdentifyData(
|
||||
getBaseboardSerialNumber(),
|
||||
getHWDiskID(),
|
||||
getDisplayID()
|
||||
);
|
||||
}
|
||||
|
||||
public HardwareReportRequest.HardwareInfo getHardwareInfo(boolean needSerial) {
|
||||
HardwareReportRequest.HardwareInfo info = new HardwareReportRequest.HardwareInfo();
|
||||
info.bitness = getBitness();
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package pro.gravit.launcher.base.events.request;
|
||||
|
||||
import pro.gravit.launcher.base.events.RequestEvent;
|
||||
import pro.gravit.launcher.core.api.features.TextureUploadFeatureAPI;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class AssetUploadInfoRequestEvent extends RequestEvent {
|
||||
public class AssetUploadInfoRequestEvent extends RequestEvent implements TextureUploadFeatureAPI.TextureUploadInfo {
|
||||
public Set<String> available;
|
||||
public SlimSupportConf slimSupportConf;
|
||||
|
||||
|
@ -18,6 +19,16 @@ public String getType() {
|
|||
return "assetUploadInfo";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAvailable() {
|
||||
return available;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRequireManualSlimSkinSelect() {
|
||||
return slimSupportConf == SlimSupportConf.USER;
|
||||
}
|
||||
|
||||
public enum SlimSupportConf {
|
||||
UNSUPPORTED, USER, SERVER
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package pro.gravit.launcher.base.events.request;
|
||||
|
||||
import pro.gravit.launcher.base.events.RequestEvent;
|
||||
import pro.gravit.launcher.core.api.features.HardwareVerificationFeatureAPI;
|
||||
|
||||
public class GetSecureLevelInfoRequestEvent extends RequestEvent {
|
||||
public class GetSecureLevelInfoRequestEvent extends RequestEvent implements HardwareVerificationFeatureAPI.SecurityLevelInfo {
|
||||
public final byte[] verifySecureKey;
|
||||
public boolean enabled;
|
||||
|
||||
|
@ -19,4 +20,14 @@ public GetSecureLevelInfoRequestEvent(byte[] verifySecureKey, boolean enabled) {
|
|||
public String getType() {
|
||||
return "getSecureLevelInfo";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRequired() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getSignData() {
|
||||
return verifySecureKey;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
|
||||
import pro.gravit.launcher.base.events.ExtendedTokenRequestEvent;
|
||||
import pro.gravit.launcher.base.events.RequestEvent;
|
||||
import pro.gravit.launcher.core.api.features.HardwareVerificationFeatureAPI;
|
||||
|
||||
public class VerifySecureLevelKeyRequestEvent extends RequestEvent implements ExtendedTokenRequestEvent {
|
||||
public class VerifySecureLevelKeyRequestEvent extends RequestEvent implements ExtendedTokenRequestEvent, HardwareVerificationFeatureAPI.SecurityLevelVerification {
|
||||
public boolean needHardwareInfo;
|
||||
public boolean onlyStatisticInfo;
|
||||
public String extendedToken;
|
||||
|
@ -42,4 +43,17 @@ public String getExtendedToken() {
|
|||
public long getExtendedTokenExpire() {
|
||||
return expire;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HardwareCollectLevel getHardwareCollectLevel() {
|
||||
if(needHardwareInfo) {
|
||||
if(onlyStatisticInfo) {
|
||||
return HardwareCollectLevel.ONLY_STATISTIC;
|
||||
} else {
|
||||
return HardwareCollectLevel.ALL;
|
||||
}
|
||||
} else {
|
||||
return HardwareCollectLevel.NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -295,6 +295,11 @@ public UUID getUUID() {
|
|||
return uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMinecraftVersion() {
|
||||
return version.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return info;
|
||||
|
|
|
@ -1,35 +1,50 @@
|
|||
package pro.gravit.launcher.base.request;
|
||||
|
||||
import pro.gravit.launcher.base.Launcher;
|
||||
import pro.gravit.launcher.base.events.request.AuthRequestEvent;
|
||||
import pro.gravit.launcher.base.events.request.VerifySecureLevelKeyRequestEvent;
|
||||
import pro.gravit.launcher.base.profiles.ClientProfile;
|
||||
import pro.gravit.launcher.base.request.auth.*;
|
||||
import pro.gravit.launcher.base.request.auth.password.*;
|
||||
import pro.gravit.launcher.base.request.cabinet.AssetUploadInfoRequest;
|
||||
import pro.gravit.launcher.base.request.cabinet.GetAssetUploadUrl;
|
||||
import pro.gravit.launcher.base.request.secure.GetSecureLevelInfoRequest;
|
||||
import pro.gravit.launcher.base.request.secure.HardwareReportRequest;
|
||||
import pro.gravit.launcher.base.request.secure.VerifySecureLevelKeyRequest;
|
||||
import pro.gravit.launcher.base.request.update.ProfilesRequest;
|
||||
import pro.gravit.launcher.base.request.update.UpdateRequest;
|
||||
import pro.gravit.launcher.base.request.uuid.ProfileByUUIDRequest;
|
||||
import pro.gravit.launcher.base.request.uuid.ProfileByUsernameRequest;
|
||||
import pro.gravit.launcher.core.api.features.AuthFeatureAPI;
|
||||
import pro.gravit.launcher.core.api.features.UserFeatureAPI;
|
||||
import pro.gravit.launcher.core.api.features.ProfileFeatureAPI;
|
||||
import pro.gravit.launcher.core.LauncherNetworkAPI;
|
||||
import pro.gravit.launcher.core.api.features.*;
|
||||
import pro.gravit.launcher.core.api.method.AuthMethodPassword;
|
||||
import pro.gravit.launcher.core.api.method.password.AuthChainPassword;
|
||||
import pro.gravit.launcher.core.api.method.password.AuthOAuthPassword;
|
||||
import pro.gravit.launcher.core.api.method.password.AuthPlainPassword;
|
||||
import pro.gravit.launcher.core.api.method.password.AuthTotpPassword;
|
||||
import pro.gravit.launcher.core.api.model.SelfUser;
|
||||
import pro.gravit.launcher.core.api.model.Texture;
|
||||
import pro.gravit.launcher.core.api.model.User;
|
||||
import pro.gravit.launcher.core.hasher.HashedDir;
|
||||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.PublicKey;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class RequestFeatureAPIImpl implements AuthFeatureAPI, UserFeatureAPI, ProfileFeatureAPI {
|
||||
public class RequestFeatureAPIImpl implements AuthFeatureAPI, UserFeatureAPI, ProfileFeatureAPI, TextureUploadFeatureAPI, HardwareVerificationFeatureAPI {
|
||||
private final RequestService request;
|
||||
private final String authId;
|
||||
private final HttpClient client = HttpClient.newBuilder().build();
|
||||
|
||||
public RequestFeatureAPIImpl(RequestService request, String authId) {
|
||||
this.request = request;
|
||||
|
@ -48,7 +63,10 @@ public CompletableFuture<AuthResponse> auth(String login, AuthMethodPassword pas
|
|||
connectType = AuthRequest.ConnectTypes.CLIENT;
|
||||
}
|
||||
return request.request(new AuthRequest(login, convertAuthPasswordAll(password), authId, false, connectType))
|
||||
.thenApply(response -> new AuthResponse(response.makeUserInfo(), response.oauth));
|
||||
.thenApply(response -> {
|
||||
Request.setOAuth(authId, response.oauth);
|
||||
return new AuthResponse(response.makeUserInfo(), response.oauth);
|
||||
});
|
||||
}
|
||||
|
||||
private AuthRequest.AuthPasswordInterface convertAuthPasswordAll(AuthMethodPassword password) {
|
||||
|
@ -152,6 +170,7 @@ public CompletableFuture<SelfUser> restore(String accessToken, boolean fetchUser
|
|||
}
|
||||
return request.request(new RestoreRequest(authId, accessToken, extended, fetchUser)).thenApply(e -> {
|
||||
// TODO: invalidToken process
|
||||
Request.setOAuth(authId, new AuthRequestEvent.OAuthRequestEvent(accessToken, null, 0));
|
||||
return e.userInfo;
|
||||
});
|
||||
}
|
||||
|
@ -177,6 +196,100 @@ public CompletableFuture<UpdateInfo> fetchUpdateInfo(String dirName) {
|
|||
return request.request(new UpdateRequest(dirName)).thenApply(response -> new UpdateInfoData(response.hdir, response.url));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<TextureUploadInfo> fetchInfo() {
|
||||
return request.request(new AssetUploadInfoRequest()).thenApply(response -> response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Texture> upload(String name, byte[] bytes, UploadSettings settings) {
|
||||
return request.request(new GetAssetUploadUrl(name)).thenCompose((response) -> {
|
||||
String accessToken = response.token == null ? Request.getAccessToken() : response.token.accessToken;
|
||||
String boundary = SecurityHelper.toHex(SecurityHelper.randomBytes(32));
|
||||
String jsonOptions = settings == null ? "{}" : Launcher.gsonManager.gson.toJson(new TextureUploadOptions(settings.slim()));
|
||||
byte[] preFileData;
|
||||
try(ByteArrayOutputStream output = new ByteArrayOutputStream(256)) {
|
||||
output.write("--".getBytes(StandardCharsets.UTF_8));
|
||||
output.write(boundary.getBytes(StandardCharsets.UTF_8));
|
||||
output.write("\r\nContent-Disposition: form-data; name=\"options\"\r\nContent-Type: application/json\r\n\r\n".getBytes(StandardCharsets.UTF_8));
|
||||
output.write(jsonOptions.getBytes(StandardCharsets.UTF_8));
|
||||
output.write("\r\n--".getBytes(StandardCharsets.UTF_8));
|
||||
output.write(boundary.getBytes(StandardCharsets.UTF_8));
|
||||
output.write("\r\nContent-Disposition: form-data; name=\"file\"; filename=\"file\"\r\nContent-Type: image/png\r\n\r\n".getBytes(StandardCharsets.UTF_8));
|
||||
preFileData = output.toByteArray();
|
||||
} catch (IOException ex) {
|
||||
return CompletableFuture.failedFuture(ex);
|
||||
}
|
||||
byte[] postFileData;
|
||||
try(ByteArrayOutputStream output = new ByteArrayOutputStream(128)) {
|
||||
output.write("\r\n--".getBytes(StandardCharsets.UTF_8));
|
||||
output.write(boundary.getBytes(StandardCharsets.UTF_8));
|
||||
output.write("--\r\n".getBytes(StandardCharsets.UTF_8));
|
||||
postFileData = output.toByteArray();
|
||||
} catch (IOException ex) {
|
||||
return CompletableFuture.failedFuture(ex);
|
||||
}
|
||||
return client.sendAsync(HttpRequest.newBuilder()
|
||||
.uri(URI.create(response.url))
|
||||
.POST(HttpRequest.BodyPublishers.concat(HttpRequest.BodyPublishers.ofByteArray(preFileData),
|
||||
HttpRequest.BodyPublishers.ofByteArray(bytes),
|
||||
HttpRequest.BodyPublishers.ofByteArray(postFileData)))
|
||||
.header("Authorization", "Bearer "+accessToken)
|
||||
.header("Content-Type", "multipart/form-data; boundary=\""+boundary+"\"")
|
||||
.header("Accept", "application/json")
|
||||
.build(), HttpResponse.BodyHandlers.ofByteArray());
|
||||
}).thenCompose((response) -> {
|
||||
if(response.statusCode() >= 200 && response.statusCode() < 300) {
|
||||
try (Reader reader = new InputStreamReader(new ByteArrayInputStream(response.body()))) {
|
||||
return CompletableFuture.completedFuture(Launcher.gsonManager.gson.fromJson(reader, UserTexture.class).toLauncherTexture());
|
||||
} catch (Throwable e) {
|
||||
return CompletableFuture.failedFuture(e);
|
||||
}
|
||||
} else {
|
||||
try(Reader reader = new InputStreamReader(new ByteArrayInputStream(response.body()))) {
|
||||
UploadError error = Launcher.gsonManager.gson.fromJson(reader, UploadError.class);
|
||||
return CompletableFuture.failedFuture(new RequestException(error.error()));
|
||||
} catch (Exception ex) {
|
||||
return CompletableFuture.failedFuture(ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<SecurityLevelInfo> getSecurityInfo() {
|
||||
return request.request(new GetSecureLevelInfoRequest()).thenApply(response -> response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<SecurityLevelVerification> privateKeyVerification(PublicKey publicKey, byte[] signature) {
|
||||
return request.request(new VerifySecureLevelKeyRequest(publicKey.getEncoded(), signature)).thenApply(response -> response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> sendHardwareInfo(HardwareStatisticData statisticData, HardwareIdentifyData identifyData) {
|
||||
if(statisticData == null && identifyData == null) { // Hardware info token special
|
||||
return request.request(new HardwareReportRequest()).thenApply(response -> null);
|
||||
} else {
|
||||
var hardwareInfo = new HardwareReportRequest.HardwareInfo();
|
||||
if(statisticData != null) {
|
||||
hardwareInfo.bitness = statisticData.arch() == Arch.X86 || statisticData.arch() == Arch.ARM32 ? 32 : 64;
|
||||
hardwareInfo.totalMemory = statisticData.totalPhysicalMemory();
|
||||
hardwareInfo.logicalProcessors = statisticData.logicalProcessors();
|
||||
hardwareInfo.physicalProcessors = statisticData.physicalProcessors();
|
||||
hardwareInfo.processorMaxFreq = statisticData.processorMaxFreq();
|
||||
hardwareInfo.battery = statisticData.battery();
|
||||
hardwareInfo.graphicCard = statisticData.graphicCard();
|
||||
}
|
||||
if(identifyData != null) {
|
||||
hardwareInfo.hwDiskId = identifyData.persistentStorageId();
|
||||
hardwareInfo.displayId = identifyData.edid();
|
||||
hardwareInfo.baseboardSerialNumber = identifyData.baseboardSerialNumber();
|
||||
}
|
||||
return request.request(new HardwareReportRequest(hardwareInfo)).thenApply(response -> null);
|
||||
}
|
||||
}
|
||||
|
||||
public record UpdateInfoData(HashedDir hdir, String url) implements ProfileFeatureAPI.UpdateInfo {
|
||||
@Override
|
||||
public HashedDir getHashedDir() {
|
||||
|
@ -188,4 +301,19 @@ public String getUrl() {
|
|||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
public record TextureUploadOptions(boolean modelSlim) {
|
||||
|
||||
}
|
||||
|
||||
public record UserTexture(@LauncherNetworkAPI String url, @LauncherNetworkAPI String digest, @LauncherNetworkAPI Map<String, String> metadata) {
|
||||
|
||||
Texture toLauncherTexture() {
|
||||
return new pro.gravit.launcher.base.profiles.Texture(url, SecurityHelper.fromHex(digest), metadata);
|
||||
}
|
||||
}
|
||||
|
||||
public record UploadError(@LauncherNetworkAPI String error) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,13 @@
|
|||
public class HardwareReportRequest extends Request<HardwareReportRequestEvent> {
|
||||
public HardwareInfo hardware;
|
||||
|
||||
public HardwareReportRequest() {
|
||||
}
|
||||
|
||||
public HardwareReportRequest(HardwareInfo hardware) {
|
||||
this.hardware = hardware;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "hardwareReport";
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
package pro.gravit.launcher.core.api.features;
|
||||
|
||||
import pro.gravit.utils.helper.JVMHelper;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface HardwareVerificationFeatureAPI extends FeatureAPI {
|
||||
CompletableFuture<SecurityLevelInfo> getSecurityInfo();
|
||||
CompletableFuture<SecurityLevelVerification> privateKeyVerification(PublicKey publicKey, byte[] signature);
|
||||
CompletableFuture<Void> sendHardwareInfo(HardwareStatisticData statisticData, HardwareIdentifyData identifyData);
|
||||
|
||||
|
||||
interface SecurityLevelInfo {
|
||||
boolean isRequired();
|
||||
byte[] getSignData();
|
||||
}
|
||||
interface SecurityLevelVerification {
|
||||
HardwareCollectLevel getHardwareCollectLevel();
|
||||
|
||||
enum HardwareCollectLevel {
|
||||
NONE, ONLY_STATISTIC, ALL
|
||||
}
|
||||
}
|
||||
|
||||
record HardwareStatisticData(Arch arch, Os os, long totalPhysicalMemory,
|
||||
int logicalProcessors, int physicalProcessors,
|
||||
long processorMaxFreq, boolean battery,
|
||||
String graphicCard) {
|
||||
|
||||
}
|
||||
|
||||
record HardwareIdentifyData(String baseboardSerialNumber, String persistentStorageId,
|
||||
byte[] edid) {
|
||||
|
||||
}
|
||||
|
||||
enum Arch {
|
||||
X86("x86"), X86_64("x86-64"), ARM64("arm64"), ARM32("arm32");
|
||||
|
||||
public final String name;
|
||||
|
||||
Arch(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
enum Os {
|
||||
WINDOWS("windows"), LINUX("linux"), MACOS("macos");
|
||||
|
||||
public final String name;
|
||||
|
||||
Os(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ interface UpdateInfo {
|
|||
interface ClientProfile {
|
||||
String getName();
|
||||
UUID getUUID();
|
||||
String getMinecraftVersion();
|
||||
String getDescription();
|
||||
List<OptionalMod> getOptionalMods();
|
||||
String getProperty(String name);
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface TextureUploadFeatureAPI {
|
||||
public interface TextureUploadFeatureAPI extends FeatureAPI {
|
||||
String FEATURE_NAME = "assetupload";
|
||||
CompletableFuture<TextureUploadInfo> fetchInfo();
|
||||
CompletableFuture<Texture> upload(String name, byte[] bytes, UploadSettings settings);
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package pro.gravit.utils.helper;
|
||||
|
||||
import pro.gravit.launcher.core.api.features.HardwareVerificationFeatureAPI;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.OperatingSystemMXBean;
|
||||
|
@ -56,7 +58,7 @@ public static int getBuild() {
|
|||
return Runtime.version().update();
|
||||
}
|
||||
|
||||
public static String getNativeExtension(JVMHelper.OS OS_TYPE) {
|
||||
public static String getNativeExtension(OS OS_TYPE) {
|
||||
return switch (OS_TYPE) {
|
||||
case MUSTDIE -> ".dll";
|
||||
case LINUX -> ".so";
|
||||
|
@ -64,7 +66,7 @@ public static String getNativeExtension(JVMHelper.OS OS_TYPE) {
|
|||
};
|
||||
}
|
||||
|
||||
public static String getNativePrefix(JVMHelper.OS OS_TYPE) {
|
||||
public static String getNativePrefix(OS OS_TYPE) {
|
||||
return switch (OS_TYPE) {
|
||||
case LINUX, MACOSX -> "lib";
|
||||
default -> "";
|
||||
|
@ -124,6 +126,15 @@ public enum ARCH {
|
|||
|
||||
public final String name;
|
||||
|
||||
public static HardwareVerificationFeatureAPI.Arch toHardwareFeatureArch(ARCH arch) {
|
||||
return switch (arch) {
|
||||
case X86 -> HardwareVerificationFeatureAPI.Arch.X86;
|
||||
case X86_64 -> HardwareVerificationFeatureAPI.Arch.X86_64;
|
||||
case ARM64 -> HardwareVerificationFeatureAPI.Arch.ARM64;
|
||||
case ARM32 -> HardwareVerificationFeatureAPI.Arch.ARM32;
|
||||
};
|
||||
}
|
||||
|
||||
ARCH(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
@ -138,6 +149,14 @@ public enum OS {
|
|||
this.name = name;
|
||||
}
|
||||
|
||||
public static HardwareVerificationFeatureAPI.Os toHardwareFeatureOs(OS os) {
|
||||
return switch (os) {
|
||||
case MUSTDIE -> HardwareVerificationFeatureAPI.Os.WINDOWS;
|
||||
case LINUX -> HardwareVerificationFeatureAPI.Os.LINUX;
|
||||
case MACOSX -> HardwareVerificationFeatureAPI.Os.MACOS;
|
||||
};
|
||||
}
|
||||
|
||||
public static OS byName(String name) {
|
||||
if (name.startsWith("Windows"))
|
||||
return MUSTDIE;
|
||||
|
|
Loading…
Reference in a new issue