From a44c5c00a7ef6cc559052edc55ae34e5b367ebe5 Mon Sep 17 00:00:00 2001 From: Tau Date: Mon, 26 Nov 2018 17:24:58 -0500 Subject: [PATCH] Split up aimedb.js --- src/aimedb.js | 200 ----------------------------------------- src/aimedb/cmd.js | 70 +++++++++++++++ src/aimedb/frame.js | 74 +++++++++++++++ src/aimedb/index.js | 41 +++++++++ src/aimedb/pipeline.js | 33 +++++++ 5 files changed, 218 insertions(+), 200 deletions(-) delete mode 100644 src/aimedb.js create mode 100644 src/aimedb/cmd.js create mode 100644 src/aimedb/frame.js create mode 100644 src/aimedb/index.js create mode 100644 src/aimedb/pipeline.js diff --git a/src/aimedb.js b/src/aimedb.js deleted file mode 100644 index 1cbbcfb..0000000 --- a/src/aimedb.js +++ /dev/null @@ -1,200 +0,0 @@ -const crypto = require('crypto') -const { Transform, pipeline } = require('stream') - -const K = Buffer.from('Copyright(C)SEGA', 'utf8') - -class Deframer extends Transform { - constructor(options) { - super({ - readableObjectMode: true, - ...options, - }) - - this.state = Buffer.alloc(0) - } - - _transform(chunk, encoding, callback) { - this.state = Buffer.concat([ this.state, chunk ]) - - if (this.state.length < 8) { - return - } - - const magic = this.state.readUInt16LE(0) - - if (magic != 0xA13E) { - return callback(new Error(`Invalid magic (decimal ${magic})`)) - } - - const len = this.state.readUInt16LE(6) - - if (this.state.length < len) { - return - } - - console.log('Aimedb: Recv', this.state) - - const cmd = this.state.readUInt16LE(4) - const payload = this.state.slice(8, len) - - this.state = this.state.slice(len) - - return callback(null, { cmd, payload }) - } -} - -class Decoder extends Transform { - constructor(options) { - super({ - readableObjectMode: true, - writableObjectMode: true, - ...options, - }) - } - - static ident(payload) { - const gameId = payload.toString('ascii', 2, 6) - const keychipId = payload.toString('ascii', 12, 23) - - return { gameId, keychipId } - } - - _transform(chunk, encoding, callback) { - const { cmd, payload } = chunk - - switch (cmd) { - case 0x0064: - return callback(null, { - cmd: 'hello', - ...Decoder.ident(payload) - }) - - case 0x0066: - return callback(null, { - cmd: 'goodbye', - }) - - default: - return callback(new Error(`Unknown AimeDB command ${cmd}`)) - } - } -} - -class Encoder extends Transform { - constructor(options) { - super({ - readableObjectMode: true, - writableObjectMode: true, - ...options, - }) - } - - _transform(chunk, encoding, callback) { - console.log('Aimedb: Encode', chunk) - - let payload - - switch (chunk.cmd) { - case 'hello': - payload = Buffer.alloc(24) - payload.writeInt16LE(chunk.status) - - return callback(null, { cmd: 0x0065, payload }) - - default: - return callback(newError(`Unimplemented response: ${cmd}`)) - } - } -} - -class Framer extends Transform { - constructor(options) { - super({ - writableObjectMode: true, - ...options - }) - } - - _transform(chunk, encoding, callback) { - const { cmd, payload } = chunk - - // Message size must be rounded up to a multiple of 16 - - const len = 8 + payload.length - const buf = Buffer.alloc((len + 15) & ~15) - - buf.writeUInt16LE(0xA13E, 0) // Magic? - buf.writeUInt16LE(0x3087, 2) // ??? - buf.writeUInt16LE(cmd, 4) - buf.writeUInt16LE(buf.length, 6) - payload.copy(buf, 8) - - console.log('Aimedb: Send', buf) - - callback(null, buf) - } -} - -function setup(socket) { - const input = pipeline( - socket, - crypto - .createDecipheriv('aes-128-ecb', K, null) - .setAutoPadding(false), - new Deframer(), - new Decoder(), - ) - - const output = new Encoder() - - pipeline( - output, - new Framer(), - crypto - .createCipheriv('aes-128-ecb', K, null) - .setAutoPadding(false), - socket, - ) - - return { input, output } -} - -async function aimedb(socket) { - console.log('Aimedb: Connection opened') - - const { input, output } = setup(socket) - - try { - for await (const req of input) { - console.log('Aimedb: Decode', req) - - const { cmd } = req - let payload - - switch (cmd) { - case 'hello': - console.log('Aimedb: Hello') - output.write({ cmd, status: 1 }) - - break - - case 'goodbye': - console.log('Aimedb: Goodbye') - - break - - default: - console.log('Aimedb: Handler not implemented!') - - break - } - } - } catch (e) { - console.log('Aimedb: Connection error:\n', e) - } - - console.log('Aimedb: Connection closed') - socket.end() -} - -module.exports = aimedb diff --git a/src/aimedb/cmd.js b/src/aimedb/cmd.js new file mode 100644 index 0000000..4db8187 --- /dev/null +++ b/src/aimedb/cmd.js @@ -0,0 +1,70 @@ +const { Transform } = require('stream') + +class Decoder extends Transform { + constructor(options) { + super({ + readableObjectMode: true, + writableObjectMode: true, + ...options, + }) + } + + static ident(payload) { + const gameId = payload.toString('ascii', 2, 6) + const keychipId = payload.toString('ascii', 12, 23) + + return { gameId, keychipId } + } + + _transform(chunk, encoding, callback) { + const { cmd, payload } = chunk + + switch (cmd) { + case 0x0064: + return callback(null, { + cmd: 'hello', + ...Decoder.ident(payload) + }) + + case 0x0066: + return callback(null, { + cmd: 'goodbye', + }) + + default: + return callback(new Error(`Unknown AimeDB command ${cmd}`)) + } + } +} + +class Encoder extends Transform { + constructor(options) { + super({ + readableObjectMode: true, + writableObjectMode: true, + ...options, + }) + } + + _transform(chunk, encoding, callback) { + console.log('Aimedb: Encode', chunk) + + let payload + + switch (chunk.cmd) { + case 'hello': + payload = Buffer.alloc(24) + payload.writeInt16LE(chunk.status) + + return callback(null, { cmd: 0x0065, payload }) + + default: + return callback(newError(`Unimplemented response: ${cmd}`)) + } + } +} + +module.exports = { + Decoder, + Encoder, +} diff --git a/src/aimedb/frame.js b/src/aimedb/frame.js new file mode 100644 index 0000000..c19076e --- /dev/null +++ b/src/aimedb/frame.js @@ -0,0 +1,74 @@ +const { Transform } = require('stream') + +class Deframer extends Transform { + constructor(options) { + super({ + readableObjectMode: true, + ...options, + }) + + this.state = Buffer.alloc(0) + } + + _transform(chunk, encoding, callback) { + this.state = Buffer.concat([ this.state, chunk ]) + + if (this.state.length < 8) { + return + } + + const magic = this.state.readUInt16LE(0) + + if (magic != 0xA13E) { + return callback(new Error(`Invalid magic (decimal ${magic})`)) + } + + const len = this.state.readUInt16LE(6) + + if (this.state.length < len) { + return + } + + console.log('Aimedb: Recv', this.state) + + const cmd = this.state.readUInt16LE(4) + const payload = this.state.slice(8, len) + + this.state = this.state.slice(len) + + return callback(null, { cmd, payload }) + } +} + +class Framer extends Transform { + constructor(options) { + super({ + writableObjectMode: true, + ...options + }) + } + + _transform(chunk, encoding, callback) { + const { cmd, payload } = chunk + + // Message size must be rounded up to a multiple of 16 + + const len = 8 + payload.length + const buf = Buffer.alloc((len + 15) & ~15) + + buf.writeUInt16LE(0xA13E, 0) // Magic? + buf.writeUInt16LE(0x3087, 2) // ??? + buf.writeUInt16LE(cmd, 4) + buf.writeUInt16LE(buf.length, 6) + payload.copy(buf, 8) + + console.log('Aimedb: Send', buf) + + callback(null, buf) + } +} + +module.exports = { + Deframer, + Framer, +} diff --git a/src/aimedb/index.js b/src/aimedb/index.js new file mode 100644 index 0000000..a79d210 --- /dev/null +++ b/src/aimedb/index.js @@ -0,0 +1,41 @@ +const setup = require('./pipeline') + +async function aimedb(socket) { + console.log('Aimedb: Connection opened') + + const { input, output } = setup(socket) + + try { + for await (const req of input) { + console.log('Aimedb: Decode', req) + + const { cmd } = req + let payload + + switch (cmd) { + case 'hello': + console.log('Aimedb: Hello') + output.write({ cmd, status: 1 }) + + break + + case 'goodbye': + console.log('Aimedb: Goodbye') + + break + + default: + console.log('Aimedb: Handler not implemented!') + + break + } + } + } catch (e) { + console.log('Aimedb: Connection error:\n', e) + } + + console.log('Aimedb: Connection closed') + socket.end() +} + +module.exports = aimedb diff --git a/src/aimedb/pipeline.js b/src/aimedb/pipeline.js new file mode 100644 index 0000000..1e97a4c --- /dev/null +++ b/src/aimedb/pipeline.js @@ -0,0 +1,33 @@ +const crypto = require('crypto') +const { pipeline } = require('stream') + +const { Deframer, Framer } = require('./frame') +const { Decoder, Encoder } = require('./cmd') + +const K = Buffer.from('Copyright(C)SEGA', 'utf8') + +function setup(socket) { + const input = pipeline( + socket, + crypto + .createDecipheriv('aes-128-ecb', K, null) + .setAutoPadding(false), + new Deframer(), + new Decoder(), + ) + + const output = new Encoder() + + pipeline( + output, + new Framer(), + crypto + .createCipheriv('aes-128-ecb', K, null) + .setAutoPadding(false), + socket, + ) + + return { input, output } +} + +module.exports = setup