reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
const { scaffoldModule } = require("./scaffold-module");
|
||||
|
||||
const DEFAULT_SUBTLE_METHODS = [
|
||||
"digest",
|
||||
"generateKey",
|
||||
"importKey",
|
||||
"exportKey",
|
||||
"sign",
|
||||
"verify",
|
||||
"encrypt",
|
||||
"decrypt",
|
||||
];
|
||||
|
||||
function toFiniteNumber(value, fallback) {
|
||||
return Number.isFinite(value) ? value : fallback;
|
||||
}
|
||||
|
||||
function normalizeCryptoSeed(seed = {}) {
|
||||
const cryptoSeed = seed.crypto && typeof seed.crypto === "object" ? seed.crypto : seed;
|
||||
return {
|
||||
exposeCrypto: cryptoSeed.exposeCrypto !== false,
|
||||
installMsCrypto: cryptoSeed.installMsCrypto !== false,
|
||||
maxGetRandomValuesLength: toFiniteNumber(cryptoSeed.maxGetRandomValuesLength, 65536),
|
||||
hasSubtle: cryptoSeed.hasSubtle !== false,
|
||||
subtleMethods: Array.isArray(cryptoSeed.subtleMethods) && cryptoSeed.subtleMethods.length
|
||||
? cryptoSeed.subtleMethods.slice()
|
||||
: DEFAULT_SUBTLE_METHODS.slice(),
|
||||
randomValues: Array.isArray(cryptoSeed.randomValues)
|
||||
? cryptoSeed.randomValues.map((item) => Number(item) & 255)
|
||||
: [],
|
||||
};
|
||||
}
|
||||
|
||||
function renderCryptoPatch(seed) {
|
||||
const serializedSeed = JSON.stringify(seed, null, 2);
|
||||
return `(
|
||||
function installCryptoModule(globalObject) {
|
||||
const seed = ${serializedSeed};
|
||||
const protector = globalObject.__envNativeProtector || null;
|
||||
const integerArrayTags = new Set([
|
||||
"[object Int8Array]",
|
||||
"[object Uint8Array]",
|
||||
"[object Uint8ClampedArray]",
|
||||
"[object Int16Array]",
|
||||
"[object Uint16Array]",
|
||||
"[object Int32Array]",
|
||||
"[object Uint32Array]"
|
||||
]);
|
||||
const randomPool = Array.isArray(seed.randomValues) ? seed.randomValues.slice() : [];
|
||||
|
||||
function defineValue(target, key, value) {
|
||||
Object.defineProperty(target, key, {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
writable: true,
|
||||
value: value
|
||||
});
|
||||
}
|
||||
|
||||
function createQuotaError(message) {
|
||||
if (typeof DOMException === "function") {
|
||||
return new DOMException(message, "QuotaExceededError");
|
||||
}
|
||||
const error = new Error(message);
|
||||
error.name = "QuotaExceededError";
|
||||
return error;
|
||||
}
|
||||
|
||||
function assertIntegerTypedArray(view) {
|
||||
const tag = Object.prototype.toString.call(view);
|
||||
if (!integerArrayTags.has(tag)) {
|
||||
throw new TypeError("Failed to execute 'getRandomValues' on 'Crypto': parameter 1 is not an integer array view.");
|
||||
}
|
||||
if (view.byteLength > seed.maxGetRandomValuesLength) {
|
||||
throw createQuotaError("Failed to execute 'getRandomValues' on 'Crypto': The ArrayBufferView's byte length exceeds the number of bytes of entropy available via this API.");
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
function nextByte(index) {
|
||||
if (randomPool.length) {
|
||||
return randomPool[index % randomPool.length] & 255;
|
||||
}
|
||||
return (index * 73 + 19) & 255;
|
||||
}
|
||||
|
||||
function getRandomValues(view) {
|
||||
assertIntegerTypedArray(view);
|
||||
for (let index = 0; index < view.length; index += 1) {
|
||||
view[index] = nextByte(index);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
const subtle = {};
|
||||
for (const methodName of seed.subtleMethods) {
|
||||
subtle[methodName] = function subtlePlaceholder() {
|
||||
return Promise.reject(new Error("SubtleCrypto method not implemented: " + methodName));
|
||||
};
|
||||
if (protector && protector.repairFunctionMeta) {
|
||||
protector.repairFunctionMeta(subtle[methodName], { name: methodName, length: 0 });
|
||||
}
|
||||
if (protector && protector.protectFunction) {
|
||||
protector.protectFunction(subtle[methodName], methodName);
|
||||
}
|
||||
}
|
||||
|
||||
const cryptoObject = {};
|
||||
defineValue(cryptoObject, "getRandomValues", getRandomValues);
|
||||
if (seed.hasSubtle) {
|
||||
defineValue(cryptoObject, "subtle", subtle);
|
||||
}
|
||||
|
||||
if (protector && protector.repairFunctionMeta) {
|
||||
protector.repairFunctionMeta(getRandomValues, { name: "getRandomValues", length: 1 });
|
||||
}
|
||||
if (protector && protector.protectFunction) {
|
||||
protector.protectFunction(getRandomValues, "getRandomValues");
|
||||
}
|
||||
|
||||
if (seed.exposeCrypto) {
|
||||
Object.defineProperty(globalObject, "crypto", {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
writable: true,
|
||||
value: cryptoObject
|
||||
});
|
||||
}
|
||||
|
||||
if (seed.installMsCrypto) {
|
||||
Object.defineProperty(globalObject, "msCrypto", {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
writable: true,
|
||||
value: cryptoObject
|
||||
});
|
||||
}
|
||||
|
||||
return cryptoObject;
|
||||
})(typeof globalThis !== "undefined" ? globalThis : window);
|
||||
`;
|
||||
}
|
||||
|
||||
function buildCryptoModule(seed = {}, options = {}) {
|
||||
const module = scaffoldModule("crypto-module");
|
||||
const normalizedSeed = normalizeCryptoSeed(seed);
|
||||
|
||||
module.patchPlan = [
|
||||
"挂载 window.crypto 与可选的 window.msCrypto。",
|
||||
"按种子构建 getRandomValues,并复刻整数 TypedArray 校验和长度上限。",
|
||||
"按模块契约补出 subtle 对象的基础方法占位,并接入 native 保护接口。",
|
||||
];
|
||||
|
||||
module.patchCode = renderCryptoPatch(normalizedSeed, options);
|
||||
module.runtimeState = {
|
||||
crypto: normalizedSeed,
|
||||
};
|
||||
module.validation = [
|
||||
"调用 crypto.getRandomValues(new Uint8Array(16)),确认返回原数组实例。",
|
||||
"调用 crypto.getRandomValues(new Uint16Array(8)) 与 new Uint32Array(4)。",
|
||||
"传入 Float32Array 时应抛出 TypeError。",
|
||||
"传入超出 maxGetRandomValuesLength 的视图时应抛出 QuotaExceededError。",
|
||||
"检查 crypto.getRandomValues.toString() 与 crypto.getRandomValues.name/length。",
|
||||
];
|
||||
module.residualRisk = [
|
||||
"subtle 仍为占位实现,只适合绕过存在性检测,不适合真实密码流程。",
|
||||
"如果目标站点会比对 DOMException message/code,仍需依据真实浏览器日志继续收敛。",
|
||||
];
|
||||
|
||||
if (options.includeLegacyAlias === false) {
|
||||
module.runtimeState.crypto.installMsCrypto = false;
|
||||
}
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildCryptoModule,
|
||||
normalizeCryptoSeed,
|
||||
};
|
||||
Reference in New Issue
Block a user