mirror of
https://github.com/GravitLauncher/Launcher
synced 2025-01-09 00:59:44 +03:00
Крупное обновление LauncherRequest
Есть совместимость с предыдущими версиями
This commit is contained in:
parent
23677bd5a3
commit
96bca9aa5d
17 changed files with 328 additions and 104 deletions
|
@ -4,6 +4,7 @@
|
|||
import java.nio.file.Path;
|
||||
|
||||
import ru.gravit.launcher.LauncherAPI;
|
||||
import ru.gravit.launcher.serialize.signed.DigestBytesHolder;
|
||||
import ru.gravit.utils.helper.IOHelper;
|
||||
import ru.gravit.launcher.serialize.signed.SignedBytesHolder;
|
||||
import ru.gravit.launchserver.LaunchServer;
|
||||
|
@ -15,8 +16,8 @@ public abstract class LauncherBinary {
|
|||
|
||||
protected final Path binaryFile;
|
||||
protected final Path syncBinaryFile;
|
||||
private volatile SignedBytesHolder binary;
|
||||
private volatile byte[] hash;
|
||||
private volatile DigestBytesHolder binary;
|
||||
private volatile byte[] sign;
|
||||
|
||||
|
||||
protected LauncherBinary(LaunchServer server, Path binaryFile) {
|
||||
|
@ -41,19 +42,19 @@ public final boolean exists() {
|
|||
}
|
||||
|
||||
|
||||
public final SignedBytesHolder getBytes() {
|
||||
public final DigestBytesHolder getBytes() {
|
||||
return binary;
|
||||
}
|
||||
|
||||
public final byte[] getHash() {
|
||||
return hash;
|
||||
public final byte[] getSign() {
|
||||
return sign;
|
||||
}
|
||||
|
||||
|
||||
public final boolean sync() throws IOException {
|
||||
boolean exists = exists();
|
||||
binary = exists ? new SignedBytesHolder(IOHelper.read(syncBinaryFile), server.privateKey) : null;
|
||||
hash = exists ? SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA512,IOHelper.newInput(syncBinaryFile)) : null;
|
||||
binary = exists ? new DigestBytesHolder(IOHelper.read(syncBinaryFile), SecurityHelper.DigestAlgorithm.SHA512) : null;
|
||||
sign = exists ? SecurityHelper.sign(IOHelper.read(syncBinaryFile),server.privateKey) : null;
|
||||
|
||||
return exists;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import ru.gravit.launcher.LauncherAPI;
|
||||
import ru.gravit.launchserver.response.auth.*;
|
||||
import ru.gravit.launchserver.response.update.*;
|
||||
import ru.gravit.utils.helper.LogHelper;
|
||||
import ru.gravit.launcher.request.RequestException;
|
||||
import ru.gravit.launcher.request.RequestType;
|
||||
|
@ -15,10 +15,6 @@
|
|||
import ru.gravit.launchserver.response.profile.BatchProfileByUsernameResponse;
|
||||
import ru.gravit.launchserver.response.profile.ProfileByUUIDResponse;
|
||||
import ru.gravit.launchserver.response.profile.ProfileByUsernameResponse;
|
||||
import ru.gravit.launchserver.response.update.LauncherResponse;
|
||||
import ru.gravit.launchserver.response.update.ProfilesResponse;
|
||||
import ru.gravit.launchserver.response.update.UpdateListResponse;
|
||||
import ru.gravit.launchserver.response.update.UpdateResponse;
|
||||
|
||||
public abstract class Response {
|
||||
@FunctionalInterface
|
||||
|
@ -47,6 +43,7 @@ public static void registerResponses() {
|
|||
registerResponse(RequestType.PROFILE_BY_USERNAME.getNumber(), ProfileByUsernameResponse::new);
|
||||
registerResponse(RequestType.PROFILE_BY_UUID.getNumber(), ProfileByUUIDResponse::new);
|
||||
|
||||
registerResponse(RequestType.LEGACYLAUNCHER.getNumber(), LegacyLauncherResponse::new);
|
||||
registerResponse(RequestType.LAUNCHER.getNumber(), LauncherResponse::new);
|
||||
registerResponse(RequestType.UPDATE_LIST.getNumber(), UpdateListResponse::new);
|
||||
registerResponse(RequestType.UPDATE.getNumber(), UpdateResponse::new);
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
package ru.gravit.launchserver.response.update;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
import ru.gravit.utils.helper.SecurityHelper;
|
||||
import ru.gravit.launcher.profiles.ClientProfile;
|
||||
import ru.gravit.launcher.serialize.HInput;
|
||||
import ru.gravit.launcher.serialize.HOutput;
|
||||
import ru.gravit.launcher.serialize.signed.DigestBytesHolder;
|
||||
import ru.gravit.launcher.serialize.signed.SignedBytesHolder;
|
||||
import ru.gravit.launcher.serialize.signed.SignedObjectHolder;
|
||||
import ru.gravit.launchserver.LaunchServer;
|
||||
import ru.gravit.launchserver.manangers.SessionManager;
|
||||
import ru.gravit.launchserver.response.Response;
|
||||
import ru.gravit.launchserver.socket.Client;
|
||||
import ru.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.SignatureException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
public final class LauncherResponse extends Response {
|
||||
|
||||
|
@ -21,25 +26,23 @@ public LauncherResponse(LaunchServer server, long session, HInput input, HOutput
|
|||
@Override
|
||||
public void reply() throws IOException {
|
||||
// Resolve launcher binary
|
||||
SignedBytesHolder bytes = (input.readBoolean() ? server.launcherEXEBinary : server.launcherBinary).getBytes();
|
||||
DigestBytesHolder bytes = (input.readBoolean() ? server.launcherEXEBinary : server.launcherBinary).getBytes();
|
||||
if (bytes == null) {
|
||||
requestError("Missing launcher binary");
|
||||
return;
|
||||
}
|
||||
writeNoError(output);
|
||||
|
||||
// Update launcher binary
|
||||
output.writeByteArray(bytes.getSign(), -SecurityHelper.RSA_KEY_LENGTH);
|
||||
output.flush();
|
||||
if (input.readBoolean()) {
|
||||
Client client = server.sessionManager.getOrNewClient(session);
|
||||
byte[] digest = input.readByteArray(0);
|
||||
if(!Arrays.equals(bytes.getDigest(), digest))
|
||||
{
|
||||
writeNoError(output);
|
||||
output.writeBoolean(true);
|
||||
output.writeByteArray(bytes.getBytes(), 0);
|
||||
return; // Launcher will be restarted
|
||||
client.checkSign = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Write clients profiles list
|
||||
Collection<SignedObjectHolder<ClientProfile>> profiles = server.getProfiles();
|
||||
output.writeLength(profiles.size(), 0);
|
||||
for (SignedObjectHolder<ClientProfile> profile : profiles)
|
||||
profile.write(output);
|
||||
writeNoError(output);
|
||||
output.writeBoolean(false);
|
||||
client.checkSign = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
package ru.gravit.launchserver.response.update;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
import ru.gravit.launcher.serialize.signed.DigestBytesHolder;
|
||||
import ru.gravit.launchserver.binary.LauncherBinary;
|
||||
import ru.gravit.utils.helper.SecurityHelper;
|
||||
import ru.gravit.launcher.profiles.ClientProfile;
|
||||
import ru.gravit.launcher.serialize.HInput;
|
||||
import ru.gravit.launcher.serialize.HOutput;
|
||||
import ru.gravit.launcher.serialize.signed.SignedBytesHolder;
|
||||
import ru.gravit.launcher.serialize.signed.SignedObjectHolder;
|
||||
import ru.gravit.launchserver.LaunchServer;
|
||||
import ru.gravit.launchserver.response.Response;
|
||||
|
||||
public final class LegacyLauncherResponse extends Response {
|
||||
|
||||
public LegacyLauncherResponse(LaunchServer server, long session, HInput input, HOutput output, String ip) {
|
||||
super(server, session, input, output, ip);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reply() throws IOException {
|
||||
// Resolve launcher binary
|
||||
LauncherBinary bytes = (input.readBoolean() ? server.launcherEXEBinary : server.launcherBinary);
|
||||
if (bytes == null) {
|
||||
requestError("Missing launcher binary");
|
||||
return;
|
||||
}
|
||||
writeNoError(output);
|
||||
|
||||
// Update launcher binary
|
||||
output.writeByteArray(bytes.getSign(), -SecurityHelper.RSA_KEY_LENGTH);
|
||||
output.flush();
|
||||
if (input.readBoolean()) {
|
||||
output.writeByteArray(bytes.getBytes().getBytes(), 0);
|
||||
return; // Launcher will be restarted
|
||||
}
|
||||
requestError("You must update");
|
||||
}
|
||||
}
|
|
@ -23,7 +23,7 @@ public void reply() throws IOException {
|
|||
// Resolve launcher binary
|
||||
Client client = server.sessionManager.getClient(session);
|
||||
input.readBoolean();
|
||||
if(client.type == Client.Type.USER) {
|
||||
if(client.type == Client.Type.USER && !client.checkSign) {
|
||||
LogHelper.warning("User session: %d ip %s try get profiles",session,ip);
|
||||
requestError("Assess denied");
|
||||
return;
|
||||
|
|
|
@ -10,6 +10,7 @@ public class Client {
|
|||
public Type type;
|
||||
public ClientProfile profile;
|
||||
public boolean isAuth;
|
||||
public boolean checkSign;
|
||||
public ClientPermissions permissions;
|
||||
public String username;
|
||||
|
||||
|
@ -20,6 +21,7 @@ public Client(long session) {
|
|||
isAuth = false;
|
||||
permissions = ClientPermissions.DEFAULT;
|
||||
username = "";
|
||||
checkSign = false;
|
||||
}
|
||||
//Данные ваторизации
|
||||
public void up() {
|
||||
|
|
|
@ -27,7 +27,7 @@ public void execute(WebSocketService service, ChannelHandlerContext ctx, Client
|
|||
byte[] bytes = Base64.getDecoder().decode(hash);
|
||||
if(launcher_type == 1) // JAR
|
||||
{
|
||||
byte[] hash = LaunchServer.server.launcherBinary.getHash();
|
||||
byte[] hash = LaunchServer.server.launcherBinary.getBytes().getDigest();
|
||||
if(hash == null) service.sendObjectAndClose(ctx, new Result(true,JAR_URL));
|
||||
if(Arrays.equals(bytes, hash))
|
||||
{
|
||||
|
@ -38,7 +38,7 @@ public void execute(WebSocketService service, ChannelHandlerContext ctx, Client
|
|||
}
|
||||
} else if(launcher_type == 2) //EXE
|
||||
{
|
||||
byte[] hash = LaunchServer.server.launcherEXEBinary.getHash();
|
||||
byte[] hash = LaunchServer.server.launcherEXEBinary.getBytes().getDigest();
|
||||
if(hash == null) service.sendObjectAndClose(ctx, new Result(true,EXE_URL));
|
||||
if(Arrays.equals(bytes, hash))
|
||||
{
|
||||
|
|
|
@ -147,20 +147,23 @@ function verifyLauncher(e) {
|
|||
return;
|
||||
}
|
||||
settings.lastSign = result.sign;
|
||||
settings.lastProfiles = result.profiles;
|
||||
|
||||
processing.resetOverlay();
|
||||
// Init offline if set
|
||||
if (settings.offline) {
|
||||
initOffline();
|
||||
initOffline();
|
||||
}
|
||||
|
||||
// Update profiles list and hide overlay
|
||||
updateProfilesList(result.profiles);
|
||||
overlay.hide(0, function() {
|
||||
if (cliParams.autoLogin) {
|
||||
goAuth(null);
|
||||
}
|
||||
overlay.show(processing.overlay, function(event) makeProfilesRequest(function(result) {
|
||||
settings.lastProfiles = result.profiles;
|
||||
// Update profiles list and hide overlay
|
||||
updateProfilesList(result.profiles);
|
||||
overlay.hide(0, function() {
|
||||
if (cliParams.autoLogin) {
|
||||
goAuth(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
|
@ -83,6 +83,22 @@ function makeLauncherRequest(callback) {
|
|||
task.updateMessage("Обновление списка серверов");
|
||||
startTask(task);
|
||||
}
|
||||
function makeProfilesRequest(callback) {
|
||||
var task = newRequestTask(new ProfilesRequest());
|
||||
|
||||
// Set task properties and start
|
||||
processing.setTaskProperties(task, callback, function() {
|
||||
if (settings.offline) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Repeat request, but in offline mode
|
||||
settings.offline = true;
|
||||
overlay.swap(2500, processing.overlay, function() makeProfilesRequest(callback));
|
||||
}, false);
|
||||
task.updateMessage("Обновление списка серверов");
|
||||
startTask(task);
|
||||
}
|
||||
|
||||
function makeAuthRequest(login, rsaPassword, callback) {
|
||||
var task = rsaPassword === null ? newTask(offlineAuthRequest(login)) :
|
||||
|
|
|
@ -24,6 +24,7 @@ var JoinServerRequest = JoinServerRequestClass.static;
|
|||
var CheckServerRequest = CheckServerRequestClass.static;
|
||||
var UpdateRequest = UpdateRequestClass.static;
|
||||
var LauncherRequest = LauncherRequestClass.static;
|
||||
var ProfilesRequest = ProfilesRequestClass.static;
|
||||
var ProfileByUsernameRequest = ProfileByUsernameRequestClass.static;
|
||||
var ProfileByUUIDRequest = ProfileByUUIDRequestClass.static;
|
||||
var BatchProfileByUsernameRequest = BatchProfileByUsernameRequestClass.static;
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
|
@ -23,6 +21,8 @@
|
|||
import ru.gravit.launcher.hasher.HashedDir;
|
||||
import ru.gravit.launcher.hasher.HashedEntry;
|
||||
import ru.gravit.launcher.hasher.HashedFile;
|
||||
import ru.gravit.launcher.request.update.LauncherRequest;
|
||||
import ru.gravit.launcher.request.update.ProfilesRequest;
|
||||
import ru.gravit.utils.HTTPRequest;
|
||||
import ru.gravit.utils.helper.CommonHelper;
|
||||
import ru.gravit.utils.helper.EnvHelper;
|
||||
|
@ -42,7 +42,7 @@
|
|||
import ru.gravit.launcher.request.auth.AuthRequest;
|
||||
import ru.gravit.launcher.request.auth.CheckServerRequest;
|
||||
import ru.gravit.launcher.request.auth.JoinServerRequest;
|
||||
import ru.gravit.launcher.request.update.LauncherRequest;
|
||||
import ru.gravit.launcher.request.update.LegacyLauncherRequest;
|
||||
import ru.gravit.launcher.request.update.UpdateRequest;
|
||||
import ru.gravit.launcher.request.uuid.BatchProfileByUsernameRequest;
|
||||
import ru.gravit.launcher.request.uuid.ProfileByUUIDRequest;
|
||||
|
@ -90,6 +90,7 @@ public static void addLauncherClassBindings(Map<String, Object> bindings) {
|
|||
bindings.put("CheckServerRequestClass", CheckServerRequest.class);
|
||||
bindings.put("UpdateRequestClass", UpdateRequest.class);
|
||||
bindings.put("LauncherRequestClass", LauncherRequest.class);
|
||||
bindings.put("ProfilesRequestClass", ProfilesRequest.class);
|
||||
bindings.put("ProfileByUsernameRequestClass", ProfileByUsernameRequest.class);
|
||||
bindings.put("ProfileByUUIDRequestClass", ProfileByUUIDRequest.class);
|
||||
bindings.put("BatchProfileByUsernameRequestClass", BatchProfileByUsernameRequest.class);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package ru.gravit.launcher.client;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.ProcessBuilder.Redirect;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
|
@ -8,7 +7,6 @@
|
|||
import java.lang.invoke.MethodType;
|
||||
import java.net.*;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
|
@ -35,10 +33,9 @@
|
|||
import ru.gravit.utils.helper.JVMHelper.OS;
|
||||
import ru.gravit.utils.helper.LogHelper;
|
||||
import ru.gravit.utils.helper.SecurityHelper;
|
||||
import ru.gravit.utils.helper.VerifyHelper;
|
||||
import ru.gravit.launcher.profiles.ClientProfile;
|
||||
import ru.gravit.launcher.profiles.PlayerProfile;
|
||||
import ru.gravit.launcher.request.update.LauncherRequest;
|
||||
import ru.gravit.launcher.request.update.LegacyLauncherRequest;
|
||||
import ru.gravit.launcher.serialize.HInput;
|
||||
import ru.gravit.launcher.serialize.HOutput;
|
||||
import ru.gravit.launcher.serialize.signed.SignedObjectHolder;
|
||||
|
@ -440,7 +437,7 @@ public static void main(String... args) throws Throwable {
|
|||
Launcher.modulesManager.initModules();
|
||||
// Verify ClientLauncher sign and classpath
|
||||
LogHelper.debug("Verifying ClientLauncher sign and classpath");
|
||||
SecurityHelper.verifySign(LauncherRequest.BINARY_PATH, params.launcherSign, publicKey);
|
||||
SecurityHelper.verifySign(LegacyLauncherRequest.BINARY_PATH, params.launcherSign, publicKey);
|
||||
LinkedList<Path> classPath = resolveClassPathList(params.clientDir, profile.object.getClassPath());
|
||||
for (Path classpathURL : classPath) {
|
||||
LauncherAgent.addJVMClassPath(classpathURL.toAbsolutePath().toString());
|
||||
|
@ -479,7 +476,7 @@ public void launchLocal(SignedObjectHolder<HashedDir> assetHDir, SignedObjectHol
|
|||
SignedObjectHolder<ClientProfile> profile, Params params) throws Throwable {
|
||||
RSAPublicKey publicKey = Launcher.getConfig().publicKey;
|
||||
LogHelper.debug("Verifying ClientLauncher sign and classpath");
|
||||
SecurityHelper.verifySign(LauncherRequest.BINARY_PATH, params.launcherSign, publicKey);
|
||||
SecurityHelper.verifySign(LegacyLauncherRequest.BINARY_PATH, params.launcherSign, publicKey);
|
||||
LinkedList<Path> classPath = resolveClassPathList(params.clientDir, profile.object.getClassPath());
|
||||
for (Path classpathURL : classPath) {
|
||||
LauncherAgent.addJVMClassPath(classpathURL.toAbsolutePath().toString());
|
||||
|
|
|
@ -5,8 +5,7 @@
|
|||
import ru.gravit.launcher.hasher.FileNameMatcher;
|
||||
import ru.gravit.launcher.hasher.HashedDir;
|
||||
import ru.gravit.launcher.request.Request;
|
||||
import ru.gravit.launcher.request.RequestException;
|
||||
import ru.gravit.launcher.request.update.LauncherRequest;
|
||||
import ru.gravit.launcher.request.update.LegacyLauncherRequest;
|
||||
import ru.gravit.launcher.serialize.signed.SignedObjectHolder;
|
||||
import ru.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
|
@ -30,17 +29,17 @@ public HashedDirRunnable offlineUpdateRequest(String dirName, Path dir, SignedOb
|
|||
};
|
||||
}
|
||||
@LauncherAPI
|
||||
public LauncherRequest.Result offlineLauncherRequest() throws IOException, SignatureException {
|
||||
public LegacyLauncherRequest.Result offlineLauncherRequest() throws IOException, SignatureException {
|
||||
if (settings.lastSign == null || settings.lastProfiles.isEmpty()) {
|
||||
Request.requestError("Запуск в оффлайн-режиме невозможен");
|
||||
}
|
||||
|
||||
// Verify launcher signature
|
||||
SecurityHelper.verifySign(LauncherRequest.BINARY_PATH,
|
||||
SecurityHelper.verifySign(LegacyLauncherRequest.BINARY_PATH,
|
||||
settings.lastSign, Launcher.getConfig().publicKey);
|
||||
|
||||
// Return last sign and profiles
|
||||
return new LauncherRequest.Result(null,settings.lastSign,settings.lastProfiles);
|
||||
return new LegacyLauncherRequest.Result(null,settings.lastSign,settings.lastProfiles);
|
||||
}
|
||||
@FunctionalInterface
|
||||
public interface HashedDirRunnable {
|
||||
|
|
|
@ -1,39 +1,34 @@
|
|||
package ru.gravit.launcher.request.update;
|
||||
|
||||
import ru.gravit.launcher.Launcher;
|
||||
import ru.gravit.launcher.LauncherAPI;
|
||||
import ru.gravit.launcher.LauncherConfig;
|
||||
import ru.gravit.launcher.profiles.ClientProfile;
|
||||
import ru.gravit.launcher.request.Request;
|
||||
import ru.gravit.launcher.request.RequestType;
|
||||
import ru.gravit.launcher.serialize.HInput;
|
||||
import ru.gravit.launcher.serialize.HOutput;
|
||||
import ru.gravit.launcher.serialize.signed.SignedObjectHolder;
|
||||
import ru.gravit.utils.helper.IOHelper;
|
||||
import ru.gravit.utils.helper.JVMHelper;
|
||||
import ru.gravit.utils.helper.LogHelper;
|
||||
import ru.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.security.SignatureException;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import ru.gravit.launcher.Launcher;
|
||||
import ru.gravit.launcher.LauncherAPI;
|
||||
import ru.gravit.launcher.LauncherConfig;
|
||||
import ru.gravit.utils.helper.IOHelper;
|
||||
import ru.gravit.utils.helper.JVMHelper;
|
||||
import ru.gravit.utils.helper.LogHelper;
|
||||
import ru.gravit.utils.helper.SecurityHelper;
|
||||
import ru.gravit.launcher.profiles.ClientProfile;
|
||||
import ru.gravit.launcher.request.Request;
|
||||
import ru.gravit.launcher.request.RequestType;
|
||||
import ru.gravit.launcher.request.update.LauncherRequest.Result;
|
||||
import ru.gravit.launcher.serialize.HInput;
|
||||
import ru.gravit.launcher.serialize.HOutput;
|
||||
import ru.gravit.launcher.serialize.signed.SignedObjectHolder;
|
||||
|
||||
public final class LauncherRequest extends Request<Result> {
|
||||
public final class LauncherRequest extends Request<LauncherRequest.Result> {
|
||||
public static final class Result {
|
||||
@LauncherAPI
|
||||
public final List<SignedObjectHolder<ClientProfile>> profiles;
|
||||
private final byte[] binary;
|
||||
private final byte[] sign;
|
||||
private final byte[] digest;
|
||||
|
||||
public Result(byte[] binary, byte[] sign, List<SignedObjectHolder<ClientProfile>> profiles) {
|
||||
public Result(byte[] binary, byte[] sign) {
|
||||
this.binary = binary == null ? null : binary.clone();
|
||||
this.sign = sign.clone();
|
||||
this.profiles = Collections.unmodifiableList(profiles);
|
||||
this.digest = sign.clone();
|
||||
}
|
||||
|
||||
@LauncherAPI
|
||||
|
@ -42,8 +37,8 @@ public byte[] getBinary() {
|
|||
}
|
||||
|
||||
@LauncherAPI
|
||||
public byte[] getSign() {
|
||||
return sign.clone();
|
||||
public byte[] getDigest() {
|
||||
return digest.clone();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,9 +50,6 @@ public byte[] getSign() {
|
|||
|
||||
@LauncherAPI
|
||||
public static void update(LauncherConfig config, Result result) throws SignatureException, IOException {
|
||||
SecurityHelper.verifySign(result.binary, result.sign, config.publicKey);
|
||||
|
||||
// Prepare process builder to start new instance (java -jar works for Launch4J's EXE too)
|
||||
List<String> args = new ArrayList<>(8);
|
||||
args.add(IOHelper.resolveJavaBin(null).toString());
|
||||
if (LogHelper.isDebugEnabled())
|
||||
|
@ -96,31 +88,23 @@ public Integer getType() {
|
|||
@Override
|
||||
@SuppressWarnings("CallToSystemExit")
|
||||
protected Result requestDo(HInput input, HOutput output) throws Exception {
|
||||
RSAPublicKey publicKey = config.publicKey;
|
||||
Path launcherPath = IOHelper.getCodeSource(LauncherRequest.class);
|
||||
byte[] digest = SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA512,launcherPath);
|
||||
byte[] sign;
|
||||
output.writeBoolean(EXE_BINARY);
|
||||
output.writeByteArray(digest,0);
|
||||
output.flush();
|
||||
readError(input);
|
||||
|
||||
// Verify launcher sign
|
||||
RSAPublicKey publicKey = config.publicKey;
|
||||
byte[] sign = input.readByteArray(-SecurityHelper.RSA_KEY_LENGTH);
|
||||
boolean shouldUpdate = !SecurityHelper.isValidSign(BINARY_PATH, sign, publicKey);
|
||||
|
||||
// Update launcher if need
|
||||
output.writeBoolean(shouldUpdate);
|
||||
output.flush();
|
||||
boolean shouldUpdate = input.readBoolean();
|
||||
if (shouldUpdate) {
|
||||
byte[] binary = input.readByteArray(0);
|
||||
SecurityHelper.verifySign(binary, sign, config.publicKey);
|
||||
return new Result(binary, sign, Collections.emptyList());
|
||||
return new Result(binary, digest);
|
||||
}
|
||||
|
||||
// Read clients profiles list
|
||||
int count = input.readLength(0);
|
||||
List<SignedObjectHolder<ClientProfile>> profiles = new ArrayList<>(count);
|
||||
for (int i = 0; i < count; i++)
|
||||
profiles.add(new SignedObjectHolder<>(input, publicKey, ClientProfile.RO_ADAPTER));
|
||||
|
||||
// Return request result
|
||||
return new Result(null, sign, profiles);
|
||||
return new Result(null, digest);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
package ru.gravit.launcher.request.update;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.security.SignatureException;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import ru.gravit.launcher.Launcher;
|
||||
import ru.gravit.launcher.LauncherAPI;
|
||||
import ru.gravit.launcher.LauncherConfig;
|
||||
import ru.gravit.utils.helper.IOHelper;
|
||||
import ru.gravit.utils.helper.JVMHelper;
|
||||
import ru.gravit.utils.helper.LogHelper;
|
||||
import ru.gravit.utils.helper.SecurityHelper;
|
||||
import ru.gravit.launcher.profiles.ClientProfile;
|
||||
import ru.gravit.launcher.request.Request;
|
||||
import ru.gravit.launcher.request.RequestType;
|
||||
import ru.gravit.launcher.request.update.LegacyLauncherRequest.Result;
|
||||
import ru.gravit.launcher.serialize.HInput;
|
||||
import ru.gravit.launcher.serialize.HOutput;
|
||||
import ru.gravit.launcher.serialize.signed.SignedObjectHolder;
|
||||
|
||||
public final class LegacyLauncherRequest extends Request<Result> {
|
||||
public static final class Result {
|
||||
@LauncherAPI
|
||||
public final List<SignedObjectHolder<ClientProfile>> profiles;
|
||||
private final byte[] binary;
|
||||
private final byte[] sign;
|
||||
|
||||
public Result(byte[] binary, byte[] sign, List<SignedObjectHolder<ClientProfile>> profiles) {
|
||||
this.binary = binary == null ? null : binary.clone();
|
||||
this.sign = sign.clone();
|
||||
this.profiles = Collections.unmodifiableList(profiles);
|
||||
}
|
||||
|
||||
@LauncherAPI
|
||||
public byte[] getBinary() {
|
||||
return binary == null ? null : binary.clone();
|
||||
}
|
||||
|
||||
@LauncherAPI
|
||||
public byte[] getSign() {
|
||||
return sign.clone();
|
||||
}
|
||||
}
|
||||
|
||||
@LauncherAPI
|
||||
public static final Path BINARY_PATH = IOHelper.getCodeSource(Launcher.class);
|
||||
|
||||
@LauncherAPI
|
||||
public static final boolean EXE_BINARY = IOHelper.hasExtension(BINARY_PATH, "exe");
|
||||
|
||||
@LauncherAPI
|
||||
public static void update(LauncherConfig config, Result result) throws SignatureException, IOException {
|
||||
SecurityHelper.verifySign(result.binary, result.sign, config.publicKey);
|
||||
|
||||
// Prepare process builder to start new instance (java -jar works for Launch4J's EXE too)
|
||||
List<String> args = new ArrayList<>(8);
|
||||
args.add(IOHelper.resolveJavaBin(null).toString());
|
||||
if (LogHelper.isDebugEnabled())
|
||||
args.add(JVMHelper.jvmProperty(LogHelper.DEBUG_PROPERTY, Boolean.toString(LogHelper.isDebugEnabled())));
|
||||
if (LauncherConfig.ADDRESS_OVERRIDE != null)
|
||||
args.add(JVMHelper.jvmProperty(LauncherConfig.ADDRESS_OVERRIDE_PROPERTY, LauncherConfig.ADDRESS_OVERRIDE));
|
||||
args.add("-jar");
|
||||
args.add(BINARY_PATH.toString());
|
||||
ProcessBuilder builder = new ProcessBuilder(args.toArray(new String[0]));
|
||||
builder.inheritIO();
|
||||
|
||||
// Rewrite and start new instance
|
||||
IOHelper.write(BINARY_PATH, result.binary);
|
||||
builder.start();
|
||||
|
||||
// Kill current instance
|
||||
JVMHelper.RUNTIME.exit(255);
|
||||
throw new AssertionError("Why Launcher wasn't restarted?!");
|
||||
}
|
||||
|
||||
@LauncherAPI
|
||||
public LegacyLauncherRequest() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
@LauncherAPI
|
||||
public LegacyLauncherRequest(LauncherConfig config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getType() {
|
||||
return RequestType.LEGACYLAUNCHER.getNumber();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("CallToSystemExit")
|
||||
protected Result requestDo(HInput input, HOutput output) throws Exception {
|
||||
output.writeBoolean(EXE_BINARY);
|
||||
output.flush();
|
||||
readError(input);
|
||||
|
||||
// Verify launcher sign
|
||||
RSAPublicKey publicKey = config.publicKey;
|
||||
byte[] sign = input.readByteArray(-SecurityHelper.RSA_KEY_LENGTH);
|
||||
boolean shouldUpdate = !SecurityHelper.isValidSign(BINARY_PATH, sign, publicKey);
|
||||
|
||||
// Update launcher if need
|
||||
output.writeBoolean(shouldUpdate);
|
||||
output.flush();
|
||||
if (shouldUpdate) {
|
||||
byte[] binary = input.readByteArray(0);
|
||||
SecurityHelper.verifySign(binary, sign, config.publicKey);
|
||||
return new Result(binary, sign, Collections.emptyList());
|
||||
}
|
||||
|
||||
// Read clients profiles list
|
||||
int count = input.readLength(0);
|
||||
List<SignedObjectHolder<ClientProfile>> profiles = new ArrayList<>(count);
|
||||
for (int i = 0; i < count; i++)
|
||||
profiles.add(new SignedObjectHolder<>(input, publicKey, ClientProfile.RO_ADAPTER));
|
||||
|
||||
// Return request result
|
||||
return new Result(null, sign, profiles);
|
||||
}
|
||||
}
|
|
@ -8,10 +8,10 @@
|
|||
|
||||
public enum RequestType implements EnumSerializer.Itf {
|
||||
PING(0), // Ping request
|
||||
LAUNCHER(1), UPDATE(2), UPDATE_LIST(3), // Update requests
|
||||
LEGACYLAUNCHER(1), UPDATE(2), UPDATE_LIST(3), // Update requests
|
||||
AUTH(4), JOIN_SERVER(5), CHECK_SERVER(6), // Auth requests
|
||||
PROFILE_BY_USERNAME(7), PROFILE_BY_UUID(8), BATCH_PROFILE_BY_USERNAME(9), // Profile requests
|
||||
PROFILES(10),SERVERAUTH(11), SETPROFILE(12),
|
||||
PROFILES(10),SERVERAUTH(11), SETPROFILE(12),LAUNCHER(13),
|
||||
CUSTOM(255); // Custom requests
|
||||
private static final EnumSerializer<RequestType> SERIALIZER = new EnumSerializer<>(RequestType.class);
|
||||
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
package ru.gravit.launcher.serialize.signed;
|
||||
|
||||
import ru.gravit.launcher.LauncherAPI;
|
||||
import ru.gravit.launcher.serialize.HInput;
|
||||
import ru.gravit.launcher.serialize.HOutput;
|
||||
import ru.gravit.launcher.serialize.stream.StreamObject;
|
||||
import ru.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.SignatureException;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class DigestBytesHolder extends StreamObject {
|
||||
protected final byte[] bytes;
|
||||
private final byte[] digest;
|
||||
|
||||
@LauncherAPI
|
||||
public DigestBytesHolder(byte[] bytes, byte[] digest, SecurityHelper.DigestAlgorithm algorithm) throws SignatureException {
|
||||
if(Arrays.equals(SecurityHelper.digest(algorithm,bytes),digest)) throw new SignatureException("Invalid digest");
|
||||
this.bytes = bytes.clone();
|
||||
this.digest = digest.clone();
|
||||
}
|
||||
|
||||
@LauncherAPI
|
||||
public DigestBytesHolder(byte[] bytes, SecurityHelper.DigestAlgorithm algorithm) {
|
||||
this.bytes = bytes.clone();
|
||||
this.digest = SecurityHelper.digest(algorithm,bytes);
|
||||
}
|
||||
|
||||
@LauncherAPI
|
||||
public DigestBytesHolder(HInput input, SecurityHelper.DigestAlgorithm algorithm) throws IOException, SignatureException {
|
||||
this(input.readByteArray(0), input.readByteArray(-SecurityHelper.RSA_KEY_LENGTH),algorithm);
|
||||
}
|
||||
|
||||
@LauncherAPI
|
||||
public final byte[] getBytes() {
|
||||
return bytes.clone();
|
||||
}
|
||||
|
||||
@LauncherAPI
|
||||
public final byte[] getDigest() {
|
||||
return digest.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void write(HOutput output) throws IOException {
|
||||
output.writeByteArray(bytes, 0);
|
||||
output.writeByteArray(digest, -SecurityHelper.RSA_KEY_LENGTH);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue