From 0e269fa493e31876059dcc446d2d34501ca03044 Mon Sep 17 00:00:00 2001 From: RK_01 <50594595+RaphiMC@users.noreply.github.com> Date: Sun, 22 Oct 2023 09:33:59 +0000 Subject: [PATCH] Implement support for chat signing (#3490) --- .../signature/model/DecoratableMessage.java | 60 ++++++++++++++ .../signature/model/MessageMetadata.java | 56 +++++++++++++ .../model/chain/v1_19_1/MessageBody.java | 82 +++++++++++++++++++ .../model/chain/v1_19_1/MessageHeader.java | 47 +++++++++++ .../model/chain/v1_19_3/MessageBody.java | 60 ++++++++++++++ .../model/chain/v1_19_3/MessageLink.java | 56 +++++++++++++ .../signature/storage/ChatSession.java | 78 ++++++++++++++++++ .../signature/storage/ChatSession1_19_0.java | 55 +++++++++++++ .../signature/storage/ChatSession1_19_1.java | 55 +++++++++++++ .../signature/storage/ChatSession1_19_3.java | 70 ++++++++++++++++ .../signature/util/DataConsumer.java | 39 +++++++++ .../viaversion/viaversion/util/GsonUtil.java | 57 ++++++++++++- .../Protocol1_19_1To1_19.java | 48 ++++++++--- .../Protocol1_19_3To1_19_1.java | 79 ++++++++++++++---- .../storage/NonceStorage.java | 34 ++++++++ 15 files changed, 847 insertions(+), 29 deletions(-) create mode 100644 api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/model/DecoratableMessage.java create mode 100644 api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/model/MessageMetadata.java create mode 100644 api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/model/chain/v1_19_1/MessageBody.java create mode 100644 api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/model/chain/v1_19_1/MessageHeader.java create mode 100644 api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/model/chain/v1_19_3/MessageBody.java create mode 100644 api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/model/chain/v1_19_3/MessageLink.java create mode 100644 api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/storage/ChatSession.java create mode 100644 api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/storage/ChatSession1_19_0.java create mode 100644 api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/storage/ChatSession1_19_1.java create mode 100644 api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/storage/ChatSession1_19_3.java create mode 100644 api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/util/DataConsumer.java create mode 100644 common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19_3to1_19_1/storage/NonceStorage.java diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/model/DecoratableMessage.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/model/DecoratableMessage.java new file mode 100644 index 000000000..b8bdcb2b0 --- /dev/null +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/model/DecoratableMessage.java @@ -0,0 +1,60 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2023-2023 ViaVersion and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.viaversion.viaversion.api.minecraft.signature.model; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +public class DecoratableMessage { + + private final String plain; + private final JsonElement decorated; + + public DecoratableMessage(final String plain) { + this(plain, createLiteralText(plain)); + } + + public DecoratableMessage(final String plain, final JsonElement decorated) { + this.plain = plain; + this.decorated = decorated; + } + + public String plain() { + return this.plain; + } + + public JsonElement decorated() { + return this.decorated; + } + + public boolean isDecorated() { + return !this.decorated.equals(createLiteralText(plain)); + } + + private static JsonElement createLiteralText(final String text) { + final JsonObject object = new JsonObject(); + object.addProperty("text", text); + return object; + } + +} diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/model/MessageMetadata.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/model/MessageMetadata.java new file mode 100644 index 000000000..4d200b8f6 --- /dev/null +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/model/MessageMetadata.java @@ -0,0 +1,56 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2023-2023 ViaVersion and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.viaversion.viaversion.api.minecraft.signature.model; + +import java.time.Instant; +import java.util.UUID; + +public class MessageMetadata { + + private final UUID sender; + private final Instant timestamp; + private final long salt; + + public MessageMetadata(final UUID sender, final Instant timestamp, final long salt) { + this.sender = sender; + this.timestamp = timestamp; + this.salt = salt; + } + + public MessageMetadata(final UUID sender, final long timestamp, final long salt) { + this(sender, Instant.ofEpochMilli(timestamp), salt); + } + + public UUID sender() { + return this.sender; + } + + public Instant timestamp() { + return this.timestamp; + } + + public long salt() { + return this.salt; + } + +} diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/model/chain/v1_19_1/MessageBody.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/model/chain/v1_19_1/MessageBody.java new file mode 100644 index 000000000..1a28ff4fe --- /dev/null +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/model/chain/v1_19_1/MessageBody.java @@ -0,0 +1,82 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2023-2023 ViaVersion and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.viaversion.viaversion.api.minecraft.signature.model.chain.v1_19_1; + +import com.viaversion.viaversion.api.minecraft.PlayerMessageSignature; +import com.viaversion.viaversion.api.minecraft.signature.model.DecoratableMessage; +import com.viaversion.viaversion.api.minecraft.signature.util.DataConsumer; +import com.viaversion.viaversion.util.GsonUtil; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.time.Instant; + +public class MessageBody { + + private static final byte HASH_SEPARATOR_BYTE = 70; + + private final DecoratableMessage content; + private final Instant timestamp; + private final long salt; + private final PlayerMessageSignature[] lastSeenMessages; + + public MessageBody(final DecoratableMessage content, final Instant timestamp, final long salt, final PlayerMessageSignature[] lastSeenMessages) { + this.content = content; + this.timestamp = timestamp; + this.salt = salt; + this.lastSeenMessages = lastSeenMessages; + } + + public void update(final DataConsumer dataConsumer) { + try { + final MessageDigest digest = MessageDigest.getInstance("SHA-256"); + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + final DataOutputStream dataOutputStream = new DataOutputStream(outputStream); + dataOutputStream.writeLong(this.salt); + dataOutputStream.writeLong(this.timestamp.getEpochSecond()); + + dataOutputStream.write(this.content.plain().getBytes(StandardCharsets.UTF_8)); + dataOutputStream.write(HASH_SEPARATOR_BYTE); + if (this.content.isDecorated()) { + dataOutputStream.write(GsonUtil.toSortedString(this.content.decorated(), null).getBytes(StandardCharsets.UTF_8)); + } + + for (PlayerMessageSignature lastSeenMessage : this.lastSeenMessages) { + dataOutputStream.writeByte(HASH_SEPARATOR_BYTE); + dataOutputStream.writeLong(lastSeenMessage.uuid().getMostSignificantBits()); + dataOutputStream.writeLong(lastSeenMessage.uuid().getLeastSignificantBits()); + dataOutputStream.write(lastSeenMessage.signatureBytes()); + } + + digest.update(outputStream.toByteArray()); + dataConsumer.accept(digest.digest()); + } catch (NoSuchAlgorithmException | IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/model/chain/v1_19_1/MessageHeader.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/model/chain/v1_19_1/MessageHeader.java new file mode 100644 index 000000000..508f350c0 --- /dev/null +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/model/chain/v1_19_1/MessageHeader.java @@ -0,0 +1,47 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2023-2023 ViaVersion and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.viaversion.viaversion.api.minecraft.signature.model.chain.v1_19_1; + +import com.viaversion.viaversion.api.minecraft.signature.util.DataConsumer; + +import java.util.UUID; + +public class MessageHeader { + + private final byte[] precedingSignature; + private final UUID sender; + + public MessageHeader(final byte[] precedingSignature, final UUID sender) { + this.precedingSignature = precedingSignature; + this.sender = sender; + } + + public void update(final DataConsumer dataConsumer) { + if (this.precedingSignature != null) { + dataConsumer.accept(this.precedingSignature); + } + + dataConsumer.accept(this.sender); + } + +} diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/model/chain/v1_19_3/MessageBody.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/model/chain/v1_19_3/MessageBody.java new file mode 100644 index 000000000..15cfc8591 --- /dev/null +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/model/chain/v1_19_3/MessageBody.java @@ -0,0 +1,60 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2023-2023 ViaVersion and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.viaversion.viaversion.api.minecraft.signature.model.chain.v1_19_3; + +import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; +import com.viaversion.viaversion.api.minecraft.PlayerMessageSignature; +import com.viaversion.viaversion.api.minecraft.signature.util.DataConsumer; + +import java.nio.charset.StandardCharsets; +import java.time.Instant; + +public class MessageBody { + + private final String content; + private final Instant timestamp; + private final long salt; + private final PlayerMessageSignature[] lastSeenMessages; + + public MessageBody(final String content, final Instant timestamp, final long salt, final PlayerMessageSignature[] lastSeenMessages) { + this.content = content; + this.timestamp = timestamp; + this.salt = salt; + this.lastSeenMessages = lastSeenMessages; + } + + public void update(final DataConsumer dataConsumer) { + dataConsumer.accept(Longs.toByteArray(this.salt)); + dataConsumer.accept(Longs.toByteArray(this.timestamp.getEpochSecond())); + final byte[] contentData = this.content.getBytes(StandardCharsets.UTF_8); + dataConsumer.accept(Ints.toByteArray(contentData.length)); + dataConsumer.accept(contentData); + + dataConsumer.accept(Ints.toByteArray(this.lastSeenMessages.length)); + for (PlayerMessageSignature messageSignatureData : this.lastSeenMessages) { + dataConsumer.accept(messageSignatureData.signatureBytes()); + } + } + +} diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/model/chain/v1_19_3/MessageLink.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/model/chain/v1_19_3/MessageLink.java new file mode 100644 index 000000000..ad7b58691 --- /dev/null +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/model/chain/v1_19_3/MessageLink.java @@ -0,0 +1,56 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2023-2023 ViaVersion and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.viaversion.viaversion.api.minecraft.signature.model.chain.v1_19_3; + +import com.google.common.primitives.Ints; +import com.viaversion.viaversion.api.minecraft.signature.util.DataConsumer; + +import java.util.UUID; + +public class MessageLink { + + private final int index; + private final UUID sender; + private final UUID sessionId; + + public MessageLink(final UUID sender, final UUID sessionId) { + this(0, sender, sessionId); + } + + public MessageLink(final int index, final UUID sender, final UUID sessionId) { + this.index = index; + this.sender = sender; + this.sessionId = sessionId; + } + + public void update(final DataConsumer dataConsumer) { + dataConsumer.accept(this.sender); + dataConsumer.accept(this.sessionId); + dataConsumer.accept(Ints.toByteArray(this.index)); + } + + public MessageLink next() { + return this.index == Integer.MAX_VALUE ? null : new MessageLink(this.index + 1, this.sender, this.sessionId); + } + +} diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/storage/ChatSession.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/storage/ChatSession.java new file mode 100644 index 000000000..a189828d8 --- /dev/null +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/storage/ChatSession.java @@ -0,0 +1,78 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2023-2023 ViaVersion and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.viaversion.viaversion.api.minecraft.signature.storage; + +import com.viaversion.viaversion.api.connection.StorableObject; +import com.viaversion.viaversion.api.minecraft.ProfileKey; +import com.viaversion.viaversion.api.minecraft.signature.util.DataConsumer; + +import java.security.PrivateKey; +import java.security.Signature; +import java.security.SignatureException; +import java.util.Objects; +import java.util.UUID; +import java.util.function.Consumer; + +public class ChatSession implements StorableObject { + + private final UUID uuid; + private final PrivateKey privateKey; + private final ProfileKey profileKey; + private final Signature signer; + + public ChatSession(final UUID uuid, final PrivateKey privateKey, final ProfileKey profileKey) { + Objects.requireNonNull(uuid, "uuid"); + Objects.requireNonNull(privateKey, "privateKey"); + Objects.requireNonNull(profileKey, "profileKey"); + this.uuid = uuid; + this.privateKey = privateKey; + this.profileKey = profileKey; + + try { + this.signer = Signature.getInstance("SHA256withRSA"); + this.signer.initSign(this.privateKey); + } catch (Throwable e) { + throw new RuntimeException("Failed to initialize signature", e); + } + } + + public UUID getUuid() { + return this.uuid; + } + + public ProfileKey getProfileKey() { + return this.profileKey; + } + + public byte[] sign(final Consumer dataConsumer) throws SignatureException { + dataConsumer.accept(bytes -> { + try { + this.signer.update(bytes); + } catch (SignatureException e) { + throw new RuntimeException(e); + } + }); + return this.signer.sign(); + } + +} diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/storage/ChatSession1_19_0.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/storage/ChatSession1_19_0.java new file mode 100644 index 000000000..ad04f4872 --- /dev/null +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/storage/ChatSession1_19_0.java @@ -0,0 +1,55 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2023-2023 ViaVersion and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.viaversion.viaversion.api.minecraft.signature.storage; + +import com.viaversion.viaversion.api.minecraft.ProfileKey; +import com.viaversion.viaversion.api.minecraft.signature.model.DecoratableMessage; +import com.viaversion.viaversion.api.minecraft.signature.model.MessageMetadata; +import com.viaversion.viaversion.util.GsonUtil; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; +import java.security.PrivateKey; +import java.security.SignatureException; +import java.util.UUID; + +public class ChatSession1_19_0 extends ChatSession { + + public ChatSession1_19_0(UUID uuid, PrivateKey privateKey, ProfileKey profileKey) { + super(uuid, privateKey, profileKey); + } + + public byte[] signChatMessage(final MessageMetadata metadata, final DecoratableMessage content) throws SignatureException { + return this.sign(signer -> { + final byte[] data = new byte[32]; + final ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN); + buffer.putLong(metadata.salt()); + buffer.putLong(metadata.sender().getMostSignificantBits()).putLong(metadata.sender().getLeastSignificantBits()); + buffer.putLong(metadata.timestamp().getEpochSecond()); + signer.accept(data); + signer.accept(GsonUtil.toSortedString(content.decorated(), null).getBytes(StandardCharsets.UTF_8)); + }); + } + +} diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/storage/ChatSession1_19_1.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/storage/ChatSession1_19_1.java new file mode 100644 index 000000000..b69d8ceea --- /dev/null +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/storage/ChatSession1_19_1.java @@ -0,0 +1,55 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2023-2023 ViaVersion and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.viaversion.viaversion.api.minecraft.signature.storage; + +import com.viaversion.viaversion.api.minecraft.PlayerMessageSignature; +import com.viaversion.viaversion.api.minecraft.ProfileKey; +import com.viaversion.viaversion.api.minecraft.signature.model.DecoratableMessage; +import com.viaversion.viaversion.api.minecraft.signature.model.MessageMetadata; +import com.viaversion.viaversion.api.minecraft.signature.model.chain.v1_19_1.MessageBody; +import com.viaversion.viaversion.api.minecraft.signature.model.chain.v1_19_1.MessageHeader; + +import java.security.PrivateKey; +import java.security.SignatureException; +import java.util.UUID; + +public class ChatSession1_19_1 extends ChatSession { + + private byte[] precedingSignature; + + public ChatSession1_19_1(UUID uuid, PrivateKey privateKey, ProfileKey profileKey) { + super(uuid, privateKey, profileKey); + } + + public byte[] signChatMessage(final MessageMetadata metadata, final DecoratableMessage content, final PlayerMessageSignature[] lastSeenMessages) throws SignatureException { + final byte[] signature = this.sign(signer -> { + final MessageHeader messageHeader = new MessageHeader(this.precedingSignature, metadata.sender()); + final MessageBody messageBody = new MessageBody(content, metadata.timestamp(), metadata.salt(), lastSeenMessages); + messageHeader.update(signer); + messageBody.update(signer); + }); + this.precedingSignature = signature; + return signature; + } + +} diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/storage/ChatSession1_19_3.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/storage/ChatSession1_19_3.java new file mode 100644 index 000000000..346b29314 --- /dev/null +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/storage/ChatSession1_19_3.java @@ -0,0 +1,70 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2023-2023 ViaVersion and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.viaversion.viaversion.api.minecraft.signature.storage; + +import com.google.common.primitives.Ints; +import com.viaversion.viaversion.api.minecraft.PlayerMessageSignature; +import com.viaversion.viaversion.api.minecraft.ProfileKey; +import com.viaversion.viaversion.api.minecraft.signature.model.MessageMetadata; +import com.viaversion.viaversion.api.minecraft.signature.model.chain.v1_19_3.MessageBody; +import com.viaversion.viaversion.api.minecraft.signature.model.chain.v1_19_3.MessageLink; + +import java.security.PrivateKey; +import java.security.SignatureException; +import java.util.UUID; + +public class ChatSession1_19_3 extends ChatSession { + + private final UUID sessionId = UUID.randomUUID(); + private MessageLink link; + + public ChatSession1_19_3(UUID uuid, PrivateKey privateKey, ProfileKey profileKey) { + super(uuid, privateKey, profileKey); + + this.link = new MessageLink(uuid, this.sessionId); + } + + public byte[] signChatMessage(final MessageMetadata metadata, final String content, final PlayerMessageSignature[] lastSeenMessages) throws SignatureException { + return this.sign(signer -> { + final MessageLink messageLink = this.nextLink(); + final MessageBody messageBody = new MessageBody(content, metadata.timestamp(), metadata.salt(), lastSeenMessages); + signer.accept(Ints.toByteArray(1)); + messageLink.update(signer); + messageBody.update(signer); + }); + } + + private MessageLink nextLink() { + final MessageLink messageLink = this.link; + if (messageLink != null) { + this.link = messageLink.next(); + } + + return messageLink; + } + + public UUID getSessionId() { + return this.sessionId; + } + +} diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/util/DataConsumer.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/util/DataConsumer.java new file mode 100644 index 000000000..d0c96439b --- /dev/null +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/signature/util/DataConsumer.java @@ -0,0 +1,39 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2023-2023 ViaVersion and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.viaversion.viaversion.api.minecraft.signature.util; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.UUID; +import java.util.function.Consumer; + +@FunctionalInterface +public interface DataConsumer extends Consumer { + + default void accept(final UUID uuid) { + final byte[] serializedUuid = new byte[16]; + ByteBuffer.wrap(serializedUuid).order(ByteOrder.BIG_ENDIAN).putLong(uuid.getMostSignificantBits()).putLong(uuid.getLeastSignificantBits()); + this.accept(serializedUuid); + } + +} diff --git a/api/src/main/java/com/viaversion/viaversion/util/GsonUtil.java b/api/src/main/java/com/viaversion/viaversion/util/GsonUtil.java index b26886dcc..79012f69e 100644 --- a/api/src/main/java/com/viaversion/viaversion/util/GsonUtil.java +++ b/api/src/main/java/com/viaversion/viaversion/util/GsonUtil.java @@ -22,8 +22,12 @@ */ package com.viaversion.viaversion.util; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; +import com.google.gson.*; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; public final class GsonUtil { private static final Gson GSON = new GsonBuilder().create(); @@ -36,4 +40,53 @@ public final class GsonUtil { public static Gson getGson() { return GSON; } + + /** + * Convert a json element to a sorted string.
+ * If the {@code comparator} is null, {@link Comparator#naturalOrder()} will be used. + * + * @param element The element to convert + * @param comparator The comparator to use + * @return The sorted string + */ + public static String toSortedString(@Nullable final JsonElement element, @Nullable final Comparator comparator) { + if (element == null) { + return null; + } else if (comparator != null) { + return sort(element, comparator).toString(); + } else { + return sort(element, Comparator.naturalOrder()).toString(); + } + } + + /** + * Sort a json element. + * + * @param element The element to sort + * @param comparator The comparator to use + * @return The sorted element + */ + public static JsonElement sort(@Nullable final JsonElement element, final Comparator comparator) { + if (element == null) { + return null; + } else if (element.isJsonArray()) { + final JsonArray array = element.getAsJsonArray(); + for (int i = 0; i < array.size(); i++) { + array.set(i, sort(array.get(i), comparator)); + } + return array; + } else if (element.isJsonObject()) { + final JsonObject object = element.getAsJsonObject(); + final JsonObject sorted = new JsonObject(); + final List keys = new ArrayList<>(object.keySet()); + keys.sort(comparator); + for (String key : keys) { + sorted.add(key, sort(object.get(key), comparator)); + } + return sorted; + } else { + return element; + } + } + } diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19_1to1_19/Protocol1_19_1To1_19.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19_1to1_19/Protocol1_19_1To1_19.java index 323389eb3..ae7da3414 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19_1to1_19/Protocol1_19_1To1_19.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19_1to1_19/Protocol1_19_1To1_19.java @@ -18,17 +18,15 @@ package com.viaversion.viaversion.protocols.protocol1_19_1to1_19; import com.github.steveice10.opennbt.stringified.SNBT; -import com.github.steveice10.opennbt.tag.builtin.ByteTag; -import com.github.steveice10.opennbt.tag.builtin.CompoundTag; -import com.github.steveice10.opennbt.tag.builtin.ListTag; -import com.github.steveice10.opennbt.tag.builtin.NumberTag; -import com.github.steveice10.opennbt.tag.builtin.StringTag; -import com.github.steveice10.opennbt.tag.builtin.Tag; +import com.github.steveice10.opennbt.tag.builtin.*; import com.google.common.base.Preconditions; import com.google.gson.JsonElement; import com.viaversion.viaversion.api.Via; import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.minecraft.ProfileKey; +import com.viaversion.viaversion.api.minecraft.signature.model.DecoratableMessage; +import com.viaversion.viaversion.api.minecraft.signature.model.MessageMetadata; +import com.viaversion.viaversion.api.minecraft.signature.storage.ChatSession1_19_0; import com.viaversion.viaversion.api.protocol.AbstractProtocol; import com.viaversion.viaversion.api.protocol.packet.State; import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers; @@ -46,9 +44,11 @@ import com.viaversion.viaversion.protocols.protocol1_19_1to1_19.storage.NonceSto import com.viaversion.viaversion.protocols.protocol1_19to1_18_2.ClientboundPackets1_19; import com.viaversion.viaversion.protocols.protocol1_19to1_18_2.ServerboundPackets1_19; import com.viaversion.viaversion.util.CipherUtil; +import org.checkerframework.checker.nullness.qual.Nullable; + import java.util.ArrayList; import java.util.List; -import org.checkerframework.checker.nullness.qual.Nullable; +import java.util.UUID; public final class Protocol1_19_1To1_19 extends AbstractProtocol { @@ -135,8 +135,28 @@ public final class Protocol1_19_1To1_19 extends AbstractProtocol { + final ChatSession1_19_0 chatSession = wrapper.user().get(ChatSession1_19_0.class); + + if (chatSession != null) { + final UUID sender = wrapper.user().getProtocolInfo().getUuid(); + final String message = wrapper.get(Type.STRING, 0); + final long timestamp = wrapper.get(Type.LONG, 0); + final long salt = wrapper.get(Type.LONG, 1); + + final MessageMetadata metadata = new MessageMetadata(sender, timestamp, salt); + final DecoratableMessage decoratableMessage = new DecoratableMessage(message); + final byte[] signature = chatSession.signChatMessage(metadata, decoratableMessage); + + wrapper.write(Type.BYTE_ARRAY_PRIMITIVE, signature); // Signature + wrapper.write(Type.BOOLEAN, decoratableMessage.isDecorated()); // Signed preview + } else { + wrapper.write(Type.BYTE_ARRAY_PRIMITIVE, new byte[0]); // Signature + wrapper.write(Type.BOOLEAN, false); // Signed preview + } + }); read(Type.PLAYER_MESSAGE_SIGNATURE_ARRAY); // Last seen messages read(Type.OPTIONAL_PLAYER_MESSAGE_SIGNATURE); // Last received message } @@ -202,10 +222,12 @@ public final class Protocol1_19_1To1_19 extends AbstractProtocol { - // Profile keys are not compatible; replace it with an empty one - final ProfileKey profileKey = wrapper.read(Type.OPTIONAL_PROFILE_KEY); - wrapper.write(Type.OPTIONAL_PROFILE_KEY, null); - if (profileKey == null) { + final ProfileKey profileKey = wrapper.read(Type.OPTIONAL_PROFILE_KEY); // Profile Key + + final ChatSession1_19_0 chatSession = wrapper.user().get(ChatSession1_19_0.class); + wrapper.write(Type.OPTIONAL_PROFILE_KEY, chatSession == null ? null : chatSession.getProfileKey()); // Profile Key + + if (profileKey == null || chatSession != null) { // Modified client that doesn't include the profile key, or already done in 1.18->1.19 protocol; no need to map it wrapper.user().put(new NonceStorage(null)); } diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19_3to1_19_1/Protocol1_19_3To1_19_1.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19_3to1_19_1/Protocol1_19_3To1_19_1.java index cf9166a27..a8a4295ac 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19_3to1_19_1/Protocol1_19_3To1_19_1.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19_3to1_19_1/Protocol1_19_3To1_19_1.java @@ -17,6 +17,7 @@ */ package com.viaversion.viaversion.protocols.protocol1_19_3to1_19_1; +import com.google.common.primitives.Longs; import com.google.gson.JsonElement; import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.data.MappingData; @@ -24,6 +25,9 @@ import com.viaversion.viaversion.api.data.MappingDataBase; import com.viaversion.viaversion.api.minecraft.PlayerMessageSignature; import com.viaversion.viaversion.api.minecraft.RegistryType; import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_19_3; +import com.viaversion.viaversion.api.minecraft.signature.model.DecoratableMessage; +import com.viaversion.viaversion.api.minecraft.signature.model.MessageMetadata; +import com.viaversion.viaversion.api.minecraft.signature.storage.ChatSession1_19_1; import com.viaversion.viaversion.api.protocol.AbstractProtocol; import com.viaversion.viaversion.api.protocol.packet.PacketWrapper; import com.viaversion.viaversion.api.protocol.packet.State; @@ -35,17 +39,21 @@ import com.viaversion.viaversion.api.type.types.version.Types1_19_3; import com.viaversion.viaversion.data.entity.EntityTrackerBase; import com.viaversion.viaversion.libs.kyori.adventure.text.Component; import com.viaversion.viaversion.libs.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import com.viaversion.viaversion.protocols.base.ClientboundLoginPackets; import com.viaversion.viaversion.protocols.base.ServerboundLoginPackets; import com.viaversion.viaversion.protocols.protocol1_19_1to1_19.ClientboundPackets1_19_1; import com.viaversion.viaversion.protocols.protocol1_19_1to1_19.ServerboundPackets1_19_1; import com.viaversion.viaversion.protocols.protocol1_19_3to1_19_1.packets.EntityPackets; import com.viaversion.viaversion.protocols.protocol1_19_3to1_19_1.packets.InventoryPackets; +import com.viaversion.viaversion.protocols.protocol1_19_3to1_19_1.storage.NonceStorage; import com.viaversion.viaversion.protocols.protocol1_19_3to1_19_1.storage.ReceivedMessagesStorage; import com.viaversion.viaversion.rewriter.CommandRewriter; import com.viaversion.viaversion.rewriter.SoundRewriter; import com.viaversion.viaversion.rewriter.StatisticsRewriter; import com.viaversion.viaversion.rewriter.TagRewriter; + import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; public final class Protocol1_19_3To1_19_1 extends AbstractProtocol { @@ -230,18 +238,31 @@ public final class Protocol1_19_3To1_19_1 extends AbstractProtocol { - // Remove signature - wrapper.read(Type.OPTIONAL_SIGNATURE_BYTES); // Signature - wrapper.write(Type.BYTE_ARRAY_PRIMITIVE, EMPTY_BYTES); - wrapper.write(Type.BOOLEAN, false); // No signed preview - + final ChatSession1_19_1 chatSession = wrapper.user().get(ChatSession1_19_1.class); final ReceivedMessagesStorage messagesStorage = wrapper.user().get(ReceivedMessagesStorage.class); + + if (chatSession != null) { + final UUID sender = wrapper.user().getProtocolInfo().getUuid(); + final String message = wrapper.get(Type.STRING, 0); + final long timestamp = wrapper.get(Type.LONG, 0); + final long salt = wrapper.get(Type.LONG, 1); + + final MessageMetadata metadata = new MessageMetadata(sender, timestamp, salt); + final DecoratableMessage decoratableMessage = new DecoratableMessage(message); + final byte[] signature = chatSession.signChatMessage(metadata, decoratableMessage, messagesStorage.lastSignatures()); + + wrapper.write(Type.BYTE_ARRAY_PRIMITIVE, signature); // Signature + wrapper.write(Type.BOOLEAN, decoratableMessage.isDecorated()); // Signed preview + } else { + wrapper.write(Type.BYTE_ARRAY_PRIMITIVE, EMPTY_BYTES); // Signature + wrapper.write(Type.BOOLEAN, false); // Signed preview + } + messagesStorage.resetUnacknowledgedCount(); wrapper.write(Type.PLAYER_MESSAGE_SIGNATURE_ARRAY, messagesStorage.lastSignatures()); wrapper.write(Type.OPTIONAL_PLAYER_MESSAGE_SIGNATURE, null); // No last unacknowledged @@ -251,20 +272,50 @@ public final class Protocol1_19_3To1_19_1 extends AbstractProtocol { + if (wrapper.user().has(ChatSession1_19_1.class)) { + wrapper.user().put(new NonceStorage(wrapper.passthrough(Type.BYTE_ARRAY_PRIMITIVE))); // Nonce + } + }); + } + }); registerServerbound(State.LOGIN, ServerboundLoginPackets.HELLO.getId(), ServerboundLoginPackets.HELLO.getId(), new PacketHandlers() { @Override public void register() { map(Type.STRING); // Name - create(Type.OPTIONAL_PROFILE_KEY, null); + handler(wrapper -> { + final ChatSession1_19_1 chatSession = wrapper.user().get(ChatSession1_19_1.class); + wrapper.write(Type.OPTIONAL_PROFILE_KEY, chatSession == null ? null : chatSession.getProfileKey()); // Profile Key + }); + map(Type.OPTIONAL_UUID); // Profile uuid } }); registerServerbound(State.LOGIN, ServerboundLoginPackets.ENCRYPTION_KEY.getId(), ServerboundLoginPackets.ENCRYPTION_KEY.getId(), new PacketHandlers() { @Override public void register() { - map(Type.BYTE_ARRAY_PRIMITIVE); // Keys - create(Type.BOOLEAN, true); // Is nonce - map(Type.BYTE_ARRAY_PRIMITIVE); // Encrypted challenge + map(Type.BYTE_ARRAY_PRIMITIVE); // Public key + handler(wrapper -> { + final ChatSession1_19_1 chatSession = wrapper.user().get(ChatSession1_19_1.class); + + final byte[] verifyToken = wrapper.read(Type.BYTE_ARRAY_PRIMITIVE); // Verify token + wrapper.write(Type.BOOLEAN, chatSession == null); // Is nonce + if (chatSession != null) { + final long salt = ThreadLocalRandom.current().nextLong(); + final byte[] signature = chatSession.sign(signer -> { + signer.accept(wrapper.user().remove(NonceStorage.class).nonce()); + signer.accept(Longs.toByteArray(salt)); + }); + wrapper.write(Type.LONG, salt); // Salt + wrapper.write(Type.BYTE_ARRAY_PRIMITIVE, signature); // Signature + } else { + wrapper.write(Type.BYTE_ARRAY_PRIMITIVE, verifyToken); // Nonce + } + }); } }); diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19_3to1_19_1/storage/NonceStorage.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19_3to1_19_1/storage/NonceStorage.java new file mode 100644 index 000000000..7a3297962 --- /dev/null +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_19_3to1_19_1/storage/NonceStorage.java @@ -0,0 +1,34 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2016-2023 ViaVersion and 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 . + */ +package com.viaversion.viaversion.protocols.protocol1_19_3to1_19_1.storage; + +import com.viaversion.viaversion.api.connection.StorableObject; +import org.checkerframework.checker.nullness.qual.Nullable; + +public final class NonceStorage implements StorableObject { + + private final byte[] nonce; + + public NonceStorage(final byte @Nullable [] nonce) { + this.nonce = nonce; + } + + public byte @Nullable [] nonce() { + return nonce; + } +}