mirror of
https://github.com/unmojang/node-minecraft-protocol.git
synced 2025-09-27 05:03:50 -04:00
successfully log in to minecraft server
next step is to add more supported packets
This commit is contained in:
parent
3bc30a97b5
commit
fd43515c42
26
README.md
26
README.md
@ -5,3 +5,29 @@ level library to write bots.
|
||||
|
||||
Hopefully eventually we can merge
|
||||
[mineflayer](https://github.com/superjoe30/mineflayer) with this project.
|
||||
|
||||
## Try it out so far
|
||||
|
||||
```
|
||||
$ MC_EMAIL=you@example.com MC_PASSWORD=your_pass node test.js
|
||||
logging in to minecraft.net
|
||||
logged in as user_name
|
||||
connect
|
||||
enc key request
|
||||
write enc key response
|
||||
confirmation enc key response
|
||||
writing 205 packet with encryption
|
||||
login request { id: 1,
|
||||
entityId: 839,
|
||||
levelType: 'default',
|
||||
gameMode: 0,
|
||||
dimension: 0,
|
||||
difficulty: 1,
|
||||
_notUsed1: 0,
|
||||
maxPlayers: 20 }
|
||||
|
||||
assert.js:102
|
||||
throw new assert.AssertionError({
|
||||
^
|
||||
AssertionError: Unrecognized packetId: 6
|
||||
```
|
||||
|
@ -46,6 +46,10 @@ Parser.prototype.connect = function(port, host) {
|
||||
});
|
||||
};
|
||||
|
||||
Parser.prototype.end = function() {
|
||||
this.client.end();
|
||||
};
|
||||
|
||||
Parser.prototype.writePacket = function(packetId, params) {
|
||||
var buffer = createPacketBuffer(packetId, params);
|
||||
var out = this.encryptionEnabled ? new Buffer(this.cipher.update(buffer), 'binary') : buffer;
|
||||
@ -64,6 +68,8 @@ var readers = {
|
||||
'string': readString,
|
||||
'byteArray': readByteArray,
|
||||
'short': readShort,
|
||||
'int': readInt,
|
||||
'byte': readByte,
|
||||
};
|
||||
|
||||
function readString (buffer, offset) {
|
||||
@ -74,7 +80,7 @@ function readString (buffer, offset) {
|
||||
var strLen = results.value;
|
||||
var strEnd = strBegin + strLen * 2;
|
||||
if (strEnd > buffer.length) return null;
|
||||
var str = fromUcs2.convert(buffer.slice(strBegin, strEnd)).toString();
|
||||
var str = fromUcs2.convert(buffer.slice(strBegin, strEnd)).toString('utf8');
|
||||
|
||||
return {
|
||||
value: str,
|
||||
@ -107,6 +113,24 @@ function readShort(buffer, offset) {
|
||||
};
|
||||
}
|
||||
|
||||
function readInt(buffer, offset) {
|
||||
if (offset + 4 > buffer.length) return null;
|
||||
var value = buffer.readInt32BE(offset);
|
||||
return {
|
||||
value: value,
|
||||
size: 4,
|
||||
};
|
||||
}
|
||||
|
||||
function readByte(buffer, offset) {
|
||||
if (offset + 1 > buffer.length) return null;
|
||||
var value = buffer.readInt8(offset);
|
||||
return {
|
||||
value: value,
|
||||
size: 1,
|
||||
};
|
||||
}
|
||||
|
||||
function StringWriter(value) {
|
||||
this.value = value;
|
||||
this.encoded = toUcs2.convert(value);
|
||||
|
@ -35,7 +35,7 @@
|
||||
"type": "byte"
|
||||
},
|
||||
{
|
||||
"name": "userName",
|
||||
"name": "username",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
|
129
test.js
129
test.js
@ -2,15 +2,23 @@ var Parser = require('./lib/parser')
|
||||
, ursa = require('ursa')
|
||||
, crypto = require('crypto')
|
||||
, assert = require('assert')
|
||||
, superagent = require('superagent')
|
||||
|
||||
var input = {
|
||||
email: process.env.MC_EMAIL,
|
||||
password: process.env.MC_PASSWORD,
|
||||
serverHost: 'localhost',
|
||||
serverPort: 25565,
|
||||
};
|
||||
|
||||
var parser = new Parser();
|
||||
parser.on('connect', function() {
|
||||
console.info("connect");
|
||||
parser.writePacket(Parser.HANDSHAKE, {
|
||||
protocolVersion: 51,
|
||||
userName: 'superjoe30',
|
||||
serverHost: 'localhost',
|
||||
serverPort: 25565,
|
||||
username: loginSession.username,
|
||||
serverHost: input.serverHost,
|
||||
serverPort: input.serverPort,
|
||||
});
|
||||
});
|
||||
parser.on('packet', function(packet) {
|
||||
@ -27,7 +35,44 @@ parser.on('error', function(err) {
|
||||
parser.on('end', function() {
|
||||
console.info("disconnect");
|
||||
});
|
||||
parser.connect(25565, 'localhost');
|
||||
|
||||
var loginSession = null;
|
||||
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) {
|
||||
loginSession = session;
|
||||
console.info("logged in as", session.username);
|
||||
cb();
|
||||
} else {
|
||||
cb(new Error("login.minecraft.net says " + session.currentGameVersion));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var packetHandlers = {
|
||||
0xFC: onEncryptionKeyResponse,
|
||||
@ -46,20 +91,46 @@ function onLoginRequest(packet) {
|
||||
|
||||
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);
|
||||
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,
|
||||
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,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -82,3 +153,31 @@ function mcPubKeyToURsa(mcPubKeyBuffer) {
|
||||
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