13
0
geforkt von Mirrors/Velocity

Update to 1.19.1 (#772)

* 1.19.1-rc1

* More signature changes

* Further 1.19.1 changes

I also started on the checkstyle update, see the developers notes
for the rest I haven't gotten around to fixing yet.

* Fix checkstyle

* Checkstyle imports

* Fix logic error

* Changes 1.19.1-pre2

* 1.19-pre3

* Progress, some parts still WIP

* Overlooked changes

* Fix ServerData

* Fix ServerLogin send check

* Workaround the broken behavior of "No Chat Reports"

Note that if we ever choose to enforce chat signatures, then the mod will just break again... not our fault if we do that, you get what you pay for.

* more

Co-authored-by: Shane Freeder <theboyetronic@gmail.com>
Co-authored-by: Andrew Steinborn <git@steinborn.me>
Dieser Commit ist enthalten in:
FivePB (Xer) 2022-07-30 23:30:03 +00:00 committet von GitHub
Ursprung 6be344d919
Commit 1a3fba4250
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 4AEE18F83AFDEB23
60 geänderte Dateien mit 883 neuen und 192 gelöschten Zeilen

Datei anzeigen

@ -11,7 +11,6 @@ import com.google.common.base.Preconditions;
import java.util.Optional;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.checkerframework.checker.nullness.qual.Nullable;
/**

Datei anzeigen

@ -10,7 +10,6 @@ package com.velocitypowered.api.event.player;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import java.util.List;
/**

Datei anzeigen

@ -58,7 +58,8 @@ public enum ProtocolVersion {
MINECRAFT_1_17_1(756, "1.17.1"),
MINECRAFT_1_18(757, "1.18", "1.18.1"),
MINECRAFT_1_18_2(758, "1.18.2"),
MINECRAFT_1_19(759, "1.19");
MINECRAFT_1_19(759, "1.19"),
MINECRAFT_1_19_1(760, "1.19.1");
private static final int SNAPSHOT_BIT = 30;

Datei anzeigen

@ -8,7 +8,6 @@
package com.velocitypowered.api.proxy;
import com.velocitypowered.api.network.ProtocolVersion;
import java.net.InetSocketAddress;
import java.util.Optional;

Datei anzeigen

@ -7,7 +7,12 @@
package com.velocitypowered.api.proxy.crypto;
import com.google.common.collect.ImmutableSet;
import com.velocitypowered.api.network.ProtocolVersion;
import java.security.PublicKey;
import java.util.Set;
import java.util.UUID;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Represents session-server cross-signed dated RSA public key.
@ -32,4 +37,41 @@ public interface IdentifiedKey extends KeySigned {
*/
boolean verifyDataSignature(byte[] signature, byte[]... toVerify);
/**
* Retrieves the signature holders UUID.
* Returns null before the {@link com.velocitypowered.api.event.connection.LoginEvent}.
*
* @return the holder UUID or null if not present
*/
@Nullable
UUID getSignatureHolder();
/**
* Retrieves the key revision.
*
* @return the key revision
*/
Revision getKeyRevision();
enum Revision {
GENERIC_V1(ImmutableSet.of(), ImmutableSet.of(ProtocolVersion.MINECRAFT_1_19)),
LINKED_V2(ImmutableSet.of(), ImmutableSet.of(ProtocolVersion.MINECRAFT_1_19_1));
final Set<Revision> backwardsCompatibleTo;
final Set<ProtocolVersion> applicableTo;
Revision(Set<Revision> backwardsCompatibleTo, Set<ProtocolVersion> applicableTo) {
this.backwardsCompatibleTo = backwardsCompatibleTo;
this.applicableTo = applicableTo;
}
public Set<Revision> getBackwardsCompatibleTo() {
return backwardsCompatibleTo;
}
public Set<ProtocolVersion> getApplicableTo() {
return applicableTo;
}
}
}

Datei anzeigen

