From d598f454366d9691d48376bbafb83eed8782e5db Mon Sep 17 00:00:00 2001 From: Jomar Milan Date: Sat, 17 Aug 2024 18:39:34 -0700 Subject: [PATCH] Create cipher object --- app.js | 22 +++++----------------- package.json | 1 + pnpm-lock.yaml | 8 ++++++++ websocket/client.js | 30 +++++++++++++++++++----------- 4 files changed, 33 insertions(+), 28 deletions(-) diff --git a/app.js b/app.js index 83eb757..7e51104 100644 --- a/app.js +++ b/app.js @@ -14,21 +14,9 @@ const wss = new WebSocketServer({ return protocols.has('com.microsoft.minecraft.wsencrypt') ? 'com.microsoft.minecraft.wsencrypt' : false; } }); -const key = await new Promise((resolve, reject) => { - crypto.generateKeyPair('ec', { - namedCurve: 'P-384', - publicKeyEncoding: { - type: 'spki', - format: 'der' - }, - }, (err, publicKey, privateKey) => { - if (err) { - reject(err); - } else { - resolve({ publicKey, privateKey }); - } - }) -}); +const ecdh = crypto.createECDH('secp384r1'); +ecdh.generateKeys(); + const salt = crypto.randomBytes(16); console.log(`Server started on port ${ port }`); @@ -42,9 +30,9 @@ wss.on('connection', async (ws) => { sendAllRenderers('connection'); - const client = new Client(ws); + const client = new Client(ws, ecdh); - await client.enableEncryption(key.publicKey, salt); + await client.enableEncryption(salt); client.subscribeEvent('PlayerDied'); client.subscribeEvent('ItemUsed'); client.subscribeEvent('PlayerMessage'); diff --git a/package.json b/package.json index 490520e..0fe2c5a 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "main": "app.js", "dependencies": { "blockly": "^11.1.1", + "eckey-utils": "^0.7.14", "get-port": "^7.1.0", "sval": "^0.5.2", "vex-dialog": "^1.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7e9d7d0..ef27eb9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: blockly: specifier: ^11.1.1 version: 11.1.1 + eckey-utils: + specifier: ^0.7.14 + version: 0.7.14 get-port: specifier: ^7.1.0 version: 7.1.0 @@ -1286,6 +1289,9 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + eckey-utils@0.7.14: + resolution: {integrity: sha512-s/mENS+mMnJjDSydy0muBQQHMTWJ1nPe8EiphANZrf+lv/1u35aP9WvWHTWqCBJ21blNIurGF7UoLjtaOpoCFw==} + ejs@3.1.10: resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} engines: {node: '>=0.10.0'} @@ -4124,6 +4130,8 @@ snapshots: eastasianwidth@0.2.0: {} + eckey-utils@0.7.14: {} + ejs@3.1.10: dependencies: jake: 10.9.2 diff --git a/websocket/client.js b/websocket/client.js index 938bb95..612b628 100644 --- a/websocket/client.js +++ b/websocket/client.js @@ -1,19 +1,22 @@ import crypto from 'node:crypto'; import { buildCommandRequest, buildSubscription } from './requests.js'; +import ecKeyUtils from 'eckey-utils'; export class Client { static clients = []; #ws; - #serverPrivateKey; + #ecdh; #commandRequests = {}; #gameEventHandlers = []; #playerKey; #sharedSecret; + #secretKey; + #cipher; - constructor(ws, privateKey) { + constructor(ws, ecdh) { this.#ws = ws; - this.#serverPrivateKey = privateKey; + this.#ecdh = ecdh; ws.on('message', this.#handleMessage.bind(this)); @@ -28,19 +31,24 @@ export class Client { this.#ws.send(JSON.stringify(buildSubscription(eventName, crypto.randomUUID()))); } - async enableEncryption(pubkey, salt) { + async enableEncryption(salt) { + const encodedKey = this.#ecdh.getPublicKey('base64'); + const encodedSalt = salt.toString('base64'); const body = await this.execute(`enableencryption "${ encodedKey }" "${ encodedSalt }"`); - this.#playerKey = crypto.createPublicKey({ + const pemKey = crypto.createPublicKey({ key: Buffer.from(body.publicKey, 'base64'), - format: 'der', - type: 'spki' + type: 'spki', + format: 'der' + }).export({ + type: 'spki', + format: 'pem' }); + this.#playerKey = ecKeyUtils.parsePem(pemKey).publicKey; - const ecdh = crypto.createECDH('P-384'); - ecdh.setPrivateKey(this.#serverPrivateKey); - - this.#sharedSecret = ecdh.computeSecret(this.#playerKey); + this.#sharedSecret = this.#ecdh.computeSecret(this.#playerKey); + this.#secretKey = crypto.hash('sha256', Buffer.concat([ salt, this.#sharedSecret ])); + this.#cipher = crypto.createCipheriv('aes-256-cbc', this.#secretKey, this.#secretKey.slice(0, 16)); } execute(command) {