cerceis-lib
Version:
Contains list of quality of life functions that is written in TypeScript and es6
326 lines (324 loc) • 11.5 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/obfuscator/index.ts
var obfuscator_exports = {};
__export(obfuscator_exports, {
Obfuscator: () => Obfuscator,
obfuscator: () => obfuscator
});
module.exports = __toCommonJS(obfuscator_exports);
// src/obfuscator/obfuscator.ts
var Obfuscator = class {
constructor() {
this._v4Prefix = "cob:v4";
this._base64UrlAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
this._randomKey = () => (Date.now() / 1e3 | 0).toString(16) + "xxxxxxxxxxxxxxxx".replace(/x/g, () => (Math.random() * 16 | 0).toString(16)).toLowerCase();
}
/**
* Preferred obfuscation API. Defaults to V4.
*/
encode(inputStr, options = {}) {
const version = options.version ?? "v4";
switch (version) {
case "v1":
return options.key === void 0 ? this.obfuscate(inputStr) : this.obfuscate(inputStr, options.key);
case "v2":
return this.obfuscatev2(inputStr);
case "v3":
return this.obfuscatev3(inputStr, this._requireKey(options.key, "v3"));
case "v4":
return this.obfuscatev4(inputStr, this._requireKey(options.key, "v4"));
}
}
/**
* Preferred deobfuscation API. V4 payloads are self-identifying.
*/
decode(inputStr, options = {}) {
const version = options.version ?? "v4";
switch (version) {
case "v1":
return this.deobfuscate(inputStr, this._requireKey(options.key, "v1"));
case "v2":
return this.deobfuscatev2(inputStr);
case "v3":
return this.deobfuscatev3(inputStr, this._requireKey(options.key, "v3"));
case "v4":
return this.deobfuscatev4(inputStr, this._requireKey(options.key, "v4"));
}
}
_buildKeyValue(key) {
let kv = "";
for (let i = 0; i < key.length; i++) kv += key.charCodeAt(i);
return "l" + kv.split("").reverse().join("") + "l";
}
/**
* V1 obfuscation - key-based character code encoding.
* If no key is provided, a random key is generated and you cannot deobfuscate.
* @param inputStr String to obfuscate.
* @param key Optional key for reversibility.
*/
obfuscate(inputStr, key = this._randomKey()) {
const kv = this._buildKeyValue(key);
let result = "";
for (let i = 0; i < inputStr.length; i++) result += inputStr.charCodeAt(i) + kv;
return result;
}
/**
* V1 deobfuscation.
* @param inputStr String to deobfuscate.
* @param key Key used during obfuscation.
*/
deobfuscate(inputStr, key) {
const kv = this._buildKeyValue(key);
const parts = inputStr.split(kv).filter((e) => e !== "");
return parts.map((e) => String.fromCharCode(Number(e))).join("");
}
_swapPairs(input) {
let result = "";
for (let i = 0; i < input.length; i++) {
if (i % 2 === 1) result += input[i] + input[i - 1];
else if (i === input.length - 1) result += input[i];
}
return result;
}
/**
* V2 obfuscation - pair-swapping with incremental key shifting.
*/
obfuscatev2(inputStr) {
const sortByLen = (a, b) => a.length > b.length ? "s" + b + a : a + b;
const reformat = (text) => {
let result = "";
for (let i = 0; i < text.length; i++) {
if (i % 2 === 1) {
result += sortByLen(String(text.charCodeAt(i - 1)), String(text.charCodeAt(i)));
if (i !== text.length - 1) result += ":";
}
if (i % 2 === 0 && i === text.length - 1)
result += "e" + String(text.charCodeAt(i));
}
return result;
};
const increment = (input) => {
return input.split(":").map((e, i) => {
if (e[0] !== "e" && e[0] !== "s") return String(Number(e) + (i + i * 2));
return e;
});
};
return increment(reformat(this._swapPairs(inputStr))).reduce((f, l) => f + ":" + l);
}
/**
* V2 deobfuscation.
*/
deobfuscatev2(inputStr) {
try {
const split = (text) => {
const len = text.length;
if (len <= 3) return [text.slice(0, 1), text.slice(1)];
if (len <= 5) return [text.slice(0, 2), text.slice(2)];
return [text.slice(0, 3), text.slice(3)];
};
let result = "";
inputStr.split(":").forEach((e, i) => {
if (e[0] === "s") {
const [a, b] = split(e.slice(1));
result += String.fromCharCode(Number(a)) + String.fromCharCode(Number(b));
} else if (e[0] === "e") {
result += String.fromCharCode(Number(e.slice(1)));
} else {
const [a, b] = split(String(Number(e) - (i + i * 2)));
result += String.fromCharCode(Number(b)) + String.fromCharCode(Number(a));
}
});
return result;
} catch {
return "Errored";
}
}
/**
* V3 obfuscation - binary rotation with hex encoding.
* @param inputStr String to obfuscate.
* @param key Key used to determine rotation amounts.
*/
obfuscatev3(inputStr, key) {
const keyValues = this._keyToValues(key);
let kIdx = 0;
let hex = "";
const next = () => {
kIdx = (kIdx + 1) % keyValues.length;
};
const rotr = (bits, n) => {
for (let i = 0; i < n; i++) bits = bits[bits.length - 1] + bits.slice(0, bits.length - 1);
return bits;
};
for (let i = 0; i < inputStr.length; i++) {
const bin = inputStr.charCodeAt(i).toString(2).padStart(16, "0");
hex += parseInt(rotr(bin, keyValues[kIdx]), 2).toString(16).padStart(4, "0");
next();
}
return hex;
}
/**
* V3 deobfuscation.
*/
deobfuscatev3(inputStr, key) {
const keyValues = this._keyToValues(key);
let kIdx = 0;
let result = "";
const next = () => {
kIdx = (kIdx + 1) % keyValues.length;
};
const rotl = (bits, n) => {
for (let i = 0; i < n; i++) bits = bits.slice(1) + bits[0];
return bits;
};
const hex2bin = (hex) => parseInt(hex, 16).toString(2).padStart(16, "0");
for (let i = 0; i < inputStr.length / 4; i++) {
const chunk = inputStr.slice(i * 4, i * 4 + 4);
result += String.fromCharCode(parseInt(rotl(hex2bin(chunk), keyValues[kIdx]), 2));
next();
}
return result;
}
/**
* V4 obfuscation - salted keyed byte-stream masking with base64url output.
* This is reversible obfuscation, not cryptographic encryption.
*/
obfuscatev4(inputStr, key) {
this._requireKey(key, "v4");
const salt = this._randomSalt();
const bytes = new TextEncoder().encode(inputStr);
const payload = this._base64UrlEncode(this._applyV4(bytes, key, salt, "encode"));
return `${this._v4Prefix}:${salt}:${payload}`;
}
/**
* V4 deobfuscation.
*/
deobfuscatev4(inputStr, key) {
this._requireKey(key, "v4");
const parts = inputStr.split(":");
if (parts.length !== 4 || `${parts[0]}:${parts[1]}` !== this._v4Prefix) {
throw new Error("Invalid V4 obfuscator payload");
}
const [, , salt, payload] = parts;
const bytes = this._base64UrlDecode(payload);
const decoded = this._applyV4(bytes, key, salt, "decode");
return new TextDecoder().decode(decoded);
}
_applyV4(bytes, key, salt, mode) {
const prng = this._xorshift32(this._seedFromKey(key, salt));
const result = new Uint8Array(bytes.length);
for (let i = 0; i < bytes.length; i++) {
const random = prng();
const mask = random & 255;
const shift = (random >>> 8) + i & 7;
result[i] = mode === "encode" ? this._rotateLeftByte(bytes[i] ^ mask, shift) : this._rotateRightByte(bytes[i], shift) ^ mask;
}
return result;
}
_seedFromKey(key, salt) {
let hash = 2166136261;
const bytes = new TextEncoder().encode(`${salt}:${key}`);
for (const byte of bytes) {
hash ^= byte;
hash = Math.imul(hash, 16777619) >>> 0;
}
return hash || 1831565813;
}
_xorshift32(seed) {
let state = seed >>> 0 || 1831565813;
return () => {
state ^= state << 13;
state >>>= 0;
state ^= state >>> 17;
state >>>= 0;
state ^= state << 5;
return state >>> 0;
};
}
_rotateLeftByte(value, shift) {
return (value << shift | value >>> 8 - shift) & 255;
}
_rotateRightByte(value, shift) {
return (value >>> shift | value << 8 - shift) & 255;
}
_randomSalt() {
const bytes = new Uint8Array(4);
if (globalThis.crypto?.getRandomValues) {
globalThis.crypto.getRandomValues(bytes);
} else {
for (let i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 256);
}
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
}
_base64UrlEncode(bytes) {
let output = "";
for (let i = 0; i < bytes.length; i += 3) {
const b0 = bytes[i];
const b1 = i + 1 < bytes.length ? bytes[i + 1] : 0;
const b2 = i + 2 < bytes.length ? bytes[i + 2] : 0;
const triplet = b0 << 16 | b1 << 8 | b2;
output += this._base64UrlAlphabet[triplet >>> 18 & 63];
output += this._base64UrlAlphabet[triplet >>> 12 & 63];
if (i + 1 < bytes.length) output += this._base64UrlAlphabet[triplet >>> 6 & 63];
if (i + 2 < bytes.length) output += this._base64UrlAlphabet[triplet & 63];
}
return output;
}
_base64UrlDecode(input) {
if (input.length % 4 === 1 || /[^A-Za-z0-9_-]/.test(input)) {
throw new Error("Invalid base64url payload");
}
const bytes = [];
for (let i = 0; i < input.length; i += 4) {
const c0 = this._base64UrlValue(input[i]);
const c1 = this._base64UrlValue(input[i + 1]);
const c2 = i + 2 < input.length ? this._base64UrlValue(input[i + 2]) : 0;
const c3 = i + 3 < input.length ? this._base64UrlValue(input[i + 3]) : 0;
const triplet = c0 << 18 | c1 << 12 | c2 << 6 | c3;
bytes.push(triplet >>> 16 & 255);
if (i + 2 < input.length) bytes.push(triplet >>> 8 & 255);
if (i + 3 < input.length) bytes.push(triplet & 255);
}
return new Uint8Array(bytes);
}
_base64UrlValue(char) {
if (char === void 0) throw new Error("Invalid base64url payload");
const value = this._base64UrlAlphabet.indexOf(char);
if (value === -1) throw new Error("Invalid base64url payload");
return value;
}
_requireKey(key, version) {
if (typeof key !== "string" || key.length === 0) {
throw new Error(`${version.toUpperCase()} obfuscation requires a non-empty key`);
}
return key;
}
_keyToValues(key) {
return key.split("").map(
(c) => String(c.charCodeAt(0)).split("").map(Number).reduce((a, b) => a + b, 0)
);
}
};
var obfuscator = new Obfuscator();
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Obfuscator,
obfuscator
});
//# sourceMappingURL=index.js.map