From e04763e61b217a4c5be27ef1a9d34f628944b16d Mon Sep 17 00:00:00 2001 From: Ibrahim Ansari Date: Tue, 28 Dec 2021 18:21:35 +0530 Subject: [PATCH] Write two-way cipher/decipher for online mode. Also remove Redmi Note 4-specific issue as it was caused by lag. --- TODO | 1 - src/minecraft/connection.ts | 22 +++++++++++++--------- src/screens/ServerScreen.tsx | 2 +- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/TODO b/TODO index b7489c8..bef75d3 100644 --- a/TODO +++ b/TODO @@ -3,7 +3,6 @@ - Fix issue with split-screen when connected. - Work on replacing auto /spawn with auto commands. - Handle graceful disconnect when being hit by the OOM killer? -- Keyboard doesn't show up after tapping on chat field on Redmi Note 4. - https://reactnative.dev/docs/performance (esp when logging in) - Respect chat message length limits. - OnePlus 3T titles are wrapped. diff --git a/src/minecraft/connection.ts b/src/minecraft/connection.ts index 0604c26..3310172 100644 --- a/src/minecraft/connection.ts +++ b/src/minecraft/connection.ts @@ -1,7 +1,9 @@ import { Cipher, createCipheriv, + createDecipheriv, createHash, + Decipher, publicEncrypt } from 'react-native-crypto' import { InteractionManager } from 'react-native' @@ -36,7 +38,8 @@ export class ServerConnection extends events.EventEmitter { socket: net.Socket disconnectTimer?: NodeJS.Timeout disconnectReason?: string - aesDigest?: Cipher + aesDecipher?: Decipher + aesCipher?: Cipher constructor(socket: net.Socket) { super() @@ -51,7 +54,8 @@ export class ServerConnection extends events.EventEmitter { const packet = this.compressionEnabled ? makeBaseCompressedPacket(this.compressionThreshold, packetId, data) : makeBasePacket(packetId, data) - return this.socket.write(packet, cb) + const toWrite = this.aesCipher ? this.aesCipher.update(packet) : packet + return this.socket.write(toWrite, cb) } onlyOneCloseCall = false @@ -112,9 +116,11 @@ const initiateConnection = async (opts: { // Run after interactions to improve user experience. InteractionManager.runAfterInteractions(() => { // Note: the entire packet is encrypted, including the length fields and the packet's data. - if (conn.aesDigest) newData = conn.aesDigest.update(newData) + // https://github.com/PrismarineJS/node-minecraft-protocol/blob/master/src/transforms/encryption.js + let finalData = newData + if (conn.aesDecipher) finalData = conn.aesDecipher.update(newData) // Buffer data for read. - conn.bufferedData = Buffer.concat([conn.bufferedData, newData]) + conn.bufferedData = Buffer.concat([conn.bufferedData, finalData]) // ;(async () => { This would need a mutex. while (true) { const packet = conn.compressionEnabled @@ -188,11 +194,6 @@ const initiateConnection = async (opts: { '\n-----END PUBLIC KEY-----' const encryptedSharedSecret = publicEncrypt(pk, sharedSecret) const encryptedVerifyToken = publicEncrypt(pk, verifyToken) - conn.aesDigest = createCipheriv( - 'aes-128-cfb8', - sharedSecret, - sharedSecret - ) // Send encryption response packet. await conn.writePacket( 0x01, @@ -204,6 +205,9 @@ const initiateConnection = async (opts: { ]) ) // From this point forward, everything is encrypted, including the Login Success packet. + const ss = sharedSecret + conn.aesDecipher = createDecipheriv('aes-128-cfb8', ss, ss) + conn.aesCipher = createCipheriv('aes-128-cfb8', ss, ss) })().catch(() => { conn.disconnectReason = '{"text":"Failed to authenticate with Mojang servers!"}' diff --git a/src/screens/ServerScreen.tsx b/src/screens/ServerScreen.tsx index b2bdc8b..6989f3b 100644 --- a/src/screens/ServerScreen.tsx +++ b/src/screens/ServerScreen.tsx @@ -360,7 +360,7 @@ const ServerScreen = () => { ) : ( - {ping === null + {ping === null // LOW-TODO: No UI feedback when No route to host. ? 'Error while pinging...' : 'Pinging...'}