[FEATURE] Implement HardwareVerificationFeatureAPI

This commit is contained in:
Gravita 2025-06-13 15:59:58 +07:00
parent a03de56dde
commit f246ab697b
12 changed files with 269 additions and 126 deletions

View file

@ -6,19 +6,14 @@
import pro.gravit.launcher.client.*; import pro.gravit.launcher.client.*;
import pro.gravit.launcher.core.api.LauncherAPI; import pro.gravit.launcher.core.api.LauncherAPI;
import pro.gravit.launcher.core.api.LauncherAPIHolder; import pro.gravit.launcher.core.api.LauncherAPIHolder;
import pro.gravit.launcher.core.api.features.AuthFeatureAPI; import pro.gravit.launcher.core.api.features.*;
import pro.gravit.launcher.core.api.features.ProfileFeatureAPI;
import pro.gravit.launcher.core.api.features.TextureUploadFeatureAPI;
import pro.gravit.launcher.core.api.features.UserFeatureAPI;
import pro.gravit.launcher.core.backend.LauncherBackendAPIHolder; import pro.gravit.launcher.core.backend.LauncherBackendAPIHolder;
import pro.gravit.launcher.runtime.backend.LauncherBackendImpl; import pro.gravit.launcher.runtime.backend.LauncherBackendImpl;
import pro.gravit.launcher.runtime.client.*; import pro.gravit.launcher.runtime.client.*;
import pro.gravit.launcher.runtime.client.events.ClientEngineInitPhase; import pro.gravit.launcher.runtime.client.events.ClientEngineInitPhase;
import pro.gravit.launcher.client.events.ClientExitPhase; import pro.gravit.launcher.client.events.ClientExitPhase;
import pro.gravit.launcher.runtime.client.events.ClientPreGuiPhase; 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.ModulesCommand;
import pro.gravit.launcher.runtime.console.SignDataCommand;
import pro.gravit.launcher.runtime.gui.NoRuntimeProvider; import pro.gravit.launcher.runtime.gui.NoRuntimeProvider;
import pro.gravit.launcher.runtime.gui.RuntimeProvider; import pro.gravit.launcher.runtime.gui.RuntimeProvider;
import pro.gravit.launcher.runtime.managers.ConsoleManager; import pro.gravit.launcher.runtime.managers.ConsoleManager;
@ -34,15 +29,8 @@
import pro.gravit.launcher.start.RuntimeModuleManager; import pro.gravit.launcher.start.RuntimeModuleManager;
import pro.gravit.utils.helper.*; import pro.gravit.utils.helper.*;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.security.KeyPair;
import java.security.SecureRandom;
import java.security.cert.X509Certificate; 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.Arrays;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@ -55,8 +43,6 @@ public class LauncherEngine {
// Instance // Instance
private final AtomicBoolean started = new AtomicBoolean(false); private final AtomicBoolean started = new AtomicBoolean(false);
public RuntimeProvider runtimeProvider; public RuntimeProvider runtimeProvider;
public ECPublicKey publicKey;
public ECPrivateKey privateKey;
public Class<? extends RuntimeProvider> basicRuntimeProvider; public Class<? extends RuntimeProvider> basicRuntimeProvider;
private LauncherEngine(boolean clientInstance, Class<? extends RuntimeProvider> basicRuntimeProvider) { private LauncherEngine(boolean clientInstance, Class<? extends RuntimeProvider> basicRuntimeProvider) {
@ -183,36 +169,6 @@ public static LauncherEngine newInstance(boolean clientInstance, Class<? extends
return new LauncherEngine(clientInstance, basicRuntimeProvider); 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 { public void start(String... args) throws Throwable {
//Launcher.modulesManager = new ClientModuleManager(this); //Launcher.modulesManager = new ClientModuleManager(this);
ClientPreGuiPhase event = new ClientPreGuiPhase(null); ClientPreGuiPhase event = new ClientPreGuiPhase(null);
@ -258,14 +214,14 @@ public void start(String... args) throws Throwable {
AuthFeatureAPI.class, impl, AuthFeatureAPI.class, impl,
UserFeatureAPI.class, impl, UserFeatureAPI.class, impl,
ProfileFeatureAPI.class, impl, ProfileFeatureAPI.class, impl,
TextureUploadFeatureAPI.class, impl)); TextureUploadFeatureAPI.class, impl,
HardwareVerificationFeatureAPI.class, impl));
}); });
LauncherBackendAPIHolder.setApi(new LauncherBackendImpl()); LauncherBackendAPIHolder.setApi(new LauncherBackendImpl());
// //
Objects.requireNonNull(args, "args"); Objects.requireNonNull(args, "args");
if (started.getAndSet(true)) if (started.getAndSet(true))
throw new IllegalStateException("Launcher has been already started"); throw new IllegalStateException("Launcher has been already started");
readKeys();
registerCommands(); registerCommands();
LauncherEngine.modulesManager.invokeEvent(new ClientEngineInitPhase(this)); LauncherEngine.modulesManager.invokeEvent(new ClientEngineInitPhase(this));
runtimeProvider.preLoad(); runtimeProvider.preLoad();
@ -274,8 +230,6 @@ public void start(String... args) throws Throwable {
} }
private void registerCommands() { private void registerCommands() {
ConsoleManager.handler.registerCommand("getpublickey", new GetPublicKeyCommand(this));
ConsoleManager.handler.registerCommand("signdata", new SignDataCommand(this));
ConsoleManager.handler.registerCommand("modules", new ModulesCommand()); ConsoleManager.handler.registerCommand("modules", new ModulesCommand());
} }
} }

