diff --git a/README.en.md b/README.en.md index f999131..22972d7 100644 --- a/README.en.md +++ b/README.en.md @@ -94,4 +94,12 @@ Configure Minecraft server with the following JVM parameter: * Realms (allowed if the option is disabled) * Telemetry (turned off if the option is disabled) * Profanity filter (turned off if the option is disabled) + +-Dauthlibinjector.profileKey={default|enabled|disabled} + Whether to enable the profile signing key feature. This feature is introduced in 22w17a, and is used to implement the multiplayer secure chat signing. + If this this feature is enabled, Minecraft will send a POST request to `/minecraftservices/player/certificates` to retrieve the key pair issued by the authentication server. + It's enabled by default if the authentication server sends feature.enable_profile_key option. + + If the profile signing key isn't present, the player will be unable to join servers that enable enforce-secure-profile=true option. + And other players' Minecraft client will log a warning when receiving an unsigned chat message. ``` diff --git a/README.md b/README.md index c53fa88..09547de 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,14 @@ gradle * 领域权限 (禁用后默认允许) * 遥测 (禁用后默认关闭) * 冒犯性内容过滤 (禁用后默认关闭) + +-Dauthlibinjector.profileKey={default|enabled|disabled} + 是否启用消息签名密钥对功能, 这一功能在 22w17a 引入, 用于多人游戏中聊天消息的数字签名. + 启用此功能后, Minecraft 会向 `/minecraftservices/player/certificates` 发送 POST 请求, 以获取由验证服务器颁发的密钥对. + 此功能需要验证服务器支持, 若验证服务器未设置 feature.enable_profile_key 选项, 则该功能默认禁用. + + 当缺少消息签名密钥时, 玩家将无法进入设置了 enforce-secure-profile=true 选项的服务器. + 而当其他玩家的客户端在收到无有效签名的聊天消息时, 会在日志中记录警告. ``` ## 捐助 diff --git a/src/main/java/moe/yushi/authlibinjector/AuthlibInjector.java b/src/main/java/moe/yushi/authlibinjector/AuthlibInjector.java index a1d4fd2..97bd342 100644 --- a/src/main/java/moe/yushi/authlibinjector/AuthlibInjector.java +++ b/src/main/java/moe/yushi/authlibinjector/AuthlibInjector.java @@ -44,6 +44,7 @@ import java.util.Set; import java.util.stream.Stream; import moe.yushi.authlibinjector.httpd.DefaultURLRedirector; import moe.yushi.authlibinjector.httpd.LegacySkinAPIFilter; +import moe.yushi.authlibinjector.httpd.ProfileKeyFilter; import moe.yushi.authlibinjector.httpd.AntiFeaturesFilter; import moe.yushi.authlibinjector.httpd.QueryProfileFilter; import moe.yushi.authlibinjector.httpd.QueryUUIDsFilter; @@ -248,6 +249,11 @@ public final class AuthlibInjector { filters.add(new AntiFeaturesFilter()); } + boolean profileKeyDefault = Boolean.TRUE.equals(config.getMeta().get("feature.enable_profile_key")); + if (!Config.profileKey.isEnabled(profileKeyDefault)) { + filters.add(new ProfileKeyFilter()); + } + return filters; } diff --git a/src/main/java/moe/yushi/authlibinjector/Config.java b/src/main/java/moe/yushi/authlibinjector/Config.java index 8c4a239..86b998d 100644 --- a/src/main/java/moe/yushi/authlibinjector/Config.java +++ b/src/main/java/moe/yushi/authlibinjector/Config.java @@ -60,6 +60,7 @@ public final class Config { public static FeatureOption mojangNamespace; public static FeatureOption legacySkinPolyfill; public static FeatureOption mojangAntiFeatures; + public static FeatureOption profileKey; public static boolean noShowServerName; private static void initDebugOptions() { @@ -204,6 +205,7 @@ public final class Config { mojangNamespace = parseFeatureOption("authlibinjector.mojangNamespace"); legacySkinPolyfill = parseFeatureOption("authlibinjector.legacySkinPolyfill"); mojangAntiFeatures = parseFeatureOption("authlibinjector.mojangAntiFeatures"); + profileKey = parseFeatureOption("authlibinjector.profileKey"); httpdDisabled = System.getProperty("authlibinjector.disableHttpd") != null; noShowServerName = System.getProperty("authlibinjector.noShowServerName") != null; } diff --git a/src/main/java/moe/yushi/authlibinjector/httpd/ProfileKeyFilter.java b/src/main/java/moe/yushi/authlibinjector/httpd/ProfileKeyFilter.java new file mode 100644 index 0000000..02e1cf2 --- /dev/null +++ b/src/main/java/moe/yushi/authlibinjector/httpd/ProfileKeyFilter.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2022 Haowei Wen and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package moe.yushi.authlibinjector.httpd; + +import java.io.IOException; +import java.util.Optional; +import moe.yushi.authlibinjector.internal.fi.iki.elonen.IHTTPSession; +import moe.yushi.authlibinjector.internal.fi.iki.elonen.Response; +import moe.yushi.authlibinjector.internal.fi.iki.elonen.Status; + +/** + * Intercepts Minecraft's request to https://api.minecraftservices.com/player/certificates, + * and returns an empty response. + */ +public class ProfileKeyFilter implements URLFilter { + + @Override + public boolean canHandle(String domain) { + return domain.equals("api.minecraftservices.com"); + } + + @Override + public Optional handle(String domain, String path, IHTTPSession session) throws IOException { + if (domain.equals("api.minecraftservices.com") && path.equals("/player/certificates") && session.getMethod().equals("POST")) { + return Optional.of(Response.newFixedLength(Status.NO_CONTENT, null, null)); + } + return Optional.empty(); + } + +}