UNPKG

mafiles

Version:

Parser for Steam Desktop Authenticator's (SDA) maFiles.

100 lines 5.66 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const crypto_1 = __importDefault(require("crypto")); const path_1 = __importDefault(require("path")); const fs_1 = require("fs"); const utils_1 = require("./utils"); const JSONbig = require('json-bigint')({ useNativeBigInt: true }); /* * Based on https://github.com/Jessecar96/SteamDesktopAuthenticator/blob/8a408f13ee24f70fffbc409cb0e050e924f4fe94/Steam%20Desktop%20Authenticator/FileEncryptor.cs#L20 */ const PBKDF2_ITERATIONS = 50000; //Set to 50k to make program not unbearably slow. May increase in future. const SALT_LENGTH = 8; const KEY_SIZE_BYTES = 32; const IV_LENGTH = 16; /** * Based on https://github.com/Jessecar96/SteamDesktopAuthenticator/blob/8a408f13ee24f70fffbc409cb0e050e924f4fe94/Steam%20Desktop%20Authenticator/FileEncryptor.cs#L62 */ function getEncryptionKey(password, salt) { return new Promise(resolve => { crypto_1.default.pbkdf2(password, Buffer.from(salt, 'base64'), PBKDF2_ITERATIONS, KEY_SIZE_BYTES, 'sha1', ((err, derivedKey) => { resolve(derivedKey); })); }); } /** * Based on https://github.com/Jessecar96/SteamDesktopAuthenticator/blob/8a408f13ee24f70fffbc409cb0e050e924f4fe94/Steam%20Desktop%20Authenticator/FileEncryptor.cs#L87 * * @param password The encryption password. * @param encryptionSalt `encryption_salt` from manifest.json * @param encryptionIv `encryption_iv` from manifest.json * @param encryptedData Content of the encrypted .maFile */ function decryptData(password, encryptionSalt, encryptionIv, encryptedData) { return __awaiter(this, void 0, void 0, function* () { const key = yield getEncryptionKey(password, encryptionSalt); const decrypter = crypto_1.default.createDecipheriv("aes-256-cbc", key, Buffer.from(encryptionIv, 'base64')); return decrypter.update(encryptedData, "base64", "utf8") + decrypter.final("utf8"); }); } /** * Attempts to read and parse a specified maFile. Supports decryption of the maFiles. * If the maFile is encrypted and the password option isn't set, then the user will be prompted for the encryption password * via STDIN. * * @param {string} manifestPath Path to the SDA manifest.json file containing more information about the maFiles. * @param {(string|BigInt)} steamid64 steamid64 of the steam account that we want the maFile of. * @param options All optional. * @param {string} options.password The password that was used for encrypting the SDA. Define this for an instant decryption. * @param {string} options.maFilePath This allows you to directly specify where the targeted maFile is located. * @returns Either the JSON parsed maFile in case of a success or `null` in case of an error. * @example * const maFile = await mafiles("./SDA/manifest.json", "76561197960287930", { * password: "3ncrypt10n_Passw0rd", * maFilePath: "./SDA/maFiles/account.maFile" * }) */ function mafiles(manifestPath, steamid64, options = null) { return __awaiter(this, void 0, void 0, function* () { const manifest = JSONbig.parse((yield fs_1.promises.readFile(manifestPath)).toString()); if (typeof steamid64 === "string") steamid64 = BigInt(steamid64); else if (typeof steamid64 !== "bigint") throw new Error(`The provided steamid64 ${steamid64} (${typeof steamid64}) is not a string nor a native BigInt.${typeof steamid64 === 'number' ? " The ID can't be used as a number due to JS limitations." : ""}`); const entry = manifest.entries.find((entry) => entry.steamid === steamid64); if (entry == null) throw new Error(`Entry with the specified steamid64 ${steamid64} was not found in ${manifestPath}`); const maFilePath = (options && options.maFilePath && options.maFilePath.length > 0) ? options.maFilePath : path_1.default.dirname(manifestPath) + `/${entry.filename}`; const maFile = yield fs_1.promises.readFile(maFilePath); if (manifest.encrypted) { let password = null; if (options && options.password) password = options.password; else { let result = yield utils_1.readPromise({ prompt: 'Enter SDA encryption password:', silent: true }); password = result.result; } if (password == null || password.length <= 0 || typeof password !== "string") throw new Error("The encryption password was not specified or is not of type string or has a length of 0"); return JSONbig.parse(yield decryptData(password, entry.encryption_salt, entry.encryption_iv, maFile.toString())); } else { return JSONbig.parse(maFile.toString()); } return null; }); } exports.default = mafiles; //# sourceMappingURL=index.js.map