@ -8,10 +8,8 @@
package com.velocitypowered.api.proxy.crypto;
import com.google.common.annotations.Beta;
import java.security.PublicKey;
import java.time.Instant;
import org.checkerframework.checker.nullness.qual.Nullable;
public interface KeySigned {
@ -56,6 +54,7 @@ public interface KeySigned {
* signer public key. Note: This will **not** check for
* expiry. You can check for expiry with {@link KeySigned#hasExpired()}.
* <p>DOES NOT WORK YET FOR MESSAGES AND COMMANDS!</p>
* Addendum: Does not work for 1.19.1 until the user has authenticated.
*
* @return validity of the signature
*/

Datei anzeigen

@ -71,12 +71,12 @@ public interface TabList {
/**
* Builds a tab list entry.
*
* @deprecated Internal usage. Use {@link TabListEntry.Builder} instead.
* @param profile profile
* @param displayName display name
* @param latency latency
* @param gameMode game mode
* @return entry
* @deprecated Internal usage. Use {@link TabListEntry.Builder} instead.
*/
@Deprecated
TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,
@ -85,13 +85,13 @@ public interface TabList {
/**
* Builds a tab list entry.
*
* @deprecated Internal usage. Use {@link TabListEntry.Builder} instead.
* @param profile profile
* @param displayName display name
* @param latency latency
* @param gameMode game mode
* @param key the player key
* @return entry
* @deprecated Internal usage. Use {@link TabListEntry.Builder} instead.
*/
@Deprecated
TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,

Datei anzeigen

@ -160,6 +160,7 @@ public interface TabListEntry extends KeyIdentifiable {
* Sets the {@link IdentifiedKey} of the {@link TabListEntry}.
* <p>This is only intended and only works for players currently <b>not</b> connected to this proxy.</p>
* <p>For any player currently connected to this proxy this will be filled automatically.</p>
* <p>Will ignore mismatching key revisions data.</p>
*
* @param playerKey key to set
* @return {@code this}, for chaining

Datei anzeigen

@ -18,7 +18,6 @@ import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import org.checkerframework.checker.nullness.qual.Nullable;
/**

Datei anzeigen

@ -11,7 +11,6 @@ import java.time.Duration;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import org.checkerframework.common.value.qual.IntRange;
import org.jetbrains.annotations.NotNull;

Datei anzeigen

@ -5,57 +5,86 @@
<!--
Checkstyle configuration that checks the Google coding conventions from Google Java Style
that can be found at https://google.github.io/styleguide/javaguide.html.
that can be found at https://google.github.io/styleguide/javaguide.html
Checkstyle is very configurable. Be sure to read the documentation at
http://checkstyle.sf.net (or in your downloaded distribution).
http://checkstyle.org (or in your downloaded distribution).
To completely disable a check, just comment it out or delete it from the file.
To suppress certain violations please review suppression filters.
Authors: Max Vetrenko, Ruslan Diachenko, Roman Ivanov.
-->
<!--
Developer notes: Fix MissingJavadocMethod/Type RequireEmptyLineBeforeBlockTagGroup and CustomImportOrder
-->
<module name = "Checker">
<property name="charset" value="UTF-8"/>
<property name="severity" value="warning"/>
<property name="fileExtensions" value="java, properties, xml"/>
<!-- Excludes all 'module-info.java' files -->
<!-- See https://checkstyle.org/config_filefilters.html -->
<module name="BeforeExecutionExclusionFileFilter">
<property name="fileNamePattern" value="module\-info\.java$"/>
</module>
<!-- https://checkstyle.org/config_filters.html#SuppressionFilter -->
<module name="SuppressionFilter">
<property name="file" value="${org.checkstyle.google.suppressionfilter.config}"
default="checkstyle-suppressions.xml" />
<property name="optional" value="true"/>
</module>
<!-- Checks for whitespace -->
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
<!-- See http://checkstyle.org/config_whitespace.html -->
<module name="FileTabCharacter">
<property name="eachLine" value="true"/>
</module>
<module name="LineLength">
<property name="fileExtensions" value="java"/>
<property name="max" value="120"/>
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
</module>
<module name="TreeWalker">
<module name="OuterTypeFilename"/>
<!-- <module name="IllegalTokenText">
<module name="IllegalTokenText">
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
<property name="format"
value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
<property name="message"
value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
</module> -->
</module>
<module name="AvoidEscapedUnicodeCharacters">
<property name="allowEscapesForControlCharacters" value="true"/>
<property name="allowByTailComment" value="true"/>
<property name="allowNonPrintableEscapes" value="true"/>
</module>
<module name="LineLength">
<property name="max" value="120"/>
<property name="ignorePattern"
value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
</module>
<module name="AvoidStarImport"/>
<module name="OneTopLevelClass"/>
<module name="NoLineWrap"/>
<module name="NoLineWrap">
<property name="tokens" value="PACKAGE_DEF, IMPORT, STATIC_IMPORT"/>
</module>
<module name="EmptyBlock">
<property name="option" value="TEXT"/>
<property name="tokens"
value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
</module>
<module name="NeedBraces"/>
<module name="LeftCurly"/>
<module name="NeedBraces">
<property name="tokens"
value="LITERAL_DO, LITERAL_ELSE, LITERAL_FOR, LITERAL_IF, LITERAL_WHILE"/>
</module>
<module name="LeftCurly">
<property name="tokens"
value="ANNOTATION_DEF, CLASS_DEF, CTOR_DEF, ENUM_CONSTANT_DEF, ENUM_DEF,
INTERFACE_DEF, LAMBDA, LITERAL_CASE, LITERAL_CATCH, LITERAL_DEFAULT,
LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF,
LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, METHOD_DEF,
OBJBLOCK, STATIC_INIT, RECORD_DEF, COMPACT_CTOR_DEF"/>
</module>
<module name="RightCurly">
<property name="id" value="RightCurlySame"/>
<property name="tokens"
@ -67,15 +96,40 @@
<property name="option" value="alone"/>
<property name="tokens"
value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT,
INSTANCE_INIT"/>
INSTANCE_INIT, ANNOTATION_DEF, ENUM_DEF, INTERFACE_DEF, RECORD_DEF,
COMPACT_CTOR_DEF"/>
</module>
<module name="SuppressionXpathSingleFilter">
<!-- suppresion is required till https://github.com/checkstyle/checkstyle/issues/7541 -->
<property name="id" value="RightCurlyAlone"/>
<property name="query" value="//RCURLY[parent::SLIST[count(./*)=1]
or preceding-sibling::*[last()][self::LCURLY]]"/>
</module>
<module name="WhitespaceAfter">
<property name="tokens"
value="COMMA, SEMI, TYPECAST, LITERAL_IF, LITERAL_ELSE, LITERAL_RETURN,
LITERAL_WHILE, LITERAL_DO, LITERAL_FOR, LITERAL_FINALLY, DO_WHILE, ELLIPSIS,
LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_CATCH, LAMBDA,
LITERAL_YIELD, LITERAL_CASE"/>
</module>
<module name="WhitespaceAround">
<property name="allowEmptyConstructors" value="true"/>
<property name="allowEmptyLambdas" value="true"/>
<property name="allowEmptyMethods" value="true"/>
<property name="allowEmptyTypes" value="true"/>
<property name="allowEmptyLoops" value="true"/>
<property name="ignoreEnhancedForColon" value="false"/>
<property name="tokens"
value="ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR,
BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, DO_WHILE, EQUAL, GE, GT, LAMBDA, LAND,
LCURLY, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY,
LITERAL_FOR, LITERAL_IF, LITERAL_RETURN, LITERAL_SWITCH, LITERAL_SYNCHRONIZED,
LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN,
NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, RCURLY, SL, SLIST, SL_ASSIGN, SR,
SR_ASSIGN, STAR, STAR_ASSIGN, LITERAL_ASSERT, TYPE_EXTENSION_AND"/>
<message key="ws.notFollowed"
value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks
may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
<message key="ws.notPreceded"
value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
</module>
@ -87,6 +141,10 @@
<module name="UpperEll"/>
<module name="ModifierOrder"/>
<module name="EmptyLineSeparator">
<property name="tokens"
value="PACKAGE_DEF, IMPORT, STATIC_IMPORT, CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
STATIC_INIT, INSTANCE_INIT, METHOD_DEF, CTOR_DEF, VARIABLE_DEF, RECORD_DEF,
COMPACT_CTOR_DEF"/>
<property name="allowNoEmptyLineBetweenFields" value="true"/>
</module>
<module name="SeparatorWrap">
@ -100,13 +158,13 @@
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<!-- ELLIPSIS is EOL until https://github.com/google/styleguide/issues/258 -->
<!-- ELLIPSIS is EOL until https://github.com/google/styleguide/issues/259 -->
<property name="id" value="SeparatorWrapEllipsis"/>
<property name="tokens" value="ELLIPSIS"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<!-- ARRAY_DECLARATOR is EOL until https://github.com/google/styleguide/issues/259 -->
<!-- ARRAY_DECLARATOR is EOL until https://github.com/google/styleguide/issues/258 -->
<property name="id" value="SeparatorWrapArrayDeclarator"/>
<property name="tokens" value="ARRAY_DECLARATOR"/>
<property name="option" value="EOL"/>
@ -122,6 +180,8 @@
value="Package name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="TypeName">
<property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
ANNOTATION_DEF, RECORD_DEF"/>
<message key="name.invalidPattern"
value="Type name ''{0}'' must match pattern ''{1}''."/>
</module>
@ -146,16 +206,30 @@
value="Catch parameter name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="LocalVariableName">
<property name="tokens" value="VARIABLE_DEF"/>
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Local variable name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="PatternVariableName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Pattern variable name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="ClassTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern"
value="Class type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="RecordComponentName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Record component name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="RecordTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern"
value="Record type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MethodTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern"
@ -179,7 +253,7 @@
</module>
<module name="Indentation">
<property name="basicOffset" value="2"/>
<property name="braceAdjustment" value="0"/>
<property name="braceAdjustment" value="2"/>
<property name="caseIndent" value="2"/>
<property name="throwsIndent" value="4"/>
<property name="lineWrappingIndentation" value="4"/>
@ -188,31 +262,51 @@
<module name="AbbreviationAsWordInName">
<property name="ignoreFinal" value="false"/>
<property name="allowedAbbreviationLength" value="1"/>
<property name="tokens"
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, ANNOTATION_DEF, ANNOTATION_FIELD_DEF,
PARAMETER_DEF, VARIABLE_DEF, METHOD_DEF, PATTERN_VARIABLE_DEF, RECORD_DEF,
RECORD_COMPONENT_DEF"/>
</module>
<module name="NoWhitespaceBeforeCaseDefaultColon"/>
<module name="OverloadMethodsDeclarationOrder"/>
<module name="VariableDeclarationUsageDistance"/>
<module name="CustomImportOrder">
<property name="sortImportsInGroupAlphabetically" value="true"/>
<property name="separateLineBetweenGroups" value="true"/>
<property name="customImportOrderRules" value="STATIC###THIRD_PARTY_PACKAGE"/>
<property name="tokens" value="IMPORT, STATIC_IMPORT, PACKAGE_DEF"/>
</module>
<module name="MethodParamPad">
<property name="tokens"
value="CTOR_DEF, LITERAL_NEW, METHOD_CALL, METHOD_DEF,
SUPER_CTOR_CALL, ENUM_CONSTANT_DEF, RECORD_DEF"/>
</module>
<module name="MethodParamPad"/>
<module name="NoWhitespaceBefore">
<property name="tokens"
value="COMMA, SEMI, POST_INC, POST_DEC, DOT, ELLIPSIS, METHOD_REF"/>
value="COMMA, SEMI, POST_INC, POST_DEC, DOT,
LABELED_STAT, METHOD_REF"/>
<property name="allowLineBreaks" value="true"/>
</module>
<module name="ParenPad"/>
<module name="ParenPad">
<property name="tokens"
value="ANNOTATION, ANNOTATION_FIELD_DEF, CTOR_CALL, CTOR_DEF, DOT, ENUM_CONSTANT_DEF,
EXPR, LITERAL_CATCH, LITERAL_DO, LITERAL_FOR, LITERAL_IF, LITERAL_NEW,
LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_WHILE, METHOD_CALL,
METHOD_DEF, QUESTION, RESOURCE_SPECIFICATION, SUPER_CTOR_CALL, LAMBDA,
RECORD_DEF"/>
</module>
<module name="OperatorWrap">
<property name="option" value="NL"/>
<property name="tokens"
value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR,
LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF "/>
LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF,
TYPE_EXTENSION_AND "/>
</module>
<module name="AnnotationLocation">
<property name="id" value="AnnotationLocationMostCases"/>
<property name="tokens"
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/>
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF,
RECORD_DEF, COMPACT_CTOR_DEF"/>
</module>
<module name="AnnotationLocation">
<property name="id" value="AnnotationLocationVariables"/>
@ -220,37 +314,59 @@
<property name="allowSamelineMultipleAnnotations" value="true"/>
</module>
<module name="NonEmptyAtclauseDescription"/>
<module name="InvalidJavadocPosition"/>
<module name="JavadocTagContinuationIndentation"/>
<module name="SummaryJavadoc">
<property name="forbiddenSummaryFragments"
value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
</module>
<module name="JavadocParagraph"/>
<!-- <module name="RequireEmptyLineBeforeBlockTagGroup"/>
-->
<module name="AtclauseOrder">
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
<property name="target"
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
</module>
<module name="JavadocMethod">
<property name="scope" value="public"/>
<property name="accessModifiers" value="public"/>
<property name="allowMissingParamTags" value="true"/>
<property name="allowMissingThrowsTags" value="true"/>
<property name="allowMissingReturnTag" value="true"/>
<property name="allowedAnnotations" value="Override, Test"/>
<property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF, COMPACT_CTOR_DEF"/>
</module>
<!-- <module name="MissingJavadocMethod">
<property name="scope" value="public"/>
<property name="minLineCount" value="2"/>
<property name="allowedAnnotations" value="Override, Test"/>
<property name="allowThrowsTagsForSubclasses" value="true"/>
<property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF,
COMPACT_CTOR_DEF"/>
</module>
<module name="MissingJavadocType">
<property name="scope" value="protected"/>
<property name="tokens"
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
RECORD_DEF, ANNOTATION_DEF"/>
<property name="excludeScope" value="nothing"/>
</module>
-->
<module name="MethodName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/>
<message key="name.invalidPattern"
value="Method name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="SingleLineJavadoc">
<property name="ignoreInlineTags" value="false"/>
</module>
<module name="SingleLineJavadoc"/>
<module name="EmptyCatchBlock">
<property name="exceptionVariableName" value="expected"/>
</module>
<module name="CommentsIndentation"/>
<module name="CommentsIndentation">
<property name="tokens" value="SINGLE_LINE_COMMENT, BLOCK_COMMENT_BEGIN"/>
</module>
<!-- https://checkstyle.org/config_filters.html#SuppressionXpathFilter -->
<module name="SuppressionXpathFilter">
<property name="file" value="${org.checkstyle.google.suppressionxpathfilter.config}"
default="checkstyle-xpath-suppressions.xml" />
<property name="optional" value="true"/>
</module>
</module>
</module>

Datei anzeigen

@ -1,5 +1,5 @@
checkstyle {
toolVersion '8.14'
toolVersion '10.3.1'
configFile new File(project.rootDir, ['config', 'checkstyle', 'checkstyle.xml'].join(File.separator))
// The build should immediately fail if we have errors.

Datei anzeigen

@ -18,7 +18,6 @@
package com.velocitypowered.proxy;
import com.velocitypowered.proxy.config.VelocityConfiguration;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
@ -26,7 +25,6 @@ import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bstats.MetricsBase;

Datei anzeigen

@ -318,9 +318,6 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
}
commandManager.setAnnounceProxyCommands(configuration.isAnnounceProxyCommands());
if (System.getProperty("auth.forceSecureProfiles") == null) {
System.setProperty("auth.forceSecureProfiles", String.valueOf(configuration.isForceKeyAuthentication()));
}
} catch (Exception e) {
logger.error("Unable to read/load/save your velocity.toml. The server will shut down.", e);
LogManager.shutdown();

Datei anzeigen

@ -24,7 +24,6 @@ import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import com.velocitypowered.api.command.BrigadierCommand;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.proxy.VelocityServer;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;

Datei anzeigen

@ -19,7 +19,6 @@ package com.velocitypowered.proxy.command.builtin;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.gson.JsonArray;
@ -36,7 +35,6 @@ import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.util.ProxyVersion;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.util.InformationUtils;
import java.net.ConnectException;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
@ -47,7 +45,6 @@ import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;

Datei anzeigen

@ -28,7 +28,6 @@ import com.google.gson.annotations.Expose;
import com.velocitypowered.api.proxy.config.ProxyConfig;
import com.velocitypowered.api.util.Favicon;
import com.velocitypowered.proxy.util.AddressUtil;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.File;
import java.io.IOException;
@ -503,6 +502,14 @@ public class VelocityConfiguration implements ProxyConfig {
}
forwardingSecret = forwardingSecretString.getBytes(StandardCharsets.UTF_8);
if (configVersion == 1.0 || configVersion == 2.0) {
config.set("force-key-authentication", config.getOrElse("force-key-authentication", true));
config.setComment("force-key-authentication",
"Should the proxy enforce the new public key security standard? By default, this is on.");
config.set("config-version", configVersion == 2.0 ? "2.5" : "1.5");
mustResave = true;
}
// Handle any cases where the config needs to be saved again
if (mustResave) {
config.save();

Datei anzeigen

@ -48,6 +48,7 @@ import com.velocitypowered.proxy.protocol.packet.TabCompleteRequest;
import com.velocitypowered.proxy.protocol.packet.TabCompleteResponse;
import com.velocitypowered.proxy.protocol.packet.chat.LegacyChat;
import com.velocitypowered.proxy.protocol.packet.chat.PlayerChat;
import com.velocitypowered.proxy.protocol.packet.chat.PlayerChatCompletion;
import com.velocitypowered.proxy.protocol.packet.chat.PlayerChatPreview;
import com.velocitypowered.proxy.protocol.packet.chat.PlayerCommand;
import com.velocitypowered.proxy.protocol.packet.chat.ServerChatPreview;
@ -263,6 +264,10 @@ public interface MinecraftSessionHandler {
return false;
}
default boolean handle(PlayerChatCompletion packet) {
return false;
}
default boolean handle(ServerData serverData) {
return false;
}

Datei anzeigen

@ -26,6 +26,8 @@ public class VelocityConstants {
public static final String VELOCITY_IP_FORWARDING_CHANNEL = "velocity:player_info";
public static final int MODERN_FORWARDING_DEFAULT = 1;
public static final int MODERN_FORWARDING_WITH_KEY = 2;
public static final int MODERN_FORWARDING_WITH_KEY_V2 = 3;
public static final int MODERN_FORWARDING_MAX_VERSION = MODERN_FORWARDING_WITH_KEY_V2;
public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
}

Datei anzeigen

@ -55,7 +55,6 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.handler.timeout.ReadTimeoutException;
import java.util.regex.Pattern;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -281,7 +280,7 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
this.playerConnection.write(
new ServerData(pingEvent.getPing().getDescriptionComponent(),
pingEvent.getPing().getFavicon().orElse(null),
packet.isPreviewsChat())
packet.isPreviewsChat(), packet.isSecureChatEnforced())
), playerConnection.eventLoop());
return true;
}

Datei anzeigen

@ -29,6 +29,7 @@ import com.velocitypowered.proxy.config.VelocityConfiguration;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.VelocityConstants;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.connection.util.ConnectionRequestResults;
import com.velocitypowered.proxy.connection.util.ConnectionRequestResults.Impl;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
@ -81,17 +82,13 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
if (configuration.getPlayerInfoForwardingMode() == PlayerInfoForwarding.MODERN
&& packet.getChannel().equals(VelocityConstants.VELOCITY_IP_FORWARDING_CHANNEL)) {
int proposedForwardingVersion = VelocityConstants.MODERN_FORWARDING_DEFAULT;
int requestedForwardingVersion = VelocityConstants.MODERN_FORWARDING_DEFAULT;
// Check version
if (packet.content().readableBytes() == 1) {
int requested = packet.content().readByte();
Preconditions.checkArgument(requested >= VelocityConstants.MODERN_FORWARDING_DEFAULT,
"Invalid modern forwarding version");
proposedForwardingVersion = Math.min(requested, VelocityConstants.MODERN_FORWARDING_WITH_KEY);
requestedForwardingVersion = packet.content().readByte();
}
ByteBuf forwardingData = createForwardingData(configuration.getForwardingSecret(),
serverConn.getPlayerRemoteAddressAsString(), serverConn.getPlayer().getGameProfile(),
serverConn.getPlayer().getIdentifiedKey(), proposedForwardingVersion);
serverConn.getPlayerRemoteAddressAsString(), serverConn.getPlayer(), requestedForwardingVersion);
LoginPluginResponse response = new LoginPluginResponse(packet.getId(), true, forwardingData);
mc.write(response);
@ -176,24 +173,61 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
}
}
private static int findForwardingVersion(int requested, ConnectedPlayer player) {
// Ensure we are in range
requested = Math.min(requested, VelocityConstants.MODERN_FORWARDING_MAX_VERSION);
if (requested > VelocityConstants.MODERN_FORWARDING_DEFAULT) {
if (player.getIdentifiedKey() != null) {
// No enhanced switch on java 11
switch (player.getIdentifiedKey().getKeyRevision()) {
case GENERIC_V1:
return VelocityConstants.MODERN_FORWARDING_WITH_KEY;
// Since V2 is not backwards compatible we have to throw the key if v2 and requested is v1
case LINKED_V2:
return requested >= VelocityConstants.MODERN_FORWARDING_WITH_KEY_V2
? VelocityConstants.MODERN_FORWARDING_WITH_KEY_V2 : VelocityConstants.MODERN_FORWARDING_DEFAULT;
default:
return VelocityConstants.MODERN_FORWARDING_DEFAULT;
}
} else {
return VelocityConstants.MODERN_FORWARDING_DEFAULT;
}
}
return VelocityConstants.MODERN_FORWARDING_DEFAULT;
}
private static ByteBuf createForwardingData(byte[] hmacSecret, String address,
GameProfile profile, @Nullable IdentifiedKey playerKey,
int requestedVersion) {
ConnectedPlayer player, int requestedVersion) {
ByteBuf forwarded = Unpooled.buffer(2048);
try {
int actualVersion = requestedVersion >= VelocityConstants.MODERN_FORWARDING_WITH_KEY
? (playerKey != null ? requestedVersion : VelocityConstants.MODERN_FORWARDING_DEFAULT) : requestedVersion;
int actualVersion = findForwardingVersion(requestedVersion, player);
ProtocolUtils.writeVarInt(forwarded, actualVersion);
ProtocolUtils.writeString(forwarded, address);
ProtocolUtils.writeUuid(forwarded, profile.getId());
ProtocolUtils.writeString(forwarded, profile.getName());
ProtocolUtils.writeProperties(forwarded, profile.getProperties());
ProtocolUtils.writeUuid(forwarded, player.getGameProfile().getId());
ProtocolUtils.writeString(forwarded, player.getGameProfile().getName());
ProtocolUtils.writeProperties(forwarded, player.getGameProfile().getProperties());
// This serves as additional redundancy. The key normally is stored in the
// login start to the server, but some setups require this.
if (actualVersion >= VelocityConstants.MODERN_FORWARDING_WITH_KEY) {
ProtocolUtils.writePlayerKey(forwarded, playerKey);
IdentifiedKey key = player.getIdentifiedKey();
assert key != null;
ProtocolUtils.writePlayerKey(forwarded, key);
// Provide the signer UUID since the UUID may differ from the
// assigned UUID. Doing that breaks the signatures anyway but the server
// should be able to verify the key independently.
if (actualVersion >= VelocityConstants.MODERN_FORWARDING_WITH_KEY_V2) {
if (key.getSignatureHolder() != null) {
forwarded.writeBoolean(true);
ProtocolUtils.writeUuid(forwarded, key.getSignatureHolder());
} else {
// Should only not be provided if the player was connected
// as offline-mode and the signer UUID was not backfilled
forwarded.writeBoolean(false);
}
}
}
SecretKey key = new SecretKeySpec(hmacSecret, "HmacSHA256");

Datei anzeigen

@ -27,6 +27,7 @@ import com.velocitypowered.api.event.permission.PermissionsSetupEvent;
import com.velocitypowered.api.event.player.GameProfileRequestEvent;
import com.velocitypowered.api.event.player.PlayerChooseInitialServerEvent;
import com.velocitypowered.api.permission.PermissionFunction;
import com.velocitypowered.api.proxy.crypto.IdentifiedKey;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.api.util.UuidUtils;
@ -35,10 +36,12 @@ import com.velocitypowered.proxy.config.PlayerInfoForwarding;
import com.velocitypowered.proxy.config.VelocityConfiguration;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.crypto.IdentifiedKeyImpl;
import com.velocitypowered.proxy.protocol.StateRegistry;
import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess;
import com.velocitypowered.proxy.protocol.packet.SetCompression;
import io.netty.buffer.ByteBuf;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@ -131,6 +134,32 @@ public class AuthSessionHandler implements MinecraftSessionHandler {
if (configuration.getPlayerInfoForwardingMode() == PlayerInfoForwarding.NONE) {
playerUniqueId = UuidUtils.generateOfflinePlayerUuid(player.getUsername());
}
if (player.getIdentifiedKey() != null) {
IdentifiedKey playerKey = player.getIdentifiedKey();
if (playerKey.getSignatureHolder() == null) {
if (playerKey instanceof IdentifiedKeyImpl) {
IdentifiedKeyImpl unlinkedKey = (IdentifiedKeyImpl) playerKey;
// Failsafe
if (!unlinkedKey.internalAddHolder(player.getUniqueId())) {
if (onlineMode) {
inbound.disconnect(Component.translatable("multiplayer.disconnect.invalid_public_key"));
return;
} else {
logger.warn("Key for player " + player.getUsername() + " could not be verified!");
}
}
} else {
logger.warn("A custom key type has been set for player " + player.getUsername());
}
} else {
if (!Objects.equals(playerKey.getSignatureHolder(), playerUniqueId)) {
logger.warn("UUID for Player " + player.getUsername() + " mismatches! "
+ "Chat/Commands signatures will not work correctly for this player!");
}
}
}
ServerLoginSuccess success = new ServerLoginSuccess();
success.setUsername(player.getUsername());
success.setProperties(player.getGameProfileProperties());

Datei anzeigen

@ -32,6 +32,7 @@ import com.velocitypowered.api.event.player.PlayerChatEvent;
import com.velocitypowered.api.event.player.PlayerClientBrandEvent;
import com.velocitypowered.api.event.player.TabCompleteEvent;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.crypto.IdentifiedKey;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
@ -67,7 +68,6 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.util.ReferenceCountUtil;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
@ -170,15 +170,39 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
if (chatResult.isAllowed()) {
Optional<String> eventMsg = pme.getResult().getMessage();
if (eventMsg.isPresent()) {
if (player.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_19) >= 0
&& player.getIdentifiedKey() != null) {
String messageNew = eventMsg.get();
if (player.getIdentifiedKey() != null) {
if (!messageNew.equals(signedMessage.getMessage())) {
if (player.getIdentifiedKey().getKeyRevision().compareTo(IdentifiedKey.Revision.LINKED_V2) >= 0) {
// Bad, very bad.
logger.fatal("A plugin tried to change a signed chat message. "
+ "This is no longer possible in 1.19.1 and newer. "
+ "Disconnecting player " + player.getUsername());
player.disconnect(Component.text("A proxy plugin caused an illegal protocol state. "
+ "Contact your network administrator."));
} else {
logger.warn("A plugin changed a signed chat message. The server may not accept it.");
}
smc.write(ChatBuilder.builder(player.getProtocolVersion())
.message(event.getMessage()).toServer());
.message(messageNew).toServer());
}
} else {
smc.write(original);
}
} else {
smc.write(ChatBuilder.builder(player.getProtocolVersion())
.message(messageNew).toServer());
}
} else {
smc.write(original);
}
} else {
if (player.getIdentifiedKey().getKeyRevision().compareTo(IdentifiedKey.Revision.LINKED_V2) >= 0) {
logger.fatal("A plugin tried to cancel a signed chat message."
+ " This is no longer possible in 1.19.1 and newer. "
+ "Disconnecting player " + player.getUsername());
player.disconnect(Component.text("A proxy plugin caused an illegal protocol state. "
+ "Contact your network administrator."));
}
}
}, smc.eventLoop())
.exceptionally((ex) -> {
@ -702,7 +726,16 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
private CompletableFuture<Void> processCommandExecuteResult(String originalCommand,
CommandResult result,
@Nullable SignedChatCommand signedCommand) {
if (result == CommandResult.denied()) {
IdentifiedKey playerKey = player.getIdentifiedKey();
if (result == CommandResult.denied() && playerKey != null) {
if (signedCommand != null && playerKey.getKeyRevision()
.compareTo(IdentifiedKey.Revision.LINKED_V2) >= 0) {
logger.fatal("A plugin tried to deny a command with signable component(s). "
+ "This is not supported. "
+ "Disconnecting player " + player.getUsername());
player.disconnect(Component.text("A proxy plugin caused an illegal protocol state. "
+ "Contact your network administrator."));
}
return CompletableFuture.completedFuture(null);
}
@ -724,6 +757,15 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
if (signedCommand != null && commandToRun.equals(signedCommand.getBaseCommand())) {
write.message(signedCommand);
} else {
if (signedCommand != null && playerKey != null && playerKey.getKeyRevision()
.compareTo(IdentifiedKey.Revision.LINKED_V2) >= 0) {
logger.fatal("A plugin tried to change a command with signed component(s). "
+ "This is not supported. "
+ "Disconnecting player " + player.getUsername());
player.disconnect(Component.text("A proxy plugin caused an illegal protocol state. "
+ "Contact your network administrator."));
return CompletableFuture.completedFuture(null);
}
write.message("/" + commandToRun);
}
return CompletableFuture.runAsync(() -> smc.write(write.toServer()), smc.eventLoop());
@ -738,6 +780,15 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
if (signedCommand != null && commandToRun.equals(signedCommand.getBaseCommand())) {
write.message(signedCommand);
} else {
if (signedCommand != null && playerKey != null && playerKey.getKeyRevision()
.compareTo(IdentifiedKey.Revision.LINKED_V2) >= 0) {
logger.fatal("A plugin tried to change a command with signed component(s). "
+ "This is not supported. "
+ "Disconnecting player " + player.getUsername());
player.disconnect(Component.text("A proxy plugin caused an illegal protocol state. "
+ "Contact your network administrator."));
return;
}
write.message("/" + commandToRun);
}
smc.write(write.toServer());
@ -746,6 +797,10 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
}
}
private void handleCommandForward() {
}
/**
* Immediately send any queued messages to the server.
*/

Datei anzeigen

@ -1068,7 +1068,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
}
@Override
public IdentifiedKey getIdentifiedKey() {
public @Nullable IdentifiedKey getIdentifiedKey() {
return playerKey;
}

Datei anzeigen

@ -25,14 +25,19 @@ import static com.velocitypowered.proxy.crypto.EncryptionUtils.generateServerId;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Longs;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.stream.JsonReader;
import com.velocitypowered.api.event.connection.PreLoginEvent;
import com.velocitypowered.api.event.connection.PreLoginEvent.PreLoginComponentResult;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.crypto.IdentifiedKey;
import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.api.util.UuidUtils;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.crypto.IdentifiedKeyImpl;
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
import com.velocitypowered.proxy.protocol.packet.EncryptionRequest;
import com.velocitypowered.proxy.protocol.packet.EncryptionResponse;
@ -45,6 +50,7 @@ import java.security.KeyPair;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadLocalRandom;
import net.kyori.adventure.text.Component;
@ -54,6 +60,7 @@ import org.apache.logging.log4j.Logger;
import org.asynchttpclient.ListenableFuture;
import org.asynchttpclient.Response;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
public class InitialLoginSessionHandler implements MinecraftSessionHandler {
@ -75,7 +82,8 @@ public class InitialLoginSessionHandler implements MinecraftSessionHandler {
this.server = Preconditions.checkNotNull(server, "server");
this.mcConnection = Preconditions.checkNotNull(mcConnection, "mcConnection");
this.inbound = Preconditions.checkNotNull(inbound, "inbound");
this.forceKeyAuthentication = Boolean.getBoolean("auth.forceSecureProfiles");
this.forceKeyAuthentication = System.getProperties().containsKey("auth.forceSecureProfiles")
? Boolean.getBoolean("auth.forceSecureProfiles") : server.getConfiguration().isForceKeyAuthentication();
}
@Override
@ -89,7 +97,16 @@ public class InitialLoginSessionHandler implements MinecraftSessionHandler {
return true;
}
if (!playerKey.isSignatureValid()) {
boolean isKeyValid;
if (playerKey.getKeyRevision() == IdentifiedKey.Revision.LINKED_V2
&& playerKey instanceof IdentifiedKeyImpl) {
IdentifiedKeyImpl keyImpl = (IdentifiedKeyImpl) playerKey;
isKeyValid = keyImpl.internalAddHolder(packet.getHolderUuid());
} else {
isKeyValid = playerKey.isSignatureValid();
}
if (!isKeyValid) {
inbound.disconnect(Component.translatable("multiplayer.disconnect.invalid_public_key"));
return true;
}
@ -214,9 +231,19 @@ public class InitialLoginSessionHandler implements MinecraftSessionHandler {
try {
Response profileResponse = hasJoinedResponse.get();
if (profileResponse.getStatusCode() == 200) {
final GameProfile profile = GENERAL_GSON.fromJson(profileResponse.getResponseBody(), GameProfile.class);
// Not so fast, now we verify the public key for 1.19.1+
if (inbound.getIdentifiedKey() != null
&& inbound.getIdentifiedKey().getKeyRevision() == IdentifiedKey.Revision.LINKED_V2
&& inbound.getIdentifiedKey() instanceof IdentifiedKeyImpl) {
IdentifiedKeyImpl key = (IdentifiedKeyImpl) inbound.getIdentifiedKey();
if (!key.internalAddHolder(profile.getId())) {
inbound.disconnect(Component.translatable("multiplayer.disconnect.invalid_public_key"));
}
}
// All went well, initialize the session.
mcConnection.setSessionHandler(new AuthSessionHandler(
server, inbound, GENERAL_GSON.fromJson(profileResponse.getResponseBody(), GameProfile.class), true
server, inbound, profile, true
));
} else if (profileResponse.getStatusCode() == 204) {
// Apparently an offline-mode user logged onto this online-mode proxy.

Datei anzeigen

@ -20,11 +20,9 @@ package com.velocitypowered.proxy.connection.registry;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.velocitypowered.api.network.ProtocolVersion;
import java.util.Map;
import java.util.Set;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.nbt.BinaryTagTypes;
import net.kyori.adventure.nbt.CompoundBinaryTag;

Datei anzeigen

@ -27,7 +27,6 @@ import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.util.ClosestLocaleMatcher;
import java.util.List;
import java.util.Locale;
import net.kyori.adventure.audience.MessageType;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.permission.PermissionChecker;

Datei anzeigen

@ -21,7 +21,6 @@ import com.google.common.base.Preconditions;
import com.google.common.io.ByteStreams;
import com.velocitypowered.proxy.util.except.QuietDecoderException;
import it.unimi.dsi.fastutil.Pair;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;

Datei anzeigen

@ -18,29 +18,37 @@
package com.velocitypowered.proxy.crypto;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.proxy.crypto.IdentifiedKey;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;
import java.time.Instant;
import java.util.Arrays;
import java.util.UUID;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
public class IdentifiedKeyImpl implements IdentifiedKey {
private final Revision revision;
private final PublicKey publicKey;
private final byte[] signature;
private final Instant expiryTemporal;
private @MonotonicNonNull Boolean isSignatureValid;
private @MonotonicNonNull UUID holder;
public IdentifiedKeyImpl(byte[] keyBits, long expiry,
public IdentifiedKeyImpl(Revision revision, byte[] keyBits, long expiry,
byte[] signature) {
this(EncryptionUtils.parseRsaPublicKey(keyBits), Instant.ofEpochMilli(expiry), signature);
this(revision, EncryptionUtils.parseRsaPublicKey(keyBits), Instant.ofEpochMilli(expiry), signature);
}
/**
* Creates an Identified key from data.
*/
public IdentifiedKeyImpl(PublicKey publicKey, Instant expiryTemporal, byte[] signature) {
public IdentifiedKeyImpl(Revision revision, PublicKey publicKey, Instant expiryTemporal, byte[] signature) {
this.revision = revision;
this.publicKey = publicKey;
this.expiryTemporal = expiryTemporal;
this.signature = signature;
@ -63,19 +71,68 @@ public class IdentifiedKeyImpl implements IdentifiedKey {
@Override
public byte[] getSignature() {
return signature;
return signature.clone();
}
@Override
public @Nullable UUID getSignatureHolder() {
return holder;
}
@Override
public Revision getKeyRevision() {
return revision;
}
/**
* Sets the uuid for this key.
* Returns false if incorrect.
*/
public boolean internalAddHolder(UUID holder) {
if (holder == null) {
return false;
}
if (this.holder == null) {
Boolean result = validateData(holder);
if (result == null || !result) {
return false;
}
isSignatureValid = true;
this.holder = holder;
return true;
}
return this.holder.equals(holder) && isSignatureValid();
}
@Override
public boolean isSignatureValid() {
if (isSignatureValid == null) {
isSignatureValid = validateData(holder);
}
return isSignatureValid != null && isSignatureValid;
}
private Boolean validateData(@Nullable UUID verify) {
if (revision == Revision.GENERIC_V1) {
String pemKey = EncryptionUtils.pemEncodeRsaKey(publicKey);
long expires = expiryTemporal.toEpochMilli();
byte[] toVerify = ("" + expires + pemKey).getBytes(StandardCharsets.US_ASCII);
isSignatureValid = EncryptionUtils.verifySignature(
return EncryptionUtils.verifySignature(
EncryptionUtils.SHA1_WITH_RSA, EncryptionUtils.getYggdrasilSessionKey(), signature, toVerify);
} else {
if (verify == null) {
return null;
}
byte[] keyBytes = publicKey.getEncoded();
byte[] toVerify = new byte[keyBytes.length + 24]; // length long * 3
ByteBuffer fixedDataSet = ByteBuffer.wrap(toVerify).order(ByteOrder.BIG_ENDIAN);
fixedDataSet.putLong(verify.getMostSignificantBits());
fixedDataSet.putLong(verify.getLeastSignificantBits());
fixedDataSet.putLong(expiryTemporal.toEpochMilli());
fixedDataSet.put(keyBytes);
return EncryptionUtils.verifySignature(EncryptionUtils.SHA1_WITH_RSA,
EncryptionUtils.getYggdrasilSessionKey(), signature, toVerify);
}
return isSignatureValid;
}
@Override
@ -90,10 +147,12 @@ public class IdentifiedKeyImpl implements IdentifiedKey {
@Override
public String toString() {
return "IdentifiedKeyImpl{"
+ "publicKey=" + publicKey
+ "revision=" + revision
+ ", publicKey=" + publicKey
+ ", signature=" + Arrays.toString(signature)
+ ", expiryTemporal=" + expiryTemporal
+ ", isSignatureValid=" + isSignatureValid
+ ", holder=" + holder
+ '}';
}

Datei anzeigen

@ -0,0 +1,49 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.crypto;
import java.util.Arrays;
import java.util.UUID;
public class SignaturePair {
private final UUID signer;
private final byte[] signature;
public SignaturePair(UUID signer, byte[] signature) {
this.signer = signer;
this.signature = signature;
}
public byte[] getSignature() {
return signature;
}
public UUID getSigner() {
return signer;
}
@Override
public String toString() {
return "SignaturePair{"
+ "signer=" + signer
+ ", signature=" + Arrays.toString(signature)
+ '}';
}
}

Datei anzeigen

@ -35,12 +35,17 @@ public class SignedChatCommand implements KeySigned {
private final boolean isPreviewSigned;
private final Map<String, byte[]> signatures;
private final SignaturePair[] previousSignatures;
private final @Nullable SignaturePair lastSignature;
/**
* Create a signed command from data.
*/
public SignedChatCommand(String command, PublicKey signer, UUID sender,
Instant expiry, Map<String, byte[]> signature, byte[] salt, boolean isPreviewSigned) {
Instant expiry, Map<String, byte[]> signature, byte[] salt,
boolean isPreviewSigned, SignaturePair[] previousSignatures,
@Nullable SignaturePair lastSignature) {
this.command = Preconditions.checkNotNull(command);
this.signer = Preconditions.checkNotNull(signer);
this.sender = Preconditions.checkNotNull(sender);
@ -48,6 +53,8 @@ public class SignedChatCommand implements KeySigned {
this.expiry = Preconditions.checkNotNull(expiry);
this.salt = Preconditions.checkNotNull(salt);
this.isPreviewSigned = isPreviewSigned;
this.previousSignatures = previousSignatures;
this.lastSignature = lastSignature;
}
@ -83,4 +90,12 @@ public class SignedChatCommand implements KeySigned {
public boolean isPreviewSigned() {
return isPreviewSigned;
}
public SignaturePair getLastSignature() {
return lastSignature;
}
public SignaturePair[] getPreviousSignatures() {
return previousSignatures;
}
}

Datei anzeigen

@ -18,18 +18,13 @@
package com.velocitypowered.proxy.crypto;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Longs;
import com.velocitypowered.api.proxy.crypto.SignedMessage;
import com.velocitypowered.proxy.util.except.QuietDecoderException;
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.PublicKey;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.TemporalAmount;
import java.util.UUID;
import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
public class SignedChatMessage implements SignedMessage {
@ -46,13 +41,17 @@ public class SignedChatMessage implements SignedMessage {
private final byte[] salt;
private final UUID sender;
//private final boolean isValid;
private final SignaturePair[] previousSignatures;
private final @Nullable SignaturePair previousSignature;
private final boolean isPreviewSigned;
/**
* Create a signed message from data.
*/
public SignedChatMessage(String message, PublicKey signer, UUID sender,
Instant expiry, byte[] signature, byte[] salt, boolean isPreviewSigned) {
Instant expiry, byte[] signature, byte[] salt,
boolean isPreviewSigned, @Nullable SignaturePair[] previousSignatures,
@Nullable SignaturePair previousSignature) {
this.message = Preconditions.checkNotNull(message);
this.signer = Preconditions.checkNotNull(signer);
this.sender = Preconditions.checkNotNull(sender);
@ -60,7 +59,8 @@ public class SignedChatMessage implements SignedMessage {
this.expiry = Preconditions.checkNotNull(expiry);
this.salt = Preconditions.checkNotNull(salt);
this.isPreviewSigned = isPreviewSigned;
this.previousSignatures = previousSignatures;
this.previousSignature = previousSignature;
//this.isValid = EncryptionUtils.verifySignature(EncryptionUtils.SHA1_WITH_RSA, signer,
// signature, salt, EncryptionUtils.longToBigEndianByteArray(
@ -83,6 +83,14 @@ public class SignedChatMessage implements SignedMessage {
return signature;
}
public SignaturePair[] getPreviousSignatures() {
return previousSignatures;
}
public SignaturePair getPreviousSignature() {
return previousSignature;
}
//@Override
//public boolean isSignatureValid() {
// return isValid;

Datei anzeigen

@ -18,7 +18,6 @@
package com.velocitypowered.proxy.plugin;
import com.velocitypowered.proxy.Velocity;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

Datei anzeigen

@ -34,7 +34,6 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.handler.codec.CorruptedFrameException;
import io.netty.handler.codec.DecoderException;
import io.netty.handler.codec.EncoderException;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
@ -42,7 +41,6 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import net.kyori.adventure.nbt.BinaryTagIO;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
@ -557,11 +555,13 @@ public enum ProtocolUtils {
* @param buf the buffer
* @return the key
*/
public static IdentifiedKey readPlayerKey(ByteBuf buf) {
public static IdentifiedKey readPlayerKey(ProtocolVersion version, ByteBuf buf) {
long expiry = buf.readLong();
byte[] key = ProtocolUtils.readByteArray(buf);
byte[] signature = ProtocolUtils.readByteArray(buf, 4096);
return new IdentifiedKeyImpl(key, expiry, signature);
IdentifiedKey.Revision revision = version.compareTo(ProtocolVersion.MINECRAFT_1_19) == 0
? IdentifiedKey.Revision.GENERIC_V1 : IdentifiedKey.Revision.LINKED_V2;
return new IdentifiedKeyImpl(revision, key, expiry, signature);
}
public enum Direction {

Datei anzeigen

@ -30,6 +30,7 @@ import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_17;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_18;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_18_2;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_19;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_19_1;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_7_2;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9;
@ -67,6 +68,7 @@ import com.velocitypowered.proxy.protocol.packet.TabCompleteRequest;
import com.velocitypowered.proxy.protocol.packet.TabCompleteResponse;
import com.velocitypowered.proxy.protocol.packet.chat.LegacyChat;
import com.velocitypowered.proxy.protocol.packet.chat.PlayerChat;
import com.velocitypowered.proxy.protocol.packet.chat.PlayerChatCompletion;
import com.velocitypowered.proxy.protocol.packet.chat.PlayerCommand;
import com.velocitypowered.proxy.protocol.packet.chat.SystemChat;
import com.velocitypowered.proxy.protocol.packet.title.LegacyTitlePacket;
@ -121,7 +123,8 @@ public enum StateRegistry {
map(0x01, MINECRAFT_1_12_1, false),
map(0x05, MINECRAFT_1_13, false),
map(0x06, MINECRAFT_1_14, false),
map(0x08, MINECRAFT_1_19, false));
map(0x08, MINECRAFT_1_19, false),
map(0x09, MINECRAFT_1_19_1, false));
serverbound.register(LegacyChat.class, LegacyChat::new,
map(0x01, MINECRAFT_1_7_2, false),
map(0x02, MINECRAFT_1_9, false),
@ -129,16 +132,19 @@ public enum StateRegistry {
map(0x02, MINECRAFT_1_12_1, false),
map(0x03, MINECRAFT_1_14, MINECRAFT_1_18_2, false));
serverbound.register(PlayerCommand.class, PlayerCommand::new,
map(0x03, MINECRAFT_1_19, false));
map(0x03, MINECRAFT_1_19, false),
map(0x04, MINECRAFT_1_19_1, false));
serverbound.register(PlayerChat.class, PlayerChat::new,
map(0x04, MINECRAFT_1_19, false));
map(0x04, MINECRAFT_1_19, false),
map(0x05, MINECRAFT_1_19_1, false));
serverbound.register(ClientSettings.class, ClientSettings::new,
map(0x15, MINECRAFT_1_7_2, false),
map(0x04, MINECRAFT_1_9, false),
map(0x05, MINECRAFT_1_12, false),
map(0x04, MINECRAFT_1_12_1, false),
map(0x05, MINECRAFT_1_14, false),
map(0x07, MINECRAFT_1_19, false));
map(0x07, MINECRAFT_1_19, false),
map(0x08, MINECRAFT_1_19_1, false));
serverbound.register(PluginMessage.class, PluginMessage::new,
map(0x17, MINECRAFT_1_7_2, false),
map(0x09, MINECRAFT_1_9, false),
@ -147,7 +153,8 @@ public enum StateRegistry {
map(0x0A, MINECRAFT_1_13, false),
map(0x0B, MINECRAFT_1_14, false),
map(0x0A, MINECRAFT_1_17, false),
map(0x0C, MINECRAFT_1_19, false));
map(0x0C, MINECRAFT_1_19, false),
map(0x0D, MINECRAFT_1_19_1, false));
serverbound.register(KeepAlive.class, KeepAlive::new,
map(0x00, MINECRAFT_1_7_2, false),
map(0x0B, MINECRAFT_1_9, false),
@ -157,7 +164,8 @@ public enum StateRegistry {
map(0x0F, MINECRAFT_1_14, false),
map(0x10, MINECRAFT_1_16, false),
map(0x0F, MINECRAFT_1_17, false),
map(0x11, MINECRAFT_1_19, false));
map(0x11, MINECRAFT_1_19, false),
map(0x12, MINECRAFT_1_19_1, false));
serverbound.register(ResourcePackResponse.class, ResourcePackResponse::new,
map(0x19, MINECRAFT_1_8, false),
map(0x16, MINECRAFT_1_9, false),
@ -166,7 +174,8 @@ public enum StateRegistry {
map(0x1F, MINECRAFT_1_14, false),
map(0x20, MINECRAFT_1_16, false),
map(0x21, MINECRAFT_1_16_2, false),
map(0x23, MINECRAFT_1_19, false));
map(0x23, MINECRAFT_1_19, false),
map(0x24, MINECRAFT_1_19_1, false));
clientbound.register(BossBar.class, BossBar::new,
map(0x0C, MINECRAFT_1_9, false),
@ -206,7 +215,8 @@ public enum StateRegistry {
map(0x18, MINECRAFT_1_16, false),
map(0x17, MINECRAFT_1_16_2, false),
map(0x18, MINECRAFT_1_17, false),
map(0x15, MINECRAFT_1_19, false));
map(0x15, MINECRAFT_1_19, false),
map(0x16, MINECRAFT_1_19_1, false));
clientbound.register(Disconnect.class, Disconnect::new,
map(0x40, MINECRAFT_1_7_2, false),
map(0x1A, MINECRAFT_1_9, false),
@ -216,7 +226,8 @@ public enum StateRegistry {
map(0x1A, MINECRAFT_1_16, false),
map(0x19, MINECRAFT_1_16_2, false),
map(0x1A, MINECRAFT_1_17, false),
map(0x17, MINECRAFT_1_19, false));
map(0x17, MINECRAFT_1_19, false),
map(0x19, MINECRAFT_1_19_1, false));
clientbound.register(KeepAlive.class, KeepAlive::new,
map(0x00, MINECRAFT_1_7_2, false),
map(0x1F, MINECRAFT_1_9, false),
@ -226,7 +237,8 @@ public enum StateRegistry {
map(0x20, MINECRAFT_1_16, false),
map(0x1F, MINECRAFT_1_16_2, false),
map(0x21, MINECRAFT_1_17, false),
map(0x1E, MINECRAFT_1_19, false));
map(0x1E, MINECRAFT_1_19, false),
map(0x20, MINECRAFT_1_19_1, false));
clientbound.register(JoinGame.class, JoinGame::new,
map(0x01, MINECRAFT_1_7_2, false),
map(0x23, MINECRAFT_1_9, false),
@ -236,7 +248,8 @@ public enum StateRegistry {
map(0x25, MINECRAFT_1_16, false),
map(0x24, MINECRAFT_1_16_2, false),
map(0x26, MINECRAFT_1_17, false),
map(0x23, MINECRAFT_1_19, false));
map(0x23, MINECRAFT_1_19, false),
map(0x25, MINECRAFT_1_19_1, false));
clientbound.register(Respawn.class, Respawn::new,
map(0x07, MINECRAFT_1_7_2, true),
map(0x33, MINECRAFT_1_9, true),
@ -248,7 +261,8 @@ public enum StateRegistry {
map(0x3A, MINECRAFT_1_16, true),
map(0x39, MINECRAFT_1_16_2, true),
map(0x3D, MINECRAFT_1_17, true),
map(0x3B, MINECRAFT_1_19, true));
map(0x3B, MINECRAFT_1_19, true),
map(0x3E, MINECRAFT_1_19_1, true));
clientbound.register(ResourcePackRequest.class, ResourcePackRequest::new,
map(0x48, MINECRAFT_1_8, false),
map(0x32, MINECRAFT_1_9, false),
@ -260,7 +274,8 @@ public enum StateRegistry {
map(0x39, MINECRAFT_1_16, false),
map(0x38, MINECRAFT_1_16_2, false),
map(0x3C, MINECRAFT_1_17, false),
map(0x3A, MINECRAFT_1_19, false));
map(0x3A, MINECRAFT_1_19, false),
map(0x3D, MINECRAFT_1_19_1, false));
clientbound.register(HeaderAndFooter.class, HeaderAndFooter::new,
map(0x47, MINECRAFT_1_8, true),
map(0x48, MINECRAFT_1_9, true),
@ -273,7 +288,8 @@ public enum StateRegistry {
map(0x53, MINECRAFT_1_16, true),
map(0x5E, MINECRAFT_1_17, true),
map(0x5F, MINECRAFT_1_18, true),
map(0x60, MINECRAFT_1_19, true));
map(0x60, MINECRAFT_1_19, true),
map(0x63, MINECRAFT_1_19_1, true));
clientbound.register(LegacyTitlePacket.class, LegacyTitlePacket::new,
map(0x45, MINECRAFT_1_8, true),
map(0x45, MINECRAFT_1_9, true),
@ -285,16 +301,20 @@ public enum StateRegistry {
map(0x4F, MINECRAFT_1_16, MINECRAFT_1_16_4, true));
clientbound.register(TitleSubtitlePacket.class, TitleSubtitlePacket::new,
map(0x57, MINECRAFT_1_17, true),
map(0x58, MINECRAFT_1_18, true));
map(0x58, MINECRAFT_1_18, true),
map(0x5B, MINECRAFT_1_19_1, true));
clientbound.register(TitleTextPacket.class, TitleTextPacket::new,
map(0x59, MINECRAFT_1_17, true),
map(0x5A, MINECRAFT_1_18, true));
map(0x5A, MINECRAFT_1_18, true),
map(0x5D, MINECRAFT_1_19_1, true));
clientbound.register(TitleActionbarPacket.class, TitleActionbarPacket::new,
map(0x41, MINECRAFT_1_17, true),
map(0x40, MINECRAFT_1_19, true));
map(0x40, MINECRAFT_1_19, true),
map(0x43, MINECRAFT_1_19_1, true));
clientbound.register(TitleTimesPacket.class, TitleTimesPacket::new,
map(0x5A, MINECRAFT_1_17, true),
map(0x5B, MINECRAFT_1_18, true));
map(0x5B, MINECRAFT_1_18, true),
map(0x5E, MINECRAFT_1_19_1, true));
clientbound.register(TitleClearPacket.class, TitleClearPacket::new,
map(0x10, MINECRAFT_1_17, true),
map(0x0D, MINECRAFT_1_19, true));
@ -308,11 +328,16 @@ public enum StateRegistry {
map(0x33, MINECRAFT_1_16, false),
map(0x32, MINECRAFT_1_16_2, false),
map(0x36, MINECRAFT_1_17, false),
map(0x34, MINECRAFT_1_19, false));
map(0x34, MINECRAFT_1_19, false),
map(0x37, MINECRAFT_1_19_1, false));
clientbound.register(SystemChat.class, SystemChat::new,
map(0x5F, MINECRAFT_1_19, true));
map(0x5F, MINECRAFT_1_19, true),
map(0x62, MINECRAFT_1_19_1, true));
clientbound.register(PlayerChatCompletion.class, PlayerChatCompletion::new,
StateRegistry.map(0x15, MINECRAFT_1_19_1, true));
clientbound.register(ServerData.class, ServerData::new,
map(0x3F, MINECRAFT_1_19, false));
map(0x3F, MINECRAFT_1_19, false),
map(0x42, MINECRAFT_1_19_1, false));
}
},
LOGIN {

Datei anzeigen

@ -46,7 +46,6 @@ import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.apache.logging.log4j.LogManager;
public class GS4QueryHandler extends SimpleChannelInboundHandler<DatagramPacket> {

Datei anzeigen

@ -78,7 +78,7 @@ public class PlayerListItem implements MinecraftPacket {
if (version.compareTo(ProtocolVersion.MINECRAFT_1_19) >= 0) {
if (buf.readBoolean()) {
item.setPlayerKey(ProtocolUtils.readPlayerKey(buf));
item.setPlayerKey(ProtocolUtils.readPlayerKey(version, buf));
}
}
break;

Datei anzeigen

@ -31,15 +31,17 @@ public class ServerData implements MinecraftPacket {
private @Nullable Component description;
private @Nullable Favicon favicon;
private boolean previewsChat;
private boolean secureChatEnforced; // Added in 1.19.1
public ServerData() {
}
public ServerData(@Nullable Component description, @Nullable Favicon favicon,
boolean previewsChat) {
boolean previewsChat, boolean secureChatEnforced) {
this.description = description;
this.favicon = favicon;
this.previewsChat = previewsChat;
this.secureChatEnforced = secureChatEnforced;
}
@Override
@ -53,6 +55,9 @@ public class ServerData implements MinecraftPacket {
this.favicon = new Favicon(ProtocolUtils.readString(buf));
}
this.previewsChat = buf.readBoolean();
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_19_1) >= 0) {
this.secureChatEnforced = buf.readBoolean();
}
}
@Override
@ -73,6 +78,9 @@ public class ServerData implements MinecraftPacket {
}
buf.writeBoolean(this.previewsChat);
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_19_1) >= 0) {
buf.writeBoolean(this.secureChatEnforced);
}
}
@Override
@ -91,4 +99,8 @@ public class ServerData implements MinecraftPacket {
public boolean isPreviewsChat() {
return previewsChat;
}
public boolean isSecureChatEnforced() {
return secureChatEnforced;
}
}

Datei anzeigen

@ -28,12 +28,15 @@ import com.velocitypowered.proxy.util.except.QuietDecoderException;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.UUID;
public class ServerLogin implements MinecraftPacket {
private static final QuietDecoderException EMPTY_USERNAME = new QuietDecoderException("Empty username!");
private @Nullable String username;
private @Nullable IdentifiedKey playerKey; // Introduced in 1.19
private @Nullable UUID holderUuid; // Used for key revision 2
public ServerLogin() {
}
@ -58,6 +61,10 @@ public class ServerLogin implements MinecraftPacket {
this.playerKey = playerKey;
}
public UUID getHolderUuid() {
return holderUuid;
}
@Override
public String toString() {
return "ServerLogin{"
@ -75,7 +82,13 @@ public class ServerLogin implements MinecraftPacket {
if (version.compareTo(ProtocolVersion.MINECRAFT_1_19) >= 0) {
if (buf.readBoolean()) {
playerKey = ProtocolUtils.readPlayerKey(buf);
playerKey = ProtocolUtils.readPlayerKey(version, buf);
}
if (version.compareTo(ProtocolVersion.MINECRAFT_1_19_1) >= 0) {
if (buf.readBoolean()) {
holderUuid = ProtocolUtils.readUuid(buf);
}
}
}
}
@ -94,6 +107,15 @@ public class ServerLogin implements MinecraftPacket {
} else {
buf.writeBoolean(false);
}
if (version.compareTo(ProtocolVersion.MINECRAFT_1_19_1) >= 0) {
if (playerKey != null && playerKey.getSignatureHolder() != null) {
buf.writeBoolean(true);
ProtocolUtils.writeUuid(buf, playerKey.getSignatureHolder());
} else {
buf.writeBoolean(false);
}
}
}
}
@ -102,9 +124,20 @@ public class ServerLogin implements MinecraftPacket {
// Accommodate the rare (but likely malicious) use of UTF-8 usernames, since it is technically
// legal on the protocol level.
int base = 1 + (16 * 4);
// Adjustments for Key-authentication
if (version.compareTo(ProtocolVersion.MINECRAFT_1_19) >= 0) {
return -1;
//TODO: ## 19
// + 1 for the boolean present/ not present
// + 8 for the long expiry
// + 2 len for varint key size
// + 294 for the key
// + 2 len for varint signature size
// + 512 for signature
base += 1 + 8 + 2 + 294 + 2 + 512;
if (version.compareTo(ProtocolVersion.MINECRAFT_1_19_1) >= 0) {
// +1 boolean uuid optional
// + 2 * 8 for the long msb/lsb
base += 1 + 8 + 8;
}
}
return base;
}

Datei anzeigen

@ -20,7 +20,6 @@ package com.velocitypowered.proxy.protocol.packet.brigadier;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.velocitypowered.api.network.ProtocolVersion;
import java.util.HashMap;
import java.util.Map;
import org.checkerframework.checker.nullness.qual.Nullable;

Datei anzeigen

@ -23,7 +23,6 @@ import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

Datei anzeigen

@ -19,7 +19,6 @@ package com.velocitypowered.proxy.protocol.packet.brigadier;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
public class RegistryKeyArgumentSerializer implements ArgumentPropertySerializer<RegistryKeyArgument> {

Datei anzeigen

@ -133,7 +133,7 @@ public class ChatBuilder {
if (version.compareTo(ProtocolVersion.MINECRAFT_1_19) >= 0) {
// hard override chat > system for now
return new SystemChat(msg, type == ChatType.CHAT ? ChatType.SYSTEM.getId() : type.getId());
return new SystemChat(msg, type == ChatType.CHAT ? ChatType.SYSTEM : type);
} else {
return new LegacyChat(ProtocolUtils.getJsonChatSerializer(version).serialize(msg), type.getId(), identity);
}

Datei anzeigen

@ -22,9 +22,11 @@ import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.crypto.IdentifiedKey;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.crypto.EncryptionUtils;
import com.velocitypowered.proxy.crypto.SignaturePair;
import com.velocitypowered.proxy.crypto.SignedChatMessage;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.util.except.QuietDecoderException;
import io.netty.buffer.ByteBuf;
import java.time.Instant;
import java.util.UUID;
@ -38,6 +40,13 @@ public class PlayerChat implements MinecraftPacket {
private @Nullable Instant expiry;
private @Nullable byte[] signature;
private @Nullable byte[] salt;
private SignaturePair[] previousMessages = new SignaturePair[0];
private @Nullable SignaturePair lastMessage;
public static final int MAXIMUM_PREVIOUS_MESSAGE_COUNT = 5;
public static final QuietDecoderException INVALID_PREVIOUS_MESSAGES =
new QuietDecoderException("Invalid previous messages");
public PlayerChat() {
}
@ -58,6 +67,8 @@ public class PlayerChat implements MinecraftPacket {
this.salt = message.getSalt();
this.signature = message.getSignature();
this.signedPreview = message.isPreviewSigned();
this.lastMessage = message.getPreviousSignature();
this.previousMessages = message.getPreviousSignatures();
}
public Instant getExpiry() {
@ -98,6 +109,23 @@ public class PlayerChat implements MinecraftPacket {
if (signedPreview && unsigned) {
throw EncryptionUtils.PREVIEW_SIGNATURE_MISSING;
}
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_19_1) >= 0) {
int size = ProtocolUtils.readVarInt(buf);
if (size < 0 || size > MAXIMUM_PREVIOUS_MESSAGE_COUNT) {
throw INVALID_PREVIOUS_MESSAGES;
}
SignaturePair[] lastSignatures = new SignaturePair[size];
for (int i = 0; i < size; i++) {
lastSignatures[i] = new SignaturePair(ProtocolUtils.readUuid(buf), ProtocolUtils.readByteArray(buf));
}
previousMessages = lastSignatures;
if (buf.readBoolean()) {
lastMessage = new SignaturePair(ProtocolUtils.readUuid(buf), ProtocolUtils.readByteArray(buf));
}
}
}
@Override
@ -109,6 +137,23 @@ public class PlayerChat implements MinecraftPacket {
ProtocolUtils.writeByteArray(buf, unsigned ? EncryptionUtils.EMPTY : signature);
buf.writeBoolean(signedPreview);
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_19_1) >= 0) {
ProtocolUtils.writeVarInt(buf, previousMessages.length);
for (SignaturePair previousMessage : previousMessages) {
ProtocolUtils.writeUuid(buf, previousMessage.getSigner());
ProtocolUtils.writeByteArray(buf, previousMessage.getSignature());
}
if (lastMessage != null) {
buf.writeBoolean(true);
ProtocolUtils.writeUuid(buf, lastMessage.getSigner());
ProtocolUtils.writeByteArray(buf, lastMessage.getSignature());
} else {
buf.writeBoolean(false);
}
}
}
/**
@ -129,7 +174,8 @@ public class PlayerChat implements MinecraftPacket {
return null;
}
return new SignedChatMessage(message, signer.getSignedPublicKey(), sender, expiry, signature, salt, signedPreview);
return new SignedChatMessage(message, signer.getSignedPublicKey(), sender, expiry, signature,
salt, signedPreview, previousMessages, lastMessage);
}
@Override

Datei anzeigen

@ -0,0 +1,70 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.protocol.packet.chat;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
public class PlayerChatCompletion implements MinecraftPacket {
private String[] completions;
private Action action;
public String[] getCompletions() {
return completions;
}
public Action getAction() {
return action;
}
public void setCompletions(String[] completions) {
this.completions = completions;
}
public void setAction(Action action) {
this.action = action;
}
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
action = Action.values()[ProtocolUtils.readVarInt(buf)];
completions = ProtocolUtils.readStringArray(buf);
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
ProtocolUtils.writeVarInt(buf, action.ordinal());
ProtocolUtils.writeStringArray(buf, completions);
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
enum Action {
ADD,
REMOVE,
ALTER
}
}

Datei anzeigen

@ -17,12 +17,16 @@
package com.velocitypowered.proxy.protocol.packet.chat;
import static com.velocitypowered.proxy.protocol.packet.chat.PlayerChat.INVALID_PREVIOUS_MESSAGES;
import static com.velocitypowered.proxy.protocol.packet.chat.PlayerChat.MAXIMUM_PREVIOUS_MESSAGE_COUNT;
import com.google.common.collect.ImmutableMap;
import com.google.common.primitives.Longs;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.crypto.IdentifiedKey;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.crypto.EncryptionUtils;
import com.velocitypowered.proxy.crypto.SignaturePair;
import com.velocitypowered.proxy.crypto.SignedChatCommand;
import com.velocitypowered.proxy.crypto.SignedChatMessage;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
@ -30,9 +34,11 @@ import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.util.except.QuietDecoderException;
import io.netty.buffer.ByteBuf;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.checkerframework.checker.nullness.qual.Nullable;
public class PlayerCommand implements MinecraftPacket {
@ -46,6 +52,8 @@ public class PlayerCommand implements MinecraftPacket {
private Instant timestamp;
private long salt;
private boolean signedPreview; // Good god. Please no.
private SignaturePair[] previousMessages = new SignaturePair[0];
private @Nullable SignaturePair lastMessage;
private Map<String, byte[]> arguments = ImmutableMap.of();
public boolean isSignedPreview() {
@ -96,6 +104,8 @@ public class PlayerCommand implements MinecraftPacket {
this.timestamp = signedCommand.getExpiryTemporal();
this.salt = Longs.fromByteArray(signedCommand.getSalt());
this.signedPreview = signedCommand.isPreviewSigned();
this.lastMessage = signedCommand.getLastSignature();
this.previousMessages = signedCommand.getPreviousSignatures();
}
@Override
@ -124,6 +134,24 @@ public class PlayerCommand implements MinecraftPacket {
if (unsigned && signedPreview) {
throw EncryptionUtils.PREVIEW_SIGNATURE_MISSING;
}
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_19_1) >= 0) {
int size = ProtocolUtils.readVarInt(buf);
if (size < 0 || size > MAXIMUM_PREVIOUS_MESSAGE_COUNT) {
throw INVALID_PREVIOUS_MESSAGES;
}
SignaturePair[] lastSignatures = new SignaturePair[size];
for (int i = 0; i < size; i++) {
lastSignatures[i] = new SignaturePair(ProtocolUtils.readUuid(buf), ProtocolUtils.readByteArray(buf));
}
previousMessages = lastSignatures;
if (buf.readBoolean()) {
lastMessage = new SignaturePair(ProtocolUtils.readUuid(buf), ProtocolUtils.readByteArray(buf));
}
}
}
@Override
@ -146,6 +174,22 @@ public class PlayerCommand implements MinecraftPacket {
buf.writeBoolean(signedPreview);
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_19_1) >= 0) {
ProtocolUtils.writeVarInt(buf, previousMessages.length);
for (SignaturePair previousMessage : previousMessages) {
ProtocolUtils.writeUuid(buf, previousMessage.getSigner());
ProtocolUtils.writeByteArray(buf, previousMessage.getSignature());
}
if (lastMessage != null) {
buf.writeBoolean(true);
ProtocolUtils.writeUuid(buf, lastMessage.getSigner());
ProtocolUtils.writeByteArray(buf, lastMessage.getSignature());
} else {
buf.writeBoolean(false);
}
}
}
/**
@ -158,8 +202,12 @@ public class PlayerCommand implements MinecraftPacket {
* @throws com.velocitypowered.proxy.util.except.QuietDecoderException when mustSign is {@code true} and the signature
* is invalid.
*/
public SignedChatCommand signedContainer(IdentifiedKey signer, UUID sender, boolean mustSign) {
if (unsigned) {
public SignedChatCommand signedContainer(
@Nullable IdentifiedKey signer, UUID sender, boolean mustSign) {
// There's a certain mod that is very broken that still signs messages but
// doesn't provide the player key. This is broken and wrong, but we need to
// work around that.
if (unsigned || signer == null) {
if (mustSign) {
throw EncryptionUtils.INVALID_SIGNATURE;
}
@ -167,7 +215,20 @@ public class PlayerCommand implements MinecraftPacket {
}
return new SignedChatCommand(command, signer.getSignedPublicKey(), sender, timestamp,
arguments, Longs.toByteArray(salt), signedPreview);
arguments, Longs.toByteArray(salt), signedPreview, previousMessages, lastMessage);
}
@Override
public String toString() {
return "PlayerCommand{"
+ "unsigned=" + unsigned
+ ", command='" + command + '\''
+ ", timestamp=" + timestamp
+ ", salt=" + salt
+ ", signedPreview=" + signedPreview
+ ", previousMessages=" + Arrays.toString(previousMessages)
+ ", arguments=" + arguments
+ '}';
}
@Override

Datei anzeigen

@ -28,15 +28,15 @@ public class SystemChat implements MinecraftPacket {
public SystemChat() {}
public SystemChat(Component component, int type) {
public SystemChat(Component component, ChatBuilder.ChatType type) {
this.component = component;
this.type = type;
}
private Component component;
private int type;
private ChatBuilder.ChatType type;
public int getType() {
public ChatBuilder.ChatType getType() {
return type;
}
@ -47,13 +47,27 @@ public class SystemChat implements MinecraftPacket {
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
component = ProtocolUtils.getJsonChatSerializer(protocolVersion).deserialize(ProtocolUtils.readString(buf));
type = ProtocolUtils.readVarInt(buf);
// System chat is never decoded so this doesn't matter for now
type = ChatBuilder.ChatType.values()[ProtocolUtils.readVarInt(buf)];
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
ProtocolUtils.writeString(buf, ProtocolUtils.getJsonChatSerializer(protocolVersion).serialize(component));
ProtocolUtils.writeVarInt(buf, type);
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_19_1) >= 0) {
switch (type) {
case SYSTEM:
buf.writeBoolean(false);
break;
case GAME_INFO:
buf.writeBoolean(true);
break;
default:
throw new IllegalArgumentException("Invalid chat type");
}
} else {
ProtocolUtils.writeVarInt(buf, type.getId());
}
}
@Override

Datei anzeigen

@ -38,7 +38,6 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.NonNull;

Datei anzeigen

@ -18,6 +18,7 @@
package com.velocitypowered.proxy.tablist;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.crypto.IdentifiedKey;
@ -226,8 +227,19 @@ public class VelocityTabList implements TabList {
if (entries.containsKey(entry.getProfile().getId())) {
PlayerListItem.Item packetItem = PlayerListItem.Item.from(entry);
IdentifiedKey selectedKey = packetItem.getPlayerKey();
Optional<Player> existing = proxyServer.getPlayer(entry.getProfile().getId());
existing.ifPresent(value -> packetItem.setPlayerKey(value.getIdentifiedKey()));
if (existing.isPresent()) {
selectedKey = existing.get().getIdentifiedKey();
}
if (selectedKey != null
&& selectedKey.getKeyRevision().getApplicableTo().contains(connection.getProtocolVersion())
&& Objects.equals(selectedKey.getSignatureHolder(), entry.getProfile().getId())) {
packetItem.setPlayerKey(selectedKey);
} else {
packetItem.setPlayerKey(null);
}
connection.write(new PlayerListItem(action, Collections.singletonList(packetItem)));
}

Datei anzeigen

@ -32,7 +32,6 @@ import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.config.ProxyConfig;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.util.ProxyVersion;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;

Datei anzeigen

@ -1,5 +1,5 @@
# Config version. Do not change this
config-version = "2.0"
config-version = "2.5"
# What port should the proxy be bound to? By default, we'll bind to all addresses on port 25577.
bind = "0.0.0.0:25577"

Datei anzeigen

@ -31,7 +31,6 @@ import com.velocitypowered.proxy.testutil.FakePluginManager;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DynamicNode;
import org.junit.jupiter.api.DynamicTest;

Datei anzeigen

@ -38,7 +38,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.protocol.packet.Handshake;
import com.velocitypowered.proxy.protocol.packet.StatusPing;
import org.junit.jupiter.api.Test;
class PacketRegistryTest {

Datei anzeigen

@ -22,13 +22,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import com.velocitypowered.api.scheduler.ScheduledTask;
import com.velocitypowered.api.scheduler.TaskStatus;
import com.velocitypowered.proxy.testutil.FakePluginManager;
import java.time.Duration;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.jupiter.api.Test;
class VelocitySchedulerTest {

Datei anzeigen

@ -22,7 +22,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import com.velocitypowered.proxy.crypto.EncryptionUtils;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import org.junit.jupiter.api.Test;
class EncryptionUtilsTest {