[FEATURE] Support Runtime encrypt and themes part 1

This commit is contained in:
Gravita 2025-06-22 19:24:39 +07:00
parent 37546e8a5a
commit 324de7226d
3 changed files with 133 additions and 0 deletions

View file

@ -0,0 +1,50 @@
package pro.gravit.launcher.runtime.backend;
import pro.gravit.launcher.base.Launcher;
import pro.gravit.launcher.base.vfs.Vfs;
import pro.gravit.launcher.base.vfs.VfsFile;
import pro.gravit.utils.helper.SecurityHelper;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
public class EncryptedVfsFile extends VfsFile {
private VfsFile parent;
private final String alg;
private final SecretKeySpec sKeySpec;
private final IvParameterSpec iKeySpec;
public EncryptedVfsFile(VfsFile parent) {
this.parent = parent;
this.alg = "AES/CBC/PKCS5Padding";
try {
byte[] compat = SecurityHelper.getAESKey(Launcher.getConfig().runtimeEncryptKey.getBytes(StandardCharsets.UTF_8));
sKeySpec = new SecretKeySpec(compat, "AES");
iKeySpec = new IvParameterSpec("8u3d90ikr7o67lsq".getBytes());
} catch (Exception e) {
throw new SecurityException(e);
}
}
@Override
public InputStream getInputStream() {
Cipher cipher;
try {
cipher = Cipher.getInstance(alg);
cipher.init(Cipher.DECRYPT_MODE, sKeySpec, iKeySpec);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException |
InvalidAlgorithmParameterException e) {
throw new SecurityException(e);
}
return new BufferedInputStream(new CipherInputStream(parent.getInputStream(), cipher));
}
}

View file

@ -1,7 +1,12 @@
package pro.gravit.launcher.runtime.backend; package pro.gravit.launcher.runtime.backend;
import pro.gravit.launcher.base.ClientPermissions; import pro.gravit.launcher.base.ClientPermissions;
import pro.gravit.launcher.base.Launcher;
import pro.gravit.launcher.base.profiles.ClientProfile; import pro.gravit.launcher.base.profiles.ClientProfile;
import pro.gravit.launcher.base.vfs.Vfs;
import pro.gravit.launcher.base.vfs.directory.FileVfsDirectory;
import pro.gravit.launcher.base.vfs.file.CachedVfsFile;
import pro.gravit.launcher.base.vfs.file.UrlVfsFile;
import pro.gravit.launcher.core.api.LauncherAPIHolder; import pro.gravit.launcher.core.api.LauncherAPIHolder;
import pro.gravit.launcher.core.api.features.*; import pro.gravit.launcher.core.api.features.*;
import pro.gravit.launcher.core.api.method.AuthMethod; import pro.gravit.launcher.core.api.method.AuthMethod;
@ -22,12 +27,15 @@
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.HWIDProvider;
import pro.gravit.launcher.runtime.utils.LauncherUpdater; import pro.gravit.launcher.runtime.utils.LauncherUpdater;
import pro.gravit.utils.helper.IOHelper;
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 pro.gravit.utils.helper.SecurityHelper;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneOffset; import java.time.ZoneOffset;
@ -370,4 +378,34 @@ public CompletableFuture<Void> processHardware() {
return CompletableFuture.failedFuture(new UnsupportedOperationException()); return CompletableFuture.failedFuture(new UnsupportedOperationException());
}); });
} }
public Path initVfsDirectory() {
Path defaultPath = Path.of("runtime");
if(isTestMode()) {
Vfs.get().put(defaultPath, new FileVfsDirectory(defaultPath));
} else {
var encryptKey = Launcher.getConfig().runtimeEncryptKey;
if(encryptKey == null) {
for(var e : Launcher.getConfig().runtime.entrySet()) {
var realPath = e.getKey();
var encodedName = "runtime/" + realPath;
try {
Vfs.get().put(defaultPath.resolve(realPath), new UrlVfsFile(IOHelper.getResourceURL(encodedName)));
} catch (NoSuchFileException ignored) {
}
}
} else {
for(var e : Launcher.getConfig().runtime.entrySet()) {
var realPath = e.getKey();
var hash = e.getValue();
var encodedName = "runtime/" + SecurityHelper.toHex(hash);
try {
Vfs.get().put(defaultPath.resolve(realPath), new CachedVfsFile(new EncryptedVfsFile(new UrlVfsFile(IOHelper.getResourceURL(encodedName)))));
} catch (NoSuchFileException ignored) {
}
}
}
}
return defaultPath;
}
} }

View file

@ -0,0 +1,45 @@
package pro.gravit.launcher.base.vfs.file;
import pro.gravit.launcher.base.vfs.VfsFile;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
public class CachedVfsFile extends VfsFile {
private final VfsFile delegate;
private volatile SoftReference<byte[]> cache;
public CachedVfsFile(VfsFile delegate) {
this.delegate = delegate;
}
private synchronized InputStream tryCache() {
try {
ByteArrayOutputStream output = new ByteArrayOutputStream();
try(InputStream input = delegate.getInputStream()) {
input.transferTo(output);
}
byte[] bytes = output.toByteArray();
cache = new SoftReference<>(bytes);
return new ByteArrayInputStream(bytes);
} catch (OutOfMemoryError | IOException ignored) {
}
return null;
}
@Override
public InputStream getInputStream() {
var cachedBytes = cache == null ? null : cache.get();
if(cachedBytes != null) {
return new ByteArrayInputStream(cachedBytes);
}
var cached = tryCache();
if(cached != null) {
return cached;
}
return delegate.getInputStream();
}
}