[FEATURE] AuthSupportHardware

This commit is contained in:
Gravita 2021-05-28 20:43:19 +07:00
parent 568852b951
commit 73c6c48f42
8 changed files with 262 additions and 12 deletions

View file

@ -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();
}

View file

@ -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<User> 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;
}
}

View file

@ -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();
}

View file

@ -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");
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;
}
}
}

View file

@ -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;
}
}

View file

@ -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);

View file

@ -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";

View file

@ -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";