Open-source multiplayer game server compatible with the RuneScape client
https://www.openrs2.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
132 lines
4.8 KiB
132 lines
4.8 KiB
package org.openrs2.protocol.login.upstream
|
|
|
|
import io.netty.buffer.ByteBuf
|
|
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters
|
|
import org.openrs2.buffer.readString
|
|
import org.openrs2.buffer.use
|
|
import org.openrs2.buffer.writeString
|
|
import org.openrs2.crypto.Rsa
|
|
import org.openrs2.crypto.StreamCipher
|
|
import org.openrs2.crypto.XTEA_BLOCK_SIZE
|
|
import org.openrs2.crypto.XteaKey
|
|
import org.openrs2.crypto.publicKey
|
|
import org.openrs2.crypto.rsa
|
|
import org.openrs2.crypto.secureRandom
|
|
import org.openrs2.crypto.xteaDecrypt
|
|
import org.openrs2.crypto.xteaEncrypt
|
|
import org.openrs2.protocol.VariableShortPacketCodec
|
|
import org.openrs2.util.Base37
|
|
import java.time.LocalDate
|
|
import javax.inject.Inject
|
|
import javax.inject.Singleton
|
|
|
|
@Singleton
|
|
public class CreateAccountCodec @Inject constructor(
|
|
private val rsaKey: RSAPrivateCrtKeyParameters
|
|
) : VariableShortPacketCodec<LoginRequest.CreateAccount>(
|
|
type = LoginRequest.CreateAccount::class.java,
|
|
opcode = 22
|
|
) {
|
|
override fun decode(input: ByteBuf, cipher: StreamCipher): LoginRequest.CreateAccount {
|
|
val build = input.readShort().toInt()
|
|
|
|
val ciphertextLen = input.readUnsignedByte().toInt()
|
|
val ciphertext = input.readSlice(ciphertextLen)
|
|
|
|
ciphertext.rsa(rsaKey).use { plaintext ->
|
|
require(plaintext.readUnsignedByte().toInt() == Rsa.MAGIC) {
|
|
"Invalid RSA magic"
|
|
}
|
|
|
|
val flags = plaintext.readUnsignedShort()
|
|
val gameNewsletters = (flags and FLAG_GAME_NEWSLETTERS) != 0
|
|
val otherNewsletters = (flags and FLAG_OTHER_NEWSLETTERS) != 0
|
|
val shareDetailsWithBusinessPartners = (flags and FLAG_SHARE_DETAILS_WITH_BUSINESS_PARTNERS) != 0
|
|
|
|
val username = Base37.decodeLowerCase(plaintext.readLong())
|
|
val k0 = plaintext.readInt()
|
|
val password = plaintext.readString()
|
|
val k1 = plaintext.readInt()
|
|
val affiliate = plaintext.readUnsignedShort()
|
|
val day = plaintext.readUnsignedByte().toInt()
|
|
val month = plaintext.readUnsignedByte().toInt()
|
|
val k2 = plaintext.readInt()
|
|
val year = plaintext.readUnsignedShort()
|
|
val country = plaintext.readUnsignedShort()
|
|
val k3 = plaintext.readInt()
|
|
|
|
val xteaKey = XteaKey(k0, k1, k2, k3)
|
|
input.xteaDecrypt(input.readerIndex(), input.readableBytes(), xteaKey)
|
|
|
|
val email = input.readString()
|
|
|
|
return LoginRequest.CreateAccount(
|
|
build,
|
|
gameNewsletters,
|
|
otherNewsletters,
|
|
shareDetailsWithBusinessPartners,
|
|
username,
|
|
password,
|
|
affiliate,
|
|
dateOfBirth = LocalDate.of(year, month + 1, day),
|
|
country,
|
|
email
|
|
)
|
|
}
|
|
}
|
|
|
|
override fun encode(input: LoginRequest.CreateAccount, output: ByteBuf, cipher: StreamCipher) {
|
|
val xteaKey = XteaKey.generate()
|
|
|
|
output.writeShort(input.build)
|
|
|
|
output.alloc().buffer().use { plaintext ->
|
|
plaintext.writeByte(Rsa.MAGIC)
|
|
|
|
var flags = 0
|
|
if (input.gameNewsletters) {
|
|
flags = flags or FLAG_GAME_NEWSLETTERS
|
|
}
|
|
if (input.otherNewsletters) {
|
|
flags = flags or FLAG_OTHER_NEWSLETTERS
|
|
}
|
|
if (input.shareDetailsWithBusinessPartners) {
|
|
flags = flags or FLAG_SHARE_DETAILS_WITH_BUSINESS_PARTNERS
|
|
}
|
|
plaintext.writeShort(flags)
|
|
|
|
plaintext.writeLong(Base37.encode(input.username))
|
|
plaintext.writeInt(xteaKey.k0)
|
|
plaintext.writeString(input.password)
|
|
plaintext.writeInt(xteaKey.k1)
|
|
plaintext.writeShort(input.affiliate)
|
|
plaintext.writeByte(input.dateOfBirth.dayOfMonth)
|
|
plaintext.writeByte(input.dateOfBirth.monthValue - 1)
|
|
plaintext.writeInt(xteaKey.k2)
|
|
plaintext.writeShort(input.dateOfBirth.year)
|
|
plaintext.writeShort(input.country)
|
|
plaintext.writeInt(xteaKey.k3)
|
|
|
|
plaintext.rsa(rsaKey.publicKey).use { ciphertext ->
|
|
output.writeByte(ciphertext.readableBytes())
|
|
output.writeBytes(ciphertext)
|
|
}
|
|
}
|
|
|
|
val xteaIndex = output.writerIndex()
|
|
|
|
output.writeString(input.email)
|
|
|
|
while ((output.writerIndex() - xteaIndex) % XTEA_BLOCK_SIZE != 0) {
|
|
output.writeByte(secureRandom.nextInt())
|
|
}
|
|
|
|
output.xteaEncrypt(xteaIndex, output.writerIndex() - xteaIndex, xteaKey)
|
|
}
|
|
|
|
private companion object {
|
|
private const val FLAG_GAME_NEWSLETTERS = 0x1
|
|
private const val FLAG_OTHER_NEWSLETTERS = 0x2
|
|
private const val FLAG_SHARE_DETAILS_WITH_BUSINESS_PARTNERS = 0x4
|
|
}
|
|
}
|
|
|