mirror of
https://github.com/unmojang/node-minecraft-protocol.git
synced 2025-09-29 06:03:33 -04:00
Merge pull request #262 from roblabla/feature-useNodeYggdrasil
Use node-yggdrasil. Fix validation problem
This commit is contained in:
commit
5207141cec
@ -48,7 +48,9 @@
|
|||||||
"prismarine-nbt": "0.0.1",
|
"prismarine-nbt": "0.0.1",
|
||||||
"readable-stream": "^1.1.0",
|
"readable-stream": "^1.1.0",
|
||||||
"superagent": "~0.10.0",
|
"superagent": "~0.10.0",
|
||||||
"ursa-purejs": "0.0.3"
|
"ursa-purejs": "0.0.3",
|
||||||
|
"uuid": "^2.0.1",
|
||||||
|
"yggdrasil": "0.1.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"ursa": "~0.8.0"
|
"ursa": "~0.8.0"
|
||||||
|
@ -1,20 +1,18 @@
|
|||||||
var mcHexDigest=require("./mcHexDigest");
|
|
||||||
var ursa=require("./ursa");
|
var ursa=require("./ursa");
|
||||||
var net = require('net');
|
var net = require('net');
|
||||||
var dns = require('dns');
|
var dns = require('dns');
|
||||||
var Client = require('./client');
|
var Client = require('./client');
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var crypto = require('crypto');
|
var crypto = require('crypto');
|
||||||
var Yggdrasil = require('./yggdrasil.js');
|
var yggdrasil = require('yggdrasil')({});
|
||||||
var getSession = Yggdrasil.getSession;
|
var yggserver = require('yggdrasil').server({});
|
||||||
var joinServer = Yggdrasil.joinServer;
|
|
||||||
var serializer = require("./transforms/serializer");
|
var serializer = require("./transforms/serializer");
|
||||||
var states = serializer.states;
|
var states = serializer.states;
|
||||||
var debug = require("./debug");
|
var debug = require("./debug");
|
||||||
|
var uuid = require('uuid');
|
||||||
|
|
||||||
module.exports=createClient;
|
module.exports=createClient;
|
||||||
|
|
||||||
|
|
||||||
Client.prototype.connect = function(port, host) {
|
Client.prototype.connect = function(port, host) {
|
||||||
var self = this;
|
var self = this;
|
||||||
if(port == 25565 && net.isIP(host) === 0) {
|
if(port == 25565 && net.isIP(host) === 0) {
|
||||||
@ -34,11 +32,11 @@ function createClient(options) {
|
|||||||
assert.ok(options, "options is required");
|
assert.ok(options, "options is required");
|
||||||
var port = options.port || 25565;
|
var port = options.port || 25565;
|
||||||
var host = options.host || 'localhost';
|
var host = options.host || 'localhost';
|
||||||
var clientToken = options.clientToken || Yggdrasil.generateUUID();
|
var clientToken = options.clientToken || uuid.v4();
|
||||||
var accessToken = options.accessToken || null;
|
var accessToken;
|
||||||
|
|
||||||
assert.ok(options.username, "username is required");
|
assert.ok(options.username, "username is required");
|
||||||
var haveCredentials = options.password != null || (clientToken != null && accessToken != null);
|
var haveCredentials = options.password != null || (clientToken != null && options.session != null);
|
||||||
var keepAlive = options.keepAlive == null ? true : options.keepAlive;
|
var keepAlive = options.keepAlive == null ? true : options.keepAlive;
|
||||||
|
|
||||||
var optVersion = options.version || require("./version").defaultVersion;
|
var optVersion = options.version || require("./version").defaultVersion;
|
||||||
@ -60,15 +58,28 @@ function createClient(options) {
|
|||||||
client.emit('error', err);
|
client.emit('error', err);
|
||||||
} else {
|
} else {
|
||||||
client.session = session;
|
client.session = session;
|
||||||
client.username = session.username;
|
client.username = session.selectedProfile.name;
|
||||||
accessToken = session.accessToken;
|
accessToken = session.accessToken;
|
||||||
client.emit('session');
|
client.emit('session');
|
||||||
client.connect(port, host);
|
client.connect(port, host);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if(accessToken != null) getSession(options.username, accessToken, clientToken, true, cb);
|
if (options.session) {
|
||||||
else getSession(options.username, options.password, clientToken, false, cb);
|
yggdrasil.validate(options.session.accessToken, function(ok) {
|
||||||
|
if (ok)
|
||||||
|
cb(null, options.sesson);
|
||||||
|
else
|
||||||
|
yggdrasil.refresh(options.session.accessToken, options.session.clientToken, function(err, _, data) {
|
||||||
|
cb(err, data);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else yggdrasil.auth({
|
||||||
|
user: options.username,
|
||||||
|
pass: options.password,
|
||||||
|
token: clientToken
|
||||||
|
}, cb);
|
||||||
} else {
|
} else {
|
||||||
// assume the server is in offline mode and just go for it.
|
// assume the server is in offline mode and just go for it.
|
||||||
client.username = options.username;
|
client.username = options.username;
|
||||||
@ -129,13 +140,8 @@ function createClient(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function joinServerRequest(cb) {
|
function joinServerRequest(cb) {
|
||||||
var hash = crypto.createHash('sha1');
|
yggserver.join(accessToken, client.session.selectedProfile.id,
|
||||||
hash.update(packet.serverId);
|
packet.serverId, sharedSecret, packet.publicKey, cb);
|
||||||
hash.update(sharedSecret);
|
|
||||||
hash.update(packet.publicKey);
|
|
||||||
|
|
||||||
var digest = mcHexDigest(hash);
|
|
||||||
joinServer(client.username, digest, accessToken, client.session.selectedProfile.id, cb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendEncryptionKeyResponse() {
|
function sendEncryptionKeyResponse() {
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
var mcHexDigest=require("./mcHexDigest");
|
|
||||||
var ursa=require("./ursa");
|
var ursa=require("./ursa");
|
||||||
var crypto = require('crypto');
|
var crypto = require('crypto');
|
||||||
var Yggdrasil = require('./yggdrasil.js');
|
var yggserver = require('yggdrasil').server({});
|
||||||
var validateSession = Yggdrasil.validateSession;
|
|
||||||
var serializer = require("./transforms/serializer");
|
var serializer = require("./transforms/serializer");
|
||||||
var states = serializer.states;
|
var states = serializer.states;
|
||||||
var bufferEqual = require('buffer-equal');
|
var bufferEqual = require('buffer-equal');
|
||||||
@ -51,7 +49,7 @@ function createServer(options) {
|
|||||||
var keepAliveTimer = null;
|
var keepAliveTimer = null;
|
||||||
var loginKickTimer = setTimeout(kickForNotLoggingIn, kickTimeout);
|
var loginKickTimer = setTimeout(kickForNotLoggingIn, kickTimeout);
|
||||||
|
|
||||||
var hash;
|
var serverId;
|
||||||
|
|
||||||
function kickForNotLoggingIn() {
|
function kickForNotLoggingIn() {
|
||||||
client.end('LoginTimeout');
|
client.end('LoginTimeout');
|
||||||
@ -119,7 +117,7 @@ function createServer(options) {
|
|||||||
var isException = !!server.onlineModeExceptions[client.username.toLowerCase()];
|
var isException = !!server.onlineModeExceptions[client.username.toLowerCase()];
|
||||||
var needToVerify = (onlineMode && !isException) || (!onlineMode && isException);
|
var needToVerify = (onlineMode && !isException) || (!onlineMode && isException);
|
||||||
if(needToVerify) {
|
if(needToVerify) {
|
||||||
var serverId = crypto.randomBytes(4).toString('hex');
|
serverId = crypto.randomBytes(4).toString('hex');
|
||||||
client.verifyToken = crypto.randomBytes(4);
|
client.verifyToken = crypto.randomBytes(4);
|
||||||
var publicKeyStrArr = serverKey.toPublicPem("utf8").split("\n");
|
var publicKeyStrArr = serverKey.toPublicPem("utf8").split("\n");
|
||||||
var publicKeyStr = "";
|
var publicKeyStr = "";
|
||||||
@ -127,8 +125,6 @@ function createServer(options) {
|
|||||||
publicKeyStr += publicKeyStrArr[i]
|
publicKeyStr += publicKeyStrArr[i]
|
||||||
}
|
}
|
||||||
client.publicKey = new Buffer(publicKeyStr, 'base64');
|
client.publicKey = new Buffer(publicKeyStr, 'base64');
|
||||||
hash = crypto.createHash("sha1");
|
|
||||||
hash.update(serverId);
|
|
||||||
client.once('encryption_begin', onEncryptionKeyResponse);
|
client.once('encryption_begin', onEncryptionKeyResponse);
|
||||||
client.write('encryption_begin', {
|
client.write('encryption_begin', {
|
||||||
serverId: serverId,
|
serverId: serverId,
|
||||||
@ -168,8 +164,6 @@ function createServer(options) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
client.setEncryption(sharedSecret);
|
client.setEncryption(sharedSecret);
|
||||||
hash.update(sharedSecret);
|
|
||||||
hash.update(client.publicKey);
|
|
||||||
|
|
||||||
var isException = !!server.onlineModeExceptions[client.username.toLowerCase()];
|
var isException = !!server.onlineModeExceptions[client.username.toLowerCase()];
|
||||||
var needToVerify = (onlineMode && !isException) || (!onlineMode && isException);
|
var needToVerify = (onlineMode && !isException) || (!onlineMode && isException);
|
||||||
@ -177,8 +171,7 @@ function createServer(options) {
|
|||||||
nextStep();
|
nextStep();
|
||||||
|
|
||||||
function verifyUsername() {
|
function verifyUsername() {
|
||||||
var digest = mcHexDigest(hash);
|
yggserver.hasJoined(client.username, serverId, sharedSecret, client.publicKey, function(err, uuid, profile) {
|
||||||
validateSession(client.username, digest, function(err, uuid, profile) {
|
|
||||||
if(err) {
|
if(err) {
|
||||||
client.end("Failed to verify username!");
|
client.end("Failed to verify username!");
|
||||||
return;
|
return;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
var Client = require('./client');
|
var Client = require('./client');
|
||||||
var Server = require('./server');
|
var Server = require('./server');
|
||||||
var Yggdrasil = require('./yggdrasil.js');
|
|
||||||
var serializer = require("./transforms/serializer");
|
var serializer = require("./transforms/serializer");
|
||||||
var utils = require("./utils");
|
var utils = require("./utils");
|
||||||
var readPackets = require("./packets").readPackets;
|
var readPackets = require("./packets").readPackets;
|
||||||
@ -17,6 +16,5 @@ module.exports = {
|
|||||||
createDeserializer:serializer.createDeserializer,
|
createDeserializer:serializer.createDeserializer,
|
||||||
readPackets:readPackets,
|
readPackets:readPackets,
|
||||||
ping: require('./ping'),
|
ping: require('./ping'),
|
||||||
yggdrasil: Yggdrasil,
|
|
||||||
supportedVersions:require("./version").supportedVersions
|
supportedVersions:require("./version").supportedVersions
|
||||||
};
|
};
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
module.exports=mcHexDigest;
|
|
||||||
|
|
||||||
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) & 0xff, i);
|
|
||||||
} else {
|
|
||||||
buffer.writeUInt8(newByte, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
104
src/yggdrasil.js
104
src/yggdrasil.js
@ -1,104 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this template, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
var superagent = require("superagent");
|
|
||||||
|
|
||||||
var loginSrv = "https://authserver.mojang.com";
|
|
||||||
|
|
||||||
function getSession(username, password, clientToken, refresh, cb) {
|
|
||||||
if(refresh) {
|
|
||||||
var accessToken = password;
|
|
||||||
superagent.post(loginSrv + "/refresh")
|
|
||||||
.type("json")
|
|
||||||
.send({
|
|
||||||
"accessToken": accessToken,
|
|
||||||
"clientToken": clientToken
|
|
||||||
})
|
|
||||||
.end(function(resp) {
|
|
||||||
if(resp.ok) {
|
|
||||||
var session = {
|
|
||||||
accessToken: resp.body.accessToken,
|
|
||||||
clientToken: resp.body.clientToken,
|
|
||||||
username: resp.body.selectedProfile.name,
|
|
||||||
selectedProfile: resp.body.selectedProfile
|
|
||||||
};
|
|
||||||
cb(null, session);
|
|
||||||
} else {
|
|
||||||
var myErr = new Error(resp.body.error);
|
|
||||||
myErr.errorMessage = resp.body.errorMessage;
|
|
||||||
myErr.cause = resp.body.cause;
|
|
||||||
cb(myErr);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
superagent.post(loginSrv + "/authenticate")
|
|
||||||
.type("json")
|
|
||||||
.send({
|
|
||||||
"agent": {
|
|
||||||
"name": "Minecraft",
|
|
||||||
"version": 1
|
|
||||||
},
|
|
||||||
"username": username,
|
|
||||||
"password": password,
|
|
||||||
"clientToken": clientToken
|
|
||||||
})
|
|
||||||
.end(function(resp) {
|
|
||||||
if(resp.ok) {
|
|
||||||
var session = resp.body;
|
|
||||||
session.username = resp.body.selectedProfile.name;
|
|
||||||
cb(null, session);
|
|
||||||
} else {
|
|
||||||
var myErr = new Error(resp.body.error);
|
|
||||||
myErr.errorMessage = resp.body.errorMessage;
|
|
||||||
myErr.cause = resp.body.cause;
|
|
||||||
cb(myErr);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function joinServer(username, serverId, accessToken, selectedProfile, cb) {
|
|
||||||
superagent.post("https://sessionserver.mojang.com/session/minecraft/join")
|
|
||||||
.type("json")
|
|
||||||
.send({
|
|
||||||
"accessToken": accessToken,
|
|
||||||
"selectedProfile": selectedProfile,
|
|
||||||
"serverId": serverId
|
|
||||||
})
|
|
||||||
.end(function(resp) {
|
|
||||||
if(resp.ok) {
|
|
||||||
cb(null);
|
|
||||||
} else {
|
|
||||||
var myErr = new Error(resp.body.error);
|
|
||||||
myErr.errorMessage = resp.body.errorMessage;
|
|
||||||
myErr.cause = resp.body.cause;
|
|
||||||
cb(myErr);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateSession(username, serverId, cb) {
|
|
||||||
superagent.get("https://sessionserver.mojang.com/session/minecraft/hasJoined?username=" + username + "&serverId=" + serverId)
|
|
||||||
.end(function(resp) {
|
|
||||||
if(resp.ok) {
|
|
||||||
if("id" in resp.body) {
|
|
||||||
cb(null, resp.body.id, resp.body);
|
|
||||||
} else {
|
|
||||||
var myErr = new Error("Failed to verify username!");
|
|
||||||
cb(myErr);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var myErr = new Error(resp.body.error);
|
|
||||||
myErr.errorMessage = resp.body.errorMessage;
|
|
||||||
myErr.cause = resp.body.cause;
|
|
||||||
cb(myErr);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.getSession = getSession;
|
|
||||||
exports.joinServer = joinServer;
|
|
||||||
exports.validateSession = validateSession;
|
|
||||||
exports.generateUUID = require("node-uuid").v4;
|
|
||||||
exports.loginType = "yggdrasil";
|
|
Loading…
x
Reference in New Issue
Block a user