View file

@ -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());
}
}
}

View file

@ -3,10 +3,7 @@
import pro.gravit.launcher.base.ClientPermissions; import pro.gravit.launcher.base.ClientPermissions;
import pro.gravit.launcher.base.profiles.ClientProfile; import pro.gravit.launcher.base.profiles.ClientProfile;
import pro.gravit.launcher.core.api.LauncherAPIHolder; import pro.gravit.launcher.core.api.LauncherAPIHolder;
import pro.gravit.launcher.core.api.features.AuthFeatureAPI; import pro.gravit.launcher.core.api.features.*;
import pro.gravit.launcher.core.api.features.CoreFeatureAPI;
import pro.gravit.launcher.core.api.features.ProfileFeatureAPI;
import pro.gravit.launcher.core.api.features.TextureUploadFeatureAPI;
import pro.gravit.launcher.core.api.method.AuthMethod; import pro.gravit.launcher.core.api.method.AuthMethod;
import pro.gravit.launcher.core.api.method.AuthMethodPassword; import pro.gravit.launcher.core.api.method.AuthMethodPassword;
import pro.gravit.launcher.core.api.model.SelfUser; import pro.gravit.launcher.core.api.model.SelfUser;
@ -17,14 +14,17 @@
import pro.gravit.launcher.core.backend.exceptions.LauncherBackendException; import pro.gravit.launcher.core.backend.exceptions.LauncherBackendException;
import pro.gravit.launcher.core.backend.extensions.Extension; import pro.gravit.launcher.core.backend.extensions.Extension;
import pro.gravit.launcher.core.backend.extensions.TextureUploadExtension; 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.NewLauncherSettings;
import pro.gravit.launcher.runtime.client.DirBridge; import pro.gravit.launcher.runtime.client.DirBridge;
import pro.gravit.launcher.runtime.client.ServerPinger; import pro.gravit.launcher.runtime.client.ServerPinger;
import pro.gravit.launcher.runtime.debug.DebugMain; import pro.gravit.launcher.runtime.debug.DebugMain;
import pro.gravit.launcher.runtime.managers.SettingsManager; import pro.gravit.launcher.runtime.managers.SettingsManager;
import pro.gravit.launcher.runtime.utils.HWIDProvider;
import pro.gravit.launcher.runtime.utils.LauncherUpdater; import pro.gravit.launcher.runtime.utils.LauncherUpdater;
import pro.gravit.utils.helper.JavaHelper; import pro.gravit.utils.helper.JavaHelper;
import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.LogHelper;
import pro.gravit.utils.helper.SecurityHelper;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
@ -50,12 +50,15 @@ public class LauncherBackendImpl implements LauncherBackendAPI, TextureUploadExt
private SettingsManager settingsManager; private SettingsManager settingsManager;
private NewLauncherSettings allSettings; private NewLauncherSettings allSettings;
private BackendSettings backendSettings; private BackendSettings backendSettings;
// Hardware
private volatile ECKeyHolder ecKeyHolder;
// Data // Data
private volatile List<ProfileFeatureAPI.ClientProfile> profiles; private volatile List<ProfileFeatureAPI.ClientProfile> profiles;
private volatile UserPermissions permissions; private volatile UserPermissions permissions;
private volatile SelfUser selfUser; private volatile SelfUser selfUser;
private volatile List<Java> availableJavas; private volatile List<Java> availableJavas;
private volatile CompletableFuture<List<Java>> availableJavasFuture; private volatile CompletableFuture<List<Java>> availableJavasFuture;
private volatile CompletableFuture<Void> processHardwareFuture;
private final Map<UUID, CompletableFuture<ServerPingInfo>> pingFutures = new ConcurrentHashMap<>(); private final Map<UUID, CompletableFuture<ServerPingInfo>> pingFutures = new ConcurrentHashMap<>();
@Override @Override
@ -76,6 +79,8 @@ private void doInit() throws Exception {
allSettings = settingsManager.getConfig(); allSettings = settingsManager.getConfig();
backendSettings = (BackendSettings) getUserSettings("backend", (k) -> new BackendSettings()); backendSettings = (BackendSettings) getUserSettings("backend", (k) -> new BackendSettings());
permissions = new ClientPermissions(); permissions = new ClientPermissions();
ecKeyHolder = new ECKeyHolder();
ecKeyHolder.readKeys();
DirBridge.dirUpdates = DirBridge.defaultUpdatesDir; DirBridge.dirUpdates = DirBridge.defaultUpdatesDir;
} }
@ -159,6 +164,9 @@ private void onAuthorize(SelfUser selfUser) {
this.selfUser = selfUser; this.selfUser = selfUser;
permissions = selfUser.getPermissions(); permissions = selfUser.getPermissions();
callback.onAuthorize(selfUser); callback.onAuthorize(selfUser);
if(processHardwareFuture == null) {
processHardwareFuture = processHardware();
}
} }
@Override @Override
@ -321,4 +329,33 @@ public CompletableFuture<TextureUploadFeatureAPI.TextureUploadInfo> fetchTexture
public CompletableFuture<Texture> uploadTexture(String name, byte[] bytes, TextureUploadFeatureAPI.UploadSettings settings) { public CompletableFuture<Texture> uploadTexture(String name, byte[] bytes, TextureUploadFeatureAPI.UploadSettings settings) {
return LauncherAPIHolder.get().get(TextureUploadFeatureAPI.class).upload(name, bytes, 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());
});
}
} }

