mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-15 10:25:06 -04:00
network: 1.19.1-pre4
This commit is contained in:
parent
1fc08c0662
commit
80027e3754
@ -15,7 +15,7 @@ package de.bixilon.minosoft.data.accounts.types.microsoft
|
|||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import de.bixilon.kutil.latch.CountUpAndDownLatch
|
import de.bixilon.kutil.latch.CountUpAndDownLatch
|
||||||
import de.bixilon.kutil.time.TimeUtil
|
import de.bixilon.kutil.time.TimeUtil.millis
|
||||||
import de.bixilon.minosoft.config.profile.profiles.account.AccountProfileManager
|
import de.bixilon.minosoft.config.profile.profiles.account.AccountProfileManager
|
||||||
import de.bixilon.minosoft.data.accounts.Account
|
import de.bixilon.minosoft.data.accounts.Account
|
||||||
import de.bixilon.minosoft.data.accounts.AccountStates
|
import de.bixilon.minosoft.data.accounts.AccountStates
|
||||||
@ -80,7 +80,7 @@ class MicrosoftAccount(
|
|||||||
// already checking
|
// already checking
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (minecraft.expires >= TimeUtil.millis / 1000) {
|
if (minecraft.expires >= millis() / 1000) {
|
||||||
return check(latch, "null")
|
return check(latch, "null")
|
||||||
}
|
}
|
||||||
if (state == AccountStates.WORKING) {
|
if (state == AccountStates.WORKING) {
|
||||||
@ -99,7 +99,7 @@ class MicrosoftAccount(
|
|||||||
|
|
||||||
private fun refreshMinecraftToken(latch: CountUpAndDownLatch?) {
|
private fun refreshMinecraftToken(latch: CountUpAndDownLatch?) {
|
||||||
state = AccountStates.REFRESHING
|
state = AccountStates.REFRESHING
|
||||||
val time = TimeUtil.millis / 1000
|
val time = millis() / 1000
|
||||||
if (time >= msa.expires) {
|
if (time >= msa.expires) {
|
||||||
// token expired
|
// token expired
|
||||||
refreshMicrosoftToken(latch)
|
refreshMicrosoftToken(latch)
|
||||||
@ -123,7 +123,7 @@ class MicrosoftAccount(
|
|||||||
|
|
||||||
private fun checkMinecraftToken(latch: CountUpAndDownLatch?) {
|
private fun checkMinecraftToken(latch: CountUpAndDownLatch?) {
|
||||||
state = AccountStates.CHECKING
|
state = AccountStates.CHECKING
|
||||||
val time = TimeUtil.millis / 1000
|
val time = millis() / 1000
|
||||||
if (time >= minecraft.expires) {
|
if (time >= minecraft.expires) {
|
||||||
// token expired
|
// token expired
|
||||||
refreshMinecraftToken(latch)
|
refreshMinecraftToken(latch)
|
||||||
@ -143,7 +143,7 @@ class MicrosoftAccount(
|
|||||||
|
|
||||||
override fun fetchKey(latch: CountUpAndDownLatch?): MinecraftPrivateKey {
|
override fun fetchKey(latch: CountUpAndDownLatch?): MinecraftPrivateKey {
|
||||||
var key = key
|
var key = key
|
||||||
if (key == null || key.isExpired()) {
|
if (key == null || key.isExpired() || key.signatureV2 == null) {
|
||||||
key = AccountUtil.fetchPrivateKey(minecraft)
|
key = AccountUtil.fetchPrivateKey(minecraft)
|
||||||
this.key = key
|
this.key = key
|
||||||
save()
|
save()
|
||||||
|
@ -13,12 +13,12 @@
|
|||||||
|
|
||||||
package de.bixilon.minosoft.data.chat.message
|
package de.bixilon.minosoft.data.chat.message
|
||||||
|
|
||||||
import de.bixilon.minosoft.data.chat.ChatMessageTypes
|
import de.bixilon.minosoft.data.chat.type.DefaultMessageTypes
|
||||||
import de.bixilon.minosoft.data.text.ChatComponent
|
import de.bixilon.minosoft.data.text.ChatComponent
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
open class PlayerChatMessage(
|
open class PlayerChatMessage(
|
||||||
text: ChatComponent,
|
text: ChatComponent,
|
||||||
type: ChatMessageTypes,
|
type: DefaultMessageTypes,
|
||||||
val sender: UUID,
|
val sender: UUID,
|
||||||
) : SimpleChatMessage(text, type)
|
) : SimpleChatMessage(text, type)
|
||||||
|
@ -13,10 +13,10 @@
|
|||||||
|
|
||||||
package de.bixilon.minosoft.data.chat.message
|
package de.bixilon.minosoft.data.chat.message
|
||||||
|
|
||||||
import de.bixilon.minosoft.data.chat.ChatMessageTypes
|
import de.bixilon.minosoft.data.chat.type.DefaultMessageTypes
|
||||||
import de.bixilon.minosoft.data.text.ChatComponent
|
import de.bixilon.minosoft.data.text.ChatComponent
|
||||||
|
|
||||||
open class SimpleChatMessage(
|
open class SimpleChatMessage(
|
||||||
override val text: ChatComponent,
|
override val text: ChatComponent,
|
||||||
val type: ChatMessageTypes,
|
val type: DefaultMessageTypes,
|
||||||
) : ChatMessage
|
) : ChatMessage
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Minosoft
|
||||||
|
* Copyright (C) 2020-2022 Moritz Zwerger 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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.registries.versions.Version
|
||||||
|
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 java.security.PrivateKey
|
||||||
|
import java.time.Instant
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class MessageChain {
|
||||||
|
private var previous: ByteArray? = null
|
||||||
|
|
||||||
|
fun signMessage(version: Version, privateKey: PrivateKey, message: String, preview: ChatComponent?, salt: Long, sender: UUID, time: Instant): 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)
|
||||||
|
buffer.writeBareByteArray(message.getSignatureBytes())
|
||||||
|
// TODo: lastseen
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
package de.bixilon.minosoft.data.chat.signature
|
package de.bixilon.minosoft.data.chat.signature
|
||||||
|
|
||||||
|
import de.bixilon.minosoft.data.chat.type.MessageType
|
||||||
import de.bixilon.minosoft.data.text.ChatComponent
|
import de.bixilon.minosoft.data.text.ChatComponent
|
||||||
|
|
||||||
data class SignedMessage(
|
data class SignedMessage(
|
||||||
@ -20,4 +21,5 @@ data class SignedMessage(
|
|||||||
val signature: ByteArray,
|
val signature: ByteArray,
|
||||||
val body: MessageBody,
|
val body: MessageBody,
|
||||||
val unsigned: ChatComponent? = null,
|
val unsigned: ChatComponent? = null,
|
||||||
|
val type: MessageType? = null,
|
||||||
)
|
)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Minosoft
|
* Minosoft
|
||||||
* Copyright (C) 2020-2022 Moritz Zwerger
|
* Copyright (C) 2020-2022 Moritz Zwerger 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 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.
|
||||||
*
|
*
|
||||||
@ -11,12 +11,13 @@
|
|||||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package de.bixilon.minosoft.data.chat
|
package de.bixilon.minosoft.data.chat.type
|
||||||
|
|
||||||
import de.bixilon.kutil.enums.EnumUtil
|
import de.bixilon.kutil.enums.EnumUtil
|
||||||
import de.bixilon.kutil.enums.ValuesEnum
|
import de.bixilon.kutil.enums.ValuesEnum
|
||||||
|
import de.bixilon.minosoft.data.chat.ChatTextPositions
|
||||||
|
|
||||||
enum class ChatMessageTypes(val position: ChatTextPositions = ChatTextPositions.CHAT) {
|
enum class DefaultMessageTypes(val position: ChatTextPositions = ChatTextPositions.CHAT) {
|
||||||
CHAT_MESSAGE,
|
CHAT_MESSAGE,
|
||||||
SYSTEM_MESSAGE,
|
SYSTEM_MESSAGE,
|
||||||
GAME_MESSAGE,
|
GAME_MESSAGE,
|
||||||
@ -28,8 +29,8 @@ enum class ChatMessageTypes(val position: ChatTextPositions = ChatTextPositions.
|
|||||||
COMMAND_TELLRAW,
|
COMMAND_TELLRAW,
|
||||||
;
|
;
|
||||||
|
|
||||||
companion object : ValuesEnum<ChatMessageTypes> {
|
companion object : ValuesEnum<DefaultMessageTypes> {
|
||||||
override val VALUES: Array<ChatMessageTypes> = values()
|
override val VALUES: Array<DefaultMessageTypes> = values()
|
||||||
override val NAME_MAP: Map<String, ChatMessageTypes> = EnumUtil.getEnumValues(VALUES)
|
override val NAME_MAP: Map<String, DefaultMessageTypes> = EnumUtil.getEnumValues(VALUES)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Minosoft
|
||||||
|
* Copyright (C) 2020-2022 Moritz Zwerger 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.bixilon.minosoft.data.chat.type
|
||||||
|
|
||||||
|
import de.bixilon.minosoft.data.text.ChatComponent
|
||||||
|
|
||||||
|
@Deprecated("Refactor")
|
||||||
|
class MessageType(
|
||||||
|
val id: Int,
|
||||||
|
val text: ChatComponent,
|
||||||
|
val targetName: ChatComponent?,
|
||||||
|
)
|
@ -12,7 +12,7 @@
|
|||||||
*/
|
*/
|
||||||
package de.bixilon.minosoft.modding.event.events
|
package de.bixilon.minosoft.modding.event.events
|
||||||
|
|
||||||
import de.bixilon.minosoft.data.chat.ChatMessageTypes
|
import de.bixilon.minosoft.data.chat.type.DefaultMessageTypes
|
||||||
import de.bixilon.minosoft.data.text.ChatComponent
|
import de.bixilon.minosoft.data.text.ChatComponent
|
||||||
import de.bixilon.minosoft.modding.event.EventInitiators
|
import de.bixilon.minosoft.modding.event.EventInitiators
|
||||||
import de.bixilon.minosoft.modding.event.events.connection.play.PlayConnectionEvent
|
import de.bixilon.minosoft.modding.event.events.connection.play.PlayConnectionEvent
|
||||||
@ -26,14 +26,14 @@ class ChatMessageReceiveEvent(
|
|||||||
connection: PlayConnection,
|
connection: PlayConnection,
|
||||||
initiator: EventInitiators,
|
initiator: EventInitiators,
|
||||||
val message: ChatComponent,
|
val message: ChatComponent,
|
||||||
val type: ChatMessageTypes,
|
val type: DefaultMessageTypes,
|
||||||
val sender: UUID?,
|
val sender: UUID?,
|
||||||
) : PlayConnectionEvent(connection, initiator), CancelableEvent {
|
) : PlayConnectionEvent(connection, initiator), CancelableEvent {
|
||||||
|
|
||||||
|
|
||||||
constructor(connection: PlayConnection, packet: ChatMessageS2CP) : this(connection, EventInitiators.SERVER, packet.message, packet.type, packet.sender)
|
constructor(connection: PlayConnection, packet: ChatMessageS2CP) : this(connection, EventInitiators.SERVER, packet.message, packet.type, packet.sender)
|
||||||
|
|
||||||
constructor(connection: PlayConnection, packet: HotbarTextS2CP) : this(connection, EventInitiators.SERVER, packet.text, ChatMessageTypes.GAME_MESSAGE, null)
|
constructor(connection: PlayConnection, packet: HotbarTextS2CP) : this(connection, EventInitiators.SERVER, packet.text, DefaultMessageTypes.GAME_MESSAGE, null)
|
||||||
|
|
||||||
constructor(connection: PlayConnection, packet: SignedChatMessageS2CP) : this(connection, EventInitiators.SERVER, packet.message, packet.type, packet.sender.uuid)
|
constructor(connection: PlayConnection, packet: SignedChatMessageS2CP) : this(connection, EventInitiators.SERVER, packet.message.body.text, DefaultMessageTypes.CHAT_MESSAGE, packet.message.header.sender)
|
||||||
}
|
}
|
||||||
|
@ -17,12 +17,16 @@ import de.bixilon.kutil.base64.Base64Util.fromBase64
|
|||||||
import de.bixilon.kutil.base64.Base64Util.toBase64
|
import de.bixilon.kutil.base64.Base64Util.toBase64
|
||||||
import de.bixilon.kutil.json.JsonObject
|
import de.bixilon.kutil.json.JsonObject
|
||||||
import de.bixilon.kutil.primitive.LongUtil.toLong
|
import de.bixilon.kutil.primitive.LongUtil.toLong
|
||||||
|
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions
|
||||||
import de.bixilon.minosoft.protocol.protocol.encryption.CryptManager
|
import de.bixilon.minosoft.protocol.protocol.encryption.CryptManager
|
||||||
import de.bixilon.minosoft.protocol.protocol.encryption.CryptManager.encodeNetwork
|
import de.bixilon.minosoft.protocol.protocol.encryption.CryptManager.encodeNetwork
|
||||||
|
import de.bixilon.minosoft.util.account.minecraft.key.MinecraftKeyPair
|
||||||
|
import de.bixilon.minosoft.util.yggdrasil.YggdrasilException
|
||||||
import de.bixilon.minosoft.util.yggdrasil.YggdrasilUtil
|
import de.bixilon.minosoft.util.yggdrasil.YggdrasilUtil
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class PlayerPublicKey(
|
class PlayerPublicKey(
|
||||||
val expiresAt: Instant,
|
val expiresAt: Instant,
|
||||||
@ -30,10 +34,6 @@ class PlayerPublicKey(
|
|||||||
val signature: ByteArray,
|
val signature: ByteArray,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
init {
|
|
||||||
check(isSignatureCorrect()) { "Yggdrasil signature invalid" }
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(nbt: JsonObject) : this(Instant.ofEpochMilli(nbt["expires_at"].toLong()), CryptManager.getPlayerPublicKey(nbt["key"].toString()), nbt["signature"].toString().fromBase64())
|
constructor(nbt: JsonObject) : this(Instant.ofEpochMilli(nbt["expires_at"].toLong()), CryptManager.getPlayerPublicKey(nbt["key"].toString()), nbt["signature"].toString().fromBase64())
|
||||||
|
|
||||||
fun toNbt(): JsonObject {
|
fun toNbt(): JsonObject {
|
||||||
@ -44,9 +44,21 @@ class PlayerPublicKey(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isSignatureCorrect(): Boolean {
|
fun isLegacySignatureCorrect(): Boolean {
|
||||||
val bytes = (expiresAt.toEpochMilli().toString() + publicKey.encodeNetwork()).toByteArray(StandardCharsets.US_ASCII)
|
val bytes = (expiresAt.toEpochMilli().toString() + publicKey.encodeNetwork()).toByteArray(StandardCharsets.US_ASCII)
|
||||||
|
|
||||||
return YggdrasilUtil.verify(bytes, signature)
|
return YggdrasilUtil.verify(bytes, signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isSignatureCorrect(uuid: UUID): Boolean {
|
||||||
|
return MinecraftKeyPair.isSignatureCorrect(uuid, expiresAt, publicKey, signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requireSignature(versionId: Int, uuid: UUID) {
|
||||||
|
if (versionId < ProtocolVersions.V_1_19_1_PRE4) {
|
||||||
|
if (!isLegacySignatureCorrect()) throw YggdrasilException()
|
||||||
|
} else {
|
||||||
|
if (!isSignatureCorrect(uuid)) throw YggdrasilException()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,11 +13,10 @@
|
|||||||
|
|
||||||
package de.bixilon.minosoft.protocol.network.connection.play
|
package de.bixilon.minosoft.protocol.network.connection.play
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.io.JsonStringEncoder
|
|
||||||
import com.google.common.primitives.Longs
|
|
||||||
import de.bixilon.kotlinglm.vec3.Vec3d
|
import de.bixilon.kotlinglm.vec3.Vec3d
|
||||||
import de.bixilon.kutil.string.WhitespaceUtil.trimWhitespaces
|
import de.bixilon.kutil.string.WhitespaceUtil.trimWhitespaces
|
||||||
import de.bixilon.minosoft.commands.stack.CommandStack
|
import de.bixilon.minosoft.commands.stack.CommandStack
|
||||||
|
import de.bixilon.minosoft.data.chat.signature.MessageChain
|
||||||
import de.bixilon.minosoft.data.text.BaseComponent
|
import de.bixilon.minosoft.data.text.BaseComponent
|
||||||
import de.bixilon.minosoft.data.text.ChatComponent
|
import de.bixilon.minosoft.data.text.ChatComponent
|
||||||
import de.bixilon.minosoft.data.text.formatting.color.ChatColors
|
import de.bixilon.minosoft.data.text.formatting.color.ChatColors
|
||||||
@ -30,17 +29,18 @@ import de.bixilon.minosoft.protocol.ProtocolUtil.encodeNetwork
|
|||||||
import de.bixilon.minosoft.protocol.packets.c2s.play.chat.ChatMessageC2SP
|
import de.bixilon.minosoft.protocol.packets.c2s.play.chat.ChatMessageC2SP
|
||||||
import de.bixilon.minosoft.protocol.packets.c2s.play.chat.SignedChatMessageC2SP
|
import de.bixilon.minosoft.protocol.packets.c2s.play.chat.SignedChatMessageC2SP
|
||||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
||||||
import de.bixilon.minosoft.protocol.protocol.encryption.CryptManager
|
|
||||||
import de.bixilon.minosoft.protocol.protocol.encryption.SignatureData
|
import de.bixilon.minosoft.protocol.protocol.encryption.SignatureData
|
||||||
import de.bixilon.minosoft.util.logging.Log
|
import de.bixilon.minosoft.util.logging.Log
|
||||||
import de.bixilon.minosoft.util.logging.LogLevels
|
import de.bixilon.minosoft.util.logging.LogLevels
|
||||||
import de.bixilon.minosoft.util.logging.LogMessageType
|
import de.bixilon.minosoft.util.logging.LogMessageType
|
||||||
|
import java.security.PrivateKey
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
class ConnectionUtil(
|
class ConnectionUtil(
|
||||||
private val connection: PlayConnection,
|
private val connection: PlayConnection,
|
||||||
) {
|
) {
|
||||||
|
private val chain = MessageChain()
|
||||||
private val random = SecureRandom()
|
private val random = SecureRandom()
|
||||||
|
|
||||||
fun sendDebugMessage(message: Any) {
|
fun sendDebugMessage(message: Any) {
|
||||||
@ -75,22 +75,17 @@ class ConnectionUtil(
|
|||||||
if (privateKey == null || !connection.version.requiresSignedChat) {
|
if (privateKey == null || !connection.version.requiresSignedChat) {
|
||||||
return connection.sendPacket(ChatMessageC2SP(message))
|
return connection.sendPacket(ChatMessageC2SP(message))
|
||||||
}
|
}
|
||||||
|
sendSignedMessage(privateKey, message)
|
||||||
|
}
|
||||||
|
|
||||||
val signature = CryptManager.createSignature(connection.version)
|
fun sendSignedMessage(privateKey: PrivateKey = connection.player.privateKey?.private!!, message: String) {
|
||||||
|
|
||||||
val salt = random.nextLong()
|
val salt = random.nextLong()
|
||||||
val time = Instant.now()
|
val time = Instant.now()
|
||||||
val uuid = connection.player.uuid
|
val uuid = connection.player.uuid
|
||||||
|
|
||||||
signature.initSign(privateKey)
|
val signature = chain.signMessage(connection.version, privateKey, message, null, salt, uuid, time)
|
||||||
|
|
||||||
signature.update(Longs.toByteArray(salt))
|
connection.sendPacket(SignedChatMessageC2SP(message.encodeNetwork(), time = time, salt = salt, signature = SignatureData(signature), false))
|
||||||
signature.update(Longs.toByteArray(uuid.mostSignificantBits))
|
|
||||||
signature.update(Longs.toByteArray(uuid.leastSignificantBits))
|
|
||||||
signature.update(Longs.toByteArray(time.epochSecond))
|
|
||||||
signature.update("""{"text":"${String(JsonStringEncoder.getInstance().quoteAsString(message))}"}""".encodeNetwork())
|
|
||||||
|
|
||||||
connection.sendPacket(SignedChatMessageC2SP(message.encodeNetwork(), time = time, signature = SignatureData(salt, signature.sign()), false))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated("message will re removed as soon as brigadier is fully implemented")
|
@Deprecated("message will re removed as soon as brigadier is fully implemented")
|
||||||
|
@ -56,7 +56,6 @@ import de.bixilon.minosoft.protocol.network.connection.play.tick.ConnectionTicke
|
|||||||
import de.bixilon.minosoft.protocol.packets.c2s.handshaking.HandshakeC2SP
|
import de.bixilon.minosoft.protocol.packets.c2s.handshaking.HandshakeC2SP
|
||||||
import de.bixilon.minosoft.protocol.packets.c2s.login.StartC2SP
|
import de.bixilon.minosoft.protocol.packets.c2s.login.StartC2SP
|
||||||
import de.bixilon.minosoft.protocol.protocol.ProtocolStates
|
import de.bixilon.minosoft.protocol.protocol.ProtocolStates
|
||||||
import de.bixilon.minosoft.protocol.protocol.encryption.CryptManager
|
|
||||||
import de.bixilon.minosoft.terminal.RunConfiguration
|
import de.bixilon.minosoft.terminal.RunConfiguration
|
||||||
import de.bixilon.minosoft.terminal.cli.CLI
|
import de.bixilon.minosoft.terminal.cli.CLI
|
||||||
import de.bixilon.minosoft.util.ServerAddress
|
import de.bixilon.minosoft.util.ServerAddress
|
||||||
@ -189,12 +188,12 @@ class PlayConnection(
|
|||||||
if (version.requiresSignedChat) {
|
if (version.requiresSignedChat) {
|
||||||
taskWorker += WorkerTask(optional = true) {
|
taskWorker += WorkerTask(optional = true) {
|
||||||
val minecraftKey = account.fetchKey(latch) ?: return@WorkerTask
|
val minecraftKey = account.fetchKey(latch) ?: return@WorkerTask
|
||||||
minecraftKey.requireSignature()
|
minecraftKey.requireSignature(account.uuid)
|
||||||
privateKey = PlayerPrivateKey(
|
privateKey = PlayerPrivateKey(
|
||||||
expiresAt = minecraftKey.expiresAt,
|
expiresAt = minecraftKey.expiresAt,
|
||||||
signature = minecraftKey.signatureBytes,
|
signature = minecraftKey.getSignature(version.versionId),
|
||||||
private = CryptManager.getPlayerPrivateKey(minecraftKey.pair.private),
|
private = minecraftKey.pair.private,
|
||||||
public = CryptManager.getPlayerPublicKey(minecraftKey.pair.public),
|
public = minecraftKey.pair.public,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ import de.bixilon.minosoft.protocol.packets.factory.LoadPacket
|
|||||||
import de.bixilon.minosoft.protocol.protocol.PlayOutByteBuffer
|
import de.bixilon.minosoft.protocol.protocol.PlayOutByteBuffer
|
||||||
import de.bixilon.minosoft.protocol.protocol.ProtocolStates
|
import de.bixilon.minosoft.protocol.protocol.ProtocolStates
|
||||||
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions
|
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions
|
||||||
import de.bixilon.minosoft.protocol.protocol.encryption.SignatureData
|
import de.bixilon.minosoft.protocol.protocol.encryption.EncryptionSignatureData
|
||||||
import de.bixilon.minosoft.util.logging.Log
|
import de.bixilon.minosoft.util.logging.Log
|
||||||
import de.bixilon.minosoft.util.logging.LogLevels
|
import de.bixilon.minosoft.util.logging.LogLevels
|
||||||
import de.bixilon.minosoft.util.logging.LogMessageType
|
import de.bixilon.minosoft.util.logging.LogMessageType
|
||||||
@ -31,7 +31,7 @@ class EncryptionC2SP private constructor(
|
|||||||
) : PlayC2SPacket {
|
) : PlayC2SPacket {
|
||||||
|
|
||||||
constructor(secret: ByteArray, nonce: ByteArray) : this(secret, nonce as Any)
|
constructor(secret: ByteArray, nonce: ByteArray) : this(secret, nonce as Any)
|
||||||
constructor(secret: ByteArray, nonce: SignatureData) : this(secret, nonce as Any)
|
constructor(secret: ByteArray, nonce: EncryptionSignatureData) : this(secret, nonce as Any)
|
||||||
|
|
||||||
override fun write(buffer: PlayOutByteBuffer) {
|
override fun write(buffer: PlayOutByteBuffer) {
|
||||||
buffer.writeByteArray(secret)
|
buffer.writeByteArray(secret)
|
||||||
@ -43,7 +43,9 @@ class EncryptionC2SP private constructor(
|
|||||||
buffer.writeByteArray(nonce)
|
buffer.writeByteArray(nonce)
|
||||||
} else {
|
} else {
|
||||||
buffer.writeBoolean(false)
|
buffer.writeBoolean(false)
|
||||||
buffer.writeSignatureData(nonce as SignatureData)
|
val nonce = nonce as EncryptionSignatureData
|
||||||
|
buffer.writeLong(nonce.salt)
|
||||||
|
buffer.writeSignatureData(nonce.signatureData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ import java.time.Instant
|
|||||||
class CommandC2SP(
|
class CommandC2SP(
|
||||||
val command: String,
|
val command: String,
|
||||||
val time: Instant = Instant.now(),
|
val time: Instant = Instant.now(),
|
||||||
|
val salt: Long,
|
||||||
val signature: CommandArgumentSignature,
|
val signature: CommandArgumentSignature,
|
||||||
val signedPreview: Boolean,
|
val signedPreview: Boolean,
|
||||||
) : PlayC2SPacket {
|
) : PlayC2SPacket {
|
||||||
@ -32,7 +33,7 @@ class CommandC2SP(
|
|||||||
override fun write(buffer: PlayOutByteBuffer) {
|
override fun write(buffer: PlayOutByteBuffer) {
|
||||||
buffer.writeString(command)
|
buffer.writeString(command)
|
||||||
buffer.writeInstant(time)
|
buffer.writeInstant(time)
|
||||||
buffer.writeLong(signature.salt)
|
buffer.writeLong(salt)
|
||||||
buffer.writeVarInt(signature.signatures.size)
|
buffer.writeVarInt(signature.signatures.size)
|
||||||
for ((argument, signature) in signature.signatures) {
|
for ((argument, signature) in signature.signatures) {
|
||||||
buffer.writeString(argument)
|
buffer.writeString(argument)
|
||||||
@ -44,12 +45,11 @@ class CommandC2SP(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun log(reducedLog: Boolean) {
|
override fun log(reducedLog: Boolean) {
|
||||||
Log.log(LogMessageType.NETWORK_PACKETS_OUT, LogLevels.VERBOSE) { "Chat message (message=$command, time=$time, signature=$signature)" }
|
Log.log(LogMessageType.NETWORK_PACKETS_OUT, LogLevels.VERBOSE) { "Command (message=$command, time=$time, signature=$signature)" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
data class CommandArgumentSignature(
|
data class CommandArgumentSignature(
|
||||||
val salt: Long,
|
|
||||||
val signatures: Map<String, ByteArray>,
|
val signatures: Map<String, ByteArray>,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ import java.time.Instant
|
|||||||
class SignedChatMessageC2SP(
|
class SignedChatMessageC2SP(
|
||||||
val message: ByteArray,
|
val message: ByteArray,
|
||||||
val time: Instant = Instant.now(),
|
val time: Instant = Instant.now(),
|
||||||
|
val salt: Long,
|
||||||
val signature: SignatureData? = null,
|
val signature: SignatureData? = null,
|
||||||
val previewed: Boolean = false,
|
val previewed: Boolean = false,
|
||||||
) : PlayC2SPacket {
|
) : PlayC2SPacket {
|
||||||
@ -38,6 +39,7 @@ class SignedChatMessageC2SP(
|
|||||||
if (buffer.versionId >= ProtocolVersions.V_22W18A) {
|
if (buffer.versionId >= ProtocolVersions.V_22W18A) {
|
||||||
buffer.writeInstant(time)
|
buffer.writeInstant(time)
|
||||||
}
|
}
|
||||||
|
buffer.writeLong(salt)
|
||||||
buffer.writeSignatureData(signature ?: SignatureData.EMPTY)
|
buffer.writeSignatureData(signature ?: SignatureData.EMPTY)
|
||||||
if (buffer.versionId >= ProtocolVersions.V_22W19A) {
|
if (buffer.versionId >= ProtocolVersions.V_22W19A) {
|
||||||
buffer.writeBoolean(previewed)
|
buffer.writeBoolean(previewed)
|
||||||
|
@ -23,6 +23,7 @@ import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket
|
|||||||
import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer
|
import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer
|
||||||
import de.bixilon.minosoft.protocol.protocol.ProtocolStates
|
import de.bixilon.minosoft.protocol.protocol.ProtocolStates
|
||||||
import de.bixilon.minosoft.protocol.protocol.encryption.CryptManager
|
import de.bixilon.minosoft.protocol.protocol.encryption.CryptManager
|
||||||
|
import de.bixilon.minosoft.protocol.protocol.encryption.EncryptionSignatureData
|
||||||
import de.bixilon.minosoft.protocol.protocol.encryption.SignatureData
|
import de.bixilon.minosoft.protocol.protocol.encryption.SignatureData
|
||||||
import de.bixilon.minosoft.util.logging.Log
|
import de.bixilon.minosoft.util.logging.Log
|
||||||
import de.bixilon.minosoft.util.logging.LogLevels
|
import de.bixilon.minosoft.util.logging.LogLevels
|
||||||
@ -57,7 +58,7 @@ class EncryptionS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket {
|
|||||||
signature.update(Longs.toByteArray(salt))
|
signature.update(Longs.toByteArray(salt))
|
||||||
val signed = signature.sign()
|
val signed = signature.sign()
|
||||||
|
|
||||||
connection.sendPacket(EncryptionC2SP(encryptedSecretKey, SignatureData(salt, signed)))
|
connection.sendPacket(EncryptionC2SP(encryptedSecretKey, EncryptionSignatureData(salt, SignatureData(signed))))
|
||||||
} else {
|
} else {
|
||||||
connection.sendPacket(EncryptionC2SP(encryptedSecretKey, CryptManager.encryptData(publicKey, nonce)))
|
connection.sendPacket(EncryptionC2SP(encryptedSecretKey, CryptManager.encryptData(publicKey, nonce)))
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
*/
|
*/
|
||||||
package de.bixilon.minosoft.protocol.packets.s2c.play.chat
|
package de.bixilon.minosoft.protocol.packets.s2c.play.chat
|
||||||
|
|
||||||
import de.bixilon.minosoft.data.chat.ChatMessageTypes
|
import de.bixilon.minosoft.data.chat.type.DefaultMessageTypes
|
||||||
import de.bixilon.minosoft.data.text.ChatComponent
|
import de.bixilon.minosoft.data.text.ChatComponent
|
||||||
import de.bixilon.minosoft.modding.event.events.ChatMessageReceiveEvent
|
import de.bixilon.minosoft.modding.event.events.ChatMessageReceiveEvent
|
||||||
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
|
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
|
||||||
@ -29,7 +29,7 @@ import java.util.*
|
|||||||
@LoadPacket(threadSafe = false)
|
@LoadPacket(threadSafe = false)
|
||||||
class ChatMessageS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket {
|
class ChatMessageS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket {
|
||||||
val message: ChatComponent = buffer.readChatComponent()
|
val message: ChatComponent = buffer.readChatComponent()
|
||||||
var type: ChatMessageTypes = ChatMessageTypes.CHAT_MESSAGE
|
var type: DefaultMessageTypes = DefaultMessageTypes.CHAT_MESSAGE
|
||||||
private set
|
private set
|
||||||
var sender: UUID? = null
|
var sender: UUID? = null
|
||||||
private set
|
private set
|
||||||
@ -42,7 +42,7 @@ class ChatMessageS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket {
|
|||||||
if (buffer.versionId >= ProtocolVersions.V_1_19_1_PRE2) {
|
if (buffer.versionId >= ProtocolVersions.V_1_19_1_PRE2) {
|
||||||
overlay = buffer.readBoolean()
|
overlay = buffer.readBoolean()
|
||||||
} else {
|
} else {
|
||||||
type = ChatMessageTypes[buffer.readVarInt()]
|
type = DefaultMessageTypes[buffer.readVarInt()]
|
||||||
if (buffer.versionId >= ProtocolVersions.V_20W21A && buffer.versionId < ProtocolVersions.V_22W17A) {
|
if (buffer.versionId >= ProtocolVersions.V_20W21A && buffer.versionId < ProtocolVersions.V_22W17A) {
|
||||||
sender = buffer.readUUID()
|
sender = buffer.readUUID()
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Minosoft
|
||||||
|
* Copyright (C) 2020-2022 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||||
|
*/
|
||||||
|
package de.bixilon.minosoft.protocol.packets.s2c.play.chat
|
||||||
|
|
||||||
|
import de.bixilon.minosoft.protocol.packets.factory.LoadPacket
|
||||||
|
import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket
|
||||||
|
import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer
|
||||||
|
import de.bixilon.minosoft.util.logging.Log
|
||||||
|
import de.bixilon.minosoft.util.logging.LogLevels
|
||||||
|
import de.bixilon.minosoft.util.logging.LogMessageType
|
||||||
|
|
||||||
|
@LoadPacket(threadSafe = false)
|
||||||
|
class HideMessageS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket {
|
||||||
|
val signature = buffer.readByteArray()
|
||||||
|
|
||||||
|
override fun log(reducedLog: Boolean) {
|
||||||
|
Log.log(LogMessageType.NETWORK_PACKETS_IN, level = LogLevels.VERBOSE) { "Hide message (signature=$signature)" }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Minosoft
|
||||||
|
* Copyright (C) 2020-2022 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||||
|
*/
|
||||||
|
package de.bixilon.minosoft.protocol.packets.s2c.play.chat
|
||||||
|
|
||||||
|
import de.bixilon.minosoft.protocol.packets.factory.LoadPacket
|
||||||
|
import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket
|
||||||
|
import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer
|
||||||
|
import de.bixilon.minosoft.util.logging.Log
|
||||||
|
import de.bixilon.minosoft.util.logging.LogLevels
|
||||||
|
import de.bixilon.minosoft.util.logging.LogMessageType
|
||||||
|
|
||||||
|
@LoadPacket(threadSafe = false)
|
||||||
|
class MessageHeaderS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket {
|
||||||
|
val header = buffer.readMessageHeader()
|
||||||
|
val signature = buffer.readByteArray()
|
||||||
|
val bodyDigest = buffer.readByteArray()
|
||||||
|
|
||||||
|
override fun log(reducedLog: Boolean) {
|
||||||
|
Log.log(LogMessageType.NETWORK_PACKETS_IN, level = LogLevels.VERBOSE) { "Message header (header=$header, signature=$signature, bodyDigest=$bodyDigest)" }
|
||||||
|
}
|
||||||
|
}
|
@ -12,25 +12,18 @@
|
|||||||
*/
|
*/
|
||||||
package de.bixilon.minosoft.protocol.packets.s2c.play.chat
|
package de.bixilon.minosoft.protocol.packets.s2c.play.chat
|
||||||
|
|
||||||
import de.bixilon.minosoft.data.chat.ChatMessageTypes
|
|
||||||
import de.bixilon.minosoft.modding.event.events.ChatMessageReceiveEvent
|
import de.bixilon.minosoft.modding.event.events.ChatMessageReceiveEvent
|
||||||
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
|
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
|
||||||
import de.bixilon.minosoft.protocol.packets.factory.LoadPacket
|
import de.bixilon.minosoft.protocol.packets.factory.LoadPacket
|
||||||
import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket
|
import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket
|
||||||
import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer
|
import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer
|
||||||
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions
|
|
||||||
import de.bixilon.minosoft.util.logging.Log
|
import de.bixilon.minosoft.util.logging.Log
|
||||||
import de.bixilon.minosoft.util.logging.LogLevels
|
import de.bixilon.minosoft.util.logging.LogLevels
|
||||||
import de.bixilon.minosoft.util.logging.LogMessageType
|
import de.bixilon.minosoft.util.logging.LogMessageType
|
||||||
|
|
||||||
@LoadPacket(threadSafe = false)
|
@LoadPacket(threadSafe = false)
|
||||||
class SignedChatMessageS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket {
|
class SignedChatMessageS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket {
|
||||||
val message = buffer.readChatComponent()
|
val message = buffer.readSignedMessage()
|
||||||
val unsignedContent = if (buffer.versionId >= ProtocolVersions.V_22W19A) buffer.readOptional { readChatComponent() } else null
|
|
||||||
var type = ChatMessageTypes[buffer.readVarInt()]
|
|
||||||
val sender = buffer.readChatMessageSender()
|
|
||||||
val sendingTime = buffer.readInstant()
|
|
||||||
val signatureData = buffer.readSignatureData()
|
|
||||||
|
|
||||||
override fun handle(connection: PlayConnection) {
|
override fun handle(connection: PlayConnection) {
|
||||||
val event = ChatMessageReceiveEvent(connection, this)
|
val event = ChatMessageReceiveEvent(connection, this)
|
||||||
@ -40,6 +33,6 @@ class SignedChatMessageS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun log(reducedLog: Boolean) {
|
override fun log(reducedLog: Boolean) {
|
||||||
Log.log(LogMessageType.NETWORK_PACKETS_IN, level = LogLevels.VERBOSE) { "Chat message (message=\"$message\", type=$type, sender=$sender, sendingTime=$sendingTime, signateDate=$signatureData)" }
|
Log.log(LogMessageType.NETWORK_PACKETS_IN, level = LogLevels.VERBOSE) { "Chat message (message=$message)" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,7 @@ class TabListS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket {
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
val publicKey = if (buffer.versionId >= ProtocolVersions.V_22W18A) buffer.readOptional { buffer.readPlayerPublicKey() } else null
|
val publicKey = if (buffer.versionId >= ProtocolVersions.V_22W18A) buffer.readOptional { buffer.readPlayerPublicKey() } else null
|
||||||
|
publicKey?.requireSignature(buffer.versionId, uuid)
|
||||||
data = TabListItemData(
|
data = TabListItemData(
|
||||||
name = name,
|
name = name,
|
||||||
properties = properties,
|
properties = properties,
|
||||||
@ -62,12 +63,15 @@ class TabListS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket {
|
|||||||
publicKey = publicKey,
|
publicKey = publicKey,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
TabListItemActions.UPDATE_GAMEMODE -> {
|
TabListItemActions.UPDATE_GAMEMODE -> {
|
||||||
data = TabListItemData(gamemode = Gamemodes[buffer.readVarInt()])
|
data = TabListItemData(gamemode = Gamemodes[buffer.readVarInt()])
|
||||||
}
|
}
|
||||||
|
|
||||||
TabListItemActions.UPDATE_LATENCY -> {
|
TabListItemActions.UPDATE_LATENCY -> {
|
||||||
data = TabListItemData(ping = buffer.readVarInt())
|
data = TabListItemData(ping = buffer.readVarInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
TabListItemActions.UPDATE_DISPLAY_NAME -> {
|
TabListItemActions.UPDATE_DISPLAY_NAME -> {
|
||||||
val hasDisplayName = buffer.readBoolean()
|
val hasDisplayName = buffer.readBoolean()
|
||||||
val displayName = if (hasDisplayName) {
|
val displayName = if (hasDisplayName) {
|
||||||
@ -80,6 +84,7 @@ class TabListS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket {
|
|||||||
displayName = displayName,
|
displayName = displayName,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
TabListItemActions.REMOVE_PLAYER -> {
|
TabListItemActions.REMOVE_PLAYER -> {
|
||||||
data = null
|
data = null
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,8 @@ import de.bixilon.minosoft.data.chat.signature.LastSeenMessage
|
|||||||
import de.bixilon.minosoft.data.chat.signature.MessageBody
|
import de.bixilon.minosoft.data.chat.signature.MessageBody
|
||||||
import de.bixilon.minosoft.data.chat.signature.MessageHeader
|
import de.bixilon.minosoft.data.chat.signature.MessageHeader
|
||||||
import de.bixilon.minosoft.data.chat.signature.SignedMessage
|
import de.bixilon.minosoft.data.chat.signature.SignedMessage
|
||||||
|
import de.bixilon.minosoft.data.chat.type.DefaultMessageTypes
|
||||||
|
import de.bixilon.minosoft.data.chat.type.MessageType
|
||||||
import de.bixilon.minosoft.data.container.ItemStackUtil
|
import de.bixilon.minosoft.data.container.ItemStackUtil
|
||||||
import de.bixilon.minosoft.data.container.stack.ItemStack
|
import de.bixilon.minosoft.data.container.stack.ItemStack
|
||||||
import de.bixilon.minosoft.data.entities.entities.player.properties.PlayerProperties
|
import de.bixilon.minosoft.data.entities.entities.player.properties.PlayerProperties
|
||||||
@ -282,21 +284,6 @@ class PlayInByteBuffer : InByteBuffer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun readChatMessageSender(): ChatMessageSender {
|
|
||||||
return ChatMessageSender(readUUID(), readChatComponent(), if (versionId >= ProtocolVersions.V_22W18A) readOptional { readChatComponent() } else null)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun readSignatureData(): SignatureData {
|
|
||||||
return SignatureData(readLong(), readByteArray())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun readPlayerPublicKey(): PlayerPublicKey? {
|
|
||||||
if (versionId <= ProtocolVersions.V_22W18A) { // ToDo: find version
|
|
||||||
return readNBT()?.let { PlayerPublicKey(it.asJsonObject()) }
|
|
||||||
}
|
|
||||||
return PlayerPublicKey(readInstant(), CryptManager.getPlayerPublicKey(readByteArray()), readByteArray())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun readInstant(): Instant {
|
fun readInstant(): Instant {
|
||||||
val time = readLong()
|
val time = readLong()
|
||||||
if (versionId >= ProtocolVersions.V_22W19A) {
|
if (versionId >= ProtocolVersions.V_22W19A) {
|
||||||
@ -360,6 +347,22 @@ class PlayInByteBuffer : InByteBuffer {
|
|||||||
return registry[readVarInt()]
|
return registry[readVarInt()]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun readChatMessageSender(): ChatMessageSender {
|
||||||
|
return ChatMessageSender(readUUID(), readChatComponent(), if (versionId >= ProtocolVersions.V_22W18A) readOptional { readChatComponent() } else null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readSignatureData(): SignatureData {
|
||||||
|
return SignatureData(readByteArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readPlayerPublicKey(): PlayerPublicKey? {
|
||||||
|
if (versionId <= ProtocolVersions.V_22W18A) { // ToDo: find version
|
||||||
|
return readNBT()?.let { PlayerPublicKey(it.asJsonObject()) }
|
||||||
|
}
|
||||||
|
return PlayerPublicKey(readInstant(), CryptManager.getPlayerPublicKey(readByteArray()), readByteArray())
|
||||||
|
}
|
||||||
|
|
||||||
fun readMessageHeader(): MessageHeader {
|
fun readMessageHeader(): MessageHeader {
|
||||||
return MessageHeader(readOptional { readByteArray() }, readUUID())
|
return MessageHeader(readOptional { readByteArray() }, readUUID())
|
||||||
}
|
}
|
||||||
@ -377,7 +380,22 @@ class PlayInByteBuffer : InByteBuffer {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun readMessageType(): MessageType {
|
||||||
|
return MessageType(readVarInt(), readChatComponent(), readOptional { readChatComponent() })
|
||||||
|
}
|
||||||
|
|
||||||
fun readSignedMessage(): SignedMessage {
|
fun readSignedMessage(): SignedMessage {
|
||||||
return SignedMessage(readMessageHeader(), readByteArray(), readMessageBody(), readOptional { readChatComponent() })
|
if (versionId < ProtocolVersions.V_1_19_1_PRE4) {
|
||||||
|
val message = readChatComponent()
|
||||||
|
val unsignedContent = if (versionId >= ProtocolVersions.V_22W19A) readOptional { readChatComponent() } else null
|
||||||
|
var type = DefaultMessageTypes[readVarInt()]
|
||||||
|
val sender = readChatMessageSender()
|
||||||
|
val sendingTime = readInstant()
|
||||||
|
val salt = readLong()
|
||||||
|
val signatureData = readSignatureData()
|
||||||
|
|
||||||
|
TODO("return message, refactor")
|
||||||
|
}
|
||||||
|
return SignedMessage(readMessageHeader(), readByteArray(), readMessageBody(), readOptional { readChatComponent() }, readMessageType())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,6 @@ class PlayOutByteBuffer(val connection: PlayConnection) : OutByteBuffer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun writeSignatureData(signature: SignatureData) {
|
fun writeSignatureData(signature: SignatureData) {
|
||||||
writeLong(signature.salt)
|
|
||||||
writeByteArray(signature.signature)
|
writeByteArray(signature.signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Minosoft
|
||||||
|
* Copyright (C) 2020-2022 Moritz Zwerger 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.bixilon.minosoft.protocol.protocol.encryption
|
||||||
|
|
||||||
|
class EncryptionSignatureData(
|
||||||
|
val salt: Long,
|
||||||
|
val signatureData: SignatureData,
|
||||||
|
)
|
@ -14,10 +14,9 @@
|
|||||||
package de.bixilon.minosoft.protocol.protocol.encryption
|
package de.bixilon.minosoft.protocol.protocol.encryption
|
||||||
|
|
||||||
data class SignatureData(
|
data class SignatureData(
|
||||||
val salt: Long,
|
|
||||||
val signature: ByteArray,
|
val signature: ByteArray,
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
val EMPTY = SignatureData(0L, byteArrayOf())
|
val EMPTY = SignatureData(byteArrayOf())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,32 +16,39 @@ package de.bixilon.minosoft.util.account.minecraft
|
|||||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import de.bixilon.kutil.base64.Base64Util.fromBase64
|
import de.bixilon.kutil.base64.Base64Util.fromBase64
|
||||||
|
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions
|
||||||
import de.bixilon.minosoft.util.account.minecraft.key.MinecraftKeyPair
|
import de.bixilon.minosoft.util.account.minecraft.key.MinecraftKeyPair
|
||||||
import de.bixilon.minosoft.util.yggdrasil.YggdrasilUtil
|
import de.bixilon.minosoft.util.yggdrasil.YggdrasilUtil
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
data class MinecraftPrivateKey(
|
data class MinecraftPrivateKey(
|
||||||
@JsonProperty("keyPair") val pair: MinecraftKeyPair,
|
@JsonProperty("keyPair") val pair: MinecraftKeyPair,
|
||||||
@JsonProperty("publicKeySignature") val signature: String,
|
@JsonProperty("publicKeySignature") val signature: String?,
|
||||||
|
@JsonProperty("publicKeySignatureV2") val signatureV2: String?,
|
||||||
@JsonProperty("expiresAt") val expiresAt: Instant,
|
@JsonProperty("expiresAt") val expiresAt: Instant,
|
||||||
@JsonProperty("refreshedAfter") val refreshedAfter: Instant,
|
@JsonProperty("refreshedAfter") val refreshedAfter: Instant,
|
||||||
) {
|
) {
|
||||||
@get:JsonIgnore val signatureBytes: ByteArray by lazy { signature.fromBase64() }
|
@get:JsonIgnore val signatureBytes: ByteArray? by lazy { signature?.fromBase64() }
|
||||||
|
@get:JsonIgnore val signatureBytesV2: ByteArray by lazy { signatureV2!!.fromBase64() }
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
fun isExpired(): Boolean {
|
fun isExpired(): Boolean {
|
||||||
val now = Instant.now()
|
val now = Instant.now()
|
||||||
return now.isAfter(expiresAt) || now.isAfter(refreshedAfter)
|
return now.isAfter(expiresAt) || now.isAfter(refreshedAfter)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val getSignedBytes: ByteArray
|
@JsonIgnore
|
||||||
get() = (expiresAt.toEpochMilli().toString() + pair.public).toByteArray(StandardCharsets.US_ASCII)
|
fun requireSignature(uuid: UUID) {
|
||||||
|
MinecraftKeyPair.requireSignature(uuid, expiresAt, pair.public, signatureBytesV2)
|
||||||
fun isSignatureCorrect(): Boolean {
|
signatureBytes?.let { YggdrasilUtil.requireSignature((expiresAt.toEpochMilli().toString() + pair.publicString).toByteArray(StandardCharsets.US_ASCII), it) }
|
||||||
return YggdrasilUtil.verify(getSignedBytes, signatureBytes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun requireSignature() {
|
fun getSignature(versionId: Int): ByteArray {
|
||||||
YggdrasilUtil.requireSignature(getSignedBytes, signatureBytes)
|
if (versionId < ProtocolVersions.V_1_19_1_PRE4) {
|
||||||
|
return signatureBytes ?: throw IllegalStateException("v1 signature required")
|
||||||
|
}
|
||||||
|
return signatureBytesV2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,39 @@
|
|||||||
|
|
||||||
package de.bixilon.minosoft.util.account.minecraft.key
|
package de.bixilon.minosoft.util.account.minecraft.key
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
|
import de.bixilon.minosoft.protocol.protocol.OutByteBuffer
|
||||||
|
import de.bixilon.minosoft.protocol.protocol.encryption.CryptManager
|
||||||
|
import de.bixilon.minosoft.util.yggdrasil.YggdrasilException
|
||||||
|
import de.bixilon.minosoft.util.yggdrasil.YggdrasilUtil
|
||||||
|
import java.security.PublicKey
|
||||||
|
import java.time.Instant
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
data class MinecraftKeyPair(
|
data class MinecraftKeyPair(
|
||||||
@JsonProperty("privateKey") val private: String,
|
@JsonProperty("privateKey") val privateString: String,
|
||||||
@JsonProperty("publicKey") val public: String,
|
@JsonProperty("publicKey") val publicString: String,
|
||||||
)
|
) {
|
||||||
|
@get:JsonIgnore val private by lazy { CryptManager.getPlayerPrivateKey(privateString) }
|
||||||
|
@get:JsonIgnore val public by lazy { CryptManager.getPlayerPublicKey(publicString) }
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
fun isSignatureCorrect(uuid: UUID, expiresAt: Instant, publicKey: PublicKey, signature: ByteArray): Boolean {
|
||||||
|
val signed = OutByteBuffer()
|
||||||
|
signed.writeUUID(uuid)
|
||||||
|
signed.writeLong(expiresAt.toEpochMilli())
|
||||||
|
signed.writeBareByteArray(publicKey.encoded)
|
||||||
|
return YggdrasilUtil.verify(signed.toArray(), signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requireSignature(uuid: UUID, expiresAt: Instant, publicKey: PublicKey, signature: ByteArray) {
|
||||||
|
if (!isSignatureCorrect(uuid, expiresAt, publicKey, signature)) {
|
||||||
|
throw YggdrasilException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user