server player extends connection

This commit is contained in:
extremeheat 2021-02-07 05:28:03 -05:00
commit da85b80c59
4 changed files with 148 additions and 114 deletions

View file

@ -80,7 +80,18 @@ function decodeLoginJWT(authTokens, skinTokens) {
return { key, userData: data, skinData }
}
function test() {
function encodeLoginJWT(localChain, mojangChain) {
const chains = []
chains.push(localChain)
for (const chain of mojangChain) {
chains.push(chain)
}
return chains
}
module.exports = { encodeLoginJWT, decodeLoginJWT }
function testServer() {
const loginPacket = require('./login.json')
// console.log(loginPacket)
@ -95,10 +106,7 @@ function test() {
}
console.log('Authed')
// console.log(loginPacket)
}
module.exports = { decodeLoginJWT }
// test()
// testServer()

View file

@ -4,15 +4,19 @@ const { Ber } = require('asn1')
const ec_pem = require('ec-pem')
const SALT = '🧂'
const curve = 'secp384r1'
function Encrypt(client, options) {
client.ecdhKeyPair = crypto.createECDH(curve)
client.ecdhKeyPair.generateKeys()
createClientChain(client)
function startClientboundEncryption(publicKey) {
console.warn('[encrypt] Pub key base64: ', publicKey)
const pubKeyBuf = readX509PublicKey(publicKey.key)
const curve = 'secp384r1'
const alice = crypto.createECDH(curve)
alice.generateKeys()
const alice = client.ecdhKeyPair
const alicePEM = ec_pem(alice, curve) // https://github.com/nodejs/node/issues/15116#issuecomment-384790125
const alicePEMPrivate = alicePEM.encodePrivateKey()
// Shared secret from bob's public key + our private key
@ -47,9 +51,27 @@ function Encrypt(client, options) {
client.startEncryption(initial)
}
function startServerboundEncryption() {
}
client.on('server.client_handshake', startClientboundEncryption)
}
function createClientChain(client) {
const alice = client.ecdhKeyPair
const alicePEM = ec_pem(alice, curve) // https://github.com/nodejs/node/issues/15116#issuecomment-384790125
const alicePEMPrivate = alicePEM.encodePrivateKey()
const x509 = writeX509PublicKey(alice.getPublicKey())
const token = JWT.sign({
salt: toBase64(SALT),
signedToken: alice.getPublicKey('base64')
}, alicePEMPrivate, { algorithm: 'ES384', header: { x5u: x509 } })
client.clientChain = token
}
function toBase64(string) {
return Buffer.from(string).toString('base64')
}

107
src/connection.js Normal file
View file

@ -0,0 +1,107 @@
const BinaryStream = require('@jsprismarine/jsbinaryutils').default
const BatchPacket = require('./BatchPacket')
const cipher = require('./transforms/encryption')
const { EventEmitter } = require('events')
const EncapsulatedPacket = require('@jsprismarine/raknet/protocol/encapsulated_packet')
class Connection extends EventEmitter {
startEncryption(iv) {
this.encryptionEnabled = true
this.decrypt = cipher.createDecryptor(this, iv)
this.encrypt = cipher.createEncryptor(this, iv)
}
write(name, params) { // TODO: Batch
console.log('Need to encode', name, params)
const batch = new BatchPacket()
const packet = this.server.serializer.createPacketBuffer({ name, params })
batch.addEncodedPacket(packet)
if (this.encryptionEnabled) {
this.sendEncryptedBatch(batch)
} else {
this.sendDecryptedBatch(batch)
}
}
writeRaw(name, buffer) { // skip protodef serializaion
// temporary hard coded stuff
const batch = new BatchPacket()
if (name == 'biome_definition_list') {
// so we can send nbt straight from file without parsing
const stream = new BinaryStream()
stream.writeUnsignedVarInt(0x7a)
stream.append(buffer)
batch.addEncodedPacket(stream.getBuffer())
// console.log('----- SENDING BIOME DEFINITIONS')
}
if (this.encryptionEnabled) {
this.sendEncryptedBatch(batch)
} else {
this.sendDecryptedBatch(batch)
}
}
sendDecryptedBatch(batch) {
const buf = batch.encode()
// send to raknet
const sendPacket = new EncapsulatedPacket();
sendPacket.reliability = 0;
sendPacket.buffer = buf
this.connection.addEncapsulatedToQueue(sendPacket)
this.connection.sendQueue()
}
sendEncryptedBatch(batch) {
const buf = batch.stream.getBuffer()
console.log('Sending encrypted batch', batch)
this.encrypt(buf)
}
// These are callbacks called from encryption.js
onEncryptedPacket = (buf) => {
console.log('ENC BUF', buf)
const packet = Buffer.concat([Buffer.from([0xfe]), buf]) // add header
const sendPacket = new EncapsulatedPacket();
sendPacket.reliability = 0
sendPacket.buffer = packet
console.log('Sending wrapped encrypted batch', packet)
this.connection.addEncapsulatedToQueue(sendPacket)
}
onDecryptedPacket = (buf) => {
console.log('Decrypted', buf)
const stream = new BinaryStream(buf)
const packets = BatchPacket.getPackets(stream)
for (const packet of packets) {
this.readPacket(packet)
}
}
handle(buffer) { // handle encapsulated
if (buffer[0] == 0xfe) { // wrapper
if (this.encryptionEnabled) {
// console.log('READING ENCRYPTED PACKET', buffer)
this.decrypt(buffer.slice(1))
} else {
const stream = new BinaryStream(buffer)
const batch = new BatchPacket(stream)
batch.decode()
const packets = batch.getPackets()
console.log('Reading ', packets.length, 'packets')
for (var packet of packets) {
this.readPacket(packet)
}
}
}
}
}
module.exports = { Connection }

View file

@ -1,14 +1,9 @@
const BinaryStream = require('@jsprismarine/jsbinaryutils').default
const Listener = require('@jsprismarine/raknet/listener')
const { ProtoDef, Parser, Serializer } = require('protodef')
const BatchPacket = require('./BatchPacket')
const { EventEmitter } = require('events')
const cipher = require('./transforms/encryption')
const { Encrypt } = require('./auth/encryption')
const { decodeLoginJWT } = require('./auth/jwt')
const EncapsulatedPacket = require('@jsprismarine/raknet/protocol/encapsulated_packet')
const { decodeLoginJWT } = require('./auth/chains')
const { Connection } = require('./connection')
var protocol = require('../data/newproto.json').types;
@ -41,7 +36,7 @@ const PLAY_STATUS = {
'LoginFailedServerFull': 7
}
class Player extends EventEmitter {
class Player extends Connection {
constructor(server, connection, options) {
super()
this.server = server
@ -100,84 +95,6 @@ class Player extends EventEmitter {
this.emit('join')
}
startEncryption(iv) {
this.encryptionEnabled = true
this.decrypt = cipher.createDecryptor(this, iv)
this.encrypt = cipher.createEncryptor(this, iv)
}
write(name, params) { // TODO: Batch
console.log('Need to encode', name, params)
const batch = new BatchPacket()
const packet = this.server.serializer.createPacketBuffer({ name, params })
batch.addEncodedPacket(packet)
if (this.encryptionEnabled) {
this.sendEncryptedBatch(batch)
} else {
this.sendDecryptedBatch(batch)
}
}
writeRaw(name, buffer) { // skip protodef serializaion
// temporary hard coded stuff
const batch = new BatchPacket()
if (name == 'biome_definition_list') {
// so we can send nbt straight from file without parsing
const stream = new BinaryStream()
stream.writeUnsignedVarInt(0x7a)
stream.append(buffer)
batch.addEncodedPacket(stream.getBuffer())
// console.log('----- SENDING BIOME DEFINITIONS')
}
if (this.encryptionEnabled) {
this.sendEncryptedBatch(batch)
} else {
this.sendDecryptedBatch(batch)
}
}
sendDecryptedBatch(batch) {
const buf = batch.encode()
// send to raknet
const sendPacket = new EncapsulatedPacket();
sendPacket.reliability = 0;
sendPacket.buffer = buf
this.connection.addEncapsulatedToQueue(sendPacket)
this.connection.sendQueue()
}
sendEncryptedBatch(batch) {
const buf = batch.stream.getBuffer()
console.log('Sending encrypted batch', batch)
this.encrypt(buf)
}
// These are callbacks called from encryption.js
onEncryptedPacket = (buf) => {
console.log('ENC BUF', buf)
const packet = Buffer.concat([Buffer.from([0xfe]), buf]) // add header
const sendPacket = new EncapsulatedPacket();
sendPacket.reliability = 0
sendPacket.buffer = packet
console.log('Sending wrapped encrypted batch', packet)
this.connection.addEncapsulatedToQueue(sendPacket)
}
onDecryptedPacket = (buf) => {
console.log('Decrypted', buf)
const stream = new BinaryStream(buf)
const packets = BatchPacket.getPackets(stream)
for (const packet of packets) {
this.readPacket(packet)
}
}
readPacket(packet) {
console.log('packet', packet)
const des = this.server.deserializer.parsePacketBuffer(packet)
@ -193,26 +110,6 @@ class Player extends EventEmitter {
console.log('ignoring, unhandled')
}
this.emit(des.data.name, des.data.params)
}
handle(buffer) { // handle encapsulated
if (buffer[0] == 0xfe) { // wrapper
if (this.encryptionEnabled) {
// console.log('READING ENCRYPTED PACKET', buffer)
this.decrypt(buffer.slice(1))
} else {
const stream = new BinaryStream(buffer)
const batch = new BatchPacket(stream)
batch.decode()
const packets = batch.getPackets()
console.log('Reading ', packets.length, 'packets')
for (var packet of packets) {
this.readPacket(packet)
}
}
}
}
}