View file

@ -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()));
}
}

View file

@ -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);
}
}

View file

@ -4,6 +4,8 @@
import oshi.hardware.*; import oshi.hardware.*;
import oshi.software.os.OperatingSystem; import oshi.software.os.OperatingSystem;
import pro.gravit.launcher.base.request.secure.HardwareReportRequest; 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; import java.util.List;
@ -104,6 +106,27 @@ public String getBaseboardSerialNumber() {
return hardware.getComputerSystem().getBaseboard().getSerialNumber(); 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) { public HardwareReportRequest.HardwareInfo getHardwareInfo(boolean needSerial) {
HardwareReportRequest.HardwareInfo info = new HardwareReportRequest.HardwareInfo(); HardwareReportRequest.HardwareInfo info = new HardwareReportRequest.HardwareInfo();
info.bitness = getBitness(); info.bitness = getBitness();

View file

@ -1,8 +1,9 @@
package pro.gravit.launcher.base.events.request; package pro.gravit.launcher.base.events.request;
import pro.gravit.launcher.base.events.RequestEvent; 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 final byte[] verifySecureKey;
public boolean enabled; public boolean enabled;
@ -19,4 +20,14 @@ public GetSecureLevelInfoRequestEvent(byte[] verifySecureKey, boolean enabled) {
public String getType() { public String getType() {
return "getSecureLevelInfo"; return "getSecureLevelInfo";
} }
@Override
public boolean isRequired() {
return enabled;
}
@Override
public byte[] getSignData() {
return verifySecureKey;
}
} }

View file

@ -2,8 +2,9 @@
import pro.gravit.launcher.base.events.ExtendedTokenRequestEvent; import pro.gravit.launcher.base.events.ExtendedTokenRequestEvent;
import pro.gravit.launcher.base.events.RequestEvent; 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 needHardwareInfo;
public boolean onlyStatisticInfo; public boolean onlyStatisticInfo;
public String extendedToken; public String extendedToken;
@ -42,4 +43,17 @@ public String getExtendedToken() {
public long getExtendedTokenExpire() { public long getExtendedTokenExpire() {
return expire; return expire;
} }
@Override
public HardwareCollectLevel getHardwareCollectLevel() {
if(needHardwareInfo) {
if(onlyStatisticInfo) {
return HardwareCollectLevel.ONLY_STATISTIC;
} else {
return HardwareCollectLevel.ALL;
}
} else {
return HardwareCollectLevel.NONE;
}
}
} }

View file

