mirror of
https://github.com/2revoemag/PSF-BotServer.git
synced 2026-02-26 09:43:37 +00:00
[packet] VNLWorldStatusMessage
Added VNL packet type from IDA. Moved definition in to its own file. Refactored PacketCoding MarshalPacket. The whole structure needs a rework. Now able to get a PlanetSide client to the server screen with a server of choice.
This commit is contained in:
parent
1643ecc1dd
commit
b8ff34c0f9
10 changed files with 363 additions and 102 deletions
135
common/src/test/scala/CryptoInterfaceTest.scala
Normal file
135
common/src/test/scala/CryptoInterfaceTest.scala
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
import org.specs2.mutable._
|
||||
import psforever.crypto.CryptoInterface
|
||||
import psforever.crypto.CryptoInterface.CryptoDHState
|
||||
import scodec.bits._
|
||||
|
||||
class CryptoInterfaceTest extends Specification {
|
||||
"Crypto interface" should {
|
||||
"correctly initialize" in {
|
||||
CryptoInterface.initialize()
|
||||
ok
|
||||
}
|
||||
|
||||
"encrypt and decrypt" in {
|
||||
val key = hex"41414141"
|
||||
val plaintext = ByteVector.fill(16)(0x42)
|
||||
|
||||
val crypto = new CryptoInterface.CryptoState(key, key)
|
||||
|
||||
val ciphertext = crypto.encrypt(plaintext)
|
||||
val decrypted = crypto.decrypt(ciphertext)
|
||||
|
||||
crypto.close
|
||||
decrypted mustEqual plaintext
|
||||
ciphertext mustNotEqual plaintext
|
||||
}
|
||||
|
||||
"encrypt and decrypt must handle no bytes" in {
|
||||
val key = hex"41414141"
|
||||
val empty = ByteVector.empty
|
||||
|
||||
val crypto = new CryptoInterface.CryptoState(key, key)
|
||||
|
||||
val ciphertext = crypto.encrypt(empty)
|
||||
val decrypted = crypto.decrypt(ciphertext)
|
||||
|
||||
crypto.close
|
||||
|
||||
ciphertext mustEqual empty
|
||||
decrypted mustEqual empty
|
||||
}
|
||||
|
||||
"encrypt and decrypt must only accept block aligned inputs" in {
|
||||
val key = hex"41414141"
|
||||
val badPad = ByteVector.fill(CryptoInterface.RC5_BLOCK_SIZE-1)('a')
|
||||
|
||||
val crypto = new CryptoInterface.CryptoState(key, key)
|
||||
|
||||
crypto.encrypt(badPad) must throwA[IllegalArgumentException]
|
||||
crypto.decrypt(badPad) must throwA[IllegalArgumentException]
|
||||
|
||||
crypto.close
|
||||
ok
|
||||
}
|
||||
|
||||
"arrive at a shared secret" in {
|
||||
val server = new CryptoInterface.CryptoDHState()
|
||||
val client = new CryptoInterface.CryptoDHState()
|
||||
|
||||
// 1. Client generates p, g, and its key pair
|
||||
client.start()
|
||||
|
||||
// 2. Client sends p and g to server who then generates a key pair as well
|
||||
server.start(client.getModulus, client.getGenerator)
|
||||
|
||||
// 3. Both parties come to a shared secret
|
||||
val clientAgreed = client.agree(server.getPublicKey)
|
||||
val serverAgreed = server.agree(client.getPublicKey)
|
||||
|
||||
// Free resources
|
||||
server.close
|
||||
client.close
|
||||
|
||||
clientAgreed mustEqual serverAgreed
|
||||
}
|
||||
|
||||
"must fail to agree on a secret with a bad public key" in {
|
||||
val server = new CryptoInterface.CryptoDHState()
|
||||
val client = new CryptoInterface.CryptoDHState()
|
||||
|
||||
// 1. Client generates p, g, and its key pair
|
||||
client.start()
|
||||
|
||||
// 2. Client sends p and g to server who then generates a key pair as well
|
||||
server.start(client.getModulus, client.getGenerator)
|
||||
|
||||
// 3. Client agrees with a bad public key, so it must fail
|
||||
val clientAgreed = client.agree(client.getPublicKey)
|
||||
val serverAgreed = server.agree(client.getPublicKey)
|
||||
|
||||
// Free resources
|
||||
server.close
|
||||
client.close
|
||||
|
||||
clientAgreed mustNotEqual serverAgreed
|
||||
}
|
||||
|
||||
"MD5MAC correctly" in {
|
||||
val key = hex"377b60f8790f91b35a9da82945743da9"
|
||||
val message = ByteVector(Array[Byte]('m', 'a', 's', 't', 'e', 'r', ' ', 's', 'e', 'c', 'r', 'e', 't')) ++
|
||||
hex"b4aea1559444a20b6112a2892de40eac00000000c8aea155b53d187076b79abab59001b600000000"
|
||||
val expected = hex"5aa15de41f5220cf5cca489155e1438c5aa15de4"
|
||||
|
||||
val output = CryptoInterface.MD5MAC(key, message, expected.length)
|
||||
|
||||
output mustEqual expected
|
||||
}
|
||||
|
||||
"safely handle multiple starts" in {
|
||||
val dontCare = ByteVector.fill(16)(0x42)
|
||||
var dh = new CryptoDHState()
|
||||
|
||||
dh.start()
|
||||
dh.start() must throwA[IllegalStateException]
|
||||
dh.close
|
||||
|
||||
dh = new CryptoDHState()
|
||||
|
||||
ok
|
||||
}
|
||||
|
||||
"prevent function calls before initialization" in {
|
||||
val dontCare = ByteVector.fill(16)(0x42)
|
||||
val dh = new CryptoDHState()
|
||||
|
||||
dh.getGenerator must throwA[IllegalStateException]
|
||||
dh.getModulus must throwA[IllegalStateException]
|
||||
dh.getPrivateKey must throwA[IllegalStateException]
|
||||
dh.getPublicKey must throwA[IllegalStateException]
|
||||
dh.agree(dontCare) must throwA[IllegalStateException]
|
||||
dh.close
|
||||
|
||||
ok
|
||||
}
|
||||
}
|
||||
}
|
||||
133
common/src/test/scala/CryptoPacketTest.scala
Normal file
133
common/src/test/scala/CryptoPacketTest.scala
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
import org.specs2.mutable._
|
||||
import psforever.net._
|
||||
import scodec.Codec
|
||||
import scodec.bits._
|
||||
|
||||
class CryptoPacketTest extends Specification {
|
||||
|
||||
"PlanetSide crypto packet" in {
|
||||
val cNonce = 656287232
|
||||
|
||||
"ClientStart" should {
|
||||
val string = hex"0000000200261e27000001f0".bits
|
||||
|
||||
"decode" in {
|
||||
val res = Codec.decode[ClientStart](string)
|
||||
res.isSuccessful mustEqual true
|
||||
res.require.value.clientNonce mustEqual cNonce
|
||||
}
|
||||
|
||||
"encode" in {
|
||||
val res = Codec.encode(ClientStart(cNonce))
|
||||
|
||||
res.require mustEqual string
|
||||
}
|
||||
}
|
||||
|
||||
"ServerStart" should {
|
||||
val sNonce = 3468803409L
|
||||
val string = hex"00261e2751bdc1ce000000000001d300000002".bits
|
||||
|
||||
"decode" in {
|
||||
val res = Codec.decode[ServerStart](string)
|
||||
val value = res.require.value
|
||||
|
||||
value.clientNonce mustEqual cNonce
|
||||
value.serverNonce mustEqual sNonce
|
||||
}
|
||||
|
||||
"encode" in {
|
||||
val res = Codec.encode(ServerStart(cNonce, sNonce))
|
||||
|
||||
res.require mustEqual string
|
||||
}
|
||||
}
|
||||
|
||||
"ClientChallengeXchg" should {
|
||||
val time = hex"962d8453"
|
||||
val timeDecoded = scodec.codecs.uint32L.decode(time.bits).require.value
|
||||
val challenge = hex"24f5997c c7d16031 d1f567e9"
|
||||
val p = hex"f57511eb 8e5d1efb 8b7f3287 d5a18b17"
|
||||
val g = hex"00000000 00000000 00000000 00000002"
|
||||
val string = (hex"0101" ++ time ++ challenge ++ hex"00 01 0002 ff 2400 00 1000" ++
|
||||
p ++ hex"1000" ++ g ++ hex"0000010307000000").bits
|
||||
|
||||
"decode" in {
|
||||
val res = Codec.decode[ClientChallengeXchg](string)
|
||||
val value = res.require.value
|
||||
|
||||
value.time mustEqual timeDecoded
|
||||
value.challenge mustEqual challenge
|
||||
value.p mustEqual p
|
||||
value.g mustEqual g
|
||||
}
|
||||
|
||||
"encode" in {
|
||||
val res = Codec.encode(ClientChallengeXchg(timeDecoded, challenge, p, g))
|
||||
|
||||
res.require mustEqual string
|
||||
}
|
||||
}
|
||||
|
||||
"ServerChallengeXchg" should {
|
||||
val time = hex"962d8453"
|
||||
val timeDecoded = scodec.codecs.uint32L.decode(time.bits).require.value
|
||||
val challenge = hex"1b0e6408 cd935ec2 429aeb58"
|
||||
val pubKey = hex"51f83ce6 45e86c3e 79c8fc70 f6ddf14b"
|
||||
val string = (hex"0201" ++ time ++ challenge ++ hex"00 01 03070000000c00 1000 " ++ pubKey ++ hex"0e").bits
|
||||
|
||||
"decode" in {
|
||||
val res = Codec.decode[ServerChallengeXchg](string)
|
||||
val value = res.require.value
|
||||
|
||||
value.time mustEqual timeDecoded
|
||||
value.challenge mustEqual challenge
|
||||
value.pubKey mustEqual pubKey
|
||||
}
|
||||
|
||||
"encode" in {
|
||||
val res = Codec.encode(ServerChallengeXchg(timeDecoded, challenge, pubKey))
|
||||
|
||||
res.require mustEqual string
|
||||
}
|
||||
}
|
||||
|
||||
"ClientFinished" should {
|
||||
val challengeResult = hex"ea3cf05d a5cb4256 8bb91aa7"
|
||||
val pubKey = hex"eddc35f2 52b02d0e 496ba273 54578e73"
|
||||
val string = (hex"10 1000" ++ pubKey ++ hex"0114 " ++ challengeResult).bits
|
||||
|
||||
"decode" in {
|
||||
val res = Codec.decode[ClientFinished](string)
|
||||
val value = res.require.value
|
||||
|
||||
value.challengeResult mustEqual challengeResult
|
||||
value.pubKey mustEqual pubKey
|
||||
}
|
||||
|
||||
"encode" in {
|
||||
val res = Codec.encode(ClientFinished(pubKey, challengeResult))
|
||||
|
||||
res.require mustEqual string
|
||||
}
|
||||
}
|
||||
|
||||
"ServerFinished" should {
|
||||
val challengeResult = hex"d64ffb8e 526311b4 af46bece"
|
||||
val string = (hex"0114" ++ challengeResult).bits
|
||||
|
||||
"decode" in {
|
||||
val res = Codec.decode[ServerFinished](string)
|
||||
val value = res.require.value
|
||||
|
||||
value.challengeResult mustEqual challengeResult
|
||||
}
|
||||
|
||||
"encode" in {
|
||||
val res = Codec.encode(ServerFinished(challengeResult))
|
||||
|
||||
res.require mustEqual string
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
54
common/src/test/scala/GamePacketTest.scala
Normal file
54
common/src/test/scala/GamePacketTest.scala
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) 2016 PSForever.net to present
|
||||
import org.specs2.mutable._
|
||||
import psforever.net._
|
||||
import scodec.bits._
|
||||
|
||||
class GamePacketTest extends Specification {
|
||||
|
||||
"PlanetSide game packet" in {
|
||||
val cNonce = 656287232
|
||||
|
||||
"VNLWorldStatusMessage" should {
|
||||
val string = hex"0597570065006c0063006f006d006500200074006f00200050006c0061006e00650074005300690064006500210020000186" ++
|
||||
hex"67656d696e69" ++ hex"0100 01 00 01459e2540 3775" ++ bin"01".toByteVector
|
||||
|
||||
"decode" in {
|
||||
PacketCoding.DecodePacket(string).require match {
|
||||
case VNLWorldStatusMessage(message, worlds) =>
|
||||
worlds.length mustEqual 1
|
||||
message mustEqual "Welcome to PlanetSide! "
|
||||
worlds{0}.name mustEqual "gemini"
|
||||
worlds{0}.empireNeed mustEqual EmpireNeed.NC
|
||||
worlds{0}.status mustEqual WorldStatus.Up
|
||||
case default =>
|
||||
true mustEqual false
|
||||
}
|
||||
}
|
||||
|
||||
"encode" in {
|
||||
val msg = VNLWorldStatusMessage("Welcome to PlanetSide! ",
|
||||
Vector(WorldInformation("gemini", WorldStatus.Up, ServerType.Beta, EmpireNeed.NC)))
|
||||
//0100 04 00 01459e2540377540
|
||||
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
pkt mustEqual string
|
||||
}
|
||||
|
||||
"encode and decode multiple worlds" in {
|
||||
val msg = VNLWorldStatusMessage("Welcome to PlanetSide! ",
|
||||
Vector(
|
||||
WorldInformation("PSForever1", WorldStatus.Up, ServerType.Released, EmpireNeed.NC),
|
||||
WorldInformation("PSForever2", WorldStatus.Down, ServerType.Beta, EmpireNeed.TR)
|
||||
))
|
||||
|
||||
val pkt = PacketCoding.EncodePacket(msg).require.toByteVector
|
||||
|
||||
println(pkt)
|
||||
|
||||
true mustEqual true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
import org.specs2.mutable._
|
||||
import psforever.crypto.CryptoInterface
|
||||
import psforever.net._
|
||||
import scodec.bits._
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue