Archived
1
0
Fork 0
This repository has been archived on 2025-03-02. You can view files and clone it, but cannot push or open issues or pull requests.
uranos/blokk-server/src/main/kotlin/space/blokk/net/JoinProcedure.kt

131 lines
4.5 KiB
Kotlin

package space.blokk.net
import io.netty.buffer.Unpooled
import space.blokk.BlokkServer
import space.blokk.GameMode
import space.blokk.chat.TextComponent
import space.blokk.net.MinecraftDataTypes.writeString
import space.blokk.net.protocols.login.EncryptionRequestPacket
import space.blokk.net.protocols.login.EncryptionResponsePacket
import space.blokk.net.protocols.login.LoginStartPacket
import space.blokk.net.protocols.login.LoginSuccessPacket
import space.blokk.net.protocols.play.JoinGamePacket
import space.blokk.net.protocols.play.PlayProtocol
import space.blokk.net.protocols.play.PlayerAbilitiesPacket
import space.blokk.net.protocols.play.ServerDifficultyPacket
import space.blokk.utils.AuthenticationHelper
import space.blokk.utils.EncryptionUtils
import space.blokk.worlds.WorldDifficulty
import space.blokk.worlds.WorldDimension
import space.blokk.worlds.WorldType
import java.security.MessageDigest
import java.util.*
import javax.crypto.Cipher
import javax.crypto.spec.SecretKeySpec
class JoinProcedure(val session: BlokkSession) {
suspend fun start(packet: LoginStartPacket) {
if (session.loginState !is BlokkSession.LoginState.NotStarted)
throw IllegalStateException("loginState is not NotStarted")
if (BlokkServer.instance.config.authenticateAndEncrypt) {
val verifyToken = EncryptionUtils.generateVerifyToken()
session.loginState = BlokkSession.LoginState.WaitingForVerification(packet.username, verifyToken)
session.send(EncryptionRequestPacket(BlokkServer.instance.x509EncodedPublicKey, verifyToken))
} else {
session.send(
LoginSuccessPacket(
UUID.nameUUIDFromBytes("OfflinePlayer:${packet.username}".toByteArray()),
packet.username
)
)
afterLogin()
}
}
suspend fun onEncryptionResponse(packet: EncryptionResponsePacket) {
var loginState = session.loginState
if (loginState !is BlokkSession.LoginState.WaitingForVerification)
throw IllegalStateException("loginState is not WaitingForVerification")
val cipher = Cipher.getInstance("RSA")
cipher.init(Cipher.DECRYPT_MODE, BlokkServer.instance.keyPair.private)
// Decrypt verify token
val verifyToken: ByteArray = cipher.doFinal(packet.verifyToken)
if (!verifyToken.contentEquals((session.loginState as BlokkSession.LoginState.WaitingForVerification).verifyToken)) {
session.disconnect(TextComponent of "Encryption failed.")
return
}
// Decrypt shared secret
val sharedSecretKey = SecretKeySpec(cipher.doFinal(packet.sharedSecret), "AES")
loginState = BlokkSession.LoginState.Encrypted(loginState.username)
session.loginState = loginState
session.enableEncryption(sharedSecretKey)
val hash = MessageDigest.getInstance("SHA-1")
hash.update(sharedSecretKey.encoded)
hash.update(BlokkServer.instance.x509EncodedPublicKey)
val hashString = EncryptionUtils.toMinecraftStyleSha1String(hash.digest())
val result = AuthenticationHelper.authenticate(hashString, loginState.username)
loginState = BlokkSession.LoginState.Success(result.username, result.uuid)
session.loginState = loginState
session.send(LoginSuccessPacket(result.uuid, result.username))
afterLogin()
}
private suspend fun afterLogin() {
session.currentProtocol = PlayProtocol
// TODO: Use real data
session.send(
JoinGamePacket(
0,
GameMode.CREATIVE,
false,
WorldDimension.OVERWORLD,
12,
WorldType.DEFAULT,
32,
reducedDebugInfo = false,
respawnScreenEnabled = true
)
)
session.sendPluginMessage(
"minecraft:brand",
Unpooled.buffer().writeString("Blokk ${BlokkServer.VERSION_WITH_V}")
)
// TODO: Use real data
session.send(
PlayerAbilitiesPacket(
invulnerable = false,
flying = false,
flyingAllowed = true,
instantlyBreakBlocks = true,
flyingSpeed = 0.2f,
fieldOfView = 0.2f
)
)
joinWorld()
}
private suspend fun joinWorld() {
session.send(
ServerDifficultyPacket(
WorldDifficulty.NORMAL,
true
)
)
}
}