Files
qinglong/scripts/ql3-physical-edge-release-evidence.cjs

1020 lines
31 KiB
JavaScript

#!/usr/bin/env node
'use strict';
const crypto = require('node:crypto');
const fs = require('node:fs');
const path = require('node:path');
const {
QingLong3PhysicalEvidenceError,
buildEvidenceReport,
canonicalDigest,
normalizeManifest,
validateAdoptionScaleEvidenceReport,
validateApplicationStartEvidenceReport,
validateComposeStorageEvidenceReport,
validateDirectServiceStartEvidenceReport,
validateDirectServiceStopEvidenceReport,
validateEvidenceWorkloads,
validateFaultEvidenceReport,
validateIdleEvidenceReport,
validateObservedPlatform,
validateServiceStartEvidenceReport,
validateTaskScaleEvidenceReport,
writeNoReplace,
} = require('./ql3-physical-edge-evidence.cjs');
const MAX_PHYSICAL_REPORT_BYTES = 4 * 1024 * 1024;
const MAX_RELEASE_ARCHIVE_BYTES = 64 * 1024 * 1024;
const MAX_PAYLOAD_BYTES = 16 * 1024;
const MAX_PUBLIC_KEY_BYTES = 8 * 1024;
const ED25519_SIGNATURE_BYTES = 64;
const SHA256_PATTERN = /^[a-f0-9]{64}$/;
const REVISION_PATTERN = /^[a-f0-9]{40}$/;
const RELEASE_SCOPE =
'operator_attests_archive_source_and_runtime_artifact_identity';
const PHYSICAL_REPORT_KEYS = Object.freeze([
'evidenceClass',
'generatedAt',
'manifest',
'observed',
'qualification',
'schemaVersion',
'sha256',
'supplementalEvidence',
'supported',
'workloads',
]);
const QUALIFICATION_KEYS = Object.freeze([
'collectedEvidence',
'physicalCandidate',
'remainingRequiredEvidence',
'violations',
]);
const OBSERVED_KEYS = Object.freeze([
'architecture',
'bootId',
'cpuCount',
'cpuModel',
'dataAvailableBytes',
'dataBytes',
'dataFilesystem',
'dataMountOptions',
'dataPath',
'distribution',
'kernel',
'libc',
'node',
'observedModel',
'platform',
'totalMemoryBytes',
'virtualizationIndicators',
]);
const EDGE_REPORT_KEYS = Object.freeze([
'cancellation',
'cases',
'gates',
'generatedAt',
'host',
'moduleLoad',
'profile',
'schemaVersion',
]);
const EDGE_CASE_KEYS = Object.freeze([
'baselineRssBytes',
'durationMs',
'exitCode',
'name',
'outcome',
'peakRssBytes',
'peakRssDeltaBytes',
]);
const SQLITE_REPORT_KEYS = Object.freeze([
'arch',
'batchSize',
'databaseBytes',
'integrityCheck',
'iterations',
'journalMode',
'maxBatchStallMs',
'node',
'platform',
'rssDeltaMb',
'synchronous',
'transactionMs',
]);
const SUPPLEMENTAL_CLASSES = Object.freeze([
'physical_edge_idle_candidate',
'physical_edge_fault_candidate',
'physical_edge_task_scale_candidate',
'physical_edge_adoption_scale_candidate',
'physical_edge_compose_storage_candidate',
'physical_edge_application_start_candidate',
'physical_edge_service_start_candidate',
'physical_edge_direct_service_start_candidate',
'physical_edge_direct_service_stop_candidate',
]);
const LIMITATIONS = Object.freeze([
'the trusted operator attests the archive, source revision and runtime artifact relationship; this verifier does not unpack or independently reproduce the archive',
'public-key distribution, rotation, revocation and transparency remain release-operator responsibilities',
'the signature does not prove firmware or bootloader time, whole-device flash behavior, physical power loss, migration capacity or cluster capacity',
'the final report remains a physical Edge candidate and never changes supported status',
]);
class QingLong3PhysicalEdgeReleaseEvidenceError extends Error {
constructor(message) {
super(`QingLong 3.0 physical Edge release evidence failed: ${message}`);
this.name = 'QingLong3PhysicalEdgeReleaseEvidenceError';
}
}
function hasExactKeys(value, expected) {
return (
value !== null &&
typeof value === 'object' &&
!Array.isArray(value) &&
JSON.stringify(Object.keys(value).sort()) === JSON.stringify(expected)
);
}
function parseArguments(argv) {
const options = { json: false };
for (const argument of argv) {
if (argument === '--') continue;
if (argument === '--json') {
options.json = true;
continue;
}
if (!options.phase && ['prepare', 'finalize'].includes(argument)) {
options.phase = argument;
continue;
}
const separator = argument.indexOf('=');
if (separator < 1) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
`unsupported argument ${argument}`,
);
}
const name = argument.slice(0, separator);
const value = argument.slice(separator + 1);
if (name === '--physical-report') options.physicalReportPath = value;
else if (name === '--release-archive') options.releaseArchivePath = value;
else if (name === '--repository') options.repository = value;
else if (name === '--revision') options.revision = value;
else if (name === '--payload') options.payloadPath = value;
else if (name === '--signature') options.signaturePath = value;
else if (name === '--trusted-public-key') options.publicKeyPath = value;
else if (name === '--expected-repository')
options.expectedRepository = value;
else if (name === '--expected-revision') options.expectedRevision = value;
else if (name === '--output') options.outputPath = value;
else {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
`unsupported argument ${name}`,
);
}
}
if (!['prepare', 'finalize'].includes(options.phase)) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
'phase must be prepare or finalize',
);
}
const commonPaths = [
['physicalReportPath', options.physicalReportPath],
['releaseArchivePath', options.releaseArchivePath],
['payloadPath', options.payloadPath],
];
const phasePaths =
options.phase === 'finalize'
? [
['signaturePath', options.signaturePath],
['publicKeyPath', options.publicKeyPath],
['outputPath', options.outputPath],
]
: [];
for (const [label, value] of [...commonPaths, ...phasePaths]) {
if (!path.isAbsolute(value ?? '')) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
`${label} must be absolute`,
);
}
}
if (options.phase === 'prepare') {
validateRepository(options.repository, 'repository');
validateRevision(options.revision, 'revision');
const invalid = [
options.signaturePath,
options.publicKeyPath,
options.expectedRepository,
options.expectedRevision,
options.outputPath,
].some((value) => value !== undefined);
if (invalid) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
'prepare received a finalize-only option',
);
}
} else {
validateRepository(options.expectedRepository, 'expectedRepository');
validateRevision(options.expectedRevision, 'expectedRevision');
if (options.repository !== undefined || options.revision !== undefined) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
'finalize received a prepare-only option',
);
}
}
return Object.freeze(options);
}
function validateRepository(value, label) {
if (
typeof value !== 'string' ||
value.length < 3 ||
value.length > 512 ||
!/^[A-Za-z0-9][A-Za-z0-9._:/@+~-]+$/.test(value)
) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(`${label} is invalid`);
}
return value;
}
function validateRevision(value, label) {
if (!REVISION_PATTERN.test(value ?? '')) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
`${label} must be a 40-character lowercase Git revision`,
);
}
return value;
}
function stableStatIdentity(stat) {
return [stat.dev, stat.ino, stat.size, stat.mtimeNs, stat.ctimeNs].join(':');
}
function openStableInput(filePath, label, maximumBytes, privateInput) {
if (!path.isAbsolute(filePath) || fs.realpathSync(filePath) !== filePath) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
`${label} must use a canonical absolute path`,
);
}
const before = fs.lstatSync(filePath, { bigint: true });
if (!before.isFile() || before.isSymbolicLink()) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
`${label} must be a regular file without symlinks`,
);
}
const mode = Number(before.mode & 0o777n);
const effectiveUserId = process.geteuid?.();
if (
privateInput &&
(!Number.isSafeInteger(effectiveUserId) ||
before.uid !== BigInt(effectiveUserId) ||
mode !== 0o600)
) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
`${label} must be a current-user-owned 0600 file`,
);
}
if (!privateInput && (mode & 0o022) !== 0) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
`${label} must not be group- or other-writable`,
);
}
if (before.size < 1n || before.size > BigInt(maximumBytes)) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
`${label} exceeds its bounded size`,
);
}
const descriptor = fs.openSync(
filePath,
fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0),
);
const opened = fs.fstatSync(descriptor, { bigint: true });
if (stableStatIdentity(before) !== stableStatIdentity(opened)) {
fs.closeSync(descriptor);
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
`${label} changed while it was opened`,
);
}
return Object.freeze({ descriptor, before });
}
function finishStableInput(filePath, label, descriptor, before) {
const afterDescriptor = fs.fstatSync(descriptor, { bigint: true });
const afterPath = fs.lstatSync(filePath, { bigint: true });
if (
stableStatIdentity(before) !== stableStatIdentity(afterDescriptor) ||
stableStatIdentity(before) !== stableStatIdentity(afterPath)
) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
`${label} changed while it was read`,
);
}
}
function readStableBuffer(filePath, label, maximumBytes, privateInput) {
const { descriptor, before } = openStableInput(
filePath,
label,
maximumBytes,
privateInput,
);
try {
const contents = Buffer.alloc(Number(before.size));
let offset = 0;
while (offset < contents.length) {
const count = fs.readSync(
descriptor,
contents,
offset,
contents.length - offset,
offset,
);
if (count === 0) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
`${label} ended before its declared size`,
);
}
offset += count;
}
finishStableInput(filePath, label, descriptor, before);
return contents;
} finally {
fs.closeSync(descriptor);
}
}
function hashStableFile(filePath, label, maximumBytes) {
const { descriptor, before } = openStableInput(
filePath,
label,
maximumBytes,
false,
);
try {
const digest = crypto.createHash('sha256');
const buffer = Buffer.alloc(64 * 1024);
let offset = 0;
while (offset < Number(before.size)) {
const count = fs.readSync(
descriptor,
buffer,
0,
Math.min(buffer.length, Number(before.size) - offset),
offset,
);
if (count === 0) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
`${label} ended before its declared size`,
);
}
digest.update(buffer.subarray(0, count));
offset += count;
}
finishStableInput(filePath, label, descriptor, before);
return Object.freeze({
sha256: digest.digest('hex'),
bytes: Number(before.size),
});
} finally {
fs.closeSync(descriptor);
}
}
function parsePrivateJson(filePath, label, maximumBytes) {
const contents = readStableBuffer(filePath, label, maximumBytes, true);
try {
return JSON.parse(contents.toString('utf8'));
} catch (error) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
`${label} is not valid JSON: ${error.message}`,
);
}
}
function validateExactObserved(observed) {
if (
!hasExactKeys(observed, OBSERVED_KEYS) ||
!hasExactKeys(observed?.distribution, ['id', 'versionId']) ||
observed.platform !== 'linux' ||
!['arm64', 'x64', 'arm'].includes(observed.architecture) ||
!/^v24\.[0-9]+\.[0-9]+$/.test(observed.node ?? '') ||
!/^[a-f0-9-]{36}$/.test(observed.bootId ?? '') ||
!path.isAbsolute(observed.dataPath ?? '') ||
!Number.isSafeInteger(observed.cpuCount) ||
observed.cpuCount < 1 ||
!Number.isSafeInteger(observed.totalMemoryBytes) ||
observed.totalMemoryBytes < 1 ||
!Number.isSafeInteger(observed.dataBytes) ||
observed.dataBytes < 1 ||
!Number.isSafeInteger(observed.dataAvailableBytes) ||
observed.dataAvailableBytes < 0 ||
observed.dataAvailableBytes > observed.dataBytes ||
!Array.isArray(observed.dataMountOptions) ||
observed.dataMountOptions.length < 1 ||
observed.dataMountOptions.some(
(option) => typeof option !== 'string' || option.length > 128,
) ||
!Array.isArray(observed.virtualizationIndicators) ||
![
observed.cpuModel,
observed.observedModel,
observed.kernel,
observed.libc,
observed.dataFilesystem,
observed.distribution.id,
observed.distribution.versionId,
].every(
(value) =>
typeof value === 'string' && value.length >= 1 && value.length <= 384,
)
) {
return Object.freeze(['physical observed platform shape is invalid']);
}
return Object.freeze([]);
}
function validRssMeasurement(value) {
return (
hasExactKeys(value, ['rssAfterBytes', 'rssBeforeBytes', 'rssDeltaBytes']) &&
Number.isSafeInteger(value.rssBeforeBytes) &&
value.rssBeforeBytes > 0 &&
Number.isSafeInteger(value.rssAfterBytes) &&
value.rssAfterBytes > 0 &&
Number.isSafeInteger(value.rssDeltaBytes) &&
value.rssDeltaBytes ===
Math.max(0, value.rssAfterBytes - value.rssBeforeBytes)
);
}
function validEdgeCase(value, expectedName, outputRequired) {
const expectedKeys = outputRequired
? [...EDGE_CASE_KEYS, 'output'].sort()
: EDGE_CASE_KEYS;
if (
!hasExactKeys(value, expectedKeys) ||
value.name !== expectedName ||
value.outcome !== 'succeeded' ||
value.exitCode !== 0 ||
!Number.isFinite(value.durationMs) ||
value.durationMs < 0 ||
!Number.isSafeInteger(value.baselineRssBytes) ||
value.baselineRssBytes < 1 ||
!Number.isSafeInteger(value.peakRssBytes) ||
value.peakRssBytes < value.baselineRssBytes ||
value.peakRssDeltaBytes !==
Math.max(0, value.peakRssBytes - value.baselineRssBytes)
) {
return false;
}
return (
!outputRequired ||
(hasExactKeys(value.output, ['bytes', 'lines', 'writes']) &&
Number.isSafeInteger(value.output.bytes) &&
value.output.bytes > 0 &&
value.output.lines === 10_000 &&
Number.isSafeInteger(value.output.writes) &&
value.output.writes > 0)
);
}
function validateExactBaseWorkloads(workloads, observed) {
const violations = [];
if (!Array.isArray(workloads) || workloads.length !== 3) {
return Object.freeze(['physical evidence workloads are incomplete']);
}
const edge = workloads[0]?.report;
if (
!hasExactKeys(edge, EDGE_REPORT_KEYS) ||
!hasExactKeys(edge?.host, [
'architecture',
'cpuCount',
'node',
'platform',
'totalMemoryBytes',
]) ||
edge.host.platform !== observed.platform ||
edge.host.architecture !== observed.architecture ||
edge.host.node !== observed.node ||
!Number.isSafeInteger(edge.host.cpuCount) ||
edge.host.cpuCount < 1 ||
!Number.isSafeInteger(edge.host.totalMemoryBytes) ||
edge.host.totalMemoryBytes < 1 ||
!Number.isFinite(Date.parse(edge.generatedAt ?? '')) ||
!validRssMeasurement(edge.moduleLoad) ||
!Array.isArray(edge.cases) ||
edge.cases.length !== 2 ||
!validEdgeCase(edge.cases[0], 'single_noop', false) ||
!validEdgeCase(edge.cases[1], 'stdout_10000_lines', true) ||
!hasExactKeys(edge.cancellation, [
'durationMs',
'killSignalSent',
'outcome',
'termSignalSent',
]) ||
!Number.isFinite(edge.cancellation.durationMs) ||
edge.cancellation.durationMs < 0 ||
edge.cancellation.outcome !== 'cancelled' ||
typeof edge.cancellation.termSignalSent !== 'boolean' ||
typeof edge.cancellation.killSignalSent !== 'boolean' ||
!hasExactKeys(edge.gates, [
'maxCancelMs',
'maxRssDeltaMb',
'passed',
'violations',
]) ||
edge.gates.maxCancelMs !== 5000 ||
edge.gates.maxRssDeltaMb !== 96 ||
edge.gates.passed !== true ||
!Array.isArray(edge.gates.violations) ||
edge.gates.violations.length !== 0
) {
violations.push('Edge executor report is not an exact recorder result');
}
const sqlite = workloads[1]?.report;
if (
!hasExactKeys(sqlite, SQLITE_REPORT_KEYS) ||
sqlite.platform !== observed.platform ||
sqlite.arch !== observed.architecture ||
sqlite.node !== observed.node ||
sqlite.iterations !== 250 ||
sqlite.batchSize !== 10 ||
sqlite.journalMode !== 'delete' ||
sqlite.synchronous !== 'full' ||
sqlite.integrityCheck !== 'ok' ||
!Number.isSafeInteger(sqlite.databaseBytes) ||
sqlite.databaseBytes < 1 ||
!Number.isFinite(sqlite.maxBatchStallMs) ||
sqlite.maxBatchStallMs < 0 ||
!Number.isFinite(sqlite.rssDeltaMb) ||
sqlite.rssDeltaMb < 0 ||
!hasExactKeys(sqlite.transactionMs, ['max', 'p50', 'p95', 'p99']) ||
![
sqlite.transactionMs.p50,
sqlite.transactionMs.p95,
sqlite.transactionMs.p99,
sqlite.transactionMs.max,
].every((value) => Number.isFinite(value) && value >= 0) ||
sqlite.transactionMs.p50 > sqlite.transactionMs.p95 ||
sqlite.transactionMs.p95 > sqlite.transactionMs.p99 ||
sqlite.transactionMs.p99 > sqlite.transactionMs.max
) {
violations.push('Node SQLite report is not an exact recorder result');
}
if (workloads[2]?.report?.identity?.node !== observed.node) {
violations.push('Plugin Package recovery Node identity did not match');
}
return Object.freeze(violations);
}
function validateSupplementalEvidence(report, manifest, observed) {
const violations = [];
const counts = new Map();
const directStart = report.supplementalEvidence.find(
({ evidenceClass }) =>
evidenceClass === 'physical_edge_direct_service_start_candidate',
);
for (const evidence of report.supplementalEvidence) {
const evidenceClass = evidence?.evidenceClass;
if (!SUPPLEMENTAL_CLASSES.includes(evidenceClass)) {
violations.push(`unknown supplemental evidence class ${evidenceClass}`);
continue;
}
const discriminator =
evidenceClass === 'physical_edge_fault_candidate'
? `${evidenceClass}:${evidence.manifest?.fault}`
: evidenceClass;
counts.set(discriminator, (counts.get(discriminator) ?? 0) + 1);
if (counts.get(discriminator) > 1) {
violations.push(`duplicate supplemental evidence ${discriminator}`);
continue;
}
let evidenceViolations;
if (evidenceClass === 'physical_edge_idle_candidate') {
evidenceViolations = validateIdleEvidenceReport(
evidence,
manifest,
observed,
);
} else if (evidenceClass === 'physical_edge_fault_candidate') {
evidenceViolations = validateFaultEvidenceReport(
evidence,
manifest,
observed,
);
} else if (evidenceClass === 'physical_edge_task_scale_candidate') {
evidenceViolations = validateTaskScaleEvidenceReport(
evidence,
manifest,
observed,
);
} else if (evidenceClass === 'physical_edge_adoption_scale_candidate') {
evidenceViolations = validateAdoptionScaleEvidenceReport(
evidence,
manifest,
observed,
);
} else if (evidenceClass === 'physical_edge_compose_storage_candidate') {
evidenceViolations = validateComposeStorageEvidenceReport(
evidence,
manifest,
observed,
);
} else if (evidenceClass === 'physical_edge_application_start_candidate') {
evidenceViolations = validateApplicationStartEvidenceReport(
evidence,
manifest,
observed,
);
} else if (evidenceClass === 'physical_edge_service_start_candidate') {
evidenceViolations = validateServiceStartEvidenceReport(
evidence,
manifest,
observed,
);
} else if (
evidenceClass === 'physical_edge_direct_service_start_candidate'
) {
evidenceViolations = validateDirectServiceStartEvidenceReport(
evidence,
manifest,
observed,
);
} else {
evidenceViolations = validateDirectServiceStopEvidenceReport(
evidence,
manifest,
observed,
directStart,
);
}
violations.push(
...evidenceViolations.map(
(violation) => `${evidenceClass}: ${violation}`,
),
);
}
return Object.freeze(violations);
}
function validatePhysicalEvidenceReport(report) {
const violations = [];
if (!hasExactKeys(report, PHYSICAL_REPORT_KEYS)) {
return Object.freeze(['physical evidence report shape is invalid']);
}
if (!hasExactKeys(report.qualification, QUALIFICATION_KEYS)) {
return Object.freeze(['physical evidence qualification shape is invalid']);
}
let manifest;
try {
manifest = normalizeManifest(report.manifest);
} catch (error) {
return Object.freeze([
`physical evidence manifest is invalid: ${error.message}`,
]);
}
if (
report.schemaVersion !== 1 ||
report.evidenceClass !== 'physical_edge_candidate' ||
report.supported !== false ||
!Number.isFinite(Date.parse(report.generatedAt ?? '')) ||
!Array.isArray(report.workloads) ||
!Array.isArray(report.supplementalEvidence)
) {
violations.push('physical evidence identity is invalid');
}
violations.push(
...validateExactObserved(report.observed),
...validateObservedPlatform(manifest, report.observed),
...validateEvidenceWorkloads(report.workloads, report.observed),
...validateExactBaseWorkloads(report.workloads, report.observed),
);
if (Array.isArray(report.supplementalEvidence)) {
violations.push(
...validateSupplementalEvidence(report, manifest, report.observed),
);
}
let rebuilt;
try {
rebuilt = buildEvidenceReport({
manifest,
observed: report.observed,
workloads: report.workloads,
supplementalEvidence: report.supplementalEvidence,
generatedAt: report.generatedAt,
});
} catch (error) {
violations.push(`physical evidence could not be rebuilt: ${error.message}`);
}
if (rebuilt && JSON.stringify(rebuilt) !== JSON.stringify(report)) {
violations.push('physical evidence report was not an exact reconstruction');
}
if (
report.qualification.physicalCandidate !== true ||
report.qualification.violations.length !== 0
) {
violations.push('physical evidence candidate did not pass');
}
const directStart = report.supplementalEvidence.find?.(
({ evidenceClass }) =>
evidenceClass === 'physical_edge_direct_service_start_candidate',
);
if (!directStart) {
violations.push('direct release service start evidence is required');
}
const releaseRequirementCount =
report.qualification.remainingRequiredEvidence.filter?.(
(item) => item === 'release_archive_signature',
).length ?? 0;
if (releaseRequirementCount !== 1) {
violations.push('release archive signature requirement is not exact');
}
return Object.freeze(violations);
}
function requirePhysicalEvidenceReport(report) {
const violations = validatePhysicalEvidenceReport(report);
if (violations.length > 0) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
`physical evidence rejected: ${violations.join('; ')}`,
);
}
return Object.freeze(report);
}
function buildSigningPayload({
physicalReport,
releaseArchive,
repository,
revision,
signedAt,
}) {
validateRepository(repository, 'repository');
validateRevision(revision, 'revision');
if (!Number.isFinite(Date.parse(signedAt ?? ''))) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
'signedAt must be an ISO timestamp',
);
}
if (
!SHA256_PATTERN.test(releaseArchive?.sha256 ?? '') ||
!Number.isSafeInteger(releaseArchive?.bytes) ||
releaseArchive.bytes < 1 ||
releaseArchive.bytes > MAX_RELEASE_ARCHIVE_BYTES
) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
'release archive identity is invalid',
);
}
const directStart = physicalReport.supplementalEvidence.find(
({ evidenceClass }) =>
evidenceClass === 'physical_edge_direct_service_start_candidate',
);
const artifact = directStart.observed.artifact;
const after = directStart.observed.after;
return Object.freeze({
schemaVersion: 1,
attestationClass: 'physical_edge_release_archive_binding',
algorithm: 'Ed25519',
repository,
revision,
signedAt,
scope: RELEASE_SCOPE,
device: Object.freeze({
deviceId: physicalReport.manifest.deviceId,
deviceModel: physicalReport.manifest.deviceModel,
soc: physicalReport.manifest.soc,
architecture: physicalReport.observed.architecture,
filesystem: physicalReport.observed.dataFilesystem,
storageMedium: physicalReport.manifest.storageMedium,
bootId: physicalReport.observed.bootId,
}),
physicalEvidence: Object.freeze({
sha256: physicalReport.sha256,
generatedAt: physicalReport.generatedAt,
}),
release: Object.freeze({
archiveSha256: releaseArchive.sha256,
archiveBytes: releaseArchive.bytes,
profile: 'edge',
artifactSha256: artifact.artifactSha256,
artifactMetadataSha256: artifact.artifactMetadataSha256,
artifactFiles: artifact.artifactFiles,
artifactBytes: artifact.artifactBytes,
entrypointSha256: artifact.entrypointSha256,
nodeSha256: after.nodeSha256,
nodeVersion: after.nodeVersion,
}),
});
}
function readExactPayload(filePath) {
const contents = readStableBuffer(
filePath,
'signing payload',
MAX_PAYLOAD_BYTES,
true,
);
let payload;
try {
payload = JSON.parse(contents.toString('utf8'));
} catch (error) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
`signing payload is not valid JSON: ${error.message}`,
);
}
const canonical = Buffer.from(JSON.stringify(payload), 'utf8');
if (
contents.length !== canonical.length ||
!crypto.timingSafeEqual(contents, canonical)
) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
'signing payload must contain exact canonical JSON without a newline',
);
}
return Object.freeze({ payload, contents });
}
function readTrustedPublicKey(filePath) {
const pem = readStableBuffer(
filePath,
'trusted public key',
MAX_PUBLIC_KEY_BYTES,
false,
);
let key;
try {
key = crypto.createPublicKey(pem);
} catch (error) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
`trusted public key is invalid: ${error.message}`,
);
}
if (key.asymmetricKeyType !== 'ed25519') {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
'trusted public key must be Ed25519',
);
}
const der = key.export({ type: 'spki', format: 'der' });
return Object.freeze({
key,
fingerprintSha256: crypto.createHash('sha256').update(der).digest('hex'),
});
}
function buildFinalReport({ physicalReport, payload, signature, fingerprint }) {
const remainingRequiredEvidence =
physicalReport.qualification.remainingRequiredEvidence.filter(
(item) => item !== 'release_archive_signature',
);
const collectedEvidence = [
...physicalReport.qualification.collectedEvidence,
'release_archive_signature_or_attestation',
];
const body = {
schemaVersion: 1,
evidenceClass: 'physical_edge_release_candidate',
supported: false,
generatedAt: new Date().toISOString(),
payload,
trust: {
algorithm: 'Ed25519',
publicKeyFingerprintSha256: fingerprint,
},
signature: {
encoding: 'base64',
value: signature.toString('base64'),
},
qualification: {
passed: true,
violations: [],
collectedEvidence,
remainingRequiredEvidence,
},
limitations: LIMITATIONS,
};
return Object.freeze({ ...body, sha256: canonicalDigest(body) });
}
function preparePhase(options) {
const physicalReport = requirePhysicalEvidenceReport(
parsePrivateJson(
options.physicalReportPath,
'physical evidence report',
MAX_PHYSICAL_REPORT_BYTES,
),
);
const releaseArchive = hashStableFile(
options.releaseArchivePath,
'release archive',
MAX_RELEASE_ARCHIVE_BYTES,
);
const payload = buildSigningPayload({
physicalReport,
releaseArchive,
repository: options.repository,
revision: options.revision,
signedAt: new Date().toISOString(),
});
const serialized = JSON.stringify(payload);
writeNoReplace(options.payloadPath, serialized);
process.stdout.write(
`${JSON.stringify(payload, null, options.json ? 0 : 2)}\n`,
);
}
function finalizePhase(options) {
const physicalReport = requirePhysicalEvidenceReport(
parsePrivateJson(
options.physicalReportPath,
'physical evidence report',
MAX_PHYSICAL_REPORT_BYTES,
),
);
const releaseArchive = hashStableFile(
options.releaseArchivePath,
'release archive',
MAX_RELEASE_ARCHIVE_BYTES,
);
const { payload, contents } = readExactPayload(options.payloadPath);
const expectedPayload = buildSigningPayload({
physicalReport,
releaseArchive,
repository: options.expectedRepository,
revision: options.expectedRevision,
signedAt: payload.signedAt,
});
if (JSON.stringify(payload) !== JSON.stringify(expectedPayload)) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
'signing payload did not match the expected source, archive and evidence',
);
}
const signature = readStableBuffer(
options.signaturePath,
'detached signature',
ED25519_SIGNATURE_BYTES,
true,
);
if (signature.length !== ED25519_SIGNATURE_BYTES) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
'detached Ed25519 signature must be exactly 64 bytes',
);
}
const trustedKey = readTrustedPublicKey(options.publicKeyPath);
if (!crypto.verify(null, contents, trustedKey.key, signature)) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
'detached signature verification failed',
);
}
const report = buildFinalReport({
physicalReport,
payload,
signature,
fingerprint: trustedKey.fingerprintSha256,
});
const serialized = `${JSON.stringify(report, null, options.json ? 0 : 2)}\n`;
writeNoReplace(options.outputPath, serialized);
process.stdout.write(serialized);
}
function main() {
if (Number(process.versions.node.split('.')[0]) < 24) {
throw new QingLong3PhysicalEdgeReleaseEvidenceError(
'Node.js 24 or newer is required',
);
}
const options = parseArguments(process.argv.slice(2));
if (options.phase === 'prepare') preparePhase(options);
else finalizePhase(options);
}
module.exports = {
ED25519_SIGNATURE_BYTES,
LIMITATIONS,
MAX_RELEASE_ARCHIVE_BYTES,
QingLong3PhysicalEdgeReleaseEvidenceError,
buildFinalReport,
buildSigningPayload,
hashStableFile,
parseArguments,
readExactPayload,
readTrustedPublicKey,
validatePhysicalEvidenceReport,
validateExactBaseWorkloads,
validateExactObserved,
};
if (require.main === module) {
try {
main();
} catch (error) {
const message =
error instanceof QingLong3PhysicalEvidenceError || error instanceof Error
? error.message
: String(error);
process.stderr.write(`${message}\n`);
process.exitCode = 1;
}
}