mirror of
https://github.com/djhackersdev/minime.git
synced 2026-03-21 17:54:13 -05:00
wip
This commit is contained in:
parent
acc1590aa2
commit
558c05095a
33
src/idz/bigint.ts
Normal file
33
src/idz/bigint.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
export function byteString(n: bigint, length: number) {
|
||||
const result = Buffer.alloc(length);
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
const shift = 8n * BigInt(i);
|
||||
const byte = (n >> shift) & 0xffn;
|
||||
|
||||
result[i] = Number(byte);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// i pick the one implementation language that doesn't have this built in
|
||||
|
||||
export function modPow(b: bigint, e: bigint, m: bigint) {
|
||||
// https://en.wikipedia.org/wiki/Modular_exponentiation#Right-to-left_binary_method
|
||||
|
||||
let result = 1n;
|
||||
|
||||
b = b % m;
|
||||
|
||||
while (e > 0n) {
|
||||
if ((e & 1n) === 1n) {
|
||||
result = (result * b) % m;
|
||||
}
|
||||
|
||||
e = e >> 1n;
|
||||
b = (b * b) % m;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
91
src/idz/decoder.ts
Normal file
91
src/idz/decoder.ts
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
import { Transform } from "stream";
|
||||
|
||||
import { MSG, MSG_LEN } from "./defs";
|
||||
|
||||
const readers = new Map();
|
||||
|
||||
function readHeader(buf) {
|
||||
return {
|
||||
blah: "blah",
|
||||
};
|
||||
}
|
||||
|
||||
readers.set(MSG.GET_CONFIG_DATA_REQ, buf => {
|
||||
return { type: "get_config_data_req" };
|
||||
});
|
||||
|
||||
readers.set(MSG.GET_CONFIG_DATA_2_REQ, buf => {
|
||||
return { type: "get_config_data_2_req" };
|
||||
});
|
||||
|
||||
readers.set(MSG.GET_SERVER_LIST_REQ, buf => {
|
||||
return { type: "get_server_list_req" };
|
||||
});
|
||||
|
||||
export class Decoder extends Transform {
|
||||
state: Buffer;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
readableObjectMode: true,
|
||||
writableObjectMode: true,
|
||||
});
|
||||
|
||||
this.state = Buffer.alloc(0);
|
||||
}
|
||||
|
||||
_transform(chunk, encoding, callback) {
|
||||
this.state = Buffer.concat([this.state, chunk]);
|
||||
|
||||
// Read header
|
||||
|
||||
if (this.state.length < 0x04) {
|
||||
return callback(null);
|
||||
}
|
||||
|
||||
const magic = this.state.readUInt32LE(0);
|
||||
|
||||
if (magic !== 0x01020304) {
|
||||
return callback(
|
||||
new Error(
|
||||
"Invalid magic number, cryptographic processing probably incorrect."
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.length < 0x30) {
|
||||
return callback(null);
|
||||
}
|
||||
|
||||
const header = readHeader(this.state);
|
||||
|
||||
if (this.state.length < 0x32) {
|
||||
return callback(null);
|
||||
}
|
||||
|
||||
const msgCode = this.state.readUInt16LE(0x30);
|
||||
const msgLen = MSG_LEN.get(msgCode);
|
||||
const reader = readers.get(msgCode);
|
||||
|
||||
if (msgLen === undefined || reader === undefined) {
|
||||
return callback(
|
||||
new Error(
|
||||
`Unknown command code ${msgCode.toString(16)}, cannot continue`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.length < 0x30 + msgLen) {
|
||||
return callback(null);
|
||||
}
|
||||
|
||||
const reqBuf = this.state.slice(0x30, 0x30 + msgLen);
|
||||
const payload = reader(reqBuf);
|
||||
|
||||
console.log("Idz: RAW:", reqBuf.toString("hex"));
|
||||
console.log("Idz: Header:", header);
|
||||
console.log("Idz: Payload:", payload);
|
||||
|
||||
return callback(null, { header, payload });
|
||||
}
|
||||
}
|
||||
14
src/idz/defs.ts
Normal file
14
src/idz/defs.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
export const MSG = {
|
||||
GET_CONFIG_DATA_REQ: 0x0004,
|
||||
GET_CONFIG_DATA_RES: 0x0005,
|
||||
GET_SERVER_LIST_REQ: 0x0006,
|
||||
GET_SERVER_LIST_RES: 0x0007,
|
||||
GET_CONFIG_DATA_2_REQ: 0x00ab,
|
||||
GET_CONFIG_DATA_2_RES: 0x00ac,
|
||||
};
|
||||
|
||||
export const MSG_LEN = new Map();
|
||||
|
||||
MSG_LEN.set(MSG.GET_CONFIG_DATA_REQ, 0x0050);
|
||||
MSG_LEN.set(MSG.GET_CONFIG_DATA_2_REQ, 0x0010);
|
||||
MSG_LEN.set(MSG.GET_SERVER_LIST_REQ, 0x0020);
|
||||
76
src/idz/encoder.ts
Normal file
76
src/idz/encoder.ts
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
import { Transform } from "stream";
|
||||
|
||||
import { MSG } from "./defs";
|
||||
|
||||
const writers = new Map<string, (foo: any) => Buffer>();
|
||||
|
||||
writers.set("get_config_data_res", obj => {
|
||||
const buf = Buffer.alloc(0x01a0);
|
||||
|
||||
buf.writeUInt16LE(MSG.GET_CONFIG_DATA_RES, 0x0000);
|
||||
buf.writeUInt16LE(obj.status, 0x0002);
|
||||
|
||||
return buf;
|
||||
});
|
||||
|
||||
writers.set("get_config_data_2_res", obj => {
|
||||
const buf = Buffer.alloc(0x230);
|
||||
|
||||
buf.writeUInt16LE(MSG.GET_CONFIG_DATA_2_RES, 0x0000);
|
||||
buf.writeUInt16LE(obj.status, 0x0002);
|
||||
|
||||
return buf;
|
||||
});
|
||||
|
||||
writers.set("get_server_list_res", obj => {
|
||||
const buf = Buffer.alloc(0x04b0);
|
||||
|
||||
buf.writeUInt16LE(MSG.GET_SERVER_LIST_RES, 0x0000);
|
||||
buf.writeUInt16LE(obj.status, 0x0002);
|
||||
buf.write(obj.userDb.addr, 0x0004);
|
||||
buf.writeUInt16LE(obj.userDb.tcp, 0x0084);
|
||||
buf.writeUInt16LE(obj.userDb.http, 0x0086);
|
||||
buf.write(obj.matchAddr, 0x0088);
|
||||
buf.writeUInt16LE(obj.matchPort.tcp, 0x0108);
|
||||
buf.writeUInt16LE(obj.matchPort.udpSend, 0x010a);
|
||||
buf.writeUInt16LE(obj.matchPort.udpRecv, 0x010c);
|
||||
buf.writeUInt16LE(obj.tagMatchPort.tcp, 0x010e);
|
||||
buf.writeUInt16LE(obj.tagMatchPort.udpSend, 0x0110);
|
||||
buf.writeUInt16LE(obj.tagMatchPort.udpRecv, 0x0112);
|
||||
buf.write(obj.event.addr, 0x0114);
|
||||
buf.writeUInt16LE(obj.event.tcp, 0x0194);
|
||||
buf.write(obj.screenshot.addr, 0x0198);
|
||||
buf.writeUInt16LE(obj.screenshot.tcp, 0x0218);
|
||||
buf.write(obj.pingReturn, 0x021c);
|
||||
buf.write(obj.echo1.addr, 0x029c);
|
||||
buf.write(obj.echo2.addr, 0x031c);
|
||||
buf.writeUInt16LE(obj.echo1.udp, 0x39c);
|
||||
buf.writeUInt16LE(obj.echo2.udp, 0x39e);
|
||||
buf.write(obj.newsUrl, 0x03a0);
|
||||
buf.write(obj.reportErrorUrl, 0x0424);
|
||||
|
||||
return buf;
|
||||
});
|
||||
|
||||
export class Encoder extends Transform {
|
||||
constructor() {
|
||||
super({
|
||||
readableObjectMode: true,
|
||||
writableObjectMode: true,
|
||||
});
|
||||
}
|
||||
|
||||
_transform(obj, encoding, callback) {
|
||||
console.log("Idz: Response:", obj);
|
||||
|
||||
const writer = writers.get(obj.type);
|
||||
|
||||
if (writer === undefined) {
|
||||
return callback(new Error(`No writer for type ${obj.type}`));
|
||||
}
|
||||
|
||||
const buf = writer(obj);
|
||||
|
||||
return callback(null, buf);
|
||||
}
|
||||
}
|
||||
80
src/idz/index.ts
Normal file
80
src/idz/index.ts
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import { hostname } from "os";
|
||||
|
||||
import setup from "./setup";
|
||||
|
||||
export default async function idz(socket) {
|
||||
const { input, output } = setup(socket);
|
||||
|
||||
console.log("Idz: Connection opened");
|
||||
|
||||
try {
|
||||
for await (const msg of input) {
|
||||
switch (msg.payload.type) {
|
||||
case "get_server_list_req":
|
||||
const myHost = hostname();
|
||||
|
||||
output.write({
|
||||
type: "get_server_list_res",
|
||||
status: 1,
|
||||
userDb: {
|
||||
addr: myHost,
|
||||
tcp: 10000,
|
||||
http: 10001,
|
||||
},
|
||||
matchAddr: myHost,
|
||||
matchPort: {
|
||||
tcp: 10002,
|
||||
udpSend: 10003,
|
||||
udpRecv: 10004,
|
||||
},
|
||||
tagMatchPort: {
|
||||
tcp: 10005,
|
||||
udpSend: 10006,
|
||||
udpRecv: 10007,
|
||||
},
|
||||
event: {
|
||||
addr: myHost,
|
||||
tcp: 10008,
|
||||
},
|
||||
screenshot: {
|
||||
addr: myHost,
|
||||
tcp: 10009,
|
||||
},
|
||||
pingReturn: myHost,
|
||||
echo1: {
|
||||
addr: myHost,
|
||||
udp: 10010,
|
||||
},
|
||||
echo2: {
|
||||
addr: myHost,
|
||||
udp: 10011,
|
||||
},
|
||||
newsUrl: `http://${myHost}:10012/news`,
|
||||
reportErrorUrl: `http://${myHost}:10013/error`,
|
||||
});
|
||||
|
||||
break;
|
||||
|
||||
case "get_config_data_req":
|
||||
output.write({
|
||||
type: "get_config_data_res",
|
||||
status: 1,
|
||||
});
|
||||
|
||||
break;
|
||||
|
||||
case "get_config_data_2_req":
|
||||
output.write({
|
||||
type: "get_config_data_2_res",
|
||||
status: 1,
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("Idz: Error", e);
|
||||
}
|
||||
|
||||
console.log("Idz: Connection closed");
|
||||
}
|
||||
11
src/idz/ping.ts
Normal file
11
src/idz/ping.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { createSocket } from "dgram";
|
||||
|
||||
export default function createPing(port: number) {
|
||||
const socket = createSocket("udp4");
|
||||
|
||||
socket.bind(port);
|
||||
socket.on("message", (msg, rinfo) => {
|
||||
console.log(`Idz Ping: Ping from ${rinfo.address}:${rinfo.port}`);
|
||||
socket.send(msg, rinfo.port, rinfo.address);
|
||||
});
|
||||
}
|
||||
62
src/idz/setup.ts
Normal file
62
src/idz/setup.ts
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import { createCipheriv, createDecipheriv } from "crypto";
|
||||
import { Socket } from "net";
|
||||
import { pipeline } from "stream";
|
||||
|
||||
import { byteString, modPow } from "./bigint";
|
||||
import { Decoder } from "./decoder";
|
||||
import { Encoder } from "./encoder";
|
||||
|
||||
// Proof-of-concept, so we only ever use one of the ten RSA keys
|
||||
const key = {
|
||||
N: 4922323266120814292574970172377860734034664704992758249880018618131907367614177800329506877981986877921220485681998287752778495334541127048495486311792061n,
|
||||
d: 1163847742215766215216916151663017691387519688859977157498780867776436010396072628219119707788340687440419444081289736279466637153082223960965411473296473n,
|
||||
e: 3961365081960959178294197133768419551060435043430437330799371731939550352626564261219865471710058480523874787120718634318364066605378505537556570049131337n,
|
||||
hashN: 2662304617,
|
||||
};
|
||||
|
||||
// Proof-of-concept, so we only use one fixed session key
|
||||
const sessionKey = 0xffddeeccbbaa99887766554433221100n;
|
||||
|
||||
// -- TEST --
|
||||
const test1 = modPow(sessionKey, key.e, key.N);
|
||||
const test2 = modPow(test1, key.d, key.N);
|
||||
|
||||
console.log("RSA ENC :", byteString(test1, 0x40).toString("hex"));
|
||||
console.log("RSA ENCDEC :", byteString(test2, 0x40).toString("hex"));
|
||||
// -- TEST --
|
||||
|
||||
export default function setup(socket: Socket) {
|
||||
//
|
||||
// Construct and transmit setup message
|
||||
//
|
||||
|
||||
const keyEnc = modPow(sessionKey, key.e, key.N);
|
||||
const msg = Buffer.alloc(0x48);
|
||||
|
||||
msg.set(byteString(keyEnc, 0x40), 0x00);
|
||||
msg.writeUInt32LE(0x01020304, 0x40); // Meaning of this field is unknown
|
||||
msg.writeUInt32LE(key.hashN, 0x44);
|
||||
|
||||
socket.write(msg);
|
||||
|
||||
//
|
||||
// Set up pipeline
|
||||
//
|
||||
|
||||
const keybuf = byteString(sessionKey, 0x10);
|
||||
const input = pipeline(
|
||||
socket,
|
||||
createDecipheriv("aes-128-ecb", keybuf, null).setAutoPadding(false),
|
||||
new Decoder()
|
||||
);
|
||||
|
||||
const output = new Encoder();
|
||||
|
||||
pipeline(
|
||||
output,
|
||||
createCipheriv("aes-128-ecb", keybuf, null).setAutoPadding(false),
|
||||
socket
|
||||
);
|
||||
|
||||
return { input, output };
|
||||
}
|
||||
|
|
@ -7,6 +7,8 @@ import aimedb from "./aimedb";
|
|||
import billing from "./billing";
|
||||
import chunithm from "./chunithm";
|
||||
import diva from "./diva";
|
||||
import idz from "./idz";
|
||||
import idzPing from "./idz/ping";
|
||||
import startup from "./startup";
|
||||
|
||||
const tls = {
|
||||
|
|
@ -21,4 +23,10 @@ https.createServer(tls, billing).listen(8443);
|
|||
http.createServer(chunithm).listen(9000);
|
||||
http.createServer(diva).listen(9001);
|
||||
|
||||
net.createServer(idz).listen(10000);
|
||||
idzPing(10001);
|
||||
idzPing(10003);
|
||||
idzPing(10010);
|
||||
idzPing(10011);
|
||||
|
||||
console.log("Startup OK");
|
||||
|
|
|
|||
|
|
@ -4,10 +4,15 @@ import read = require("raw-body");
|
|||
import { unzipSync } from "zlib";
|
||||
import { hostname } from "os";
|
||||
|
||||
const services = new Map();
|
||||
const myHost = hostname();
|
||||
const uris = new Map<string, string>();
|
||||
|
||||
services.set("SDBT", 9000); // Chunithm
|
||||
services.set("SBZV", 9001); // Project Diva Future Tone
|
||||
uris.set("SDBT", `http://${myHost}:9000/`); // Chunithm
|
||||
uris.set("SBZV", `http://${myHost}:9001/`); // Project Diva Future Tone
|
||||
|
||||
const hosts = new Map<string, string>();
|
||||
|
||||
hosts.set("SDDF", `${myHost}:10000`); // Initial D Zero
|
||||
|
||||
const app = express();
|
||||
|
||||
|
|
@ -60,9 +65,6 @@ app.use(async function(req, res, next) {
|
|||
app.post("/sys/servlet/PowerOn", function(req, resp) {
|
||||
console.log("\n--- Startup Request ---\n\n", req.body);
|
||||
|
||||
const portNo = services.get(req.body.game_id);
|
||||
const uri = portNo !== undefined ? `http://${hostname()}:${portNo}/` : "";
|
||||
|
||||
// Cut milliseconds out of ISO timestamp
|
||||
|
||||
const now = new Date();
|
||||
|
|
@ -71,8 +73,8 @@ app.post("/sys/servlet/PowerOn", function(req, resp) {
|
|||
|
||||
const resParams = {
|
||||
stat: 1,
|
||||
uri,
|
||||
host: "",
|
||||
uri: uris.get(req.body.game_id) || "",
|
||||
host: hosts.get(req.body.game_id) || "",
|
||||
place_id: "123",
|
||||
name: "Name",
|
||||
nickname: "Nick",
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
"moduleResolution": "node",
|
||||
"outDir": "./bin/",
|
||||
"strictNullChecks": true,
|
||||
"target": "es2017"
|
||||
"target": "esnext"
|
||||
},
|
||||
"include": ["./src/"],
|
||||
"exclude": ["./src/**/*.test.ts"]
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user