[FEATURE][EXPERIMENTAL] SecureLevel

This commit is contained in:
Gravit 2020-03-20 08:44:24 +07:00
parent 8875146be3
commit 8dddb08255
No known key found for this signature in database
GPG key ID: 061981E1E85D3216
16 changed files with 213 additions and 84 deletions

View file

@ -0,0 +1,44 @@
package pro.gravit.launchserver.auth.protect;
import pro.gravit.launcher.events.request.GetSecureLevelInfoRequestEvent;
import pro.gravit.launchserver.auth.protect.interfaces.SecureProtectHandler;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
import pro.gravit.utils.helper.SecurityHelper;
public class AdvancedProtectHandler extends ProtectHandler implements SecureProtectHandler {
@Override
public String generateSecureToken(AuthResponse.AuthContext context) {
return SecurityHelper.randomStringToken();
}
@Override
public String generateClientSecureToken() {
return SecurityHelper.randomStringToken();
}
@Override
public boolean verifyClientSecureToken(String token, String secureKey) {
return true;
}
@Override
public boolean allowGetAccessToken(AuthResponse.AuthContext context) {
return (context.authType == AuthResponse.ConnectTypes.CLIENT) && context.client.isSecure;
}
@Override
public void checkLaunchServerLicense() {
}
@Override
public GetSecureLevelInfoRequestEvent onGetSecureLevelInfo(GetSecureLevelInfoRequestEvent event) {
return event;
}
@Override
public boolean allowGetSecureLevelInfo(Client client) {
return client.isSecure;
}
}

View file

@ -12,6 +12,7 @@ public static void registerHandlers() {
if (!registredHandl) { if (!registredHandl) {
providers.register("none", NoProtectHandler.class); providers.register("none", NoProtectHandler.class);
providers.register("std", StdProtectHandler.class); providers.register("std", StdProtectHandler.class);
providers.register("advanced", AdvancedProtectHandler.class);
registredHandl = true; registredHandl = true;
} }
} }

View file

@ -0,0 +1,23 @@
package pro.gravit.launchserver.auth.protect.interfaces;
import pro.gravit.launcher.events.request.GetSecureLevelInfoRequestEvent;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.utils.helper.SecurityHelper;
import java.security.SignatureException;
import java.security.interfaces.ECPublicKey;
import java.security.spec.InvalidKeySpecException;
public interface SecureProtectHandler {
default byte[] generateSecureLevelKey()
{
return SecurityHelper.randomBytes(128);
}
default void verifySecureLevelKey(byte[] publicKey, byte[] signature) throws InvalidKeySpecException, SignatureException {
if(publicKey == null || signature == null) throw new InvalidKeySpecException();
ECPublicKey pubKey = SecurityHelper.toPublicECKey(publicKey);
SecurityHelper.newECVerifySignature(pubKey).update(signature);
}
GetSecureLevelInfoRequestEvent onGetSecureLevelInfo(GetSecureLevelInfoRequestEvent event);
boolean allowGetSecureLevelInfo(Client client);
}

View file

@ -19,6 +19,7 @@ public class Client {
public ClientPermissions permissions; public ClientPermissions permissions;
public String username; public String username;
public String verifyToken; public String verifyToken;
public TrustLevel trustLevel;
public transient LogHelper.OutputEnity logOutput; public transient LogHelper.OutputEnity logOutput;
public transient AuthProviderPair auth; public transient AuthProviderPair auth;
@ -48,4 +49,8 @@ public enum Type {
SERVER, SERVER,
USER USER
} }
public static class TrustLevel
{
public byte[] verifySecureKey;
}
} }

View file

