aere-quantum/exposure-report/noble.mjs

36 lines
3.4 KiB
JavaScript

// noble.mjs (quantum exposure report, C12): the ONE place the cryptographic dependencies are loaded. The tool needs
// @noble/hashes 1.x (keccak-256, RIPEMD-160) and @noble/curves 1.x (secp256k1: public-key recovery from a signature, point decoding).
// They are declared in package.json beside this file and resolved, in this order: from a `node_modules` reachable from this directory
// (`npm install --ignore-scripts` here, or in a parent directory that already holds them), then from the directory named by
// AERE_EXPUNERE_NODE_MODULES (the test sets it to the tool's own `node_modules` for the copies it runs, which have none), then from
// the AERE anchor verifier's own `node_modules` when this directory lives in the AERE development tree. A candidate is USED only when it has the exports the tool
// calls (secp256k1.Signature, secp256k1.ProjectivePoint.fromHex, secp256k1.CURVE.n, keccak_256, ripemd160): @noble/curves 2.x, for
// one, has neither ProjectivePoint nor CURVE, and a parent `node_modules` holding it is passed over for the next candidate. When no
// candidate fits, the error carries the code EAEREDEP, names what was found, and gives the command to run; the report tool and the
// test exit with 2 ("could not run") and say so, instead of crashing inside an import.
import path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
const AICI = path.dirname(fileURLToPath(import.meta.url));
export class LipsaDependenta extends Error { constructor(sub, gasite = []) { super(`DEPENDENCY MISSING: ${sub} is not installed in a usable version${gasite.length ? ' (found without the exports this tool calls, so not used: ' + gasite.join('; ') + ')' : ''}; run \`npm install --ignore-scripts --no-audit --no-fund\` in ${AICI} (dependencies are declared in its package.json)`); this.code = 'EAEREDEP'; } }
// the candidates outside this directory: the directory named by AERE_EXPUNERE_NODE_MODULES, then the AERE anchor verifier's node_modules
const dinAfara = (sub) => [process.env.AERE_EXPUNERE_NODE_MODULES, path.join(AICI, '..', '..', '..', 'verificator-falcon', 'node_modules')].filter(Boolean).map((d) => pathToFileURL(path.join(d, sub)).href);
async function incarca(sub, potrivit) {
const incercari = [sub, sub.replace(/\.js$/, ''), pathToFileURL(path.join(AICI, 'node_modules', sub)).href, ...dinAfara(sub)];
const nepotrivite = [];
for (const s of incercari) {
let m;
try { m = await import(s); } catch (e) { if (e && e.code && !/ERR_MODULE_NOT_FOUND|ERR_PACKAGE_PATH_NOT_EXPORTED|ERR_UNSUPPORTED_DIR_IMPORT/.test(e.code)) throw e; continue; }
if (potrivit(m)) return m;
nepotrivite.push(s.startsWith('file:') ? path.relative(AICI, fileURLToPath(s)) : s);
}
throw new LipsaDependenta(sub, [...new Set(nepotrivite)]);
}
const sha3 = await incarca('@noble/hashes/sha3.js', (m) => typeof m.keccak_256 === 'function');
const rmd = await incarca('@noble/hashes/ripemd160.js', (m) => typeof m.ripemd160 === 'function');
const curbaPotrivita = (m) => { const c = m.secp256k1; return !!c && typeof c.Signature === 'function' && !!c.ProjectivePoint && typeof c.ProjectivePoint.fromHex === 'function' && !!c.CURVE && typeof c.CURVE.n === 'bigint' && typeof c.getPublicKey === 'function'; };
const curves = await incarca('@noble/curves/secp256k1.js', curbaPotrivita);
export const keccak_256 = sha3.keccak_256;
export const ripemd160 = rmd.ripemd160;
export const secp256k1 = curves.secp256k1;