diff --git a/src/main/java/de/bixilon/minosoft/commands/stack/CommandStackEntry.kt b/src/main/java/de/bixilon/minosoft/commands/stack/CommandStackEntry.kt index 111f42e14..90238eb33 100644 --- a/src/main/java/de/bixilon/minosoft/commands/stack/CommandStackEntry.kt +++ b/src/main/java/de/bixilon/minosoft/commands/stack/CommandStackEntry.kt @@ -1,6 +1,6 @@ /* * Minosoft - * Copyright (C) 2020-2022 Moritz Zwerger + * Copyright (C) 2020-2023 Moritz Zwerger * * 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. * @@ -25,6 +25,6 @@ data class CommandStackEntry( ) { fun sign(connection: PlayConnection, chain: MessageChain, key: PrivateKey, salt: Long, time: Instant): ByteArray { - return chain.signMessage(connection.version, key, data.toString(), null, salt, connection.player.uuid, time, LastSeenMessageList(emptyArray())) + return chain.signMessage(key, data.toString(), null, salt, connection.player.uuid, time, LastSeenMessageList(emptyArray())) } } diff --git a/src/main/java/de/bixilon/minosoft/data/chat/signature/MessageChain.kt b/src/main/java/de/bixilon/minosoft/data/chat/signature/MessageChain.kt index 288b9d280..39ed6ba80 100644 --- a/src/main/java/de/bixilon/minosoft/data/chat/signature/MessageChain.kt +++ b/src/main/java/de/bixilon/minosoft/data/chat/signature/MessageChain.kt @@ -1,6 +1,6 @@ /* * Minosoft - * Copyright (C) 2020-2022 Moritz Zwerger + * Copyright (C) 2020-2023 Moritz Zwerger * * 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. * @@ -13,68 +13,17 @@ package de.bixilon.minosoft.data.chat.signature -import com.fasterxml.jackson.core.io.JsonStringEncoder -import com.google.common.hash.Hashing -import com.google.common.primitives.Longs +import de.bixilon.minosoft.data.chat.signature.signer.MessageSigner import de.bixilon.minosoft.data.text.ChatComponent -import de.bixilon.minosoft.protocol.ProtocolUtil.encodeNetwork -import de.bixilon.minosoft.protocol.protocol.OutByteBuffer -import de.bixilon.minosoft.protocol.protocol.ProtocolVersions -import de.bixilon.minosoft.protocol.protocol.encryption.CryptManager import de.bixilon.minosoft.protocol.versions.Version import java.security.PrivateKey import java.time.Instant import java.util.* -class MessageChain { - private var previous: ByteArray? = null +class MessageChain(version: Version) { + val signer = MessageSigner.forVersion(version) - fun signMessage(version: Version, privateKey: PrivateKey, message: String, preview: ChatComponent?, salt: Long, sender: UUID, time: Instant, lastSeen: LastSeenMessageList): ByteArray { - val signature = CryptManager.createSignature(version) - - signature.initSign(privateKey) - - if (version < ProtocolVersions.V_1_19_1_PRE4) { - signature.update(Longs.toByteArray(salt)) - signature.update(Longs.toByteArray(sender.mostSignificantBits)) - signature.update(Longs.toByteArray(sender.leastSignificantBits)) - signature.update(Longs.toByteArray(time.epochSecond)) - signature.update(message.getSignatureBytes()) - } else { - val buffer = OutByteBuffer() - buffer.writeLong(salt) - buffer.writeLong(time.epochSecond) - if (version.versionId >= ProtocolVersions.V_1_19_2) { // ToDo: This changed somewhere after 1.19.1-pre5 - buffer.writeBareString(message) - } else { - buffer.writeBareByteArray(message.getSignatureBytes()) - } - - if (version.versionId >= ProtocolVersions.V_1_19_1_PRE5) { - buffer.writeByte(0x46) - // ToDo: send preview text (optional) - - for (entry in lastSeen.messages) { - buffer.writeByte(0x46) - buffer.writeUUID(entry.profile) - buffer.writeBareByteArray(entry.signature) - } - } - val hash = Hashing.sha256().hashBytes(buffer.toArray()).asBytes() - - previous?.let { signature.update(it) } - signature.update(Longs.toByteArray(sender.mostSignificantBits)) - signature.update(Longs.toByteArray(sender.leastSignificantBits)) - signature.update(hash) - } - - val singed = signature.sign() - this.previous = singed - - return singed - } - - private fun String.getSignatureBytes(): ByteArray { - return """{"text":"${String(JsonStringEncoder.getInstance().quoteAsString(this))}"}""".encodeNetwork() + fun signMessage(privateKey: PrivateKey, message: String, preview: ChatComponent?, salt: Long, sender: UUID, time: Instant, lastSeen: LastSeenMessageList): ByteArray { + return signer.signMessage(privateKey, message, preview, salt, sender, time, lastSeen) } } diff --git a/src/main/java/de/bixilon/minosoft/data/chat/signature/signer/MessageSigner.kt b/src/main/java/de/bixilon/minosoft/data/chat/signature/signer/MessageSigner.kt new file mode 100644 index 000000000..22dba6be8 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/chat/signature/signer/MessageSigner.kt @@ -0,0 +1,41 @@ +/* + * Minosoft + * Copyright (C) 2020-2023 Moritz Zwerger + * + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.chat.signature.signer + +import de.bixilon.minosoft.data.chat.signature.LastSeenMessageList +import de.bixilon.minosoft.data.text.ChatComponent +import de.bixilon.minosoft.protocol.protocol.ProtocolVersions +import de.bixilon.minosoft.protocol.versions.Version +import java.security.PrivateKey +import java.time.Instant +import java.util.* + +interface MessageSigner { + + fun signMessage(privateKey: PrivateKey, message: String, preview: ChatComponent?, salt: Long, sender: UUID, time: Instant, lastSeen: LastSeenMessageList): ByteArray + + + companion object { + + fun forVersion(version: Version): MessageSigner { + if (version < ProtocolVersions.V_1_19_1_PRE4) { + return MessageSigner1(version) + } + if (version < ProtocolVersions.V_22W42A) { + return MessageSigner2(version) + } + return MessageSigner3(version) + } + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/chat/signature/signer/MessageSigner1.kt b/src/main/java/de/bixilon/minosoft/data/chat/signature/signer/MessageSigner1.kt new file mode 100644 index 000000000..c37cd4e13 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/chat/signature/signer/MessageSigner1.kt @@ -0,0 +1,43 @@ +/* + * Minosoft + * Copyright (C) 2020-2023 Moritz Zwerger + * + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.chat.signature.signer + +import com.google.common.primitives.Longs +import de.bixilon.minosoft.data.chat.signature.LastSeenMessageList +import de.bixilon.minosoft.data.chat.signature.signer.MessageSigningUtil.getSignatureBytes +import de.bixilon.minosoft.data.text.ChatComponent +import de.bixilon.minosoft.protocol.protocol.encryption.CryptManager +import de.bixilon.minosoft.protocol.versions.Version +import java.security.PrivateKey +import java.time.Instant +import java.util.* + +class MessageSigner1( + private val version: Version, +) : MessageSigner { + + override fun signMessage(privateKey: PrivateKey, message: String, preview: ChatComponent?, salt: Long, sender: UUID, time: Instant, lastSeen: LastSeenMessageList): ByteArray { + val signature = CryptManager.createSignature(version) + + signature.initSign(privateKey) + + signature.update(Longs.toByteArray(salt)) + signature.update(Longs.toByteArray(sender.mostSignificantBits)) + signature.update(Longs.toByteArray(sender.leastSignificantBits)) + signature.update(Longs.toByteArray(time.epochSecond)) + signature.update(message.getSignatureBytes()) + + return signature.sign() + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/chat/signature/signer/MessageSigner2.kt b/src/main/java/de/bixilon/minosoft/data/chat/signature/signer/MessageSigner2.kt new file mode 100644 index 000000000..6726b2583 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/chat/signature/signer/MessageSigner2.kt @@ -0,0 +1,68 @@ +/* + * Minosoft + * Copyright (C) 2020-2023 Moritz Zwerger + * + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.chat.signature.signer + +import com.google.common.hash.Hashing +import com.google.common.primitives.Longs +import de.bixilon.minosoft.data.chat.signature.LastSeenMessageList +import de.bixilon.minosoft.data.chat.signature.signer.MessageSigningUtil.getSignatureBytes +import de.bixilon.minosoft.data.text.ChatComponent +import de.bixilon.minosoft.protocol.protocol.OutByteBuffer +import de.bixilon.minosoft.protocol.protocol.ProtocolVersions +import de.bixilon.minosoft.protocol.protocol.encryption.CryptManager +import de.bixilon.minosoft.protocol.versions.Version +import java.security.PrivateKey +import java.time.Instant +import java.util.* + +class MessageSigner2( + private val version: Version, +) : MessageSigner { + private var previous: ByteArray? = null + + override fun signMessage(privateKey: PrivateKey, message: String, preview: ChatComponent?, salt: Long, sender: UUID, time: Instant, lastSeen: LastSeenMessageList): ByteArray { + val signature = CryptManager.createSignature(version) + + signature.initSign(privateKey) + + + val buffer = OutByteBuffer() + buffer.writeLong(salt) + buffer.writeLong(time.epochSecond) + if (version.versionId >= ProtocolVersions.V_1_19_2) { // ToDo: This changed somewhere after 1.19.1-pre5 + buffer.writeBareString(message) + } else { + buffer.writeBareByteArray(message.getSignatureBytes()) + } + + if (version.versionId >= ProtocolVersions.V_1_19_1_PRE5) { + buffer.writeByte(0x46) + // ToDo: send preview text (optional) + + for (entry in lastSeen.messages) { + buffer.writeByte(0x46) + buffer.writeUUID(entry.profile) + buffer.writeBareByteArray(entry.signature) + } + } + val hash = Hashing.sha256().hashBytes(buffer.toArray()).asBytes() + + previous?.let { signature.update(it) } + signature.update(Longs.toByteArray(sender.mostSignificantBits)) + signature.update(Longs.toByteArray(sender.leastSignificantBits)) + signature.update(hash) + + return signature.sign() + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/chat/signature/signer/MessageSigner3.kt b/src/main/java/de/bixilon/minosoft/data/chat/signature/signer/MessageSigner3.kt new file mode 100644 index 000000000..c80c621db --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/chat/signature/signer/MessageSigner3.kt @@ -0,0 +1,30 @@ +/* + * Minosoft + * Copyright (C) 2020-2023 Moritz Zwerger + * + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.chat.signature.signer + +import de.bixilon.minosoft.data.chat.signature.LastSeenMessageList +import de.bixilon.minosoft.data.text.ChatComponent +import de.bixilon.minosoft.protocol.versions.Version +import java.security.PrivateKey +import java.time.Instant +import java.util.* + +class MessageSigner3( + private val version: Version, +) : MessageSigner { + + override fun signMessage(privateKey: PrivateKey, message: String, preview: ChatComponent?, salt: Long, sender: UUID, time: Instant, lastSeen: LastSeenMessageList): ByteArray { + TODO("Not yet implemented!") + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/chat/signature/signer/MessageSigningUtil.kt b/src/main/java/de/bixilon/minosoft/data/chat/signature/signer/MessageSigningUtil.kt new file mode 100644 index 000000000..972e00a57 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/chat/signature/signer/MessageSigningUtil.kt @@ -0,0 +1,24 @@ +/* + * Minosoft + * Copyright (C) 2020-2023 Moritz Zwerger + * + * 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.chat.signature.signer + +import com.fasterxml.jackson.core.io.JsonStringEncoder +import de.bixilon.minosoft.protocol.ProtocolUtil.encodeNetwork + +object MessageSigningUtil { + + fun String.getSignatureBytes(): ByteArray { + return """{"text":"${String(JsonStringEncoder.getInstance().quoteAsString(this))}"}""".encodeNetwork() + } +} diff --git a/src/main/java/de/bixilon/minosoft/protocol/network/connection/play/ConnectionUtil.kt b/src/main/java/de/bixilon/minosoft/protocol/network/connection/play/ConnectionUtil.kt index 715d751e4..4b052c236 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/network/connection/play/ConnectionUtil.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/network/connection/play/ConnectionUtil.kt @@ -1,6 +1,6 @@ /* * Minosoft - * Copyright (C) 2020-2022 Moritz Zwerger + * Copyright (C) 2020-2023 Moritz Zwerger * * 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. * @@ -47,19 +47,19 @@ import java.time.Instant class ConnectionUtil( private val connection: PlayConnection, ) { - private val chain = MessageChain() + private val chain = MessageChain(connection.version) private val random = SecureRandom() fun sendDebugMessage(message: Any) { val component = BaseComponent(RenderConstants.DEBUG_MESSAGES_PREFIX, ChatComponent.of(message).apply { this.setFallbackColor(ChatColors.BLUE) }) - connection.fire(InternalMessageReceiveEvent(connection, InternalChatMessage(component))) + connection.events.fire(InternalMessageReceiveEvent(connection, InternalChatMessage(component))) Log.log(LogMessageType.CHAT_IN, LogLevels.INFO) { component } } fun sendInternal(message: Any) { val component = ChatComponent.of(message) val prefixed = BaseComponent(RenderConstants.INTERNAL_MESSAGES_PREFIX, component) - connection.fire(InternalMessageReceiveEvent(connection, InternalChatMessage(if (connection.profiles.gui.chat.internal.hidden) prefixed else component))) + connection.events.fire(InternalMessageReceiveEvent(connection, InternalChatMessage(if (connection.profiles.gui.chat.internal.hidden) prefixed else component))) Log.log(LogMessageType.CHAT_IN, LogLevels.INFO) { prefixed } } @@ -78,7 +78,7 @@ class ConnectionUtil( if (message.length > connection.version.maxChatMessageSize) { throw IllegalArgumentException("Message length (${message.length} can not exceed ${connection.version.maxChatMessageSize})") } - if (connection.fire(ChatMessageSendEvent(connection, message))) { + if (connection.events.fire(ChatMessageSendEvent(connection, message))) { return } Log.log(LogMessageType.CHAT_OUT) { message } @@ -97,7 +97,7 @@ class ConnectionUtil( val acknowledgement = Acknowledgement.EMPTY val signature: ByteArray? = if (connection.network.encrypted) { - chain.signMessage(connection.version, privateKey, message, null, salt, uuid, time, acknowledgement.lastSeen) + chain.signMessage(privateKey, message, null, salt, uuid, time, acknowledgement.lastSeen) } else { null } @@ -129,7 +129,7 @@ class ConnectionUtil( connection.world.particleRenderer?.removeAllParticles() connection.player.openedContainer?.let { connection.player.openedContainer = null - connection.fire(ContainerCloseEvent(connection, it.id ?: -1, it)) + connection.events.fire(ContainerCloseEvent(connection, it.id ?: -1, it)) } connection.player.healthCondition = HealthCondition() connection.world.time = WorldTime()