@ -21,8 +21,8 @@
import pro.gravit.launchserver.socket.response.profile.BatchProfileByUsername; import pro.gravit.launchserver.socket.response.profile.BatchProfileByUsername;
import pro.gravit.launchserver.socket.response.profile.ProfileByUUIDResponse; import pro.gravit.launchserver.socket.response.profile.ProfileByUUIDResponse;
import pro.gravit.launchserver.socket.response.profile.ProfileByUsername; import pro.gravit.launchserver.socket.response.profile.ProfileByUsername;
import pro.gravit.launchserver.socket.response.secure.GetSecureTokenResponse; import pro.gravit.launchserver.socket.response.secure.GetSecureLevelInfoResponse;
import pro.gravit.launchserver.socket.response.secure.VerifySecureTokenResponse; import pro.gravit.launchserver.socket.response.secure.VerifySecureLevelKeyResponse;
import pro.gravit.launchserver.socket.response.update.LauncherResponse; import pro.gravit.launchserver.socket.response.update.LauncherResponse;
import pro.gravit.launchserver.socket.response.update.UpdateListResponse; import pro.gravit.launchserver.socket.response.update.UpdateListResponse;
import pro.gravit.launchserver.socket.response.update.UpdateResponse; import pro.gravit.launchserver.socket.response.update.UpdateResponse;
@ -123,12 +123,12 @@ public static void registerResponses() {
providers.register("batchProfileByUsername", BatchProfileByUsername.class); providers.register("batchProfileByUsername", BatchProfileByUsername.class);
providers.register("profileByUsername", ProfileByUsername.class); providers.register("profileByUsername", ProfileByUsername.class);
providers.register("profileByUUID", ProfileByUUIDResponse.class); providers.register("profileByUUID", ProfileByUUIDResponse.class);
providers.register("getSecureToken", GetSecureTokenResponse.class);
providers.register("verifySecureToken", VerifySecureTokenResponse.class);
providers.register("getAvailabilityAuth", GetAvailabilityAuthResponse.class); providers.register("getAvailabilityAuth", GetAvailabilityAuthResponse.class);
providers.register("register", RegisterResponse.class); providers.register("register", RegisterResponse.class);
providers.register("setPassword", SetPasswordResponse.class); providers.register("setPassword", SetPasswordResponse.class);
providers.register("exit", ExitResponse.class); providers.register("exit", ExitResponse.class);
providers.register("getSecureLevelInfo", GetSecureLevelInfoResponse.class);
providers.register("verifySecureLevelKey", VerifySecureLevelKeyResponse.class);
} }
public void sendObject(ChannelHandlerContext ctx, Object obj) { public void sendObject(ChannelHandlerContext ctx, Object obj) {

View file

@ -0,0 +1,35 @@
package pro.gravit.launchserver.socket.response.secure;
import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.events.request.GetSecureLevelInfoRequestEvent;
import pro.gravit.launchserver.auth.protect.interfaces.SecureProtectHandler;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.SimpleResponse;
public class GetSecureLevelInfoResponse extends SimpleResponse {
@Override
public String getType() {
return "getSecureLevelInfo";
}
@Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
if(!(server.config.protectHandler instanceof SecureProtectHandler))
{
GetSecureLevelInfoRequestEvent response = new GetSecureLevelInfoRequestEvent(null);
response.enabled = false;
sendResult(response);
}
SecureProtectHandler secureProtectHandler = (SecureProtectHandler) server.config.protectHandler;
if(!secureProtectHandler.allowGetSecureLevelInfo(client))
{
sendError("Permissions denied");
return;
}
if(client.trustLevel == null) client.trustLevel = new Client.TrustLevel();
if(client.trustLevel.verifySecureKey == null) client.trustLevel.verifySecureKey = secureProtectHandler.generateSecureLevelKey();
GetSecureLevelInfoRequestEvent response = new GetSecureLevelInfoRequestEvent(client.trustLevel.verifySecureKey);
response.enabled = true;
sendResult(secureProtectHandler.onGetSecureLevelInfo(response));
}
}

View file

@ -1,20 +0,0 @@
package pro.gravit.launchserver.socket.response.secure;
import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.events.request.GetSecureTokenRequestEvent;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.SimpleResponse;
public class GetSecureTokenResponse extends SimpleResponse {
@Override
public String getType() {
return "getSecureToken";
}
@Override
public void execute(ChannelHandlerContext ctx, Client client) {
String secureToken = server.config.protectHandler.generateClientSecureToken();
client.verifyToken = secureToken;
sendResult(new GetSecureTokenRequestEvent(secureToken));
}
}

View file

@ -0,0 +1,41 @@
package pro.gravit.launchserver.socket.response.secure;
import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.events.request.VerifySecureLevelKeyRequestEvent;
import pro.gravit.launchserver.auth.protect.interfaces.SecureProtectHandler;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.SimpleResponse;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
public class VerifySecureLevelKeyResponse extends SimpleResponse {
public byte[] publicKey;
public byte[] signature;
@Override
public String getType() {
return "verifySecureLevelKey";
}
@Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
if(!(server.config.protectHandler instanceof SecureProtectHandler))
{
sendError("This method not allowed");
return;
}
SecureProtectHandler secureProtectHandler = (SecureProtectHandler) server.config.protectHandler;
try {
secureProtectHandler.verifySecureLevelKey(publicKey, signature);
} catch (InvalidKeySpecException e)
{
sendError("Invalid public key");
return;
} catch (SignatureException e)
{
sendError("Invalid signature");
return;
}
sendResult(new VerifySecureLevelKeyRequestEvent());
}
}

View file

@ -1,22 +0,0 @@
package pro.gravit.launchserver.socket.response.secure;
import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.events.request.VerifySecureTokenRequestEvent;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.SimpleResponse;
public class VerifySecureTokenResponse extends SimpleResponse {
public String secureToken;
@Override
public String getType() {
return "verifySecureToken";
}
@Override
public void execute(ChannelHandlerContext ctx, Client client) {
boolean success = server.config.protectHandler.verifyClientSecureToken(secureToken, client.verifyToken);
if (success) client.isSecure = true;
sendResult(new VerifySecureTokenRequestEvent(success));
}
}

View file

@ -0,0 +1,17 @@
package pro.gravit.launcher.events.request;
import pro.gravit.launcher.events.RequestEvent;
public class GetSecureLevelInfoRequestEvent extends RequestEvent {
public final byte[] verifySecureKey;
public boolean enabled;
public GetSecureLevelInfoRequestEvent(byte[] verifySecureKey) {
this.verifySecureKey = verifySecureKey;
}
@Override
public String getType() {
return "getSecureLevelInfo";
}
}

View file

@ -1,18 +0,0 @@
package pro.gravit.launcher.events.request;
import pro.gravit.launcher.LauncherNetworkAPI;
import pro.gravit.launcher.events.RequestEvent;
public class GetSecureTokenRequestEvent extends RequestEvent {
@LauncherNetworkAPI
public final String secureToken;
@Override
public String getType() {
return "GetSecureToken";
}
public GetSecureTokenRequestEvent(String secureToken) {
this.secureToken = secureToken;
}
}

View file

@ -0,0 +1,10 @@
package pro.gravit.launcher.events.request;
import pro.gravit.launcher.events.RequestEvent;
public class VerifySecureLevelKeyRequestEvent extends RequestEvent {
@Override
public String getType() {
return "verifySecureLevelKey";
}
}

View file

@ -1,18 +0,0 @@
package pro.gravit.launcher.events.request;
import pro.gravit.launcher.LauncherNetworkAPI;
import pro.gravit.launcher.events.RequestEvent;
public class VerifySecureTokenRequestEvent extends RequestEvent {
@LauncherNetworkAPI
public final boolean success;
@Override
public String getType() {
return "verifySecureToken";
}
public VerifySecureTokenRequestEvent(boolean success) {
this.success = success;
}
}

View file

@ -0,0 +1,11 @@
package pro.gravit.launcher.request.secure;
import pro.gravit.launcher.events.request.GetSecureLevelInfoRequestEvent;
import pro.gravit.launcher.request.Request;
public class GetSecureLevelInfoRequest extends Request<GetSecureLevelInfoRequestEvent> {
@Override
public String getType() {
return "getSecureLevelInfo";
}
}

View file

@ -0,0 +1,19 @@
package pro.gravit.launcher.request.secure;
import pro.gravit.launcher.events.request.VerifySecureLevelKeyRequestEvent;
import pro.gravit.launcher.request.Request;
public class VerifySecureLevelKeyRequest extends Request<VerifySecureLevelKeyRequestEvent> {
public final byte[] publicKey;
public final byte[] signature;
public VerifySecureLevelKeyRequest(byte[] publicKey, byte[] signature) {
this.publicKey = publicKey;
this.signature = signature;
}
@Override
public String getType() {
return "verifySecureLevelKey";
}
}

View file

@ -11,6 +11,7 @@
import pro.gravit.launcher.hasher.HashedEntryAdapter; import pro.gravit.launcher.hasher.HashedEntryAdapter;
import pro.gravit.launcher.request.WebSocketEvent; import pro.gravit.launcher.request.WebSocketEvent;
import pro.gravit.launcher.request.auth.AuthRequest; import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.launcher.request.secure.VerifySecureLevelKeyRequest;
import pro.gravit.utils.ProviderMap; import pro.gravit.utils.ProviderMap;
import pro.gravit.utils.UniversalJsonAdapter; import pro.gravit.utils.UniversalJsonAdapter;
import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.LogHelper;
@ -97,8 +98,6 @@ public void registerResults() {
results.register("error", ErrorRequestEvent.class); results.register("error", ErrorRequestEvent.class);
results.register("update", UpdateRequestEvent.class); results.register("update", UpdateRequestEvent.class);
results.register("restoreSession", RestoreSessionRequestEvent.class); results.register("restoreSession", RestoreSessionRequestEvent.class);
results.register("getSecureToken", GetSecureTokenRequestEvent.class);
results.register("verifySecureToken", VerifySecureTokenRequestEvent.class);
results.register("log", LogEvent.class); results.register("log", LogEvent.class);
results.register("cmdExec", ExecCommandRequestEvent.class); results.register("cmdExec", ExecCommandRequestEvent.class);
results.register("getAvailabilityAuth", GetAvailabilityAuthRequestEvent.class); results.register("getAvailabilityAuth", GetAvailabilityAuthRequestEvent.class);
@ -108,6 +107,8 @@ public void registerResults() {
results.register("notification", NotificationEvent.class); results.register("notification", NotificationEvent.class);
results.register("signal", SignalEvent.class); results.register("signal", SignalEvent.class);
results.register("exit", ExitRequestEvent.class); results.register("exit", ExitRequestEvent.class);
results.register("getSecureLevelInfo", GetSecureLevelInfoRequestEvent.class);
results.register("verifySecureLevelKey", VerifySecureLevelKeyRequestEvent.class);
} }
public void waitIfNotConnected() { public void waitIfNotConnected() {