@runejs/core
Version:
Core logging, networking, and buffer functionality for RuneJS applications.
111 lines • 5.09 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Xtea = void 0;
const tslib_1 = require("tslib");
const path_1 = tslib_1.__importDefault(require("path"));
const fs_1 = tslib_1.__importDefault(require("fs"));
const logger_1 = require("../logger");
const buffer_1 = require("../buffer");
const toInt = value => value | 0;
class Xtea {
static loadKeys(xteaConfigPath) {
var _a;
if (!fs_1.default.existsSync(xteaConfigPath)) {
logger_1.logger.error(`Error loading XTEA keys: ${xteaConfigPath} was not found.`);
return null;
}
const stats = fs_1.default.statSync(xteaConfigPath);
if (!stats.isDirectory()) {
logger_1.logger.error(`Error loading XTEA keys: ${xteaConfigPath} is not a directory.`);
return null;
}
const xteaKeys = new Map();
const xteaFileNames = fs_1.default.readdirSync(xteaConfigPath);
for (const fileName of xteaFileNames) {
try {
const gameVersionString = fileName.substring(0, fileName.indexOf('.json'));
if (!gameVersionString) {
logger_1.logger.error(`Error loading XTEA config file ${fileName}: No game version supplied.`);
continue;
}
const gameVersion = Number(gameVersionString);
if (!gameVersion || isNaN(gameVersion)) {
logger_1.logger.error(`Error loading XTEA config file ${fileName}: Invalid game version supplied.`);
continue;
}
const fileContent = fs_1.default.readFileSync(path_1.default.join(xteaConfigPath, fileName), 'utf-8');
const xteaConfigList = JSON.parse(fileContent);
if (!(xteaConfigList === null || xteaConfigList === void 0 ? void 0 : xteaConfigList.length)) {
logger_1.logger.error(`Error loading XTEA config file ${fileName}: File is empty.`);
continue;
}
for (const xteaConfig of xteaConfigList) {
if (!(xteaConfig === null || xteaConfig === void 0 ? void 0 : xteaConfig.name) || !((_a = xteaConfig === null || xteaConfig === void 0 ? void 0 : xteaConfig.key) === null || _a === void 0 ? void 0 : _a.length)) {
continue;
}
const { name: fileName, key } = xteaConfig;
let fileKeys = [];
if (xteaKeys.has(fileName)) {
fileKeys = xteaKeys.get(fileName);
}
fileKeys.push({ gameVersion, key });
xteaKeys.set(fileName, fileKeys);
}
}
catch (error) {
logger_1.logger.error(`Error loading XTEA config file ${fileName}:`, error);
}
}
return xteaKeys;
}
static validKeys(keys) {
return (keys === null || keys === void 0 ? void 0 : keys.length) === 4 && (keys[0] !== 0 || keys[1] !== 0 || keys[2] !== 0 || keys[3] !== 0);
}
// @TODO unit testing
static encrypt(input, keys, length) {
const encryptedBuffer = new buffer_1.ByteBuffer(length);
const chunks = length / 8;
input.readerIndex = 0;
for (let i = 0; i < chunks; i++) {
let v0 = input.get('int');
let v1 = input.get('int');
let sum = 0;
const delta = -0x61c88647;
let rounds = 32;
while (rounds-- > 0) {
v0 += ((sum + keys[sum & 3]) ^ (v1 + ((v1 >>> 5) ^ (v1 << 4))));
sum += delta;
v1 += ((v0 + ((v0 >>> 5) ^ (v0 << 4))) ^ (keys[(sum >>> 11) & 3] + sum));
}
encryptedBuffer.put(v0, 'int');
encryptedBuffer.put(v1, 'int');
}
return encryptedBuffer.flipWriter();
}
// @TODO unit testing
static decrypt(input, keys, length) {
if (!(keys === null || keys === void 0 ? void 0 : keys.length)) {
return input;
}
const output = new buffer_1.ByteBuffer(length);
const numBlocks = Math.floor(length / 8);
for (let block = 0; block < numBlocks; block++) {
let v0 = input.get('int');
let v1 = input.get('int');
let sum = 0x9E3779B9 * 32;
for (let i = 0; i < 32; i++) {
v1 -= ((toInt(v0 << 4) ^ toInt(v0 >>> 5)) + v0) ^ (sum + keys[(sum >>> 11) & 3]);
v1 = toInt(v1);
sum -= 0x9E3779B9;
v0 -= ((toInt(v1 << 4) ^ toInt(v1 >>> 5)) + v1) ^ (sum + keys[sum & 3]);
v0 = toInt(v0);
}
output.put(v0, 'int');
output.put(v1, 'int');
}
input.copy(output, output.writerIndex, input.readerIndex);
return output;
}
}
exports.Xtea = Xtea;
//# sourceMappingURL=xtea.js.map