// Generated, do not edit. sha256:0a4af87a128e6b903b5ce63c00bf6f500061dd0feb2e18ca3a3d4791c738eff9 import { readFileSync } from 'node:fs'; import { createHash } from 'node:crypto'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { schnorr } from '@noble/curves/secp256k1.js'; import { CONFIG } from '../shared/config.js'; import { installDir } from './portfolios.mjs'; import { services } from './registers.mjs'; const L = CONFIG.LICENSE; const HEX64 = /^[0-9a-f]{64}$/; const HEX128 = /^[0-9a-f]{128}$/; const DATE = /^\d{4}-\d{2}-\d{2}$/; const FIELDS = ['buyer', 'level', 'issued', 'majorFrom', 'majorTo']; export function licenseMessage(body) { const lines = FIELDS.map((f) => `${f}=${body[f]}`); return [L.MESSAGE_PREFIX, ...lines].join('\n'); } function messageHash(body) { return createHash('sha256').update(licenseMessage(body), 'utf-8').digest(); } const ISSUER_FIELDS = ['key', 'validFrom', 'validTo']; export function issuerMessage(issuer) { return [L.ISSUER_PREFIX, ...ISSUER_FIELDS.map((f) => `${f}=${issuer[f]}`)].join('\n'); } const sha256 = (text) => createHash('sha256').update(text, 'utf-8').digest(); const schnorrOk = (sig, hash, key) => { try { return schnorr.verify(Buffer.from(sig, 'hex'), hash, Buffer.from(key, 'hex')); } catch { return false; } }; class LicenseError extends Error {} function refuse(reason) { throw new LicenseError(reason); } export function parseLicense(text) { let raw; try { raw = JSON.parse(text); } catch { refuse('The licence file is not valid JSON.'); } if (!raw || typeof raw !== 'object' || Array.isArray(raw)) refuse('The licence file is not an object.'); if (!L.FORMATS.includes(raw.format)) refuse(`The licence has format ${JSON.stringify(raw.format)}, this app reads format ${L.FORMATS.join(' and ')}.`); if (typeof raw.signature !== 'string' || !HEX128.test(raw.signature)) refuse('The signature is missing or is not 128 hex characters.'); const body = raw.license; if (!body || typeof body !== 'object' || Array.isArray(body)) refuse('The licence file has no `license` object.'); const extra = Object.keys(body).filter((k) => !FIELDS.includes(k)); if (extra.length) refuse(`The licence carries unknown fields: ${extra.join(', ')}.`); const missing = FIELDS.filter((f) => !(f in body)); if (missing.length) refuse(`The licence is missing fields: ${missing.join(', ')}.`); if (typeof body.buyer !== 'string' || !HEX64.test(body.buyer)) refuse('The buyer key is not 64 hex characters.'); if (!L.LEVELS.includes(body.level)) refuse(`Unknown level ${JSON.stringify(body.level)}.`); if (typeof body.issued !== 'string' || !DATE.test(body.issued)) refuse('The issue date is not a YYYY-MM-DD date.'); if (!Number.isInteger(body.majorFrom) || body.majorFrom < 1) refuse('`majorFrom` is not a major version of 1 or higher.'); const unlimited = body.majorTo === L.UNLIMITED; if (!unlimited && (!Number.isInteger(body.majorTo) || body.majorTo < body.majorFrom)) { refuse(`\`majorTo\` is neither '${L.UNLIMITED}' nor a whole number from \`majorFrom\` up.`); } if (raw.format === 1) return { format: raw.format, license: body, signature: raw.signature }; const issuer = raw.issuer; if (!issuer || typeof issuer !== 'object' || Array.isArray(issuer)) refuse('A format 2 licence has no `issuer` object.'); const issuerExtra = Object.keys(issuer).filter((k) => ![...ISSUER_FIELDS, 'signature'].includes(k)); if (issuerExtra.length) refuse(`The issuer carries unknown fields: ${issuerExtra.join(', ')}.`); if (typeof issuer.key !== 'string' || !HEX64.test(issuer.key)) refuse('The issuer key is not 64 hex characters.'); if (typeof issuer.validFrom !== 'string' || !DATE.test(issuer.validFrom)) refuse('`validFrom` of the issuer is not a YYYY-MM-DD date.'); if (typeof issuer.validTo !== 'string' || !DATE.test(issuer.validTo)) refuse('`validTo` of the issuer is not a YYYY-MM-DD date.'); if (issuer.validTo < issuer.validFrom) refuse('The issuer is valid until before it is valid from.'); if (typeof issuer.signature !== 'string' || !HEX128.test(issuer.signature)) refuse('The signature of the issuer is missing or is not 128 hex characters.'); if (body.issued < issuer.validFrom || body.issued > issuer.validTo) refuse('The licence was issued outside the period of its issuer.'); return { format: raw.format, license: body, signature: raw.signature, issuer }; } export function verifySignature(parsed, sellerKey = L.SELLER_KEY) { if (!HEX64.test(sellerKey)) return false; if (parsed.format === 2) { if (!schnorrOk(parsed.issuer.signature, sha256(issuerMessage(parsed.issuer)), sellerKey)) return false; return schnorrOk(parsed.signature, messageHash(parsed.license), parsed.issuer.key); } return schnorrOk(parsed.signature, messageHash(parsed.license), sellerKey); } export function coversVersion(license, appVersion) { const m = /^(\d+)\.\d+\.\d+$/.exec(String(appVersion)); if (!m) return false; const major = Number(m[1]); if (major < 1) return false; if (major < license.majorFrom) return false; return license.majorTo === L.UNLIMITED ? true : major <= license.majorTo; } export function appVersion() { const pad = join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json'); return JSON.parse(readFileSync(pad, 'utf8')).version; } export function licenseState(version = appVersion(), dir = undefined) { const major = Number(/^(\d+)\./.exec(String(version))?.[1]); if (!Number.isInteger(major)) return { level: 'free', enforced: true, why: 'unknown-version' }; const forced = process.env.ASSETAPP_LICENSE_STATE; if (forced === 'free') return { level: 'free', enforced: true, why: 'forced' }; const develop = major < 1 && services.get('develop') !== null; if (forced === 'full' && develop) return { level: 'full', enforced: false, why: 'forced-develop' }; if (develop) return { level: 'full', enforced: false, why: 'develop' }; const uitkomst = dir === undefined ? readLicense() : readLicense(dir); if (uitkomst.state === 'valid' && coversVersion(uitkomst.license, version)) { return { level: uitkomst.license.level, enforced: true, why: 'licensed', license: uitkomst.license }; } return { level: 'free', enforced: true, why: uitkomst.state === 'valid' ? 'out-of-range' : uitkomst.state }; } export function maxFor(soort, state = licenseState()) { const tabel = L.FREE.MAX; if (!(soort in tabel)) throw new Error(`Unknown capacity: ${soort}. Known: ${Object.keys(tabel).join(', ')}.`); return state.level === 'full' ? null : tabel[soort]; } export function capacityReached(soort, huidig, state = licenseState()) { const max = maxFor(soort, state); if (max === null) return null; return huidig >= max ? max : null; } export function allowedAssets(state = licenseState()) { return state.level === 'full' ? null : L.FREE.ASSETS; } export function hasFeature(naam, state = licenseState()) { const tabel = L.FREE.HAS; if (!(naam in tabel)) throw new Error(`Unknown feature: ${naam}. Known: ${Object.keys(tabel).join(', ')}.`); return state.level === 'full' ? true : tabel[naam]; } export function readLicense(dir = installDir()) { let text; try { text = readFileSync(join(dir, L.FILE_NAME), 'utf8'); } catch (err) { if (err.code === 'ENOENT') return { state: 'none' }; return { state: 'unreadable', reason: err.message }; } try { const parsed = parseLicense(text); if (!verifySignature(parsed)) return { state: 'invalid', reason: 'The signature does not belong to this licence.' }; return { state: 'valid', license: parsed.license }; } catch (err) { if (err instanceof LicenseError) return { state: 'invalid', reason: err.message }; throw err; } }