@ -1,20 +1,21 @@
package pro.gravit.launcher.base.request; package pro.gravit.launcher.base.request;
import pro.gravit.launcher.base.Launcher; import pro.gravit.launcher.base.Launcher;
import pro.gravit.launcher.base.events.request.VerifySecureLevelKeyRequestEvent;
import pro.gravit.launcher.base.profiles.ClientProfile; import pro.gravit.launcher.base.profiles.ClientProfile;
import pro.gravit.launcher.base.request.auth.*; import pro.gravit.launcher.base.request.auth.*;
import pro.gravit.launcher.base.request.auth.password.*; import pro.gravit.launcher.base.request.auth.password.*;
import pro.gravit.launcher.base.request.cabinet.AssetUploadInfoRequest; import pro.gravit.launcher.base.request.cabinet.AssetUploadInfoRequest;
import pro.gravit.launcher.base.request.cabinet.GetAssetUploadUrl; 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.ProfilesRequest;
import pro.gravit.launcher.base.request.update.UpdateRequest; import pro.gravit.launcher.base.request.update.UpdateRequest;
import pro.gravit.launcher.base.request.uuid.ProfileByUUIDRequest; import pro.gravit.launcher.base.request.uuid.ProfileByUUIDRequest;
import pro.gravit.launcher.base.request.uuid.ProfileByUsernameRequest; import pro.gravit.launcher.base.request.uuid.ProfileByUsernameRequest;
import pro.gravit.launcher.core.LauncherNetworkAPI; import pro.gravit.launcher.core.LauncherNetworkAPI;
import pro.gravit.launcher.core.api.features.AuthFeatureAPI; import pro.gravit.launcher.core.api.features.*;
import pro.gravit.launcher.core.api.features.TextureUploadFeatureAPI;
import pro.gravit.launcher.core.api.features.UserFeatureAPI;
import pro.gravit.launcher.core.api.features.ProfileFeatureAPI;
import pro.gravit.launcher.core.api.method.AuthMethodPassword; 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.AuthChainPassword;
import pro.gravit.launcher.core.api.method.password.AuthOAuthPassword; import pro.gravit.launcher.core.api.method.password.AuthOAuthPassword;
@ -32,13 +33,14 @@
import java.net.http.HttpRequest; import java.net.http.HttpRequest;
import java.net.http.HttpResponse; import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.PublicKey;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
public class RequestFeatureAPIImpl implements AuthFeatureAPI, UserFeatureAPI, ProfileFeatureAPI, TextureUploadFeatureAPI { public class RequestFeatureAPIImpl implements AuthFeatureAPI, UserFeatureAPI, ProfileFeatureAPI, TextureUploadFeatureAPI, HardwareVerificationFeatureAPI {
private final RequestService request; private final RequestService request;
private final String authId; private final String authId;
private final HttpClient client = HttpClient.newBuilder().build(); private final HttpClient client = HttpClient.newBuilder().build();
@ -249,6 +251,40 @@ public CompletableFuture<Texture> upload(String name, byte[] bytes, UploadSettin
}); });
} }
@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 { public record UpdateInfoData(HashedDir hdir, String url) implements ProfileFeatureAPI.UpdateInfo {
@Override @Override
public HashedDir getHashedDir() { public HashedDir getHashedDir() {

View file

@ -8,6 +8,13 @@
public class HardwareReportRequest extends Request<HardwareReportRequestEvent> { public class HardwareReportRequest extends Request<HardwareReportRequestEvent> {
public HardwareInfo hardware; public HardwareInfo hardware;
public HardwareReportRequest() {
}
public HardwareReportRequest(HardwareInfo hardware) {
this.hardware = hardware;
}
@Override @Override
public String getType() { public String getType() {
return "hardwareReport"; return "hardwareReport";

View file

@ -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;
}
}
}

View file

@ -1,5 +1,7 @@
package pro.gravit.utils.helper; package pro.gravit.utils.helper;
import pro.gravit.launcher.core.api.features.HardwareVerificationFeatureAPI;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.management.ManagementFactory; import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean; import java.lang.management.OperatingSystemMXBean;
@ -56,7 +58,7 @@ public static int getBuild() {
return Runtime.version().update(); return Runtime.version().update();
} }
public static String getNativeExtension(JVMHelper.OS OS_TYPE) { public static String getNativeExtension(OS OS_TYPE) {
return switch (OS_TYPE) { return switch (OS_TYPE) {
case MUSTDIE -> ".dll"; case MUSTDIE -> ".dll";
case LINUX -> ".so"; 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) { return switch (OS_TYPE) {
case LINUX, MACOSX -> "lib"; case LINUX, MACOSX -> "lib";
default -> ""; default -> "";
@ -124,6 +126,15 @@ public enum ARCH {
public final String name; 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) { ARCH(String name) {
this.name = name; this.name = name;
} }
@ -138,6 +149,14 @@ public enum OS {
this.name = name; 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) { public static OS byName(String name) {
if (name.startsWith("Windows")) if (name.startsWith("Windows"))
return MUSTDIE; return MUSTDIE;