From c889f2c5f7ed3bd815c01a1af08d771a0cef5239 Mon Sep 17 00:00:00 2001 From: roblabla Date: Wed, 30 Sep 2015 12:35:08 +0200 Subject: [PATCH] Use node-yggdrasil. Fix validation problem --- package.json | 4 +- src/createClient.js | 42 ++++++++++-------- src/createServer.js | 15 ++----- src/index.js | 2 - src/mcHexDigest.js | 30 ------------- src/yggdrasil.js | 104 -------------------------------------------- 6 files changed, 31 insertions(+), 166 deletions(-) delete mode 100644 src/mcHexDigest.js delete mode 100644 src/yggdrasil.js diff --git a/package.json b/package.json index cc35acf..a1ca394 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,9 @@ "prismarine-nbt": "0.0.1", "readable-stream": "^1.1.0", "superagent": "~0.10.0", - "ursa-purejs": "0.0.3" + "ursa-purejs": "0.0.3", + "uuid": "^2.0.1", + "yggdrasil": "0.1.0" }, "optionalDependencies": { "ursa": "~0.8.0" diff --git a/src/createClient.js b/src/createClient.js index 4ed201d..c5eb70e 100644 --- a/src/createClient.js +++ b/src/createClient.js @@ -1,20 +1,18 @@ -var mcHexDigest=require("./mcHexDigest"); var ursa=require("./ursa"); var net = require('net'); var dns = require('dns'); var Client = require('./client'); var assert = require('assert'); var crypto = require('crypto'); -var Yggdrasil = require('./yggdrasil.js'); -var getSession = Yggdrasil.getSession; -var joinServer = Yggdrasil.joinServer; +var yggdrasil = require('yggdrasil')({}); +var yggserver = require('yggdrasil').server({}); var serializer = require("./transforms/serializer"); var states = serializer.states; var debug = require("./debug"); +var uuid = require('uuid'); module.exports=createClient; - Client.prototype.connect = function(port, host) { var self = this; if(port == 25565 && net.isIP(host) === 0) { @@ -34,11 +32,11 @@ function createClient(options) { assert.ok(options, "options is required"); var port = options.port || 25565; var host = options.host || 'localhost'; - var clientToken = options.clientToken || Yggdrasil.generateUUID(); - var accessToken = options.accessToken || null; + var clientToken = options.clientToken || uuid.v4(); + var accessToken; 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 optVersion = options.version || require("./version").defaultVersion; @@ -60,15 +58,28 @@ function createClient(options) { client.emit('error', err); } else { client.session = session; - client.username = session.username; + client.username = session.selectedProfile.name; accessToken = session.accessToken; client.emit('session'); client.connect(port, host); } }; - if(accessToken != null) getSession(options.username, accessToken, clientToken, true, cb); - else getSession(options.username, options.password, clientToken, false, cb); + if (options.session) { + 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 { // assume the server is in offline mode and just go for it. client.username = options.username; @@ -129,13 +140,8 @@ function createClient(options) { } function joinServerRequest(cb) { - var hash = crypto.createHash('sha1'); - hash.update(packet.serverId); - hash.update(sharedSecret); - hash.update(packet.publicKey); - - var digest = mcHexDigest(hash); - joinServer(client.username, digest, accessToken, client.session.selectedProfile.id, cb); + yggserver.join(accessToken, client.session.selectedProfile.id, + packet.serverId, sharedSecret, packet.publicKey, cb); } function sendEncryptionKeyResponse() { diff --git a/src/createServer.js b/src/createServer.js index 428c8d6..4e39335 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -1,8 +1,6 @@ -var mcHexDigest=require("./mcHexDigest"); var ursa=require("./ursa"); var crypto = require('crypto'); -var Yggdrasil = require('./yggdrasil.js'); -var validateSession = Yggdrasil.validateSession; +var yggserver = require('yggdrasil').server({}); var serializer = require("./transforms/serializer"); var states = serializer.states; var bufferEqual = require('buffer-equal'); @@ -51,7 +49,7 @@ function createServer(options) { var keepAliveTimer = null; var loginKickTimer = setTimeout(kickForNotLoggingIn, kickTimeout); - var hash; + var serverId; function kickForNotLoggingIn() { client.end('LoginTimeout'); @@ -119,7 +117,7 @@ function createServer(options) { var isException = !!server.onlineModeExceptions[client.username.toLowerCase()]; var needToVerify = (onlineMode && !isException) || (!onlineMode && isException); if(needToVerify) { - var serverId = crypto.randomBytes(4).toString('hex'); + serverId = crypto.randomBytes(4).toString('hex'); client.verifyToken = crypto.randomBytes(4); var publicKeyStrArr = serverKey.toPublicPem("utf8").split("\n"); var publicKeyStr = ""; @@ -127,8 +125,6 @@ function createServer(options) { publicKeyStr += publicKeyStrArr[i] } client.publicKey = new Buffer(publicKeyStr, 'base64'); - hash = crypto.createHash("sha1"); - hash.update(serverId); client.once('encryption_begin', onEncryptionKeyResponse); client.write('encryption_begin', { serverId: serverId, @@ -168,8 +164,6 @@ function createServer(options) { return; } client.setEncryption(sharedSecret); - hash.update(sharedSecret); - hash.update(client.publicKey); var isException = !!server.onlineModeExceptions[client.username.toLowerCase()]; var needToVerify = (onlineMode && !isException) || (!onlineMode && isException); @@ -177,8 +171,7 @@ function createServer(options) { nextStep(); function verifyUsername() { - var digest = mcHexDigest(hash); - validateSession(client.username, digest, function(err, uuid, profile) { + yggserver.hasJoined(client.username, serverId, sharedSecret, client.publicKey, function(err, uuid, profile) { if(err) { client.end("Failed to verify username!"); return; diff --git a/src/index.js b/src/index.js index 1f61ae1..59251c1 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,5 @@ var Client = require('./client'); var Server = require('./server'); -var Yggdrasil = require('./yggdrasil.js'); var serializer = require("./transforms/serializer"); var utils = require("./utils"); var readPackets = require("./packets").readPackets; @@ -17,6 +16,5 @@ module.exports = { createDeserializer:serializer.createDeserializer, readPackets:readPackets, ping: require('./ping'), - yggdrasil: Yggdrasil, supportedVersions:require("./version").supportedVersions }; diff --git a/src/mcHexDigest.js b/src/mcHexDigest.js deleted file mode 100644 index 4abf8f1..0000000 --- a/src/mcHexDigest.js +++ /dev/null @@ -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); - } - } - } -} diff --git a/src/yggdrasil.js b/src/yggdrasil.js deleted file mode 100644 index f9d11d4..0000000 --- a/src/yggdrasil.js +++ /dev/null @@ -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";