feat: 1.21 port
This commit is contained in:
@@ -7,5 +7,5 @@ dependencies {
|
||||
// which get remapped to the correct annotations on each platform.
|
||||
// Do NOT use other classes from Fabric Loader.
|
||||
modImplementation "net.fabricmc:fabric-loader:$rootProject.fabric_loader_version"
|
||||
compileOnly("com.mojang:authlib:4.0.43")
|
||||
compileOnly("com.mojang:authlib:6.0.54")
|
||||
}
|
||||
|
||||
@@ -3,16 +3,13 @@ package net.magicterra.skinfix.inject;
|
||||
import com.mojang.authlib.Environment;
|
||||
import com.mojang.authlib.EnvironmentParser;
|
||||
import com.mojang.authlib.GameProfileRepository;
|
||||
import com.mojang.authlib.HttpAuthenticationService;
|
||||
import com.mojang.authlib.exceptions.AuthenticationException;
|
||||
import com.mojang.authlib.minecraft.MinecraftSessionService;
|
||||
import com.mojang.authlib.minecraft.UserApiService;
|
||||
import com.mojang.authlib.minecraft.client.MinecraftClient;
|
||||
import com.mojang.authlib.yggdrasil.ServicesKeySet;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilEnvironment;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilGameProfileRepository;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilServicesKeyInfo;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilUserApiService;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
|
||||
@@ -37,13 +34,14 @@ public class InjectYggdrasilAuthenticationService extends YggdrasilAuthenticatio
|
||||
super(proxy, environment);
|
||||
this.environment = environment;
|
||||
|
||||
final URL publicKeySetUrl = HttpAuthenticationService.constantURL("https://mc.gardel.top/minecraftservices/publickeys");
|
||||
this.servicesKeySet = YggdrasilServicesKeyInfo.get(publicKeySetUrl, this);
|
||||
final MinecraftClient client = MinecraftClient.unauthenticated(proxy);
|
||||
final URL publicKeySetUrl = constantURL("https://mc.gardel.top/minecraftservices/publickeys");
|
||||
this.servicesKeySet = YggdrasilServicesKeyInfo.get(publicKeySetUrl, client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MinecraftSessionService createMinecraftSessionService() {
|
||||
return new InjectYggdrasilMinecraftSessionService(this, environment);
|
||||
return new InjectYggdrasilMinecraftSessionService(servicesKeySet, getProxy(), environment);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -53,21 +51,6 @@ public class InjectYggdrasilAuthenticationService extends YggdrasilAuthenticatio
|
||||
|
||||
@Override
|
||||
public GameProfileRepository createProfileRepository() {
|
||||
return new YggdrasilGameProfileRepository(this, Environment.create(
|
||||
environment.getAuthHost(),
|
||||
"https://mc.gardel.top/api",
|
||||
environment.getSessionHost(),
|
||||
environment.getServicesHost(),
|
||||
environment.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserApiService createUserApiService(String accessToken) throws AuthenticationException {
|
||||
return new YggdrasilUserApiService(accessToken, getProxy(), Environment.create(
|
||||
environment.getAuthHost(),
|
||||
environment.getAccountsHost(),
|
||||
environment.getSessionHost(),
|
||||
"https://mc.gardel.top/minecraftservices",
|
||||
environment.getName()));
|
||||
return new YggdrasilGameProfileRepository(getProxy(), new Environment(environment.sessionHost(), "https://mc.gardel.top/minecraftservices", environment.name()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
package net.magicterra.skinfix.inject;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.mojang.authlib.Environment;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.minecraft.InsecurePublicKeyException;
|
||||
import com.mojang.authlib.SignatureState;
|
||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
||||
import com.mojang.authlib.minecraft.MinecraftProfileTextures;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import com.mojang.authlib.yggdrasil.ServicesKeySet;
|
||||
import com.mojang.authlib.yggdrasil.ServicesKeyType;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService;
|
||||
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.Proxy;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -28,12 +26,15 @@ import org.slf4j.LoggerFactory;
|
||||
* @since 2025-04-26 22:11
|
||||
*/
|
||||
public class InjectYggdrasilMinecraftSessionService extends YggdrasilMinecraftSessionService {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(InjectYggdrasilMinecraftSessionService.class);
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(YggdrasilMinecraftSessionService.class);
|
||||
|
||||
private final Gson gson;
|
||||
|
||||
protected InjectYggdrasilMinecraftSessionService(YggdrasilAuthenticationService service, Environment env) {
|
||||
super(service, env);
|
||||
private final ServicesKeySet servicesKeySet;
|
||||
|
||||
protected InjectYggdrasilMinecraftSessionService(ServicesKeySet servicesKeySet, Proxy proxy, Environment env) {
|
||||
super(servicesKeySet, proxy, env);
|
||||
this.servicesKeySet = servicesKeySet;
|
||||
try {
|
||||
Field field = YggdrasilMinecraftSessionService.class.getDeclaredField("gson");
|
||||
field.setAccessible(true);
|
||||
@@ -44,52 +45,47 @@ public class InjectYggdrasilMinecraftSessionService extends YggdrasilMinecraftSe
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> getTextures(GameProfile profile, boolean requireSecure) throws InsecurePublicKeyException {
|
||||
final Property textureProperty = Iterables.getFirst(profile.getProperties().get("textures"), null);
|
||||
|
||||
if (textureProperty == null) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
final String value = requireSecure ? getSecurePropertyValue(textureProperty) : textureProperty.getValue();
|
||||
public MinecraftProfileTextures unpackTextures(Property packedTextures) {
|
||||
final String value = packedTextures.value();
|
||||
final SignatureState signatureState = getPropertySignatureState(packedTextures);
|
||||
|
||||
final MinecraftTexturesPayload result;
|
||||
try {
|
||||
final String json = new String(Base64.getDecoder().decode(value), StandardCharsets.UTF_8);
|
||||
result = gson.fromJson(json, MinecraftTexturesPayload.class);
|
||||
} catch (final JsonParseException e) {
|
||||
} catch (final JsonParseException | IllegalArgumentException e) {
|
||||
LOGGER.error("Could not decode textures payload", e);
|
||||
return new HashMap<>();
|
||||
return MinecraftProfileTextures.EMPTY;
|
||||
}
|
||||
|
||||
if (result == null || result.getTextures() == null) {
|
||||
return new HashMap<>();
|
||||
if (result == null || result.textures() == null || result.textures().isEmpty()) {
|
||||
return MinecraftProfileTextures.EMPTY;
|
||||
}
|
||||
|
||||
for (final Map.Entry<MinecraftProfileTexture.Type, MinecraftProfileTexture> entry : result.getTextures().entrySet()) {
|
||||
final Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> textures = result.textures();
|
||||
for (final Map.Entry<MinecraftProfileTexture.Type, MinecraftProfileTexture> entry : textures.entrySet()) {
|
||||
final String url = entry.getValue().getUrl();
|
||||
if (!InjectTextureUrlChecker.isAllowedTextureDomain(url)) {
|
||||
LOGGER.error("Textures payload contains blocked domain: {}", url);
|
||||
return new HashMap<>();
|
||||
return MinecraftProfileTextures.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
return result.getTextures();
|
||||
return new MinecraftProfileTextures(
|
||||
textures.get(MinecraftProfileTexture.Type.SKIN),
|
||||
textures.get(MinecraftProfileTexture.Type.CAPE),
|
||||
textures.get(MinecraftProfileTexture.Type.ELYTRA),
|
||||
signatureState
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSecurePropertyValue(final Property property) throws InsecurePublicKeyException {
|
||||
private SignatureState getPropertySignatureState(final Property property) {
|
||||
if (!property.hasSignature()) {
|
||||
LOGGER.error("Signature is missing from Property {}", property.getName());
|
||||
throw new InsecurePublicKeyException.MissingException();
|
||||
return SignatureState.UNSIGNED;
|
||||
}
|
||||
|
||||
final ServicesKeySet servicesKeySet = getAuthenticationService().getServicesKeySet();
|
||||
if (servicesKeySet.keys(ServicesKeyType.PROFILE_PROPERTY).stream().noneMatch(key -> key.validateProperty(property))) {
|
||||
LOGGER.error("Property {} has been tampered with (signature invalid)", property.getName());
|
||||
throw new InsecurePublicKeyException.InvalidException("Property has been tampered with (signature invalid)");
|
||||
return SignatureState.INVALID;
|
||||
}
|
||||
|
||||
return property.getValue();
|
||||
return SignatureState.SIGNED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
{
|
||||
"required": true,
|
||||
"package": "net.magicterra.skinfix.mixin",
|
||||
"compatibilityLevel": "JAVA_17",
|
||||
"compatibilityLevel": "JAVA_21",
|
||||
"minVersion": "0.8",
|
||||
"client": [
|
||||
"MinecraftMixin"
|
||||
],
|
||||
"mixins": [
|
||||
"MinecraftMixin",
|
||||
"ProfilePublicKeyDataMixin"
|
||||
],
|
||||
"mixins": [],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user