UNPKG

minecraft-protocol

Version:

Parse and serialize minecraft packets, plus authentication and encryption.

90 lines (79 loc) 3.19 kB
'use strict' const crypto = require('crypto') const debug = require('debug')('minecraft-protocol') const yggdrasil = require('yggdrasil') const { concat } = require('../transforms/binaryStream') module.exports = function (client, options) { const yggdrasilServer = yggdrasil.server({ agent: options.agent, host: options.sessionServer || 'https://sessionserver.mojang.com' }) client.once('encryption_begin', onEncryptionKeyRequest) function onEncryptionKeyRequest (packet) { crypto.randomBytes(16, gotSharedSecret) function gotSharedSecret (err, sharedSecret) { if (err) { debug(err) client.emit('error', err) client.end('encryptionSecretError') return } if (options.haveCredentials) { joinServerRequest(onJoinServerResponse) } else { if (packet.serverId !== '-') { debug('This server appears to be an online server and you are providing no password, the authentication will probably fail') } sendEncryptionKeyResponse() } function onJoinServerResponse (err) { if (err) { client.emit('error', err) client.end('encryptionLoginError') } else { sendEncryptionKeyResponse() } } function joinServerRequest (cb) { yggdrasilServer.join(options.accessToken, client.session.selectedProfile.id, packet.serverId, sharedSecret, packet.publicKey, cb) } function sendEncryptionKeyResponse () { const mcData = require('minecraft-data')(client.version) const pubKey = mcPubKeyToPem(packet.publicKey) const encryptedSharedSecretBuffer = crypto.publicEncrypt({ key: pubKey, padding: crypto.constants.RSA_PKCS1_PADDING }, sharedSecret) const encryptedVerifyTokenBuffer = crypto.publicEncrypt({ key: pubKey, padding: crypto.constants.RSA_PKCS1_PADDING }, packet.verifyToken) if (mcData.supportFeature('signatureEncryption')) { const salt = BigInt(Date.now()) client.write('encryption_begin', { sharedSecret: encryptedSharedSecretBuffer, hasVerifyToken: client.profileKeys == null, crypto: client.profileKeys ? { salt, messageSignature: crypto.sign('sha256WithRSAEncryption', concat('buffer', packet.verifyToken, 'i64', salt), client.profileKeys.private) } : { verifyToken: encryptedVerifyTokenBuffer } }) } else { client.write('encryption_begin', { sharedSecret: encryptedSharedSecretBuffer, verifyToken: encryptedVerifyTokenBuffer }) } client.setEncryption(sharedSecret) } } } } function mcPubKeyToPem (mcPubKeyBuffer) { let pem = '-----BEGIN PUBLIC KEY-----\n' let base64PubKey = mcPubKeyBuffer.toString('base64') const maxLineLength = 64 while (base64PubKey.length > 0) { pem += base64PubKey.substring(0, maxLineLength) + '\n' base64PubKey = base64PubKey.substring(maxLineLength) } pem += '-----END PUBLIC KEY-----\n' return pem }