encryption + complete example. The ideas are there, but it doesn't seems to be accepted by the client yet
This commit is contained in:
parent
2e1e3f8bb2
commit
66111dad54
3 changed files with 222 additions and 80 deletions
|
|
@ -15,17 +15,96 @@ var server = pmp.createServer({
|
|||
});
|
||||
|
||||
server.on('connection', function(client) {
|
||||
client.on("mcpe", packet => console.log(packet));
|
||||
|
||||
client.on("login", data => {
|
||||
console.log(client.displayName + '(' + client.XUID + ') ' + ' joined the game');
|
||||
|
||||
client.on("mcpe",packet => console.log(packet));
|
||||
|
||||
client.on("login",packet => {
|
||||
console.log("aaaaa");
|
||||
|
||||
client.writeMCPE("player_status",{
|
||||
status:0
|
||||
});
|
||||
/*
|
||||
client.writeMCPE('move_player', {
|
||||
entityId: [0,0],
|
||||
x: 1,
|
||||
y: 64 + 1.62,
|
||||
z: 1,
|
||||
yaw: 0,
|
||||
headYaw: 0,
|
||||
pitch: 0,
|
||||
mode: 0,
|
||||
onGround: 1
|
||||
});
|
||||
|
||||
client.writeMCPE("start_game",{
|
||||
seed:-1,
|
||||
dimension:0,
|
||||
generator:1,
|
||||
gamemode:1,
|
||||
entityId:[0,0],
|
||||
spawnX:1,
|
||||
spawnY:1,
|
||||
spawnZ:1,
|
||||
x:0,
|
||||
y:1+1.62,
|
||||
z:0,
|
||||
isLoadedInCreative:0,
|
||||
dayCycleStopTime:0,
|
||||
eduMode:0,
|
||||
unknown:""
|
||||
});*/
|
||||
/*
|
||||
client.writeMCPE('set_spawn_position', {
|
||||
x: 1,
|
||||
y: 64,
|
||||
z: 1
|
||||
});
|
||||
client.writeMCPE("set_time",{
|
||||
time:0,
|
||||
started:1
|
||||
});
|
||||
|
||||
client.writeMCPE('respawn', {
|
||||
x: 1,
|
||||
y: 64,
|
||||
z: 1
|
||||
});*/
|
||||
});
|
||||
|
||||
client.on('error', err => {
|
||||
client.on("request_chunk_radius",() => {
|
||||
client.writeMCPE('chunk_radius_update',{
|
||||
chunk_radius:1
|
||||
});
|
||||
|
||||
for (let x = -1; x <=1; x++) {
|
||||
for (let z = -1; z <=1; z++) {
|
||||
client.writeBatch([{"name":"mcpe","params":{name:"full_chunk_data",params:{
|
||||
chunkX: x,
|
||||
chunkZ: z,
|
||||
order: 1,
|
||||
chunkData:fs.readFileSync(__dirname+"/chunk")
|
||||
}}}]);
|
||||
}
|
||||
}
|
||||
|
||||
client.writeMCPE('player_status', {
|
||||
status: 3
|
||||
});
|
||||
|
||||
client.writeMCPE('set_time', {
|
||||
time: 0,
|
||||
started: 1
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
client.on('error', function(err) {
|
||||
console.log(err.stack);
|
||||
});
|
||||
|
||||
client.on('end', () => {
|
||||
client.on('end',function() {
|
||||
console.log("client left");
|
||||
})
|
||||
});
|
||||
});
|
||||
31
examples/server_simple.js
Normal file
31
examples/server_simple.js
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
'use strict';
|
||||
|
||||
var pmp = require('../');
|
||||
var fs = require("fs");
|
||||
|
||||
if(process.argv.length !=4) {
|
||||
console.log("Usage: node server.js <host> <port>");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var server = pmp.createServer({
|
||||
host: process.argv[2],
|
||||
port: parseInt(process.argv[3]),
|
||||
name: 'MCPE;Minecraft: PE Server;81 81;0.15.0;0;20'
|
||||
});
|
||||
|
||||
server.on('connection', function(client) {
|
||||
client.on("mcpe", packet => console.log(packet));
|
||||
|
||||
client.on("login", data => {
|
||||
console.log(client.displayName + '(' + client.XUID + ') ' + ' joined the game');
|
||||
});
|
||||
|
||||
client.on('error', err => {
|
||||
console.log(err.stack);
|
||||
});
|
||||
|
||||
client.on('end', () => {
|
||||
console.log("client left");
|
||||
})
|
||||
});
|
||||
|
|
@ -3,11 +3,13 @@ const raknet = require('raknet');
|
|||
const zlib = require('zlib');
|
||||
const ProtoDef = require('protodef').ProtoDef;
|
||||
const Parser = require('protodef').Parser;
|
||||
const Serializer = require('protodef').Serializer;
|
||||
const jwt = require('jwt-simple');
|
||||
const crypto = require('crypto');
|
||||
const Ber = require('asn1').Ber;
|
||||
const merge=require("lodash.merge");
|
||||
const assert=require("assert");
|
||||
var debug = require('debug')("raknet");
|
||||
// const BN = require('bn.js');
|
||||
|
||||
const batchProto = new ProtoDef();
|
||||
|
|
@ -32,12 +34,50 @@ dataProto.addType("data_chain", ["container", [{
|
|||
}]]);
|
||||
|
||||
|
||||
function writeLI64(value, buffer, offset) {
|
||||
buffer.writeInt32LE(value[0], offset+4);
|
||||
buffer.writeInt32LE(value[1], offset);
|
||||
return offset + 8;
|
||||
}
|
||||
|
||||
function computeCheckSum(packetPlaintext,sendCounter,secretKeyBytes) {
|
||||
let digest = crypto.createHash('sha256');
|
||||
let counter=new Buffer(8);
|
||||
writeLI64(sendCounter,counter,0);
|
||||
digest.update(counter);
|
||||
digest.update(packetPlaintext);
|
||||
digest.update(secretKeyBytes);
|
||||
let hash = digest.digest();
|
||||
|
||||
return hash.slice(0,8);
|
||||
}
|
||||
|
||||
|
||||
function readX509PublicKey(key) {
|
||||
var reader = new Ber.Reader(new Buffer(key, "base64"));
|
||||
reader.readSequence();
|
||||
reader.readSequence();
|
||||
reader.readOID();
|
||||
reader.readOID();
|
||||
return new Buffer(reader.readString(Ber.BitString, true)).slice(1);
|
||||
}
|
||||
|
||||
function writeX509PublicKey(key) {
|
||||
var writer = new Ber.Writer();
|
||||
writer.startSequence();
|
||||
writer.startSequence();
|
||||
writer.writeOID("1.2.840.10045.2.1");
|
||||
writer.writeOID("1.3.132.0.34");
|
||||
writer.endSequence();
|
||||
writer.writeBuffer(Buffer.concat([new Buffer([0x00]),key]),Ber.BitString);
|
||||
writer.endSequence();
|
||||
return writer.buffer.toString("base64");
|
||||
}
|
||||
|
||||
const Transform = require('stream').Transform;
|
||||
|
||||
|
||||
const PUBLIC_KEY = 'MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8ELkixyLcwlZryUQcu1TvPOmI2B7vX83ndnWRUaXm74wFfa5f/lwQNTfrLVHa2PmenpGI6JhIMUJaWZrjmMj90NoKNFSNBuKdm8rYiXsfaz3K36x/1U26HpG0ZxK/V1V';
|
||||
var encryptionEnabled = false;
|
||||
var sendCounter = 0;
|
||||
|
||||
function createServer(options) {
|
||||
options = options || {};
|
||||
|
|
@ -57,41 +97,19 @@ function createServer(options) {
|
|||
server.maxPlayers = options['max-players'] || 20;
|
||||
server.playerCount = 0;
|
||||
|
||||
function readX509PublicKey(key) {
|
||||
var reader = new Ber.Reader(new Buffer(key, "base64"));
|
||||
reader.readSequence();
|
||||
reader.readSequence();
|
||||
reader.readOID();
|
||||
reader.readOID();
|
||||
return new Buffer(reader.readString(Ber.BitString, true)).slice(1);
|
||||
}
|
||||
|
||||
function writeX509PublicKey(key) {
|
||||
var writer = new Ber.Writer();
|
||||
writer.startSequence();
|
||||
writer.startSequence();
|
||||
writer.writeOID("1.2.840.10045.2.1");
|
||||
writer.writeOID("1.3.132.0.34");
|
||||
writer.endSequence();
|
||||
writer.writeBuffer(Buffer.concat([new Buffer([0x00]),key]),Ber.BitString);
|
||||
writer.endSequence();
|
||||
return writer.buffer.toString("base64");
|
||||
}
|
||||
|
||||
server.on("connection", function(client) {
|
||||
|
||||
client.counter=0;
|
||||
client.receiveCounter=0;
|
||||
client.sendCounter=0;
|
||||
|
||||
client.encryptionEnabled = false;
|
||||
|
||||
//if(encryptionEnabled) {
|
||||
// client.on('mcpe', packet => { decipher.write(packet); })
|
||||
//} else {
|
||||
client.on("mcpe", packet => {
|
||||
console.log("actual mcpe",packet);
|
||||
client.emit(packet.name, packet.params)
|
||||
});
|
||||
//}
|
||||
|
||||
// decipher.on('data', data => console.log(data))
|
||||
|
||||
client.on("game_login", packet => {
|
||||
var body = packet.body;
|
||||
|
|
@ -133,81 +151,68 @@ function createServer(options) {
|
|||
publicKey: pubKeyServer,
|
||||
serverToken: "SO SECRET VERY SECURE" // obviously, this is super secure (it's not, change it)
|
||||
});
|
||||
//console.log('secret', new Buffer(client.secretKeyBytes));
|
||||
var decipher = crypto.createDecipheriv('aes-256-cfb8', client.secretKeyBytes, client.secretKeyBytes.slice(0,16));
|
||||
let customPackets=JSON.parse(JSON.stringify(require("../data/protocol")));
|
||||
customPackets['types']['encapsulated_packet'][1][1]['type'][1]['fields']['mcpe_encrypted'] = 'restBuffer';
|
||||
customPackets['types']['encapsulated_packet'][1][0]['type'][1]['mappings']['0xfe'] = 'mcpe_encrypted';
|
||||
client.encapsulatedPacketParser.proto.addTypes(merge(require('raknet').protocol, customPackets).types);
|
||||
encryptionEnabled = true;
|
||||
client.encryptionEnabled = true;
|
||||
|
||||
client.on("mcpe_encrypted", packet => {
|
||||
//console.log('THE CONSOLELOG')
|
||||
//console.log(packet);
|
||||
//console.log('---------------')
|
||||
console.log("raw",packet);
|
||||
decipher.write(packet);
|
||||
});
|
||||
decipher.on('data', data => console.log('decrypt', data));
|
||||
|
||||
|
||||
function writeLI64(value, buffer, offset) {
|
||||
buffer.writeInt32LE(value[0], offset+4);
|
||||
buffer.writeInt32LE(value[1], offset);
|
||||
return offset + 8;
|
||||
}
|
||||
|
||||
function computeCheckSum(packetPlaintext,sendCounter,secretKeyBytes) {
|
||||
let digest = crypto.createHash('sha256');
|
||||
let counter=new Buffer(8);
|
||||
writeLI64(sendCounter,counter,0);
|
||||
digest.update(counter);
|
||||
digest.update(packetPlaintext);
|
||||
digest.update(secretKeyBytes);
|
||||
let hash = digest.digest();
|
||||
|
||||
return hash.slice(0,8);
|
||||
}
|
||||
client.cipher=crypto.createCipheriv('aes-256-cfb8', client.secretKeyBytes, client.secretKeyBytes.slice(0,16));
|
||||
|
||||
|
||||
const checksumTransform = new Transform({
|
||||
transform(chunk,enc,cb) {
|
||||
const packet=chunk.slice(0,chunk.length-8);
|
||||
const checksum=chunk.slice(chunk.length-8);
|
||||
const computedCheckSum=computeCheckSum(packet,[0,client.counter],client.secretKeyBytes);
|
||||
const pass=checksum.toString("hex")==computedCheckSum.toString("hex");
|
||||
const computedCheckSum=computeCheckSum(packet,[0,client.receiveCounter],client.secretKeyBytes);
|
||||
//assert.equal(checksum.toString("hex"),computedCheckSum.toString("hex"));
|
||||
client.counter++;
|
||||
if(pass) this.push(packet);
|
||||
client.receiveCounter++;
|
||||
if(checksum.toString("hex")==computedCheckSum.toString("hex")) this.push(packet);
|
||||
cb();
|
||||
}
|
||||
});
|
||||
client.addChecksumTransform = new Transform({
|
||||
transform(chunk,enc,cb) {
|
||||
const packet=Buffer.concat([chunk,computeCheckSum(chunk,[0,client.sendCounter],client.secretKeyBytes)]);
|
||||
client.sendCounter++;
|
||||
this.push(packet);
|
||||
cb();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
checksumTransform.on("data",data => console.log('sliced',data));
|
||||
decipher.pipe(checksumTransform);
|
||||
const proto=new ProtoDef();
|
||||
proto.addTypes(require("./datatypes/minecraft"));
|
||||
proto.addTypes(require("../data/protocol").types);
|
||||
const mcpePacketParser=new Parser(proto,"mcpe_packet");
|
||||
checksumTransform.pipe(mcpePacketParser);
|
||||
mcpePacketParser.on("data",parsed => console.log("parsed data",parsed));
|
||||
mcpePacketParser.on("data",parsed => client.emitPacket(parsed));
|
||||
client.mcpePacketParser=new Parser(proto,"mcpe_packet");
|
||||
client.mcpePacketSerializer=new Serializer(proto,"mcpe_packet");
|
||||
checksumTransform.pipe(client.mcpePacketParser);
|
||||
client.mcpePacketParser.on("data",parsed => console.log("parsed data",parsed));
|
||||
client.mcpePacketParser.on("data",parsed => {if(parsed.data.name=="batch") parsed.data.name="batch_non_encrypted"; return client.emitPacket(parsed)});
|
||||
|
||||
//client.on('mcpe', packet => { decipher.write(packet); })
|
||||
|
||||
client.emit('login', {
|
||||
displayName: client.displayName,
|
||||
randomId: client.randomId,
|
||||
skinData: client.skinData,
|
||||
skinId: client.skinId,
|
||||
identity: client.identity,
|
||||
XUID: client.XUID
|
||||
});
|
||||
client.mcpePacketSerializer.pipe(client.addChecksumTransform);
|
||||
client.addChecksumTransform.pipe(client.cipher);
|
||||
});
|
||||
|
||||
client.writeMCPE = (name, packet) => {
|
||||
client.writeEncapsulated("mcpe", {
|
||||
name: name,
|
||||
params: packet
|
||||
});
|
||||
client.writeMCPE = (name, params) => {
|
||||
if(client.encryptionEnabled) {
|
||||
client.mcpePacketSerializer.write({ name, params });
|
||||
client.cipher.on('data', data => console.log("crypted",data));
|
||||
client.cipher.on('data', data => client.writeEncapsulated("mcpe_encrypted", data));
|
||||
}
|
||||
else {
|
||||
client.writeEncapsulated("mcpe", { name, params });
|
||||
}
|
||||
};
|
||||
|
||||
client.writeBatch = function(packets) {
|
||||
|
|
@ -222,10 +227,37 @@ function createServer(options) {
|
|||
|
||||
client.on('batch', function(packet) {
|
||||
var buf = zlib.inflateSync(packet.payload);
|
||||
console.log("in batch",buf);
|
||||
var packets = batchProto.parsePacketBuffer("insideBatch", buf).data;
|
||||
packets.forEach(packet => client.readEncapsulatedPacket(Buffer.concat([new Buffer([0xfe]), packet])));
|
||||
});
|
||||
|
||||
client.on('client_to_server_handshake',() => {
|
||||
console.log("plop");
|
||||
client.emit('login', {
|
||||
displayName: client.displayName,
|
||||
randomId: client.randomId,
|
||||
skinData: client.skinData,
|
||||
skinId: client.skinId,
|
||||
identity: client.identity,
|
||||
XUID: client.XUID
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
client.on('batch_non_encrypted', function(packet) {
|
||||
var buf = zlib.inflateSync(packet.payload);
|
||||
var packets = batchProto.parsePacketBuffer("insideBatch", buf).data;
|
||||
packets.forEach(packet => {
|
||||
try {
|
||||
debug("handle mcpe",packet);
|
||||
var r = client.mcpePacketParser.parsePacketBuffer(packet);
|
||||
client.emitPacket(r);
|
||||
}
|
||||
catch(err) {
|
||||
client.emit("error",err);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
return server;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue