mirror of
https://github.com/unmojang/node-minecraft-protocol.git
synced 2025-09-28 13:45:37 -04:00
refactor into nice npm module. closes #7
This commit is contained in:
parent
dc2aa5222c
commit
c405ac21f8
24
README.md
24
README.md
@ -20,6 +20,26 @@ Parse and serialize minecraft packets, plus authentication and encryption.
|
||||
|
||||
Supports Minecraft version 1.4.6
|
||||
|
||||
## Try it out so far
|
||||
## Usage
|
||||
|
||||
`MC_EMAIL=you@example.com MC_PASSWORD=your_pass node test.js`
|
||||
### Echo example
|
||||
|
||||
Listen for chat messages and echo them back.
|
||||
|
||||
```js
|
||||
var mc = require('minecraft-protocol');
|
||||
var client = mc.createClient({
|
||||
host: "localhost", // optional
|
||||
port: 25565, // optional
|
||||
username: "player",
|
||||
email: "email@example.com", // email and password are required only for
|
||||
password: "12345678", // encrypted and online servers
|
||||
});
|
||||
client.on('packet', function(packet) {
|
||||
if (packet.id !== 0x03) return;
|
||||
if (packet.message.indexOf(client.session.username) !== -1) return;
|
||||
client.writePacket(0x03, {
|
||||
message: packet.message,
|
||||
});
|
||||
});
|
||||
```
|
||||
|
@ -3,31 +3,149 @@ var net = require('net')
|
||||
, util = require('util')
|
||||
, assert = require('assert')
|
||||
, Iconv = require('iconv').Iconv
|
||||
, packets = require('../packets.json')
|
||||
, ursa = require('ursa')
|
||||
, crypto = require('crypto')
|
||||
, superagent = require('superagent')
|
||||
, Batch = require('batch')
|
||||
, packets = require('./packets.json')
|
||||
, toUcs2 = new Iconv('UTF-8', 'utf16be')
|
||||
, fromUcs2 = new Iconv('utf16be', 'UTF-8')
|
||||
|
||||
require('buffer-more-ints');
|
||||
|
||||
module.exports = Parser;
|
||||
exports.createClient = createClient;
|
||||
|
||||
function Parser(options) {
|
||||
function createClient(options) {
|
||||
// defaults
|
||||
options = options || {};
|
||||
var port = options.port || 25565;
|
||||
var host = options.host || 'localhost';
|
||||
assert.ok(options.username, "username is required");
|
||||
var haveCredentials = options.email && options.password;
|
||||
|
||||
var packetHandlers = {
|
||||
0x00: onKeepAlive,
|
||||
0xFC: onEncryptionKeyResponse,
|
||||
0xFD: onEncryptionKeyRequest,
|
||||
};
|
||||
|
||||
var client = new Client();
|
||||
client.on('connect', function() {
|
||||
client.writePacket(0x02, {
|
||||
protocolVersion: packets.meta.protocolVersion,
|
||||
username: options.username,
|
||||
serverHost: host,
|
||||
serverPort: port,
|
||||
});
|
||||
});
|
||||
client.on('packet', function(packet) {
|
||||
var handler = packetHandlers[packet.id];
|
||||
if (handler) handler(packet);
|
||||
});
|
||||
client.connect(port, host);
|
||||
|
||||
return client;
|
||||
|
||||
function onKeepAlive(packet) {
|
||||
client.writePacket(0x00, {
|
||||
keepAliveId: packet.keepAliveId
|
||||
});
|
||||
}
|
||||
|
||||
function onEncryptionKeyRequest(packet) {
|
||||
if (! haveCredentials) {
|
||||
var err = new Error("server is in online mode and no credentials supplied");
|
||||
err.code = 'ENOCRED';
|
||||
client.emit('error', err);
|
||||
client.end();
|
||||
return;
|
||||
}
|
||||
var hash = crypto.createHash('sha1');
|
||||
hash.update(packet.serverId);
|
||||
var batch = new Batch();
|
||||
batch.push(function(cb) { getLoginSession(options.email, options.password, cb); });
|
||||
batch.push(function(cb) { crypto.randomBytes(16, cb); });
|
||||
batch.end(function (err, results) {
|
||||
if (err) {
|
||||
client.emit('error', err);
|
||||
client.end();
|
||||
return
|
||||
}
|
||||
|
||||
client.session = results[0];
|
||||
client.emit('session');
|
||||
|
||||
var sharedSecret = results[1];
|
||||
hash.update(sharedSecret);
|
||||
hash.update(packet.publicKey);
|
||||
var digest = mcHexDigest(hash);
|
||||
var request = superagent.get("http://session.minecraft.net/game/joinserver.jsp");
|
||||
request.query({
|
||||
user: client.session.username,
|
||||
sessionId: client.session.id,
|
||||
serverId: digest,
|
||||
});
|
||||
request.end(function(err, resp) {
|
||||
var myErr;
|
||||
if (err) {
|
||||
client.emit('error', err);
|
||||
client.end();
|
||||
} else if (resp.serverError) {
|
||||
myErr = new Error("session.minecraft.net is broken: " + resp.status);
|
||||
myErr.code = 'EMCSESSION500';
|
||||
client.emit('error', myErr);
|
||||
client.end();
|
||||
} else if (resp.clientError) {
|
||||
myErr = new Error("session.minecraft.net rejected request: " + resp.status + " " + resp.text);
|
||||
myErr.code = 'EMCSESSION400';
|
||||
client.emit('error', myErr);
|
||||
client.end();
|
||||
} else {
|
||||
sendEncryptionKeyResponse();
|
||||
}
|
||||
});
|
||||
|
||||
function sendEncryptionKeyResponse() {
|
||||
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');
|
||||
client.cipher = crypto.createCipheriv('aes-128-cfb8', sharedSecret, sharedSecret);
|
||||
client.decipher = crypto.createDecipheriv('aes-128-cfb8', sharedSecret, sharedSecret);
|
||||
client.writePacket(0xfc, {
|
||||
sharedSecret: encryptedSharedSecretBuffer,
|
||||
verifyToken: encryptedVerifyTokenBuffer,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onEncryptionKeyResponse(packet) {
|
||||
assert.strictEqual(packet.sharedSecret.length, 0);
|
||||
assert.strictEqual(packet.verifyToken.length, 0);
|
||||
client.encryptionEnabled = true;
|
||||
client.writePacket(0xcd, { payload: 0 });
|
||||
}
|
||||
}
|
||||
|
||||
function Client(options) {
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.client = null;
|
||||
this.socket = null;
|
||||
this.encryptionEnabled = false;
|
||||
this.cipher = null;
|
||||
this.decipher = null;
|
||||
}
|
||||
util.inherits(Parser, EventEmitter);
|
||||
util.inherits(Client, EventEmitter);
|
||||
|
||||
Parser.prototype.connect = function(port, host) {
|
||||
Client.prototype.connect = function(port, host) {
|
||||
var self = this;
|
||||
self.client = net.connect(port, host, function() {
|
||||
self.socket = net.connect(port, host, function() {
|
||||
self.emit('connect');
|
||||
});
|
||||
var incomingBuffer = new Buffer(0);
|
||||
self.client.on('data', function(data) {
|
||||
self.socket.on('data', function(data) {
|
||||
if (self.encryptionEnabled) data = new Buffer(self.decipher.update(data), 'binary');
|
||||
incomingBuffer = Buffer.concat([incomingBuffer, data]);
|
||||
var parsed;
|
||||
@ -39,24 +157,23 @@ Parser.prototype.connect = function(port, host) {
|
||||
}
|
||||
});
|
||||
|
||||
self.client.on('error', function(err) {
|
||||
self.socket.on('error', function(err) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
|
||||
self.client.on('end', function() {
|
||||
self.socket.on('close', function() {
|
||||
self.emit('end');
|
||||
});
|
||||
};
|
||||
|
||||
Parser.prototype.end = function() {
|
||||
this.client.end();
|
||||
Client.prototype.end = function() {
|
||||
this.socket.end();
|
||||
};
|
||||
|
||||
Parser.prototype.writePacket = function(packetId, params) {
|
||||
Client.prototype.writePacket = function(packetId, params) {
|
||||
var buffer = createPacketBuffer(packetId, params);
|
||||
var out = this.encryptionEnabled ? new Buffer(this.cipher.update(buffer), 'binary') : buffer;
|
||||
if (this.encryptionEnabled) console.log("writing", packetId, "packet with encryption");
|
||||
this.client.write(out);
|
||||
this.socket.write(out);
|
||||
};
|
||||
|
||||
var writers = {
|
||||
@ -598,7 +715,6 @@ function createPacketBuffer(packetId, params) {
|
||||
function parsePacket(buffer) {
|
||||
if (buffer.length < 1) return null;
|
||||
var packetId = buffer.readUInt8(0);
|
||||
console.log("parsing packet " + packetId);
|
||||
var size = 1;
|
||||
var results = { id: packetId };
|
||||
var packetInfo = packets[packetId];
|
||||
@ -623,78 +739,80 @@ function parsePacket(buffer) {
|
||||
};
|
||||
}
|
||||
|
||||
// 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;
|
||||
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');
|
||||
}
|
||||
|
||||
function mcHexDigest(hash) {
|
||||
var buffer = new Buffer(hash.digest(), 'binary');
|
||||
// check for negative hashes
|
||||
var negative = buffer.readInt8(0) < 0;
|
||||
if (negative) performTwosCompliment(buffer);
|
||||
var digest = buffer.toString('hex');
|
||||
// trim leading zeroes
|
||||
digest = digest.replace(/^0+/g, '');
|
||||
if (negative) digest = '-' + digest;
|
||||
return digest;
|
||||
|
||||
function performTwosCompliment(buffer) {
|
||||
var carry = true;
|
||||
var i, newByte, value;
|
||||
for (i = buffer.length - 1; i >= 0; --i) {
|
||||
value = buffer.readUInt8(i);
|
||||
newByte = ~value & 0xff;
|
||||
if (carry) {
|
||||
carry = newByte === 0xff;
|
||||
buffer.writeUInt8(newByte + 1, i);
|
||||
} else {
|
||||
buffer.writeUInt8(newByte, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getLoginSession(email, password, cb) {
|
||||
var req = superagent.post("https://login.minecraft.net");
|
||||
req.type('form');
|
||||
req.send({
|
||||
user: email,
|
||||
password: password,
|
||||
version: packets.meta.sessionVersion,
|
||||
});
|
||||
req.end(function(err, resp) {
|
||||
var myErr;
|
||||
if (err) {
|
||||
cb(err);
|
||||
} else if (resp.serverError) {
|
||||
myErr = new Error("login.minecraft.net is broken: " + resp.status);
|
||||
myErr.code = 'ELOGIN500';
|
||||
cb(myErr);
|
||||
} else if (resp.clientError) {
|
||||
myErr = new Error("login.minecraft.net rejected request: " + resp.status + " " + resp.text);
|
||||
myErr.code = 'ELOGIN400';
|
||||
cb(myErr);
|
||||
} else {
|
||||
var values = resp.text.split(':');
|
||||
var session = {
|
||||
currentGameVersion: values[0],
|
||||
username: values[2],
|
||||
id: values[3],
|
||||
uid: values[4],
|
||||
};
|
||||
if (session.id && session.username) {
|
||||
cb(null, session);
|
||||
} else {
|
||||
myErr = new Error("login.minecraft.net rejected request: " + resp.status + " " + resp.text);
|
||||
myErr.code = 'ELOGIN400';
|
||||
cb(myErr);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
@ -32,6 +32,7 @@
|
||||
"ursa": "~0.8.0",
|
||||
"buffer-more-ints": "~0.0.1",
|
||||
"superagent": "~0.10.0",
|
||||
"iconv": "~1.2.4"
|
||||
"iconv": "~1.2.4",
|
||||
"batch": "~0.2.1"
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,8 @@
|
||||
{
|
||||
"meta": {
|
||||
"protocolVersion": 51,
|
||||
"sessionVersion": 13
|
||||
},
|
||||
"0": [
|
||||
{
|
||||
"name": "keepAliveId",
|
||||
|
205
test.js
205
test.js
@ -1,200 +1,13 @@
|
||||
var Parser = require('./lib/parser')
|
||||
, ursa = require('ursa')
|
||||
, crypto = require('crypto')
|
||||
, assert = require('assert')
|
||||
, superagent = require('superagent')
|
||||
|
||||
var input = {
|
||||
var mc = require('./');
|
||||
var client = mc.createClient({
|
||||
username: process.env.MC_USERNAME,
|
||||
email: process.env.MC_EMAIL,
|
||||
password: process.env.MC_PASSWORD,
|
||||
serverHost: 'localhost',
|
||||
serverPort: 25565,
|
||||
};
|
||||
|
||||
var parser = new Parser();
|
||||
var loginSession = null;
|
||||
parser.on('connect', function() {
|
||||
console.info("connect");
|
||||
parser.writePacket(Parser.HANDSHAKE, {
|
||||
protocolVersion: 51,
|
||||
username: loginSession.username,
|
||||
serverHost: input.serverHost,
|
||||
serverPort: input.serverPort,
|
||||
});
|
||||
client.on('packet', function(packet) {
|
||||
if (packet.id !== 0x03) return;
|
||||
if (packet.message.indexOf(client.session.username) !== -1) return;
|
||||
client.writePacket(0x03, {
|
||||
message: packet.message,
|
||||
});
|
||||
});
|
||||
parser.on('packet', function(packet) {
|
||||
var handler = packetHandlers[packet.id];
|
||||
if (handler) {
|
||||
handler(packet);
|
||||
} else {
|
||||
console.warn("No packet handler for", packet.id, "fields", packet);
|
||||
}
|
||||
});
|
||||
parser.on('error', function(err) {
|
||||
console.error("error connecting", err.stack);
|
||||
});
|
||||
parser.on('end', function() {
|
||||
console.info("disconnect");
|
||||
});
|
||||
|
||||
getLoginSession(function() {
|
||||
parser.connect(input.serverPort, input.serverHost);
|
||||
});
|
||||
|
||||
function getLoginSession(cb) {
|
||||
console.log("logging in to minecraft.net");
|
||||
var req = superagent.post("https://login.minecraft.net");
|
||||
req.type('form');
|
||||
req.send({
|
||||
user: input.email,
|
||||
password: input.password,
|
||||
version: 13,
|
||||
});
|
||||
req.end(function(err, resp) {
|
||||
if (err) {
|
||||
cb(err);
|
||||
} else if (! resp.ok) {
|
||||
cb(new Error("login.minecraft.net status " + resp.status + ": " + resp.text));
|
||||
} else {
|
||||
var values = resp.text.split(':');
|
||||
var session = {
|
||||
currentGameVersion: values[0],
|
||||
username: values[2],
|
||||
id: values[3],
|
||||
uid: values[4],
|
||||
};
|
||||
if (session.id && session.username) {
|
||||
loginSession = session;
|
||||
console.info("logged in as", session.username);
|
||||
cb();
|
||||
} else {
|
||||
cb(new Error("login.minecraft.net says " + session.currentGameVersion));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var packetHandlers = {
|
||||
0x00: onKeepAlive,
|
||||
0x01: onLoginRequest,
|
||||
0x03: onChatMessage,
|
||||
0xFC: onEncryptionKeyResponse,
|
||||
0xFD: onEncryptionKeyRequest,
|
||||
0xFF: onKick,
|
||||
};
|
||||
|
||||
function onKeepAlive(packet) {
|
||||
parser.writePacket(Parser.KEEP_ALIVE, {
|
||||
keepAliveId: packet.keepAliveId
|
||||
});
|
||||
}
|
||||
|
||||
function onKick(packet) {
|
||||
console.log("kick", packet);
|
||||
}
|
||||
|
||||
function onLoginRequest(packet) {
|
||||
console.log("login request", packet);
|
||||
}
|
||||
|
||||
function onChatMessage(packet) {
|
||||
console.log("chat message", packet);
|
||||
if (packet.message.indexOf(loginSession.username) === -1) {
|
||||
parser.writePacket(Parser.CHAT_MESSAGE, {
|
||||
message: packet.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function onEncryptionKeyRequest(packet) {
|
||||
console.log("enc key request");
|
||||
var hash = crypto.createHash('sha1');
|
||||
hash.update(packet.serverId);
|
||||
crypto.randomBytes(16, function (err, sharedSecret) {
|
||||
assert.ifError(err);
|
||||
hash.update(sharedSecret);
|
||||
hash.update(packet.publicKey);
|
||||
var digest = mcHexDigest(hash);
|
||||
var request = superagent.get("http://session.minecraft.net/game/joinserver.jsp");
|
||||
|
||||
request.query({
|
||||
user: loginSession.username,
|
||||
sessionId: loginSession.id,
|
||||
serverId: digest,
|
||||
});
|
||||
request.end(function(err, resp) {
|
||||
if (err) {
|
||||
console.error("session.minecraft.net not available");
|
||||
// TODO emit error
|
||||
} else if (! resp.ok) {
|
||||
console.error("session.minecraft.net returned error:", resp.status, resp.text);
|
||||
// TODO emit error
|
||||
} else {
|
||||
sendEncryptionKeyResponse();
|
||||
}
|
||||
});
|
||||
|
||||
function sendEncryptionKeyResponse() {
|
||||
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');
|
||||
parser.cipher = crypto.createCipheriv('aes-128-cfb8', sharedSecret, sharedSecret);
|
||||
parser.decipher = crypto.createDecipheriv('aes-128-cfb8', sharedSecret, sharedSecret);
|
||||
console.log("write enc key response");
|
||||
parser.writePacket(Parser.ENCRYPTION_KEY_RESPONSE, {
|
||||
sharedSecret: encryptedSharedSecretBuffer,
|
||||
verifyToken: encryptedVerifyTokenBuffer,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onEncryptionKeyResponse(packet) {
|
||||
console.log("confirmation enc key response");
|
||||
assert.strictEqual(packet.sharedSecret.length, 0);
|
||||
assert.strictEqual(packet.verifyToken.length, 0);
|
||||
parser.encryptionEnabled = true;
|
||||
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');
|
||||
}
|
||||
|
||||
function mcHexDigest(hash) {
|
||||
var buffer = new Buffer(hash.digest(), 'binary');
|
||||
// check for negative hashes
|
||||
var negative = buffer.readInt8(0) < 0;
|
||||
if (negative) performTwosCompliment(buffer);
|
||||
var digest = buffer.toString('hex');
|
||||
// trim leading zeroes
|
||||
digest = digest.replace(/^0+/g, '');
|
||||
if (negative) digest = '-' + digest;
|
||||
return digest;
|
||||
|
||||
function performTwosCompliment(buffer) {
|
||||
var carry = true;
|
||||
var i, newByte, value;
|
||||
for (i = buffer.length - 1; i >= 0; --i) {
|
||||
value = buffer.readUInt8(i);
|
||||
newByte = ~value & 0xff;
|
||||
if (carry) {
|
||||
carry = newByte === 0xff;
|
||||
buffer.writeUInt8(newByte + 1, i);
|
||||
} else {
|
||||
buffer.writeUInt8(newByte, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user