[FEATURE] Use buildSecrets in UpdatesProvider

This commit is contained in:
Gravita 2025-06-18 00:08:22 +07:00
parent 2d046f7d7b
commit a959414c64
4 changed files with 70 additions and 16 deletions

View file

@ -2,6 +2,8 @@
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import pro.gravit.launcher.base.config.JsonConfigurable;
import pro.gravit.launcher.base.config.SimpleConfigurable;
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.SecurityHelper; import pro.gravit.utils.helper.SecurityHelper;
@ -9,23 +11,33 @@
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Arrays; import java.util.*;
import java.util.HashMap;
import java.util.Map;
public class LocalUpdatesProvider extends UpdatesProvider { public class LocalUpdatesProvider extends UpdatesProvider {
private transient final Logger logger = LogManager.getLogger(); private transient final Logger logger = LogManager.getLogger();
public String updatesDir = "updates"; public String updatesDir = "updates";
public String binaryName = "Launcher"; public String binaryName = "Launcher";
public String buildSecretsFile = "build-secrets.json";
public Map<UpdateVariant, String> urls = new HashMap<>(Map.of( public Map<UpdateVariant, String> urls = new HashMap<>(Map.of(
UpdateVariant.JAR, "http://localhost:9274/Launcher.jar", UpdateVariant.JAR, "http://localhost:9274/Launcher.jar",
UpdateVariant.EXE, "http://localhost:9274/Launcher.exe" UpdateVariant.EXE, "http://localhost:9274/Launcher.exe"
)); ));
public transient JsonConfigurable<BuildSecretsInfo> buildSecretsJson;
private final transient Map<UpdateVariant, byte[]> hashMap = new HashMap<>(); private final transient Map<UpdateVariant, byte[]> hashMap = new HashMap<>();
@Override @Override
public void init(LaunchServer server) { public void init(LaunchServer server) {
super.init(server); super.init(server);
buildSecretsJson = new SimpleConfigurable<>(BuildSecretsInfo.class, Path.of(buildSecretsFile));
if(server.env == LaunchServer.LaunchServerEnv.TEST) {
return;
}
try {
buildSecretsJson.generateConfigIfNotExists();
buildSecretsJson.loadConfig();
} catch (Exception e) {
buildSecretsJson.setConfig(buildSecretsJson.getDefaultConfig());
}
try { try {
sync(UpdateVariant.JAR); sync(UpdateVariant.JAR);
sync(UpdateVariant.EXE); sync(UpdateVariant.EXE);
@ -35,11 +47,13 @@ public void init(LaunchServer server) {
} }
@Override @Override
public void pushUpdate(Map<UpdateVariant, Path> files) throws IOException { public void pushUpdate(List<UpdateUploadInfo> files) throws IOException {
for(var e : files.entrySet()) { for(var e : files) {
IOHelper.copy(e.getValue(), getUpdate(e.getKey())); IOHelper.copy(e.path(), getUpdate(e.variant()));
sync(e.getKey()); buildSecretsJson.getConfig().secrets().put(e.variant(), e.secrets());
sync(e.variant());
} }
buildSecretsJson.saveConfig();
} }
public void sync(UpdateVariant variant) throws IOException { public void sync(UpdateVariant variant) throws IOException {
@ -69,14 +83,32 @@ public Path getUpdate(UpdateVariant variant) {
} }
@Override @Override
public UpdateInfo checkUpdates(UpdateVariant variant, byte[] digest) { public UpdateInfo checkUpdates(UpdateVariant variant, BuildSecretsCheck buildSecretsCheck) {
byte[] hash = hashMap.get(variant); byte[] hash = hashMap.get(variant);
if (hash == null) { if (hash == null) {
return null; // We dont have this file return null; // We dont have this file
} }
if(Arrays.equals(digest, hash)) { if(checkSecureHash(buildSecretsCheck.secureHash(), buildSecretsCheck.secureSalt(), buildSecretsJson.getConfig().secrets().get(variant).secureToken()) && Arrays.equals(buildSecretsCheck.digest(), hash)) {
return null; // Launcher already updated return null; // Launcher already updated
} }
return new UpdateInfo(urls.get(variant)); return new UpdateInfo(urls.get(variant));
} }
public static final class BuildSecretsInfo {
private Map<UpdateVariant, BuildSecrets> secrets = new HashMap<>();
public BuildSecretsInfo(Map<UpdateVariant, BuildSecrets> secrets) {
this.secrets = secrets;
}
public BuildSecretsInfo() {
}
public Map<UpdateVariant, BuildSecrets> secrets() {
return secrets;
}
}
} }

View file

@ -2,10 +2,14 @@
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
import pro.gravit.utils.ProviderMap; import pro.gravit.utils.ProviderMap;
import pro.gravit.utils.helper.SecurityHelper;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.Map; import java.util.Map;
public abstract class UpdatesProvider { public abstract class UpdatesProvider {
@ -25,8 +29,16 @@ public void init(LaunchServer server) {
this.server = server; this.server = server;
} }
public abstract void pushUpdate(Map<UpdateVariant, Path> files) throws IOException; public abstract void pushUpdate(List<UpdateUploadInfo> files) throws IOException;
public abstract UpdateInfo checkUpdates(UpdateVariant variant, byte[] digest); public abstract UpdateInfo checkUpdates(UpdateVariant variant, BuildSecretsCheck buildSecretsCheck);
protected boolean checkSecureHash(String secureHash, String secureSalt, String privateSecureToken) {
if (secureHash == null || secureSalt == null) return false;
byte[] normal_hash = SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA256,
privateSecureToken.concat(".").concat(secureSalt));
byte[] launcher_hash = Base64.getDecoder().decode(secureHash);
return Arrays.equals(normal_hash, launcher_hash);
}
public void close() { public void close() {
} }
@ -38,4 +50,16 @@ public enum UpdateVariant {
public record UpdateInfo(String url) { public record UpdateInfo(String url) {
} }
public record UpdateUploadInfo(Path path, UpdateVariant variant, BuildSecrets secrets) {
}
public record BuildSecrets(String secureToken, byte[] digest) {
}
public record BuildSecretsCheck(String secureHash, String secureSalt, byte[] digest) {
}
} }

View file

@ -39,7 +39,8 @@ public void build() throws IOException {
} }
long time_end = System.currentTimeMillis(); long time_end = System.currentTimeMillis();
if(thisPath != null) { if(thisPath != null) {
server.config.updatesProvider.pushUpdate(Map.of(getVariant(), thisPath)); // TODO fix me
server.config.updatesProvider.pushUpdate(List.of(new UpdatesProvider.UpdateUploadInfo(thisPath, getVariant(), new UpdatesProvider.BuildSecrets(server.runtime.clientCheckSecret, null))));
} else { } else {
logger.warn("Missing {} binary file", getVariant()); logger.warn("Missing {} binary file", getVariant());
} }

View file

@ -49,10 +49,7 @@ public void execute(ChannelHandlerContext ctx, Client client) {
variant = UpdatesProvider.UpdateVariant.EXE; variant = UpdatesProvider.UpdateVariant.EXE;
} }
byte[] hashToCheck = bytes; byte[] hashToCheck = bytes;
if(!checkSecure(secureHash, secureSalt)) { UpdatesProvider.UpdateInfo info = server.config.updatesProvider.checkUpdates(variant, new UpdatesProvider.BuildSecretsCheck(secureHash, secureSalt, hashToCheck));
hashToCheck = null; // Always need update
}
UpdatesProvider.UpdateInfo info = server.config.updatesProvider.checkUpdates(variant, hashToCheck);
if (info != null) { if (info != null) {
sendResult(new LauncherRequestEvent(true, info.url())); sendResult(new LauncherRequestEvent(true, info.url()));
} else { } else {