131 lines
4.5 KiB
Kotlin
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
|
|
)
|
|
)
|
|
}
|
|
}
|