From 321a97acb404fd690a6484afd538baf7f4d57d07 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 31 Dec 2012 23:53:36 -0500 Subject: [PATCH] successfully getting the encryption key response --- lib/parser.js | 103 +++++++++++++++++++++++++++++++++++++++++++++++++- packets.json | 16 ++++++++ test.js | 49 ++++++++++++++++++------ 3 files changed, 155 insertions(+), 13 deletions(-) diff --git a/lib/parser.js b/lib/parser.js index 7ef93a3..dc0d98a 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -49,6 +49,7 @@ var writers = { 'int': IntWriter, 'byte': ByteWriter, 'string': StringWriter, + 'byteArray': ByteArrayWriter, }; var readers = { @@ -107,8 +108,19 @@ function StringWriter(value) { StringWriter.prototype.write = function(buffer, offset) { buffer.writeInt16BE(this.value.length, offset); this.encoded.copy(buffer, offset + 2); +}; + +function ByteArrayWriter(value) { + assert.ok(Buffer.isBuffer(value), "non buffer passed to ByteArrayWriter"); + this.value = value; + this.size = 2 + value.length; } +ByteArrayWriter.prototype.write = function(buffer, offset) { + buffer.writeInt16BE(this.value.length, offset); + this.value.copy(buffer, offset + 2); +}; + function ByteWriter(value) { this.value = value; this.size = 1; @@ -118,6 +130,15 @@ ByteWriter.prototype.write = function(buffer, offset) { buffer.writeInt8(this.value, offset); } +function UByteWriter(value) { + this.value = value; + this.size = 1; +} + +UByteWriter.prototype.write = function(buffer, offset) { + buffer.writeUInt8(this.value, offset); +}; + function IntWriter(value) { this.value = value; this.size = 4; @@ -129,11 +150,13 @@ IntWriter.prototype.write = function(buffer, offset) { function createPacketBuffer(packetId, params) { var size = 1; - var fields = [ new ByteWriter(packetId) ]; + var fields = [ new UByteWriter(packetId) ]; var packet = packets[packetId]; packet.forEach(function(fieldInfo) { var value = params[fieldInfo.name]; - var field = new writers[fieldInfo.type](value); + var Writer = writers[fieldInfo.type]; + assert.ok(Writer, "missing writer for data type: " + fieldInfo.type); + var field = new Writer(value); size += field.size; fields.push(field); }); @@ -171,3 +194,79 @@ function parsePacket(buffer) { results: results, }; } + +// packet ids +Parser.KEEP_ALIVE = 0x00; +Parser.LOGIN_REQUEST = 0x01; +Parser.HANDSHAKE = 0x02; +Parser.CHAT_MESSAGE = 0x03; +Parser.TIME_UPDATE = 0x04; +Parser.ENTITY_EQUIPMENT = 0x05; +Parser.SPAWN_POSITION = 0x06; +Parser.USE_ENTITY = 0x07; +Parser.UPDATE_HEALTH = 0x08; +Parser.RESPAWN = 0x09; +Parser.PLAYER = 0x0A; +Parser.PLAYER_POSITION = 0x0B; +Parser.PLAYER_LOOK = 0x0C; +Parser.PLAYER_POSITION_AND_LOOK = 0x0D; +Parser.PLAYER_DIGGING = 0x0E; +Parser.PLAYER_BLOCK_PLACEMENT = 0x0F; +Parser.HELD_ITEM_CHANGE = 0x10; +Parser.USE_BED = 0x11; +Parser.ANIMATION = 0x12; +Parser.ENTITY_ACTION = 0x13; +Parser.SPAWN_NAMED_ENTITY = 0x14; +Parser.COLLECT_ITEM = 0x16; +Parser.SPAWN_OBJECT_VEHICLE = 0x17; +Parser.SPAWN_MOB = 0x18; +Parser.SPAWN_PAINTING = 0x19; +Parser.SPAWN_EXPERIENCE_ORB = 0x1A; +Parser.ENTITY_VELOCITY = 0x1C; +Parser.DESTROY_ENTITY = 0x1D; +Parser.ENTITY = 0x1E; +Parser.ENTITY_RELATIVE_MOVE = 0x1F; +Parser.ENTITY_LOOK = 0x20; +Parser.ENTITY_LOOK_AND_RELATIVE_MOVE = 0x21; +Parser.ENTITY_TELEPORT = 0x22; +Parser.ENTITY_HEAD_LOOK = 0x23; +Parser.ENTITY_STATUS = 0x26; +Parser.ATTACH_ENTITY = 0x27; +Parser.ENTITY_METADATA = 0x28; +Parser.ENTITY_EFFECT = 0x29; +Parser.REMOVE_ENTITY_EFFECT = 0x2A; +Parser.SET_EXPERIENCE = 0x2B; +Parser.CHUNK_DATA = 0x33; +Parser.MULTI_BLOCK_CHANGE = 0x34; +Parser.BLOCK_CHANGE = 0x35; +Parser.BLOCK_ACTION = 0x36; +Parser.BLOCK_BREAK_ANIMATION = 0x37; +Parser.MAP_CHUNK_BULK = 0x38; +Parser.EXPLOSION = 0x3C; +Parser.SOUND_OR_PARTICLE_EFFECT = 0x3D; +Parser.NAMED_SOUND_EFFECT = 0x3E; +Parser.CHANGE_GAME_STATE = 0x46; +Parser.SPAWN_GLOBAL_ENTITY = 0x47; +Parser.OPEN_WINDOW = 0x64; +Parser.CLOSE_WINDOW = 0x65; +Parser.CLICK_WINDOW = 0x66; +Parser.SET_SLOT = 0x67; +Parser.SET_WINDOW_ITEMS = 0x68; +Parser.UPDATE_WINDOW_PROPERTY = 0x69; +Parser.CONFIRM_TRANSACTION = 0x6A; +Parser.CREATIVE_INVENTORY_ACTION = 0x6B; +Parser.ENCHANT_ITEM = 0x6C; +Parser.UPDATE_SIGN = 0x82; +Parser.ITEM_DATA = 0x83; +Parser.UPDATE_TILE_ENTITY = 0x84; +Parser.INCREMENT_STATISTIC = 0xC8; +Parser.PLAYER_LIST_ITEM = 0xC9; +Parser.PLAYER_ABILITIES = 0xCA; +Parser.TAB_COMPLETE = 0xCB; +Parser.CLIENT_SETTINGS = 0xCC; +Parser.CLIENT_STATUSES = 0xCD; +Parser.PLUGIN_MESSAGE = 0xFA; +Parser.ENCRYPTION_KEY_RESPONSE = 0xFC; +Parser.ENCRYPTION_KEY_REQUEST = 0xFD; +Parser.SERVER_LIST_PING = 0xFE; +Parser.DISCONNECT_KICK = 0xFF; diff --git a/packets.json b/packets.json index 4a19595..62a5dba 100644 --- a/packets.json +++ b/packets.json @@ -17,6 +17,22 @@ "type": "int" } ], + "205": [ + { + "name": "payload", + "type": "byte" + } + ], + "252": [ + { + "name": "sharedSecret", + "type": "byteArray" + }, + { + "name": "verifyToken", + "type": "byteArray" + } + ], "253": [ { "name": "serverId", diff --git a/test.js b/test.js index d09debc..1b8f95e 100644 --- a/test.js +++ b/test.js @@ -1,9 +1,12 @@ -var Parser = require('./lib/parser'); +var Parser = require('./lib/parser') + , ursa = require('ursa') + , crypto = require('crypto') + , assert = require('assert') var parser = new Parser(); parser.on('connect', function() { console.info("connect"); - parser.writePacket(0x02, { + parser.writePacket(Parser.HANDSHAKE, { protocolVersion: 51, userName: 'superjoe30', serverHost: 'localhost', @@ -27,19 +30,43 @@ parser.on('end', function() { parser.connect(25565, 'localhost'); var packetHandlers = { + 0xFC: onEncryptionKeyResponse, 0xFD: onEncryptionKeyRequest, }; function onEncryptionKeyRequest(packet) { - var sharedSecret = randomBuffer(16); + console.log("enc key request"); + crypto.randomBytes(16, function (err, sharedSecret) { + assert.ifError(err); + var pubKey = mcPubKeyToURsa(packet.publicKey); + var encryptedSharedSecret = pubKey.encrypt(sharedSecret, 'binary', 'base64', ursa.RSA_PKCS1_PADDING); + var encryptedSharedSecretBuffer = new Buffer(encryptedSharedSecret, 'base64'); + var encryptedVerifyToken = pubKey.encrypt(packet.verifyToken, 'binary', 'base64', ursa.RSA_PKCS1_PADDING); + var encryptedVerifyTokenBuffer = new Buffer(encryptedVerifyToken, 'base64'); + console.log("write enc key response"); + parser.writePacket(Parser.ENCRYPTION_KEY_RESPONSE, { + sharedSecret: encryptedSharedSecretBuffer, + verifyToken: encryptedVerifyTokenBuffer, + }); + }); } -function randomBuffer(size) { - var buffer = new Buffer(size); - var i, number; - for (i = 0; i < size; ++i) { - number = Math.floor(Math.random() * 256); - buffer.writeUInt8(number, i); - } - return buffer; +function onEncryptionKeyResponse(packet) { + console.log("confirmation enc key response"); + assert.strictEqual(packet.sharedSecret.length, 0); + assert.strictEqual(packet.verifyToken.length, 0); + // TODO: enable AES encryption, then we can do the below line + //parser.writePacket(Parser.CLIENT_STATUSES, { payload: 0 }); +} + +function mcPubKeyToURsa(mcPubKeyBuffer) { + var pem = "-----BEGIN PUBLIC KEY-----\n"; + var base64PubKey = mcPubKeyBuffer.toString('base64'); + var maxLineLength = 65; + while (base64PubKey.length > 0) { + pem += base64PubKey.substring(0, maxLineLength) + "\n"; + base64PubKey = base64PubKey.substring(maxLineLength); + } + pem += "-----END PUBLIC KEY-----\n"; + return ursa.createPublicKey(pem, 'utf8'); }