diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/interfaces/UserHardware.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/interfaces/UserHardware.java new file mode 100644 index 00000000..82934d0c --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/interfaces/UserHardware.java @@ -0,0 +1,13 @@ +package pro.gravit.launchserver.auth.core.interfaces; + +import pro.gravit.launcher.request.secure.HardwareReportRequest; + +public interface UserHardware { + HardwareReportRequest.HardwareInfo getHardwareInfo(); + + byte[] getPublicKey(); + + String getId(); + + boolean isBanned(); +} diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/interfaces/provider/AuthSupportHardware.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/interfaces/provider/AuthSupportHardware.java new file mode 100644 index 00000000..87facbde --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/interfaces/provider/AuthSupportHardware.java @@ -0,0 +1,96 @@ +package pro.gravit.launchserver.auth.core.interfaces.provider; + +import pro.gravit.launcher.request.secure.HardwareReportRequest; +import pro.gravit.launchserver.auth.core.User; +import pro.gravit.launchserver.auth.core.interfaces.UserHardware; +import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportHardware; +import pro.gravit.launchserver.auth.protect.hwid.HWIDProvider; +import pro.gravit.launchserver.helper.DamerauHelper; + +import java.util.Arrays; +import java.util.List; + +public interface AuthSupportHardware { + UserHardware getHardwareInfoByPublicKey(byte[] publicKey); + + UserHardware getHardwareInfoByData(HardwareReportRequest.HardwareInfo info); + + UserHardware getHardwareInfoById(String id); + + UserHardware createHardwareInfo(HardwareReportRequest.HardwareInfo info, byte[] publicKey); + + void addPublicKeyToHardwareInfo(UserHardware hardware, byte[] publicKey); + + List getUsersByHardwareInfo(UserHardware hardware); + + void banHardware(UserHardware hardware); + + void unbanHardware(UserHardware hardware); + + default UserSupportHardware fetchUserHardware(User user) { + return (UserSupportHardware) user; + } + + default void normalizeHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo) { + if (hardwareInfo.baseboardSerialNumber != null) + hardwareInfo.baseboardSerialNumber = hardwareInfo.baseboardSerialNumber.trim(); + if (hardwareInfo.hwDiskId != null) hardwareInfo.hwDiskId = hardwareInfo.hwDiskId.trim(); + } + + //Required normalize HardwareInfo + default HWIDProvider.HardwareInfoCompareResult compareHardwareInfo(HardwareReportRequest.HardwareInfo first, HardwareReportRequest.HardwareInfo second) { + HWIDProvider.HardwareInfoCompareResult result = new HWIDProvider.HardwareInfoCompareResult(); + if (first.hwDiskId == null || first.hwDiskId.isEmpty()) result.firstSpoofingLevel += 0.9; + if (first.displayId == null || first.displayId.length < 4) result.firstSpoofingLevel += 0.3; + if (first.baseboardSerialNumber == null || first.baseboardSerialNumber.trim().isEmpty()) + result.firstSpoofingLevel += 0.2; + if (second.hwDiskId == null || second.hwDiskId.trim().isEmpty()) result.secondSpoofingLevel += 0.9; + if (second.displayId == null || second.displayId.length < 4) result.secondSpoofingLevel += 0.3; + if (second.baseboardSerialNumber == null || second.baseboardSerialNumber.trim().isEmpty()) + result.secondSpoofingLevel += 0.2; + if (first.hwDiskId != null && second.hwDiskId != null) { + int hwDIskIdRate = DamerauHelper.calculateDistance(first.hwDiskId.toLowerCase(), second.hwDiskId.toLowerCase()); + if (hwDIskIdRate == 0) // 100% compare + { + result.compareLevel += 0.99; + } else if (hwDIskIdRate < 3) //Very small change + { + result.compareLevel += 0.85; + } else if (hwDIskIdRate < (first.hwDiskId.length() + second.hwDiskId.length()) / 4) { + double addLevel = hwDIskIdRate / ((double) (first.hwDiskId.length() + second.hwDiskId.length()) / 2.0); + if (addLevel > 0.0 && addLevel < 0.85) result.compareLevel += addLevel; + } + } + if (first.baseboardSerialNumber != null && second.baseboardSerialNumber != null) { + int baseboardSerialRate = DamerauHelper.calculateDistance(first.baseboardSerialNumber.toLowerCase(), second.baseboardSerialNumber.toLowerCase()); + if (baseboardSerialRate == 0) // 100% compare + { + result.compareLevel += 0.3; + } else if (baseboardSerialRate < 3) //Very small change + { + result.compareLevel += 0.15; + } + } + if (first.displayId != null && second.displayId != null) { + if (Arrays.equals(first.displayId, second.displayId)) { + result.compareLevel += 0.75; + } + } + //Check statistic info + if (first.logicalProcessors == 0 || first.physicalProcessors == 0 || first.logicalProcessors < first.physicalProcessors) //WTF + result.firstSpoofingLevel += 0.9; + if (second.logicalProcessors == 0 || second.physicalProcessors == 0 || second.logicalProcessors < second.physicalProcessors) //WTF + result.secondSpoofingLevel += 0.9; + if (first.physicalProcessors == second.physicalProcessors && first.logicalProcessors == second.logicalProcessors) + result.compareLevel += 0.05; + if (first.battery != second.battery) + result.compareLevel -= 0.05; + if (first.processorMaxFreq == second.processorMaxFreq) + result.compareLevel += 0.1; + if (first.totalMemory == second.totalMemory) + result.compareLevel += 0.1; + if (Math.abs(first.totalMemory - second.totalMemory) < 32 * 1024) + result.compareLevel += 0.05; + return result; + } +} diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/interfaces/user/UserSupportHardware.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/interfaces/user/UserSupportHardware.java new file mode 100644 index 00000000..a52da511 --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/interfaces/user/UserSupportHardware.java @@ -0,0 +1,7 @@ +package pro.gravit.launchserver.auth.core.interfaces.user; + +import pro.gravit.launchserver.auth.core.interfaces.UserHardware; + +public interface UserSupportHardware { + UserHardware getHardware(); +} diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/protect/AdvancedProtectHandler.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/protect/AdvancedProtectHandler.java index 8386c29f..8d95813c 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/protect/AdvancedProtectHandler.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/protect/AdvancedProtectHandler.java @@ -1,12 +1,18 @@ package pro.gravit.launchserver.auth.protect; +import io.jsonwebtoken.JwtParser; +import io.jsonwebtoken.Jwts; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import pro.gravit.launcher.events.request.GetSecureLevelInfoRequestEvent; import pro.gravit.launcher.events.request.HardwareReportRequestEvent; import pro.gravit.launcher.events.request.VerifySecureLevelKeyRequestEvent; +import pro.gravit.launcher.request.secure.HardwareReportRequest; import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.Reconfigurable; +import pro.gravit.launchserver.auth.AuthProviderPair; +import pro.gravit.launchserver.auth.core.interfaces.UserHardware; +import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportHardware; import pro.gravit.launchserver.auth.protect.hwid.HWIDException; import pro.gravit.launchserver.auth.protect.hwid.HWIDProvider; import pro.gravit.launchserver.auth.protect.interfaces.HardwareProtectHandler; @@ -14,9 +20,12 @@ 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.launchserver.socket.response.auth.RestoreResponse; import pro.gravit.launchserver.socket.response.secure.HardwareReportResponse; import pro.gravit.utils.command.Command; +import java.util.Base64; +import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -49,7 +58,7 @@ public boolean allowGetSecureLevelInfo(Client client) { @Override public void onHardwareReport(HardwareReportResponse response, Client client) { if (!enableHardwareFeature) { - response.sendResult(new HardwareReportRequestEvent()); + response.sendResult(new HardwareReportRequestEvent(createHardwareToken(client.username, response.hardware))); return; } try { @@ -57,36 +66,58 @@ public void onHardwareReport(HardwareReportResponse response, Client client) { response.sendError("Access denied"); return; } - provider.normalizeHardwareInfo(response.hardware); logger.debug("HardwareInfo received"); - boolean needCreate = !provider.addPublicKeyToHardwareInfo(response.hardware, client.trustLevel.publicKey, client); - logger.debug("HardwareInfo needCreate: {}", needCreate ? "true" : "false"); - if (needCreate) - provider.createHardwareInfo(response.hardware, client.trustLevel.publicKey, client); - client.trustLevel.hardwareInfo = response.hardware; + if (client.auth instanceof AuthSupportHardware) { + AuthSupportHardware authSupportHardware = (AuthSupportHardware) client.auth; + UserHardware hardware = authSupportHardware.getHardwareInfoByData(response.hardware); + if (hardware == null) { + hardware = authSupportHardware.createHardwareInfo(response.hardware, client.trustLevel.publicKey); + } else { + authSupportHardware.addPublicKeyToHardwareInfo(hardware, client.trustLevel.publicKey); + } + if (hardware.isBanned()) { + throw new SecurityException("Your hardware banned"); + } + client.trustLevel.hardwareInfo = hardware.getHardwareInfo(); + } else { + provider.normalizeHardwareInfo(response.hardware); + boolean needCreate = !provider.addPublicKeyToHardwareInfo(response.hardware, client.trustLevel.publicKey, client); + logger.debug("HardwareInfo needCreate: {}", needCreate ? "true" : "false"); + if (needCreate) + provider.createHardwareInfo(response.hardware, client.trustLevel.publicKey, client); + client.trustLevel.hardwareInfo = response.hardware; + } } catch (HWIDException e) { throw new SecurityException(e.getMessage()); } - response.sendResult(new HardwareReportRequestEvent()); + response.sendResult(new HardwareReportRequestEvent(createHardwareToken(client.username, response.hardware))); } @Override public VerifySecureLevelKeyRequestEvent onSuccessVerify(Client client) { if (enableHardwareFeature) { - if (provider == null) { + if (client.isAuth && client.auth.core instanceof AuthSupportHardware) { + UserHardware hardware = ((AuthSupportHardware) client.auth.core).getHardwareInfoByPublicKey(client.trustLevel.publicKey); + if (hardware == null) //HWID not found? + return new VerifySecureLevelKeyRequestEvent(true, false, createPublicKeyToken(client.username, client.trustLevel.publicKey)); + if (hardware.isBanned()) { + throw new SecurityException("Your hardware banned"); + } + client.trustLevel.hardwareInfo = hardware.getHardwareInfo(); + } else if (provider == null) { logger.warn("HWIDProvider null. HardwareInfo not checked!"); } else { try { client.trustLevel.hardwareInfo = provider.findHardwareInfoByPublicKey(client.trustLevel.publicKey, client); if (client.trustLevel.hardwareInfo == null) //HWID not found? - return new VerifySecureLevelKeyRequestEvent(true); + return new VerifySecureLevelKeyRequestEvent(true, false, createPublicKeyToken(client.username, client.trustLevel.publicKey)); } catch (HWIDException e) { throw new SecurityException(e.getMessage()); //Show banned message } } - return new VerifySecureLevelKeyRequestEvent(false); + return new VerifySecureLevelKeyRequestEvent(false, false, createPublicKeyToken(client.username, client.trustLevel.publicKey)); } - return new VerifySecureLevelKeyRequestEvent(); + return new VerifySecureLevelKeyRequestEvent(false, false, createPublicKeyToken(client.username, client.trustLevel.publicKey)); } @Override @@ -114,4 +145,84 @@ public void close() { if (provider != null) provider.close(); } + + public String createHardwareToken(String username, HardwareReportRequest.HardwareInfo info) { + return Jwts.builder() + .setIssuer("LaunchSerer") + .setSubject(username) + .signWith(server.keyAgreementManager.ecdsaPrivateKey) + .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 8)) + .claim("hardware", info) + .compact(); + } + + public String createPublicKeyToken(String username, byte[] publicKey) { + return Jwts.builder() + .setIssuer("LaunchSerer") + .setSubject(username) + .signWith(server.keyAgreementManager.ecdsaPrivateKey) + .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 8)) + .claim("publicKey", Base64.getEncoder().encode(publicKey)) + .compact(); + } + + public static class HardwareInfoTokenVerifier implements RestoreResponse.ExtendedTokenProvider { + private transient final LaunchServer server; + private transient final Logger logger = LogManager.getLogger(); + private final JwtParser parser; + + public HardwareInfoTokenVerifier(LaunchServer server) { + this.server = server; + this.parser = Jwts.parserBuilder() + .requireIssuer("LaunchServer") + .setSigningKey(server.keyAgreementManager.ecdsaPublicKey) + .build(); + } + + @Override + public boolean accept(Client client, AuthProviderPair pair, String extendedToken) { + try { + var parse = parser.parseClaimsJws(extendedToken); + HardwareReportRequest.HardwareInfo hardwareInfo = parse.getBody().get("hardware", HardwareReportRequest.HardwareInfo.class); + if (hardwareInfo == null) return false; + if (client.trustLevel == null) client.trustLevel = new Client.TrustLevel(); + client.trustLevel.hardwareInfo = hardwareInfo; + return true; + } catch (Throwable e) { + logger.error("Hardware JWT error", e); + } + + return false; + } + } + + public static class PublicKeyTokenVerifier implements RestoreResponse.ExtendedTokenProvider { + private transient final LaunchServer server; + private transient final Logger logger = LogManager.getLogger(); + private final JwtParser parser; + + public PublicKeyTokenVerifier(LaunchServer server) { + this.server = server; + this.parser = Jwts.parserBuilder() + .requireIssuer("LaunchServer") + .setSigningKey(server.keyAgreementManager.ecdsaPublicKey) + .build(); + } + + @Override + public boolean accept(Client client, AuthProviderPair pair, String extendedToken) { + try { + var parse = parser.parseClaimsJws(extendedToken); + String publicKey = parse.getBody().get("publicKey", String.class); + if (publicKey == null) return false; + if (client.trustLevel == null) client.trustLevel = new Client.TrustLevel(); + client.trustLevel.publicKey = Base64.getDecoder().decode(publicKey); + return true; + } catch (Throwable e) { + logger.error("Public Key JWT error", e); + } + + return false; + } + } } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/RestoreResponse.java b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/RestoreResponse.java index 64024c52..1bbd3173 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/RestoreResponse.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/RestoreResponse.java @@ -9,6 +9,7 @@ import pro.gravit.launchserver.auth.core.AuthCoreProvider; import pro.gravit.launchserver.auth.core.User; import pro.gravit.launchserver.auth.core.UserSession; +import pro.gravit.launchserver.auth.protect.AdvancedProtectHandler; import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.response.SimpleResponse; import pro.gravit.launchserver.socket.response.update.LauncherResponse; @@ -29,6 +30,8 @@ public class RestoreResponse extends SimpleResponse { public static void registerProviders(LaunchServer server) { if (!registeredProviders) { providers.put(LauncherRequestEvent.LAUNCHER_EXTENDED_TOKEN_NAME, new LauncherResponse.LauncherTokenVerifier(server)); + providers.put("publicKey", new AdvancedProtectHandler.PublicKeyTokenVerifier(server)); + providers.put("hardware", new AdvancedProtectHandler.HardwareInfoTokenVerifier(server)); registeredProviders = true; } } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/secure/HardwareReportResponse.java b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/secure/HardwareReportResponse.java index e436d775..78a629b9 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/secure/HardwareReportResponse.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/secure/HardwareReportResponse.java @@ -17,6 +17,10 @@ public String getType() { @Override public void execute(ChannelHandlerContext ctx, Client client) { + if (client.trustLevel == null || client.trustLevel.publicKey == null) { + sendError("Invalid request"); + return; + } if (server.config.protectHandler instanceof HardwareProtectHandler) { try { ((HardwareProtectHandler) server.config.protectHandler).onHardwareReport(this, client); diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/HardwareReportRequestEvent.java b/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/HardwareReportRequestEvent.java index 7305eca8..75ec5458 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/HardwareReportRequestEvent.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/HardwareReportRequestEvent.java @@ -3,6 +3,15 @@ import pro.gravit.launcher.events.RequestEvent; public class HardwareReportRequestEvent extends RequestEvent { + public String extendedToken; + + public HardwareReportRequestEvent() { + } + + public HardwareReportRequestEvent(String extendedToken) { + this.extendedToken = extendedToken; + } + @Override public String getType() { return "hardwareReport"; diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/VerifySecureLevelKeyRequestEvent.java b/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/VerifySecureLevelKeyRequestEvent.java index a2833837..ff7085e5 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/VerifySecureLevelKeyRequestEvent.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/VerifySecureLevelKeyRequestEvent.java @@ -5,6 +5,7 @@ public class VerifySecureLevelKeyRequestEvent extends RequestEvent { public boolean needHardwareInfo; public boolean onlyStatisticInfo; + public String extendedToken; public VerifySecureLevelKeyRequestEvent() { } @@ -13,6 +14,12 @@ public VerifySecureLevelKeyRequestEvent(boolean needHardwareInfo) { this.needHardwareInfo = needHardwareInfo; } + public VerifySecureLevelKeyRequestEvent(boolean needHardwareInfo, boolean onlyStatisticInfo, String extendedToken) { + this.needHardwareInfo = needHardwareInfo; + this.onlyStatisticInfo = onlyStatisticInfo; + this.extendedToken = extendedToken; + } + @Override public String getType() { return "verifySecureLevelKey";