aere-quantum/device-identity/proba-device.mjs

698 lines
129 KiB
JavaScript

#!/usr/bin/env node
// proba-device.mjs: the probe of AERE Device Identity. Every key here is made at the start of the run and discarded at its end.
//
// node proba-device.mjs the rows below; exit 0 all as required, 1 a row is not, 2 only skipped rows (NEMASURAT)
// node proba-device.mjs --json the same, one JSON object on the last line: { rows: [{ id, title, state, expects, kind }] }
// (kind: valid, partial, attack: the verdict the row requires; check: another kind of row)
//
// A row of kind ATAC requires the verdict INVALID and the EXACT set of rules that refused (`expects`): refused for another reason is
// a red row. A row PARTIAL requires that nothing refused and the EXACT set of causes that keep the verdict from VALID. The negative
// control (control-negativ-device.mjs) reads `expects`: with one rule planted out of a copy, the rows that expect that rule, and
// only they, must go red.
// The AIP-23 envelope rows need the reference verifier: AERE_VERIFY_PROOF=<verify-proof.mjs> (published at aere.network/tools).
import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import crypto from 'node:crypto'; import { spawnSync } from 'node:child_process'; import { fileURLToPath } from 'node:url';
import * as ID from '../identity/identity.mjs';
import { buildProof } from '../proof-kinds/proof-kinds.mjs';
import * as D from './device.mjs';
const AICI = path.dirname(fileURLToPath(import.meta.url));
const DEV_VERIFY = path.resolve(AICI, '..', '..', 'verificator-falcon', 'verify-proof.mjs');
const VERIFY = process.env.AERE_VERIFY_PROOF || (fs.existsSync(DEV_VERIFY) ? DEV_VERIFY : '');
const JSON_OUT = process.argv.includes('--json');
// --repede: without the rows that start processes (the command line, the reference verifier) and without the damaged bundles. For a
// search that runs the probe hundreds of times (mutanti-device.mjs); it is NOT a measurement of those rows, and it never exits 0.
const REPEDE = process.argv.includes('--repede');
const randuri = [];
const spune = (s) => { if (!JSON_OUT) console.log(s); };
const rand = (id, title, state, detail = '', expects = [], kind = 'check') => { randuri.push({ id, title, state, expects, kind }); spune(` [${state.padEnd(9)}] ${id} ${title}${state === 'VERDE' || !detail ? '' : ' -- ' + detail}`); };
const cer = (id, title, cond, detail = '', expects = []) => rand(id, title, cond ? 'VERDE' : 'ROSU', detail, expects);
const clona = (x) => JSON.parse(JSON.stringify(x));
const la = (a) => [...a].sort().join(',');
// ---------------------------------------------------------------- the world: a manufacturer, two owners, a second identity, a stranger, a device, a clone
const T0 = Math.floor(Date.now() / 1000) * 1000 - 3600000, ZI = 86400;
const at = (s) => new Date(T0 + s * 1000).toISOString(), cand = (s) => new Date(T0 + s * 1000);
const [mfr, op, cumparator, strain, altMfr, aDoua] = Array.from({ length: 6 }, () => ID.generateKeys());
const dev = D.generateDeviceKey(), clonaDev = D.generateDeviceKey();
const LID = 'https://manufacturer.example/status/1', AUD = 'https://gateway.example', NONCE = 'n-' + crypto.randomBytes(8).toString('hex');
const IMG1 = Buffer.from('firmware image 1.0.0 (probe)'), IMG2 = fs.readFileSync(path.join(AICI, 'fixturi', 'firmware-2.0.0.bin'));
const POS = JSON.parse(fs.readFileSync(path.join(AICI, 'fixturi', 'atestare-firmware.json'), 'utf8'));
const face = (o = {}) => D.manufacture({ manufacturer: mfr, deviceKey: dev.publicKey, model: 'AX-200', serial: 'SN-0001', validFrom: at(-30 * ZI), validUntil: at(3650 * ZI), status: { list: LID, index: 7 }, now: cand(-30 * ZI), ...o });
const cred = face(), credAlt = face({ deviceKey: clonaDev.publicKey, serial: 'SN-0002', status: { list: LID, index: 8 } }); // credAlt: ANOTHER device of the same manufacturer
const lista = (o = {}) => ID.createStatusList({ issuer: mfr, id: LID, validFrom: at(-3600), validUntil: at(ZI), now: cand(-3600), ...o });
const fw1 = D.releaseFirmware({ signer: mfr, model: 'AX-200', version: '1.0.0', sequence: 1, image: IMG1, now: cand(-20 * ZI) });
const fw2 = D.releaseFirmware({ signer: mfr, model: 'AX-200', version: '2.0.0', sequence: 2, image: IMG2, now: cand(-5 * ZI) });
const fw2pos = D.releaseFirmware({ signer: mfr, model: 'AX-200', version: '2.0.0', sequence: 2, image: IMG2, softwareAttestation: POS, now: cand(-5 * ZI) });
const politica = (o = {}) => D.firmwarePolicy({ manufacturer: mfr, model: 'AX-200', minSequence: 1, validFrom: at(-3600), validUntil: at(ZI), now: cand(-3600), ...o });
const t1 = D.transferOwnership({ from: mfr, to: op.public, manufacture: cred, now: cand(-10 * ZI) });
const t2 = D.transferOwnership({ from: op, to: cumparator.public, manufacture: cred, transfers: [t1], now: cand(-2 * ZI) });
const cap0 = D.ownershipHead(cred), cap1 = D.ownershipHead(cred, [t1]), cap2 = D.ownershipHead(cred, [t1, t2]);
const ACT = ['door.unlock', 'telemetry.publish'];
const aut = (owner, cap, o = {}) => D.authorize({ owner, deviceId: dev.deviceId, ownership: cap, actions: ACT, notBefore: at(-ZI), notAfter: at(ZI), now: cand(-ZI), ...o });
// a status record: the manufacturer signs as the manufacturer of the credential (no ownership head), anybody else under the head given
const stare = (by, state, seq, cap, o = {}) => D.statusRecord({ by, state, seq, validUntil: at(ZI), now: cand(-600), ...(by === mfr ? { manufacture: cred } : { deviceId: dev.deviceId, ownership: cap }), ...o });
const atest = (o = {}) => D.attest({ device: dev, nonce: NONCE, audience: AUD, firmware: fw2, ownership: cap1, action: 'telemetry.publish', now: cand(60), ...o });
const A1 = aut(op, cap1), X1 = atest(), activ = stare(op, 'active', 1, cap1);
const B = (o = {}) => D.bundle({ manufacture: cred, firmware: fw2, transfers: [t1], authorization: A1, attestation: X1, ...o });
const O = (o = {}) => ({ now: at(65), nonce: NONCE, audience: AUD, trustedManufacturers: [mfr.id], statusLists: [lista()], firmwarePolicies: [politica()], statusRecords: [activ], ...o });
// forged siblings that only a key holder can make: a statement changed and signed again with the published formulas
const reSemn = (scop, link, k, f) => { const st = clona(link.statement); f(st); return { statement: st, signature: D.signLink(scop, st, k) }; };
const reCred = (c, k, f) => { const st = clona(c.statement); f(st); return { statement: st, signature: ID.signText('credential', ID.canonical(st), k) }; };
const reAtest = (x, d, f) => { const st = clona(x.statement); f(st); return { statement: st, signature: D.signAttestation(st, d) }; };
const fals = 'aere-id:' + '11'.repeat(20); // an id that no key here derives
// the same bytes in ANOTHER base64 text: the unused bits of the last character changed where there is padding, else a padding character added
const AB = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
const altText = (s) => { const p = s.indexOf('='); if (p < 0) return s + '='; const c = AB.indexOf(s[p - 1]); return s.slice(0, p - 1) + AB[c ^ 1] + s.slice(p); };
const aceiasiOcteti = (a, b) => a !== b && Buffer.from(a, 'base64').equals(Buffer.from(b, 'base64'));
// sold on: the owner transfers the device to a second identity (a buyer, or another key of its own: nothing in the chain tells them apart)
const tx = D.transferOwnership({ from: op, to: aDoua.public, manufacture: cred, transfers: [t1], now: cand(-300) }), capx = D.ownershipHead(cred, [t1, tx]);
const Bx = () => D.bundle({ manufacture: cred, firmware: fw2, transfers: [t1, tx], authorization: aut(aDoua, capx, { now: cand(-200), notBefore: at(-200) }), attestation: atest({ ownership: capx }) });
const activX = stare(aDoua, 'active', 1, capx, { now: cand(-100) });
// sold, and bought back: the first owner owns the device again, under a third head
const t3 = D.transferOwnership({ from: cumparator, to: op.public, manufacture: cred, transfers: [t1, t2], now: cand(-ZI) }), cap3 = D.ownershipHead(cred, [t1, t2, t3]);
const B3 = () => D.bundle({ manufacture: cred, firmware: fw2, transfers: [t1, t2, t3], authorization: aut(op, cap3, { now: cand(-3600), notBefore: at(-3600) }), attestation: atest({ ownership: cap3 }) });
const numit = (r, cauza, text = '') => r.unjudged.includes(cauza) && r.rows.some((x) => x.unjudged === cauza && x.pass === null && x.detail.includes(text));
const judeca = (b, o) => D.verifyDevice(b, o);
function valid(id, title, b, o, extra = () => true) { const r = judeca(b, o); const neP = r.unjudged.filter((c) => D.PARTIALE.includes(c)); rand(id, title, r.verdict === 'VALID' && !r.rules.length && !neP.length && extra(r) ? 'VERDE' : 'ROSU', `${r.verdict} [${r.rules}] unjudged [${r.unjudged}]`, [], 'valid'); return r; }
function partial(id, title, b, o, cauze, extra = () => true) { const r = judeca(b, o); const neP = r.unjudged.filter((c) => D.PARTIALE.includes(c)); rand(id, title, r.verdict === 'PARTIAL' && r.valid && la(neP) === la(cauze) && extra(r) ? 'VERDE' : 'ROSU', `${r.verdict} [${r.rules}] causes [${neP}], required [${cauze}]`, [], 'partial'); return r; }
function atac(id, title, b, o, reguli, extra = () => true) { const r = judeca(b, o); rand(id, 'ATAC: ' + title, r.verdict === 'INVALID' && la(r.rules) === la(reguli) && r.subject === null && extra(r) ? 'VERDE' : 'ROSU', `${r.verdict} [${r.rules}], required exactly [${reguli}]`, reguli, 'attack'); return r; }
// ================================================================ A. the honest paths
spune('A. the honest paths');
valid('A01', 'a device made by a trusted manufacturer, sold once, authorized by its owner, attesting now with the verifier\'s nonce: VALID', B(), O(),
(r) => r.subject.deviceId === dev.deviceId && r.subject.owner === op.id && r.subject.manufacturer === mfr.id && r.subject.firmware.sequence === 2 && r.subject.action === 'telemetry.publish' && r.subject.state === 'active' && Object.entries(r.links).every(([l, s]) => s === (l === 'witness' ? 'ABSENT' : 'PASSED')));
valid('A02', 'never sold: the manufacturer is the owner and authorizes the device itself', D.bundle({ manufacture: cred, firmware: fw2, authorization: aut(mfr, cap0), attestation: atest({ ownership: cap0 }) }), O({ statusRecords: [stare(mfr, 'active', 1, null)] }), (r) => r.subject.owner === mfr.id && r.subject.transfers === 0);
valid('A03', 'sold twice: the second buyer authorizes, the device names the head after the second transfer', D.bundle({ manufacture: cred, firmware: fw2, transfers: [t1, t2], authorization: aut(cumparator, cap2), attestation: atest({ ownership: cap2 }) }), O({ statusRecords: [stare(cumparator, 'active', 1, cap2)] }), (r) => r.subject.owner === cumparator.id);
valid('A04', 'the release names a Proof of Software attestation of its image, and the verifier is handed it', B({ firmware: fw2pos }), O({ softwareAttestation: POS }));
valid('A05', 'a suspension lifted by the one who suspended: the owner\'s later record says active', B(), O({ statusRecords: [stare(op, 'suspended', 1, cap1), stare(op, 'active', 2, cap1)] }));
valid('A06', 'a status record of a stranger ("retired") is ignored, and said so', B(), O({ statusRecords: [activ, stare(strain, 'retired', 9, cap1)] }), (r) => numit(r, 'status-ignored') && !r.unjudged.includes('status-prior-ownership') && r.subject.priorOwnership.length === 0);
valid('A07', 'THE SELLER SIGNS A RETIREMENT, one dated before the sale and one under the buyer\'s head: neither binds the buyer (it cannot disable what it sold), and both are NAMED with the state they say', D.bundle({ manufacture: cred, firmware: fw2, transfers: [t1, t2], authorization: aut(cumparator, cap2), attestation: atest({ ownership: cap2 }) }),
O({ statusRecords: [stare(cumparator, 'active', 1, cap2), stare(op, 'retired', 5, cap1, { now: cand(-3 * ZI) }), stare(op, 'retired', 6, cap2)] }),
(r) => numit(r, 'status-prior-ownership', '"retired"') && !r.unjudged.includes('status-ignored') && la(r.subject.priorOwnership.map((p) => `${p.owner} ${p.state} ${p.records}`)) === `${op.id} retired 2` && r.subject.state === 'active' && r.subject.owner === cumparator.id);
valid('A08', 'a retirement dated three hours in the verifier\'s future is not in effect yet, and it is NAMED (not skipped in silence)', B(), O({ statusRecords: [activ, stare(mfr, 'retired', 1, null, { now: cand(3 * 3600), validUntil: at(ZI) })] }), (r) => numit(r, 'status-future', '"retired"'));
valid('A08b', 'a retirement of the manufacturer dated 120 s ahead (its clock runs fast, beyond the tolerance of 60 s): not in effect yet, and named', B(), O({ statusRecords: [activ, stare(mfr, 'retired', 1, null, { now: cand(65 + 120) })] }), (r) => numit(r, 'status-future', '"retired"'));
{ const r = judeca(B({ firmware: fw1, attestation: atest({ nonce: 'old', firmware: fw1 }), authorization: aut(mfr, cap1) }), O({ firmwarePolicies: [politica({ minSequence: 2 })] }));
cer('A09', 'the verifier does not stop at the first refusal: three unrelated faults, three rules named, and the links that hold still PASSED', r.verdict === 'INVALID' && la(r.rules) === 'authorization-owner,firmware-rollback,nonce' && r.links.manufacture === 'PASSED' && r.links.ownership === 'PASSED' && r.links.firmware === 'FAILED', `[${r.rules}] ${JSON.stringify(r.links)}`, ['authorization-owner', 'firmware-rollback', 'nonce']); }
valid('A11', 'sold, and bought back: the owner lifts, under its current head, the suspension it signed under its FIRST ownership (a later record of its own)', B3(), O({ statusRecords: [stare(op, 'suspended', 7, cap1), stare(op, 'active', 8, cap3)] }), (r) => r.subject.state === 'active' && r.subject.transfers === 3);
valid('A12', 'sold on to a second identity, with no status record of the owner before it in the registry: VALID, and nothing is named', Bx(), O({ statusRecords: [activX] }), (r) => r.subject.owner === aDoua.id && r.subject.priorOwnership.length === 0 && !r.unjudged.includes('status-prior-ownership'));
{ const r = judeca(B(), O()); const fara = r.rows.filter((x) => x.pass === false || (x.pass !== null && !x.rule && x.link !== 'ownership'));
cer('A10', 'every judged row names its rule; every rule named is declared; nothing private is in the verdict', !fara.length && r.rows.every((x) => !x.rule || D.REGULI.includes(x.rule)) && !/private|privat/.test(JSON.stringify(r))); }
// ================================================================ B. what was not brought: PARTIAL, by name, never VALID
spune('B. what the verifier was not handed leaves the verdict PARTIAL');
partial('B01', 'no nonce given: an old attestation could be replayed', B(), O({ nonce: null }), ['nonce']);
partial('B02', 'no audience given: an attestation made for another verifier would be accepted', B(), O({ audience: null }), ['audience']);
partial('B03', 'no trusted manufacturer named: anyone can sign a manufacture credential', B(), O({ trustedManufacturers: null }), ['manufacturer-trust']);
partial('B04', 'the manufacturer\'s recall list was not handed', B(), O({ statusLists: [] }), ['manufacture-status-unread']);
partial('B05', 'a recall list with the same id signed by ANOTHER manufacturer is not the manufacturer\'s list', B(), O({ statusLists: [ID.createStatusList({ issuer: altMfr, id: LID, revoked: [7], validFrom: at(-3600), validUntil: at(ZI), now: cand(-3600) })] }), ['manufacture-status-unread']);
partial('B06', 'an expired recall list is not read as "not recalled"', B(), O({ statusLists: [lista({ validFrom: at(-3 * ZI), validUntil: at(-ZI), now: cand(-3 * ZI) })] }), ['manufacture-status-unread']);
partial('B06b', 'a recall list signed by the manufacturer whose validity is not a time (it recalls the device), beside a clean current one: it cannot be placed in time, and is not read as "not recalled"', B(),
O({ statusLists: [lista(), (() => { const st = clona(lista({ revoked: [7] }).statement); st.validUntil = 'next year'; return { statement: st, signature: ID.signText('status-list', ID.canonical(st), mfr) }; })()] }), ['manufacture-status-unread'], (r) => numit(r, 'manufacture-status-unread', 'not a time'));
partial('B07', 'no firmware policy handed: a retracted or outdated firmware cannot be seen', B(), O({ firmwarePolicies: [] }), ['firmware-policy-unread']);
partial('B08', 'a firmware policy signed by a stranger is not the manufacturer\'s policy', B(), O({ firmwarePolicies: [D.firmwarePolicy({ manufacturer: strain, model: 'AX-200', minSequence: 0, validFrom: at(-3600), validUntil: at(ZI), now: cand(-3600) })] }), ['firmware-policy-unread']);
partial('B09', 'REVOCATION NOT READ: the status registry was not handed, so the verdict is not VALID', B(), O({ statusRecords: [] }), ['status-unread'], (r) => r.subject.state === 'unknown');
partial('B10', 'the only "active" record has expired: read the registry again', B(), O({ statusRecords: [stare(op, 'active', 1, cap1, { now: cand(-3 * ZI), validUntil: at(-ZI) })] }), ['status-unread']);
partial('B11', 'the release names a Proof of Software attestation that was not handed', B({ firmware: fw2pos }), O(), ['software-unread']);
partial('B12', 'no attestation in the bundle: nothing was said by the device now', D.bundle({ manufacture: cred, firmware: fw2, transfers: [t1], authorization: A1 }), O(), ['attestation-missing'], (r) => r.links.attestation === 'NOT_SUPPLIED');
partial('B13', 'no authorization in the bundle', D.bundle({ manufacture: cred, firmware: fw2, transfers: [t1], attestation: X1 }), O(), ['authorization-missing']);
partial('B14', 'no firmware release in the bundle: the attested digest names nothing a manufacturer signed', D.bundle({ manufacture: cred, transfers: [t1], authorization: A1, attestation: X1 }), O(), ['firmware-missing']);
partial('B15', 'no manufacture credential: the attestation alone is a key speaking for itself', D.bundle({ attestation: X1 }), O({ statusRecords: [] }), ['authorization-missing', 'firmware-missing', 'manufacture-missing', 'status-unread']);
partial('B16', 'transfers without a manufacture credential have nothing to start from', D.bundle({ firmware: fw2, transfers: [t1], authorization: A1, attestation: X1 }), O(), ['manufacture-missing', 'ownership-unrooted']);
partial('B17', 'an attestation that carries a hardware root: this verifier does not judge one, and says so', B({ attestation: reAtest(X1, dev, (st) => { st.hardwareRoot = { type: 'tpm2-quote', evidence: 'AAAA' }; }) }), O(), ['hardware-root']);
partial('B18', 'a manufacture credential that carries a hardware root (an endorsement of K): not judged either', D.bundle({ manufacture: reCred(cred, mfr, (st) => { st.claims.hardwareRoot = { type: 'ek-certificate', evidence: 'AAAA' }; }) }), O({ statusRecords: [stare(mfr, 'active', 1, null)] }),
['attestation-missing', 'authorization-missing', 'firmware-missing', 'hardware-root']);
// ================================================================ C. the attacks, each refused for its named rule
spune('C. manufacturer');
{ const impostor = D.manufacture({ manufacturer: altMfr, deviceKey: clonaDev.publicKey, model: 'AX-200', serial: 'SN-0001', validFrom: at(-30 * ZI), validUntil: at(3650 * ZI), now: cand(-30 * ZI) });
atac('C01', 'a manufacturer WITHOUT THE RIGHT: its own keys, its own id, not among those the verifier trusts', D.bundle({ manufacture: impostor }), O({ statusRecords: [] }), ['manufacturer-trusted']);
atac('C02', 'an impostor writes the trusted manufacturer\'s id beside its own keys', D.bundle({ manufacture: reCred(impostor, altMfr, (st) => { st.issuer.id = mfr.id; st.holder.id = mfr.id; }) }), O({ statusRecords: [] }), ['manufacturer-id']); }
atac('C03', 'the model edited after the manufacturer signed', D.bundle({ manufacture: (() => { const c = clona(cred); c.statement.claims.model = 'AX-900'; return c; })() }), O(), ['manufacture-signature']);
atac('C04', 'the ML-DSA-65 half of the manufacturer\'s signature removed: the classical half alone is not a signature here', D.bundle({ manufacture: (() => { const c = clona(cred); delete c.signature.mldsa65; return c; })() }), O(), ['manufacture-signature']);
atac('C05', 'the manufacture credential expired', D.bundle({ manufacture: D.manufacture({ manufacturer: mfr, deviceKey: dev.publicKey, model: 'AX-200', serial: 'SN-0001', validFrom: at(-30 * ZI), validUntil: at(-ZI), now: cand(-30 * ZI) }) }), O(), ['manufacture-window']);
atac('C06', 'RECALL: the manufacturer\'s current status list has the bit of this device set', B(), O({ statusLists: [lista({ revoked: [7] })] }), ['manufacture-revoked']);
atac('C07', 'two current lists of the manufacturer, one recalls: a recall is not lifted by another list', B(), O({ statusLists: [lista({ revoked: [7] }), lista()] }), ['manufacture-revoked']);
atac('C08', 'the manufacturer signed a status list that does not decompress', B(), O({ statusLists: [(() => { const st = clona(lista().statement); st.encodedList = Buffer.from('not gzip').toString('base64url'); return { statement: st, signature: ID.signText('status-list', ID.canonical(st), mfr) }; })()] }), ['manufacture-status-list']);
atac('C09', 'a credential of another type passed as a manufacture credential', D.bundle({ manufacture: reCred(cred, mfr, (st) => { st.type = 'ExampleMembership'; }) }), O(), ['manufacture-form']);
atac('C10', 'the manufacturer issues the credential to somebody else as holder (ownership without a signed transfer)', D.bundle({ manufacture: reCred(cred, mfr, (st) => { st.holder = { id: op.id, keys: op.public }; }) }), O(), ['manufacture-holder']);
atac('C11', 'the manufacturer signs a device id that the device key does not derive', D.bundle({ manufacture: reCred(cred, mfr, (st) => { st.claims.deviceId = clonaDev.deviceId; }) }), O(), ['device-id']);
spune('C. a classical key under a post-quantum name');
{ const ed = crypto.generateKeyPairSync('ed25519'); const spki = ed.publicKey.export({ type: 'spki', format: 'der' }).toString('base64'); const idClasic = 'aere-device:' + crypto.createHash('sha256').update(Buffer.from(spki, 'base64')).digest('hex').slice(0, 40);
const credClasic = reCred(cred, mfr, (st) => { st.claims.deviceKey = spki; st.claims.deviceId = idClasic; });
atac('C12', 'the manufacturer certifies an Ed25519 key as the device key (the id is the hash of that key)', D.bundle({ manufacture: credClasic }), O(), ['device-key']);
const st = clona(X1.statement); st.key = spki; st.deviceId = idClasic; st.ownership = D.ownershipHead(credClasic);
const sig = { alg: 'ml-dsa-65', value: crypto.sign(null, Buffer.from('aere-device/v1/attestation\n' + ID.canonical(st), 'utf8'), ed.privateKey).toString('base64') };
atac('C13', 'and the "device" attests with that Ed25519 key, the signature declared ml-dsa-65', D.bundle({ manufacture: credClasic, attestation: { statement: st, signature: sig } }), O(), ['attestation-key', 'device-key']);
atac('C14', 'the attestation alone with an Ed25519 key under the ML-DSA-65 name (no manufacture credential to contradict it)', D.bundle({ attestation: { statement: st, signature: sig } }), O(), ['attestation-key']);
const a = crypto.generateKeyPairSync('ed25519'), b = crypto.generateKeyPairSync('ed25519'); const sp = (k) => k.publicKey.export({ type: 'spki', format: 'der' }).toString('base64');
const pub = { alg: ID.ALG, ed25519: sp(a), mldsa65: sp(b) }; const idp = 'aere-id:' + crypto.createHash('sha256').update(Buffer.from(ID.canonical(pub), 'utf8')).digest('hex').slice(0, 40);
const s2 = clona(cred.statement); s2.issuer = { id: idp, keys: pub }; s2.holder = { id: idp, keys: pub }; delete s2.status; const m = Buffer.from('aere-identity/v1/credential\n' + ID.canonical(s2), 'utf8');
atac('C15', 'a manufacturer whose "ML-DSA-65" key is an Ed25519 key, both halves of the signature Ed25519', D.bundle({ manufacture: { statement: s2, signature: { alg: ID.ALG, ed25519: crypto.sign(null, m, a.privateKey).toString('base64'), mldsa65: crypto.sign(null, m, b.privateKey).toString('base64') } } }), O({ trustedManufacturers: [idp], statusRecords: [] }), ['manufacture-signature', 'manufacturer-id']);
atac('C16', 'the attestation\'s signature declares another scheme', B({ attestation: { statement: X1.statement, signature: { ...X1.signature, alg: 'ed25519' } } }), O(), ['attestation-signature']); }
spune('C. a cloned device');
atac('C17', 'A CLONE WITH ANOTHER KEY: its own key and the id of that key, behind the real device\'s manufacture credential', B({ attestation: D.attest({ device: clonaDev, nonce: NONCE, audience: AUD, firmware: fw2, ownership: cap1, action: 'telemetry.publish', now: cand(60) }) }), O(), ['attestation-device']);
atac('C18', 'a clone with another key that claims the real device\'s id', B({ attestation: reAtest(X1, clonaDev, (st) => { st.key = clonaDev.publicKey; }) }), O(), ['attestation-device', 'attestation-key']);
atac('C19', 'a clone that keeps the real key in the statement and signs with its own', B({ attestation: { statement: X1.statement, signature: D.signAttestation(X1.statement, clonaDev) } }), O(), ['attestation-signature']);
atac('C20', 'the attested action edited after the device signed', B({ attestation: (() => { const x = clona(X1); x.statement.action = 'door.unlock'; return x; })() }), O(), ['attestation-signature']);
spune('C. firmware');
atac('C21', 'FIRMWARE ROLLED BACK: a genuine older release (sequence 1) below the floor the manufacturer set (2)', B({ firmware: fw1, attestation: atest({ firmware: fw1 }) }), O({ firmwarePolicies: [politica({ minSequence: 2 })] }), ['firmware-rollback']);
atac('C22', 'two current policies, a loose one and a strict one: the strict one holds', B({ firmware: fw1, attestation: atest({ firmware: fw1 }) }), O({ firmwarePolicies: [politica({ minSequence: 2 }), politica({ minSequence: 0 })] }), ['firmware-rollback']);
atac('C23', 'the manufacturer retracted this very release (its digest is in the policy)', B(), O({ firmwarePolicies: [politica({ retracted: [fw2.statement.digest] })] }), ['firmware-retracted']);
atac('C24', 'a retraction is not lifted by another current policy that does not list it', B(), O({ firmwarePolicies: [politica(), politica({ retracted: [fw2.statement.digest] })] }), ['firmware-retracted']);
atac('C24b', 'the manufacturer\'s own tooling wrote the retracted digest in upper case: it is the same digest', B(), O({ firmwarePolicies: [reSemn('firmware-policy', politica(), mfr, (st) => { st.retracted = ['0x' + fw2.statement.digest.slice(2).toUpperCase()]; })] }), ['firmware-retracted']);
atac('C25', 'the bundle shows the new release, the device says it runs the old digest', B({ attestation: atest({ firmware: fw1 }) }), O(), ['attestation-firmware']);
atac('C26', 'a release signed by somebody who is not the manufacturer of the device', B({ firmware: D.releaseFirmware({ signer: strain, model: 'AX-200', version: '2.0.0', sequence: 2, image: IMG2 }) }), O({ firmwarePolicies: [] }), ['firmware-signer'], (r) => r.unjudged.includes('firmware-policy-unread'));
atac('C27', 'the version label edited after the release was signed', B({ firmware: (() => { const f = clona(fw2); f.statement.version = '9.9.9'; return f; })() }), O(), ['firmware-signature']);
atac('C28', 'a genuine release of the manufacturer for ANOTHER model', (() => { const f = D.releaseFirmware({ signer: mfr, model: 'BX-1', version: '2.0.0', sequence: 2, image: IMG2 }); return B({ firmware: f, attestation: atest({ firmware: f }) }); })(), O({ firmwarePolicies: [D.firmwarePolicy({ manufacturer: mfr, model: 'BX-1', validFrom: at(-3600), validUntil: at(ZI), now: cand(-3600) })] }), ['firmware-model']);
atac('C29', 'the Proof of Software attestation handed is not the one the release names', B({ firmware: fw2pos }), O({ softwareAttestation: (() => { const p = clona(POS); p.statement.subject.version = '2.0.1'; p.statementHash = '0x' + crypto.createHash('sha256').update(JSON.stringify(p.statement)).digest('hex'); return p; })() }), ['firmware-software']);
atac('C30', 'a release whose sequence is not a number', B({ firmware: reSemn('firmware', fw2, mfr, (st) => { st.sequence = '2'; }) }), O(), ['firmware-form']);
spune('C. ownership and authorization');
atac('C31', 'THE PREVIOUS OWNER STILL SIGNS: after selling, it grants an authorization under the current head', D.bundle({ manufacture: cred, firmware: fw2, transfers: [t1, t2], authorization: aut(op, cap2), attestation: atest({ ownership: cap2 }) }), O({ statusRecords: [stare(cumparator, 'active', 1, cap2)] }), ['authorization-owner']);
atac('C32', 'the previous owner\'s authorization from BEFORE the sale, shown after it', D.bundle({ manufacture: cred, firmware: fw2, transfers: [t1, t2], authorization: A1, attestation: atest({ ownership: cap2 }) }), O({ statusRecords: [stare(cumparator, 'active', 1, cap2)] }), ['authorization-owner', 'authorization-ownership']);
atac('C33', 'the current owner\'s authorization given under an older head', B({ authorization: aut(op, cap0) }), O(), ['authorization-ownership']);
atac('C34', 'the previous owner sells the device a second time', B({ transfers: [t1, reSemn('transfer', t2, mfr, (st) => { st.from = { id: mfr.id, keys: mfr.public }; })], authorization: null, attestation: null }), O({ statusRecords: [] }), ['transfer-from']);
atac('C35', 'the device still names the ownership head of before the sale', D.bundle({ manufacture: cred, firmware: fw2, transfers: [t1, t2], authorization: aut(cumparator, cap2), attestation: X1 }), O({ statusRecords: [stare(cumparator, 'active', 1, cap2)] }), ['attestation-ownership']);
atac('C36', 'the first transfer taken out of the chain', B({ transfers: [t2], authorization: null, attestation: null }), O({ statusRecords: [] }), ['transfer-from', 'transfer-prev']);
atac('C37', 'a transfer signed by a stranger in the owner\'s name', D.bundle({ manufacture: cred, transfers: [{ statement: t1.statement, signature: D.signLink('transfer', t1.statement, strain) }] }), O({ statusRecords: [] }), ['transfer-signature']);
atac('C38', 'the ML-DSA-65 half of a transfer signature removed', D.bundle({ manufacture: cred, transfers: [(() => { const t = clona(t1); delete t.signature.mldsa65; return t; })()] }), O({ statusRecords: [] }), ['transfer-signature']);
atac('C38b', 'the ML-DSA-65 half of a transfer signature replaced by a stranger\'s (the Ed25519 half is the owner\'s): one half is not enough', D.bundle({ manufacture: cred, transfers: [{ statement: t1.statement, signature: { ...t1.signature, mldsa65: D.signLink('transfer', t1.statement, strain).mldsa65 } }] }), O({ statusRecords: [] }), ['transfer-signature']);
atac('C39', 'PURPOSE SEPARATION: the owner\'s signature made for another purpose (status) shown as the signature of an authorization', B({ authorization: { statement: A1.statement, signature: D.signLink('status', A1.statement, op) } }), O(), ['authorization-signature']);
atac('C40', 'a transfer dated before the device was made', (() => { const t = reSemn('transfer', t1, mfr, (st) => { st.at = at(-400 * ZI); }); const c = D.ownershipHead(cred, [t]); return D.bundle({ manufacture: cred, firmware: fw2, transfers: [t], authorization: aut(op, c), attestation: atest({ ownership: c }) }); })(), O({ statusRecords: [] }), ['transfer-time'], (r) => r.unjudged.includes('status-unread'));
atac('C41', 'a transfer dated in the future', (() => { const t = reSemn('transfer', t1, mfr, (st) => { st.at = at(30 * ZI); }); return D.bundle({ manufacture: cred, transfers: [t] }); })(), O({ statusRecords: [] }), ['transfer-time']);
atac('C42', 'a transfer of ANOTHER device spliced into the chain', D.bundle({ manufacture: cred, transfers: [reSemn('transfer', t1, mfr, (st) => { st.deviceId = clonaDev.deviceId; })] }), O({ statusRecords: [] }), ['transfer-device']);
atac('C43', 'a transfer whose recipient id is not derived from the recipient keys', D.bundle({ manufacture: cred, transfers: [reSemn('transfer', t1, mfr, (st) => { st.to.id = 'aere-id:' + '11'.repeat(20); })] }), O({ statusRecords: [] }), ['transfer-ids']);
atac('C44', 'a link of another kind in the chain', D.bundle({ manufacture: cred, transfers: [reSemn('transfer', t1, mfr, (st) => { st.kind = 'aere-device-transfer-v0'; })] }), O({ statusRecords: [] }), ['transfer-form']);
{ let l = [], de = mfr; const chei = [op, cumparator]; for (let i = 0; i < 65; i++) { const catre = chei[i % 2]; l.push(D.transferOwnership({ from: de, to: catre.public, manufacture: cred, transfers: l, now: cand(-10 * ZI + i) })); de = catre; }
atac('C45', 'a chain of 65 transfers', D.bundle({ manufacture: cred, transfers: l }), O({ statusRecords: [] }), ['chain-length']); }
atac('C46', 'the device asks for an action its owner did not grant', B({ authorization: aut(op, cap1, { actions: ['door.unlock'] }) }), O(), ['authorization-action']);
atac('C47', 'the authorization expired an hour before', B({ authorization: aut(op, cap1, { notBefore: at(-2 * ZI), notAfter: at(-3600) }) }), O(), ['authorization-window']);
atac('C48', 'an authorization of ANOTHER device, by the same owner', B({ authorization: D.authorize({ owner: op, deviceId: clonaDev.deviceId, ownership: cap1, actions: ACT, notBefore: at(-ZI), notAfter: at(ZI), now: cand(-ZI) }) }), O(), ['authorization-device']);
atac('C49', 'an authorization whose action list is not in its normal form', B({ authorization: reSemn('authorization', A1, op, (st) => { st.actions = ['telemetry.publish', 'door.unlock']; }) }), O(), ['authorization-form']);
atac('C50', 'the granted actions widened after the owner signed', B({ authorization: (() => { const a = clona(A1); a.statement.actions = ['door.unlock', 'firmware.install', 'telemetry.publish']; return a; })() }), O(), ['authorization-signature']);
spune('C. status: a revocation that is read has effect');
atac('C51', 'REVOCATION IGNORED? no: the manufacturer retired the device, and the registry was read', B(), O({ statusRecords: [activ, stare(mfr, 'retired', 1, null)] }), ['status-retired']);
atac('C52', 'the manufacturer\'s retirement is never lifted: the owner\'s later "active" does not undo it', B(), O({ statusRecords: [stare(mfr, 'retired', 1, null), stare(op, 'active', 9, cap1)] }), ['status-retired']);
atac('C53', 'nor does the manufacturer\'s own later "active" undo its retirement', B(), O({ statusRecords: [stare(mfr, 'retired', 1, null), stare(mfr, 'active', 2, null)] }), ['status-retired']);
atac('C53b', 'nor does a SALE undo the manufacturer\'s retirement: the device retired by its manufacturer, sold on, the new owner says active', Bx(), O({ statusRecords: [stare(mfr, 'retired', 1, null), activX] }), ['status-retired']);
atac('C54', 'the owner retired the device', B(), O({ statusRecords: [activ, stare(op, 'retired', 2, cap1)] }), ['status-retired']);
atac('C54b', 'the owner\'s retirement holds while it owns the device: its own later "active" does not undo it', B(), O({ statusRecords: [stare(op, 'retired', 2, cap1), stare(op, 'active', 9, cap1)] }), ['status-retired']);
atac('C55', 'the manufacturer suspended the device; the owner\'s "active" does not lift a suspension it did not make', B(), O({ statusRecords: [stare(mfr, 'suspended', 1, null), stare(op, 'active', 9, cap1)] }), ['status-suspended']);
atac('C56', 'the owner suspended the device (its latest record)', B(), O({ statusRecords: [activ, stare(op, 'suspended', 2, cap1)] }), ['status-suspended']);
atac('C57', 'a suspension does not expire: its record is past validUntil and still holds', B(), O({ statusRecords: [stare(op, 'suspended', 2, cap1, { now: cand(-3 * ZI), validUntil: at(-ZI) })] }), ['status-suspended']);
atac('C58', 'two records of one signer with the same number, one says suspended: the stricter holds', B(), O({ statusRecords: [stare(op, 'active', 3, cap1), stare(op, 'suspended', 3, cap1)] }), ['status-suspended']);
spune('G. status and ownership: whose record counts, and for how long');
// a record of the owner that names no ownership head: the producer refuses to write one, so it is signed by hand with the published formula
const faraCap = (rec, k) => reSemn('status', rec, k, (st) => { st.ownership = null; });
atac('G01', 'THE OWNER OF NOW RETIRES THE DEVICE in a record that names NO ownership head, beside its own current "active": it is the owner\'s record all the same', B(), O({ statusRecords: [activ, faraCap(stare(op, 'retired', 2, cap1), op)] }), ['status-retired']);
atac('G02', 'the owner of now suspends the device in a record that names no ownership head', B(), O({ statusRecords: [activ, faraCap(stare(op, 'suspended', 2, cap1), op)] }), ['status-suspended']);
atac('G03', 'sold, and bought back: the suspension the owner signed under its FIRST ownership holds now that it owns the device again (it can only harm itself)', B3(), O({ statusRecords: [stare(op, 'active', 1, cap3), stare(op, 'suspended', 7, cap1)] }), ['status-suspended']);
atac('G04', 'sold, and bought back: the retirement the owner signed under its first ownership holds as well', B3(), O({ statusRecords: [stare(op, 'active', 1, cap3), stare(op, 'retired', 2, cap1)] }), ['status-retired']);
partial('G05', 'sold, and bought back: the only record is the owner\'s "active" of its FIRST ownership: it is not believed under another head, and said so', B3(), O({ statusRecords: [stare(op, 'active', 1, cap1)] }), ['status-unread'], (r) => numit(r, 'status-ignored', 'only under the current ownership head') && r.subject.state === 'unknown');
valid('G06', 'RETIREMENT, THEN A SALE: the owner retired the device and transferred it to a second identity, which says active under the new head. The retirement does not bind the new owner, and it is NAMED with the state it says (the whole registry was read)', Bx(), O({ statusRecords: [stare(op, 'retired', 1, cap1, { now: cand(-900) }), activX] }),
(r) => numit(r, 'status-prior-ownership', '"retired"') && la(r.subject.priorOwnership.map((p) => `${p.owner} ${p.state} ${p.records}`)) === `${op.id} retired 1` && r.subject.state === 'active' && r.subject.owner === aDoua.id && r.links.status === 'PASSED');
valid('G07', 'SUSPENSION, THEN A SALE: the same, the state named is "suspended"', Bx(), O({ statusRecords: [stare(op, 'suspended', 1, cap1, { now: cand(-900) }), activX] }),
(r) => numit(r, 'status-prior-ownership', '"suspended"') && la(r.subject.priorOwnership.map((p) => `${p.owner} ${p.state} ${p.records}`)) === `${op.id} suspended 1` && r.subject.state === 'active');
valid('G08', 'what a previous owner\'s records say: retired if any says so, else the state of its highest record', Bx(), O({ statusRecords: [activX, stare(op, 'suspended', 2, cap1), stare(op, 'active', 3, cap1)] }), (r) => numit(r, 'status-prior-ownership') && la(r.subject.priorOwnership.map((p) => `${p.state} ${p.records}`)) === 'active 2'
&& judeca(Bx(), O({ statusRecords: [activX, stare(op, 'retired', 1, cap1), stare(op, 'active', 9, cap1)] })).subject.priorOwnership[0].state === 'retired'
&& judeca(Bx(), O({ statusRecords: [activX, stare(op, 'active', 4, cap1), stare(op, 'suspended', 4, cap1)] })).subject.priorOwnership[0].state === 'suspended');
atac('G09', 'a retirement of the manufacturer whose date is not a time is applied all the same (who may retire said "retired")', B(), O({ statusRecords: [activ, reSemn('status', stare(mfr, 'retired', 1, null), mfr, (st) => { st.at = 'yesterday'; })] }), ['status-retired']);
partial('G10', 'the only record is an "active" of the owner whose validity is not a time: it is not believed', B(), O({ statusRecords: [reSemn('status', activ, op, (st) => { st.validUntil = 'next year'; })] }), ['status-unread'], (r) => numit(r, 'status-ignored', 'not a time'));
atac('G11', 'a retirement dated 30 s ahead of the verifier\'s clock is inside the tolerance: in effect', B(), O({ statusRecords: [activ, stare(mfr, 'retired', 1, null, { now: cand(95) })] }), ['status-retired']);
partial('G12', 'a record signed by the MANUFACTURER in a version this verifier does not read (it says retired), beside the owner\'s current "active": the registry is not read as clean', B(), O({ statusRecords: [activ, reSemn('status', stare(mfr, 'retired', 1, null), mfr, (st) => { st.v = 2; })] }), ['status-unread'], (r) => numit(r, 'status-unread', 'cannot be seen') && r.subject.state === 'active');
partial('G13', 'a record of the OWNER with a state this verifier does not know, beside its current "active"', B(), O({ statusRecords: [activ, reSemn('status', stare(op, 'retired', 2, cap1), op, (st) => { st.state = 'destroyed'; })] }), ['status-unread'], (r) => numit(r, 'status-unread', 'cannot be seen'));
valid('G14', 'a record of a PREVIOUS owner that this verifier cannot read does not bind the owner of now either: named, nothing is said about its state', Bx(), O({ statusRecords: [activX, reSemn('status', stare(op, 'retired', 1, cap1), op, (st) => { st.state = 'destroyed'; })] }), (r) => numit(r, 'status-prior-ownership', 'cannot read') && r.subject.priorOwnership.length === 0 && !r.unjudged.includes('status-unread'));
spune('C. the attestation: replay, staleness, another verifier');
atac('C59', 'AN ATTESTATION REPLAYED WITH AN OLD NONCE: the verifier gave a new one', B(), O({ nonce: 'n-' + crypto.randomBytes(8).toString('hex') }), ['nonce']);
atac('C60', 'an attestation made for one verifier shown to another', B(), O({ audience: 'https://another-gateway.example' }), ['audience']);
atac('C61', 'an hour-old attestation', B(), O({ now: at(3725) }), ['freshness']);
atac('C62', 'an attestation dated ten minutes in the verifier\'s future', B({ attestation: atest({ now: cand(665) }) }), O(), ['freshness']);
atac('C63', 'a file of another kind where the attestation should be', B({ attestation: reAtest(X1, dev, (st) => { st.kind = 'aere-device-status'; }) }), O(), ['attestation-form']);
spune('C. the witness (a reading of the notarized envelope that the verifier made)');
const PLIC = D.proofOfDevice(X1, buildProof);
cer('C64', 'the AIP-23 envelope of an attestation commits to it: kind device, the device id, the hash of the signed attestation, the hash of K', PLIC.kind === 'aere-proof-of-device-attestation' && PLIC.statement.deviceId === dev.deviceId && PLIC.statement.attestationHash === D.attestationHash(X1) && PLIC.statement.createdAt === X1.statement.at && !JSON.stringify(PLIC).includes(NONCE));
{ const citire = (dt) => [{ statementHash: PLIC.statementHash, at: Math.floor((T0 + 60000) / 1000) + dt, chainId: 28001 }];
valid('C65', 'an envelope nobody notarized claims nothing: VALID, the witness link PASSED on the commitment alone', B({ anchor: PLIC }), O(), (r) => r.links.witness === 'PASSED');
partial('C66', 'the envelope CLAIMS a notarization and the verifier read nothing on the chain: a claim is not a reading', B({ anchor: { ...PLIC, notarization: { chainId: 28001, firstSeen: 1 } } }), O(), ['witness-unread']);
valid('C67', 'with the verifier\'s own reading (the envelope was on the chain 40 s after the attestation): VALID', B({ anchor: { ...PLIC, notarization: { chainId: 28001, firstSeen: 1 } } }), O({ anchors: citire(40) }), (r) => r.subject.witness && r.subject.witness.chainId === 28001 && r.subject.witness.at === citire(40)[0].at);
atac('C68', 'the device dated its attestation AFTER the moment its envelope was already on the chain', B({ anchor: PLIC }), O({ anchors: citire(-600) }), ['witness-time']);
atac('C69', 'the envelope of ANOTHER attestation attached to this one', B({ anchor: D.proofOfDevice(atest({ now: cand(61) }), buildProof) }), O(), ['witness-envelope']);
atac('C70', 'the envelope edited after its hash', B({ anchor: (() => { const e = clona(PLIC); e.statement.posture = 'firmware-sequence-9'; return e; })() }), O(), ['witness-envelope']);
valid('C71', 'a reading of another envelope is not a reading of this one (ignored)', B({ anchor: PLIC }), O({ anchors: [{ statementHash: '0x' + '22'.repeat(32), at: 1 }] }));
partial('C71b', 'a reading of this envelope whose time is in MILLISECONDS is not a reading (taken for seconds it would lie thousands of years ahead and bound nothing): not used, and said', B({ anchor: PLIC }), O({ anchors: [{ statementHash: PLIC.statementHash, at: T0 + 100000, chainId: 28001 }] }), ['witness-unread'], (r) => numit(r, 'witness-unread', 'milliseconds') && r.subject.witness === null && r.rows.filter((x) => x.unjudged === 'witness-unread').length === 1); }
// ================================================================ F. the halves: a check made of several conditions has a row for each
// Each row here differs from an honest bundle in ONE condition of a compound check, so that the condition, taken out alone, turns
// exactly that row red (the negative control plants each of them out).
spune('F. the halves of the compound checks, one row each');
atac('F01', 'the manufacture credential is not valid yet: it starts tomorrow', D.bundle({ manufacture: face({ validFrom: at(ZI) }) }), O({ statusRecords: [] }), ['manufacture-window']);
atac('F02', 'the authorization begins in an hour', B({ authorization: aut(op, cap1, { notBefore: at(3600) }) }), O(), ['authorization-window']);
atac('F03', 'the authorization was not in force yet when the device attested: it begins 63 s after the attestation (on the verifier\'s clock, with its tolerance, it has begun)', B({ authorization: aut(op, cap1, { notBefore: at(123) }) }), O(), ['authorization-window']);
atac('F04', 'the authorization ended between the attestation and the verification', B({ authorization: aut(op, cap1, { notAfter: at(62) }) }), O(), ['authorization-window']);
atac('F05', 'the authorization ends before the time the device wrote in its attestation (dated 35 s ahead, inside the tolerance)', B({ authorization: aut(op, cap1, { notAfter: at(66) }), attestation: atest({ now: cand(100) }) }), O(), ['authorization-window']);
atac('F06', 'the authorization begins after the verifier\'s clock and its tolerance; the attestation is dated later still', B({ authorization: aut(op, cap1, { notBefore: at(135) }), attestation: atest({ now: cand(100) }) }), O(), ['authorization-window']);
atac('F07', 'a transfer that carries position 7 at position 1 (the hash of the link before it is right)', D.bundle({ manufacture: cred, transfers: [reSemn('transfer', t1, mfr, (st) => { st.seq = 7; })] }), O({ statusRecords: [] }), ['transfer-prev']);
atac('F08', 'a transfer at the right position that names another link before it', D.bundle({ manufacture: cred, transfers: [reSemn('transfer', t1, mfr, (st) => { st.prev = '0x' + '33'.repeat(32); })] }), O({ statusRecords: [] }), ['transfer-prev']);
atac('F09', 'a chain with no manufacture credential beside it: the GIVER of the first transfer carries an id its keys do not derive', D.bundle({ transfers: [reSemn('transfer', t1, mfr, (st) => { st.from.id = fals; })] }), O({ statusRecords: [] }), ['transfer-ids']);
atac('F10', 'the device attests the digest of the release shown under another sequence number (9)', B({ attestation: atest({ firmware: { digest: fw2.statement.digest, sequence: 9 } }) }), O(), ['attestation-firmware']);
atac('F11', 'the device attests the sequence number of the release shown with another digest', B({ attestation: atest({ firmware: { digest: fw1.statement.digest, sequence: 2 } }) }), O(), ['attestation-firmware']);
partial('F12', 'the only firmware policy handed is the manufacturer\'s policy for ANOTHER model', B(), O({ firmwarePolicies: [D.firmwarePolicy({ manufacturer: mfr, model: 'BX-1', minSequence: 0, validFrom: at(-3600), validUntil: at(ZI), now: cand(-3600) })] }), ['firmware-policy-unread']);
partial('F13', 'the only firmware policy handed has expired', B(), O({ firmwarePolicies: [politica({ validFrom: at(-7200), validUntil: at(-10), now: cand(-7200) })] }), ['firmware-policy-unread']);
valid('F14', 'an expired policy with a higher floor beside a current one: the current one is the policy of now', B({ firmware: fw1, attestation: atest({ firmware: fw1 }) }), O({ firmwarePolicies: [politica(), politica({ minSequence: 2, validFrom: at(-7200), validUntil: at(-10), now: cand(-7200) })] }));
partial('F15', 'the only firmware policy handed is not in force yet: it starts tomorrow', B(), O({ firmwarePolicies: [politica({ validFrom: at(ZI), validUntil: at(2 * ZI) })] }), ['firmware-policy-unread']);
{ const faraPrefix = reSemn('firmware-policy', politica(), mfr, (st) => { st.retracted = [fw2.statement.digest.slice(2)]; }); // signed by the manufacturer's own tooling, by hand: the producer refuses to write it
partial('F16', 'A SIGNED POLICY WHOSE RETRACTED ENTRY IS NOT A DIGEST (the 64 hex digits of this release, without 0x) is not read as a policy that retracts nothing', B(), O({ firmwarePolicies: [faraPrefix] }), ['firmware-policy-unread'], (r) => numit(r, 'firmware-policy-unread', 'not a digest'));
partial('F17', 'the same policy beside a clean current one: still not read as clean', B(), O({ firmwarePolicies: [politica(), faraPrefix] }), ['firmware-policy-unread'], (r) => numit(r, 'firmware-policy-unread', 'not a digest') && r.rows.some((x) => x.rule === 'firmware-retracted' && x.pass === true));
atac('F18', 'a policy that cannot be read does not hide what a readable one refuses: the floor of the readable policy holds', B({ firmware: fw1, attestation: atest({ firmware: fw1 }) }), O({ firmwarePolicies: [politica({ minSequence: 2 }), faraPrefix] }), ['firmware-rollback'], (r) => numit(r, 'firmware-policy-unread', 'not a digest')); }
partial('F19', 'a signed policy whose floor is not a whole number, beside a clean one', B(), O({ firmwarePolicies: [politica(), reSemn('firmware-policy', politica(), mfr, (st) => { st.minSequence = '1'; })] }), ['firmware-policy-unread'], (r) => numit(r, 'firmware-policy-unread', 'not a whole number'));
partial('F19b', 'a signed policy whose validity is not a time, beside a clean one', B(), O({ firmwarePolicies: [politica(), reSemn('firmware-policy', politica(), mfr, (st) => { st.validUntil = 'next week'; })] }), ['firmware-policy-unread'], (r) => numit(r, 'firmware-policy-unread', 'not a time'));
partial('F19c', 'a signed policy of a version this verifier does not read (it retracts this release), beside a clean one', B(), O({ firmwarePolicies: [politica(), reSemn('firmware-policy', politica({ retracted: [fw2.statement.digest] }), mfr, (st) => { st.v = 2; })] }), ['firmware-policy-unread'], (r) => numit(r, 'firmware-policy-unread', 'version'));
atac('F20', 'the manufacturer certifies the device key written in ANOTHER base64 text (a padding character added): one key has one text', D.bundle({ manufacture: reCred(cred, mfr, (st) => { st.claims.deviceKey = dev.publicKey + '='; }) }), O({ statusRecords: [] }), ['device-key'], () => aceiasiOcteti(dev.publicKey + '=', dev.publicKey));
atac('F21', 'the attestation carries the device key in another base64 text', D.bundle({ attestation: reAtest(X1, dev, (st) => { st.key = dev.publicKey + '='; }) }), O({ statusRecords: [] }), ['attestation-key']);
valid('F22', 'the registry also holds the manufacturer\'s retirement of ANOTHER device: it does not concern this one', B(), O({ statusRecords: [activ, D.statusRecord({ by: mfr, manufacture: credAlt, state: 'retired', seq: 1, validUntil: at(ZI), now: cand(-600) })] }), (r) => !r.unjudged.includes('status-ignored') && r.subject.state === 'active');
atac('F23', 'the manufacture credential names as holder ANOTHER ID beside the manufacturer\'s own keys', D.bundle({ manufacture: reCred(cred, mfr, (st) => { st.holder = { id: fals, keys: mfr.public }; }) }), O({ statusRecords: [] }), ['manufacture-holder']);
atac('F24', 'the manufacture credential names as holder the manufacturer\'s id beside OTHER keys', D.bundle({ manufacture: reCred(cred, mfr, (st) => { st.holder = { id: mfr.id, keys: op.public }; }) }), O({ statusRecords: [] }), ['manufacture-holder']);
valid('F25', 'a "retired" record signed with the owner\'s keys under an id those keys do not derive is not the owner\'s record: ignored, and said so', B(), O({ statusRecords: [activ, reSemn('status', stare(op, 'retired', 9, cap1), op, (st) => { st.by.id = fals; })] }), (r) => numit(r, 'status-ignored') && r.subject.state === 'active');
atac('F26', 'a release with no manufacture credential beside it, whose signer carries an id its keys do not derive', D.bundle({ firmware: reSemn('firmware', fw2, mfr, (st) => { st.signer.id = fals; }) }), O({ statusRecords: [] }), ['firmware-signer']);
atac('F27', 'an authorization with no chain beside it, whose owner carries an id its keys do not derive', D.bundle({ authorization: reSemn('authorization', A1, op, (st) => { st.owner.id = fals; }), attestation: X1 }), O({ statusRecords: [] }), ['authorization-owner']);
atac('F28', 'the Proof of Software attestation handed IS the one the release names, and it does not list the image of that release', (() => { const f = reSemn('firmware', fw1, mfr, (st) => { st.softwareAttestationHash = POS.statementHash; }); return B({ firmware: f, attestation: atest({ firmware: f }) }); })(), O({ softwareAttestation: POS }), ['firmware-software']);
atac('F29', 'a forged envelope: the hash of this attestation under ANOTHER device id, its statementHash recomputed', B({ anchor: (() => { const e = clona(PLIC); e.statement.deviceId = clonaDev.deviceId; e.statementHash = '0x' + crypto.createHash('sha256').update(JSON.stringify(e.statement)).digest('hex'); return e; })() }), O(), ['witness-envelope']);
atac('F30', 'a bundle of another version', { ...B(), v: 2 }, O(), ['form']);
atac('F31', 'a bundle whose transfers are not a list', { ...B(), transfers: 'x' }, O(), ['form']);
atac('F32', 'AN ATTESTATION DATED 120 s AHEAD of the verifier\'s clock: beyond its tolerance of 60 s, well inside the allowed age of 300 s', B({ attestation: atest({ now: cand(185) }) }), O(), ['freshness']);
valid('F33', 'an attestation dated 30 s ahead of the verifier\'s clock is inside the tolerance (a clock that runs fast)', B({ attestation: atest({ now: cand(95) }) }), O());
atac('F34', 'with the verifier\'s tolerance set to zero, an attestation dated 5 s ahead is refused', B({ attestation: atest({ now: cand(70) }) }), O({ clockSkewS: 0 }), ['freshness']);
{ const alt = altText(t1.signature.ed25519);
atac('F35', 'a transfer whose Ed25519 signature is the same bytes in ANOTHER base64 text (the unused bits of its last character changed)', D.bundle({ manufacture: cred, transfers: [{ statement: t1.statement, signature: { ...t1.signature, ed25519: alt } }] }), O({ statusRecords: [] }), ['transfer-signature'], () => aceiasiOcteti(alt, t1.signature.ed25519)); }
{ const alt = altText(X1.signature.value);
atac('F36', 'the attestation signature is the same bytes in another base64 text', B({ attestation: { statement: X1.statement, signature: { ...X1.signature, value: alt } } }), O(), ['attestation-signature'], () => aceiasiOcteti(alt, X1.signature.value)); }
{ const alt = altText(cred.signature.mldsa65);
atac('F37', 'the manufacturer\'s signature is the same bytes in another base64 text', D.bundle({ manufacture: { statement: cred.statement, signature: { ...cred.signature, mldsa65: alt } } }), O({ statusRecords: [] }), ['manufacture-signature'], () => aceiasiOcteti(alt, cred.signature.mldsa65)); }
// ================================================================ H. more conditions of the compound checks (a second adversarial review, 2026-10-05)
// A status record signed by hand with the published formula: who holds the keys can write any form (the producer refuses most of these)
const stMana = (by, state, seq, cap, peste = {}) => { const statement = { v: 1, kind: 'aere-device-status', deviceId: dev.deviceId, state, seq, ownership: cap, by: { id: by.id, keys: by.public }, at: at(-600), validUntil: at(ZI), ...peste }; return { statement, signature: D.signLink('status', statement, by) }; };
const hashPlic = (st) => '0x' + crypto.createHash('sha256').update(JSON.stringify(st)).digest('hex');
spune('H. the order of a list, the edges of the intervals, the form of a time');
atac('H01', 'the registry lists [suspended 5, active 3]: the record with the HIGHEST number holds, not the last one in the list', B(), O({ statusRecords: [stare(op, 'suspended', 5, cap1), stare(op, 'active', 3, cap1)] }), ['status-suspended']);
atac('H02', 'the registry lists [suspended 5, active 5]: of two with one number the stricter holds, whichever comes last', B(), O({ statusRecords: [stare(op, 'suspended', 5, cap1), stare(op, 'active', 5, cap1)] }), ['status-suspended']);
valid('H03', 'the registry lists [active 6, suspended 5]: the later record of the signer lifts its suspension, whatever the order of the list', B(), O({ statusRecords: [stare(op, 'active', 6, cap1), stare(op, 'suspended', 5, cap1)] }));
{ let l = []; const ids = [mfr, op]; for (let i = 0; i < D.MAX_TRANSFERS; i++) l = [...l, D.transferOwnership({ from: ids[i % 2], to: ids[(i + 1) % 2].public, manufacture: cred, transfers: l, now: cand(-10 * ZI + i) })];
const cap = D.ownershipHead(cred, l), own = ids[D.MAX_TRANSFERS % 2];
valid('H04', `a chain of exactly ${D.MAX_TRANSFERS} transfers, the most that is read, is read`, D.bundle({ manufacture: cred, firmware: fw2, transfers: l, authorization: aut(own, cap), attestation: atest({ ownership: cap }) }), O({ statusRecords: [stare(own, 'active', 1, cap)] }), (r) => r.subject.transfers === D.MAX_TRANSFERS && r.subject.owner === own.id); }
{ const LID2 = 'https://manufacturer.example/status/2';
partial('H05', 'the only recall list handed is the manufacturer\'s own, current and clean, under ANOTHER id than the one the credential names', B(), O({ statusLists: [lista({ id: LID2 })] }), ['manufacture-status-unread']);
valid('H06', 'beside the list the credential names, another list of the manufacturer whose bit 7 is set: it is the bit of another list, not a recall of this device', B(), O({ statusLists: [lista(), lista({ id: LID2, revoked: [7] })] })); }
atac('H07', 'a manufacture credential with a claim under selective disclosure (a digest in `sd`), signed by the manufacturer: every claim of a device is in the clear', D.bundle({ manufacture: reCred(cred, mfr, (st) => { st.sd = ['x']; }) }), O({ statusRecords: [] }), ['manufacture-form']);
{ const alt = clona(POS); alt.statement.kind = 'aere-proof-of-something-else'; alt.statementHash = hashPlic(alt.statement);
const fwAlt = reSemn('firmware', fw2pos, mfr, (st) => { st.softwareAttestationHash = alt.statementHash; });
atac('H08', 'the release names, by its hash, an attestation that is not a Proof of Software attestation, and that one is handed', B({ firmware: fwAlt, attestation: atest({ firmware: fwAlt }) }), O({ softwareAttestation: alt }), ['firmware-software']);
const editat = clona(POS); editat.statement.subject.version = '2.0.1';
atac('H09', 'the Proof of Software attestation handed carries the hash the release names, and its statement was edited: it no longer hashes to it', B({ firmware: fw2pos }), O({ softwareAttestation: editat }), ['firmware-software']); }
partial('H10', 'a signed policy with a NEGATIVE floor (-1), the only one handed: not read as a floor', B(), O({ firmwarePolicies: [reSemn('firmware-policy', politica(), mfr, (st) => { st.minSequence = -1; })] }), ['firmware-policy-unread'], (r) => numit(r, 'firmware-policy-unread', 'not a whole number'));
partial('H11', 'the only record is an "active" of the owner whose number is not whole (1.5)', B(), O({ statusRecords: [stMana(op, 'active', 1.5, cap1)] }), ['status-unread'], (r) => numit(r, 'status-unread', 'a number this verifier does not read'));
partial('H12', 'a current "active" and a "suspended" of the owner whose number is written as text ("7"): it cannot be placed in order, and is not passed over', B(), O({ statusRecords: [activ, stMana(op, 'suspended', '7', cap1)] }), ['status-unread'], (r) => numit(r, 'status-unread', 'a number this verifier does not read'));
atac('H13', 'the manufacture credential expired 30 s before the verifier\'s clock: an ending has no tolerance', D.bundle({ manufacture: face({ validUntil: at(35) }) }), O({ statusRecords: [] }), ['manufacture-window']);
{ const c = face({ validFrom: at(95) }), t = D.transferOwnership({ from: mfr, to: op.public, manufacture: c, now: cand(-10 * ZI) }), cap = D.ownershipHead(c, [t]);
valid('H14', 'the manufacture credential begins 30 s after the verifier\'s clock: a beginning has the tolerance', D.bundle({ manufacture: c, firmware: fw2, transfers: [t], authorization: aut(op, cap), attestation: atest({ ownership: cap }) }), O({ statusRecords: [stare(op, 'active', 1, cap)] })); }
partial('H15', 'the only "active" expired 30 s before the verifier\'s clock', B(), O({ statusRecords: [stare(op, 'active', 1, cap1, { validUntil: at(35) })] }), ['status-unread']);
atac('H16', 'an attestation made 330 s before the verifier\'s clock (30 s past the allowed age of 300 s)', B(), O({ now: at(390) }), ['freshness']);
valid('H17', 'an attestation made exactly 300 s before the verifier\'s clock is at the edge of the allowed age, inside it', B(), O({ now: at(360) }));
partial('H18', 'the only firmware policy expired 30 s before the verifier\'s clock', B(), O({ firmwarePolicies: [politica({ validUntil: at(35) })] }), ['firmware-policy-unread']);
partial('H19', 'the only recall list expired 30 s before the verifier\'s clock', B(), O({ statusLists: [lista({ validUntil: at(35) })] }), ['manufacture-status-unread']);
atac('H20', 'an authorization whose signature (the right bytes) declares another scheme ("rsa-pss")', B({ authorization: { statement: A1.statement, signature: { ...A1.signature, alg: 'rsa-pss' } } }), O(), ['authorization-signature']);
atac('H21', 'an authorization signed by the owner whose end is written without a time zone (a parser would read it as local time): not an RFC 3339 UTC time', B({ authorization: reSemn('authorization', A1, op, (st) => { st.notAfter = at(ZI).replace(/[.][0-9]+Z$/, ''); }) }), O(), ['unreadable']);
partial('H22', 'the only record is an "active" of the owner whose validity is written with a zone offset (+03:00): not believed', B(), O({ statusRecords: [stMana(op, 'active', 1, cap1, { validUntil: at(ZI).replace(/[.][0-9]+Z$/, '+03:00') })] }), ['status-unread'], (r) => numit(r, 'status-ignored', 'not a time'));
{ const sec = Math.floor((T0 + 60000) / 1000), ci = (dt, at2 = (x) => x) => ({ statementHash: PLIC.statementHash, at: at2(sec + dt), chainId: 28001 });
atac('H23', 'two readings of the same envelope, one 600 s BEFORE the date of the attestation and one 40 s after it: the earliest reading is the bound', B({ anchor: PLIC }), O({ anchors: [ci(-600), ci(40)] }), ['witness-time']);
atac('H24', 'the same two readings in the other order', B({ anchor: PLIC }), O({ anchors: [ci(40), ci(-600)] }), ['witness-time']);
atac('H25', 'the envelope carries the kind of another envelope (a Proof of AI attestation), the rest untouched', B({ anchor: { ...PLIC, kind: 'aere-proof-of-ai-attestation' } }), O(), ['witness-envelope']);
atac('H26', 'the statement of the envelope is of another kind, its hash recomputed', B({ anchor: (() => { const e = clona(PLIC); e.statement.kind = 'aere-proof-of-ai'; e.statementHash = hashPlic(e.statement); return e; })() }), O(), ['witness-envelope']);
partial('H27', 'a reading whose time is written as text, 600 s before the date of the attestation: it is not a reading (as a number it would refuse the attestation)', B({ anchor: PLIC }), O({ anchors: [ci(-600, String)] }), ['witness-unread']); }
{ const r = judeca(B(), O({ statusRecords: [stare(op, 'retired', 2, cap1), stare(mfr, 'retired', 1, null)] }));
cer('H28', 'retired by its owner (first in the list) and by its manufacturer: the row names the retirement that is final, the manufacturer\'s', r.verdict === 'INVALID' && la(r.rules) === 'status-retired' && r.rows.some((x) => x.rule === 'status-retired' && x.pass === false && x.detail.includes(mfr.id) && x.detail.includes('never lifted')), `[${r.rules}] ${(r.rows.find((x) => x.rule === 'status-retired') || {}).detail}`, ['status-retired']); }
atac('H29', 'the second transfer is dated before the first (and after the device was made): each transfer comes after the one before it', D.bundle({ manufacture: cred, transfers: [t1, reSemn('transfer', t2, op, (st) => { st.at = at(-11 * ZI); })] }), O({ statusRecords: [] }), ['transfer-time']);
atac('H30', 'a chain broken at the form of its second link keeps no owner and no head: the first owner\'s record is nobody\'s record then', B({ transfers: [t1, reSemn('transfer', t2, op, (st) => { st.kind = 'aere-device-transfer-v0'; })] }), O(), ['transfer-form'], (r) => r.unjudged.includes('status-unread') && r.unjudged.includes('status-ignored'));
// ================================================================ I. what is handed in another shape, or in too great a number, is named
spune('I. what the verifier was handed in another shape is not passed over');
{ const retr = stare(op, 'retired', 2, cap1), spus = (r, c) => numit(r, c, 'nothing in them was read'), polCurata = politica(), listaCurata = lista();
partial('I01', 'the registry handed as [active, [retired]] (a list inside the list): the retirement was not read, and the verdict is not VALID', B(), O({ statusRecords: [activ, [retr]] }), ['status-unread'], (r) => spus(r, 'status-unread') && r.subject.state === 'active');
partial('I02', 'the registry handed as [active, { records: [retired] }] (a registry object as an element)', B(), O({ statusRecords: [activ, { records: [retr] }] }), ['status-unread'], (r) => spus(r, 'status-unread'));
partial('I03', 'the policies handed as [clean, [one that retracts this release]]', B(), O({ firmwarePolicies: [politica(), [politica({ retracted: [fw2.statement.digest] })]] }), ['firmware-policy-unread'], (r) => spus(r, 'firmware-policy-unread'));
partial('I04', 'the recall lists handed as [clean, [one that recalls this device]]', B(), O({ statusLists: [lista(), [lista({ revoked: [7] })]] }), ['manufacture-status-unread'], (r) => spus(r, 'manufacture-status-unread'));
partial('I05', 'the readings handed as [[a reading 600 s before the date of the attestation]]', B({ anchor: PLIC }), O({ anchors: [[{ statementHash: PLIC.statementHash, at: Math.floor((T0 + 60000) / 1000) - 600 }]] }), ['witness-unread'], (r) => spus(r, 'witness-unread'));
partial('I06', 'the registry holds null, a number, a string, an empty object and an object without a statement beside a current "active": five things that are not status records', B(), O({ statusRecords: [null, 5, 'x', {}, { statement: null }, activ] }), ['status-unread'], (r) => numit(r, 'status-unread', '5 of the 6'));
partial('I07', 'a firmware policy handed where a status record is expected: a file of another kind is not passed over', B(), O({ statusRecords: [activ, politica()] }), ['status-unread'], (r) => spus(r, 'status-unread'));
partial('I08', `${D.MAX_STATUS_RECORDS + 1} status records handed, the retirement last: the first ${D.MAX_STATUS_RECORDS} are judged, the rest is said not to have been read`, B(), O({ statusRecords: [...Array.from({ length: D.MAX_STATUS_RECORDS }, () => activ), retr] }), ['status-unread'], (r) => numit(r, 'status-unread', `the first ${D.MAX_STATUS_RECORDS} are judged`));
atac('I09', `exactly ${D.MAX_STATUS_RECORDS} status records handed, the retirement last: all are judged`, B(), O({ statusRecords: [...Array.from({ length: D.MAX_STATUS_RECORDS - 1 }, () => activ), retr] }), ['status-retired'], (r) => !r.rows.some((x) => x.name.includes('than are judged')));
{ const unul = stare(op, 'retired', 1, cap1, { now: cand(-900) }); const r = judeca(Bx(), O({ statusRecords: [activX, ...Array.from({ length: 300 }, () => unul)] }));
cer('I10', '300 copies of one record of a previous owner: ONE row names them, with their number', r.verdict === 'VALID' && r.rows.filter((x) => x.unjudged === 'status-prior-ownership').length === 1 && numit(r, 'status-prior-ownership', '300 record(s)') && la(r.subject.priorOwnership.map((p) => `${p.state} ${p.records}`)) === 'retired 300' && r.rows.length < 60, `${r.verdict}, ${r.rows.length} rows`); }
partial('I11', `${D.MAX_FIRMWARE_POLICIES + 1} firmware policies handed, the one that retracts this release last`, B(), O({ firmwarePolicies: [...Array.from({ length: D.MAX_FIRMWARE_POLICIES }, () => polCurata), politica({ retracted: [fw2.statement.digest] })] }), ['firmware-policy-unread'], (r) => numit(r, 'firmware-policy-unread', `the first ${D.MAX_FIRMWARE_POLICIES} are judged`));
partial('I12', `${D.MAX_STATUS_LISTS + 1} recall lists handed, the one that recalls this device last`, B(), O({ statusLists: [...Array.from({ length: D.MAX_STATUS_LISTS }, () => listaCurata), lista({ revoked: [7] })] }), ['manufacture-status-unread'], (r) => numit(r, 'manufacture-status-unread', `the first ${D.MAX_STATUS_LISTS} are judged`));
partial('I13', `${D.MAX_READINGS + 1} readings handed, the one that refuses the attestation last`, B({ anchor: PLIC }), O({ anchors: [...Array.from({ length: D.MAX_READINGS }, () => ({ statementHash: PLIC.statementHash, at: Math.floor((T0 + 60000) / 1000) + 40 })), { statementHash: PLIC.statementHash, at: Math.floor((T0 + 60000) / 1000) - 600 }] }), ['witness-unread'], (r) => numit(r, 'witness-unread', `the first ${D.MAX_READINGS} are judged`)); }
// ================================================================ J. the device id a status record names
spune('J. the device id in a status record is compared like a digest');
{ const SUS = dev.deviceId.replace(/[0-9a-f]{40}$/, (h) => h.toUpperCase());
atac('J01', 'THE MANUFACTURER\'S RETIREMENT names the device id with its hex in UPPER CASE (its tooling writes it so): it is this device', B(), O({ statusRecords: [activ, stMana(mfr, 'retired', 1, null, { deviceId: SUS })] }), ['status-retired'], () => SUS !== dev.deviceId && SUS.toLowerCase() === dev.deviceId);
valid('J02', 'the only record is the owner\'s "active" with the id in upper case: it is this device\'s record', B(), O({ statusRecords: [stMana(op, 'active', 1, cap1, { deviceId: SUS })] }), (r) => r.subject.state === 'active');
partial('J03', 'THE OWNER\'S RETIREMENT names the device id with a space after it: not a device id, and it may be meant for this device', B(), O({ statusRecords: [activ, stMana(op, 'retired', 2, cap1, { deviceId: dev.deviceId + ' ' })] }), ['status-unread'], (r) => numit(r, 'status-unread', 'not a device id'));
partial('J04', 'a record of the manufacturer whose device id is a number', B(), O({ statusRecords: [activ, stMana(mfr, 'retired', 1, null, { deviceId: 7 })] }), ['status-unread'], (r) => numit(r, 'status-unread', 'not a device id'));
valid('J05', 'a stranger\'s record whose device id is not a device id: ignored, and said so', B(), O({ statusRecords: [activ, stMana(strain, 'retired', 1, null, { deviceId: dev.deviceId + ' ' })] }), (r) => numit(r, 'status-ignored'));
valid('J06', 'a previous owner\'s record whose device id is not a device id: ignored, and said so', Bx(), O({ statusRecords: [activX, stMana(op, 'retired', 1, cap1, { deviceId: 'x' })] }), (r) => numit(r, 'status-ignored', 'previous owner') && r.subject.priorOwnership.length === 0); }
// ================================================================ K. another trusted manufacturer, and the chain that is shown
spune('K. another trusted manufacturer; a chain shown shorter than it is');
{ const cred2 = D.manufacture({ manufacturer: altMfr, deviceKey: dev.publicKey, model: 'AX-200', serial: 'SN-0001', validFrom: at(-ZI), validUntil: at(3650 * ZI), now: cand(-ZI) });
const fwB = D.releaseFirmware({ signer: altMfr, model: 'AX-200', version: '2.0.0', sequence: 2, image: IMG2, now: cand(-3600) }), polB = D.firmwarePolicy({ manufacturer: altMfr, model: 'AX-200', minSequence: 1, validFrom: at(-3600), validUntil: at(ZI), now: cand(-3600) });
const capB = D.ownershipHead(cred2), activB = D.statusRecord({ by: altMfr, manufacture: cred2, state: 'active', seq: 1, validUntil: at(ZI), now: cand(-600) });
const bB = D.bundle({ manufacture: cred2, firmware: fwB, authorization: D.authorize({ owner: altMfr, deviceId: dev.deviceId, ownership: capB, actions: ACT, notBefore: at(-1800), notAfter: at(ZI), now: cand(-1800) }), attestation: D.attest({ device: dev, nonce: NONCE, audience: AUD, firmware: fwB, ownership: capB, action: 'telemetry.publish', now: cand(60) }) });
const retrasA = stare(mfr, 'retired', 1, null, { now: cand(-2 * ZI) });
partial('K01', 'THE SAME KEY CERTIFIED BY A SECOND MANUFACTURER after the first retired the device, and the verifier trusts both: the first one\'s retirement is not applied to another manufacturer\'s device, and the verdict is not VALID', bB, O({ trustedManufacturers: [mfr.id, altMfr.id], firmwarePolicies: [polB], statusRecords: [retrasA, activB] }), ['status-other-manufacturer'], (r) => numit(r, 'status-other-manufacturer', mfr.id) && r.subject.manufacturer === altMfr.id);
partial('K02', 'the other trusted manufacturer says "active" about this key: not VALID either (it speaks about a key this verifier knows under another credential)', bB, O({ trustedManufacturers: [mfr.id, altMfr.id], firmwarePolicies: [polB], statusRecords: [stare(mfr, 'active', 1, null), activB] }), ['status-other-manufacturer']);
valid('K03', 'the same record from a manufacturer the verifier does NOT trust is a stranger\'s record: ignored, and said so', bB, O({ trustedManufacturers: [altMfr.id], firmwarePolicies: [polB], statusRecords: [retrasA, activB] }), (r) => numit(r, 'status-ignored') && !r.unjudged.includes('status-other-manufacturer'));
partial('K04', 'no trusted manufacturer named at all: the other manufacturer\'s record is a stranger\'s, and the manufacturer itself is not judged', bB, O({ trustedManufacturers: null, firmwarePolicies: [polB], statusRecords: [retrasA, activB] }), ['manufacturer-trust'], (r) => numit(r, 'status-ignored')); }
// The verifier judges the chain it is SHOWN. The true chain here is manufacturer -> owner -> buyer, and the buyer retired the device.
// Whoever holds a copy of K (a software key) and the older documents shows a shorter chain: the earlier owner then looks like the owner
// of now, and the buyer's retirement is a stranger's record. These rows MEASURE that limit (README, "What it is not").
{ const retrasCump = stare(cumparator, 'retired', 1, cap2);
valid('K05', 'LIMIT, MEASURED: the chain shown one transfer short (the sale to the buyer hidden), with the earlier owner\'s authorization and "active" still current: VALID, and the buyer\'s retirement is only named as a stranger\'s', B(), O({ statusRecords: [activ, retrasCump] }), (r) => numit(r, 'status-ignored', cumparator.id) && r.subject.owner === op.id && r.subject.state === 'active');
valid('K06', 'LIMIT, MEASURED: no transfer shown at all, with the manufacturer\'s own authorization and "active" still current: VALID, and the first owner\'s retirement is only named as a stranger\'s', D.bundle({ manufacture: cred, firmware: fw2, authorization: aut(mfr, cap0), attestation: atest({ ownership: cap0 }) }), O({ statusRecords: [stare(mfr, 'active', 1, null), stare(op, 'retired', 1, cap1)] }), (r) => numit(r, 'status-ignored', op.id) && r.subject.owner === mfr.id && r.subject.transfers === 0);
atac('K07', 'what holds also for a chain shown shorter: the MANUFACTURER\'s retirement', B(), O({ statusRecords: [activ, retrasCump, stare(mfr, 'retired', 1, null)] }), ['status-retired']);
atac('K08', 'and the recall of the manufacture credential by its manufacturer', D.bundle({ manufacture: cred, firmware: fw2, authorization: aut(mfr, cap0), attestation: atest({ ownership: cap0 }) }), O({ statusLists: [lista({ revoked: [7] })], statusRecords: [stare(mfr, 'active', 1, null)] }), ['manufacture-revoked']); }
// ================================================================ M. a link is exactly { statement, signature }
spune('M. a member more in a link or in its signature is another text of it, and is refused');
{ const cuSemn = (l, k, v) => ({ statement: l.statement, signature: { ...l.signature, [k]: v } }), cuMembru = (l, k, v) => ({ ...l, [k]: v });
const peLant = (t) => { const cap = D.ownershipHead(cred, [t]); return [D.bundle({ manufacture: cred, firmware: fw2, transfers: [t], authorization: aut(op, cap), attestation: atest({ ownership: cap }) }), O({ statusRecords: [stare(op, 'active', 1, cap)] }), cap]; };
{ const [b, o, cap] = peLant(cuSemn(t1, 'note', 'x')); atac('M01', 'the same transfer (the same statement, the same signature bytes) with a member more in its signature object, and everything after it built on the hash of that text: one link would have two hashes', b, o, ['transfer-signature'], () => cap !== cap1); }
{ const [b, o, cap] = peLant(cuMembru(t1, 'comment', 'y')); atac('M02', 'the same transfer with a member more beside { statement, signature }', b, o, ['transfer-signature'], () => cap !== cap1); }
atac('M03', 'the manufacture credential with a member more beside { statement, signature }', D.bundle({ manufacture: cuMembru(cred, 'comment', 'y') }), O({ statusRecords: [] }), ['manufacture-signature']);
atac('M04', 'the manufacture credential with a member more in its signature object', D.bundle({ manufacture: cuSemn(cred, 'note', 'x') }), O({ statusRecords: [] }), ['manufacture-signature']);
atac('M05', 'the firmware release with a member more beside { statement, signature }', B({ firmware: cuMembru(fw2, 'comment', 'y') }), O(), ['firmware-signature']);
atac('M06', 'the authorization with a member more beside { statement, signature }', B({ authorization: cuMembru(A1, 'comment', 'y') }), O(), ['authorization-signature']);
atac('M07', 'the attestation with a member more beside { statement, signature }', B({ attestation: cuMembru(X1, 'comment', 'y') }), O(), ['attestation-signature']);
atac('M08', 'the attestation with a member more in its signature object', B({ attestation: cuSemn(X1, 'note', 'x') }), O(), ['attestation-signature']);
partial('M09', 'a status record with a member more in its signature object is not a signed record: the only "active" is not believed', B(), O({ statusRecords: [cuSemn(activ, 'note', 'x')] }), ['status-unread'], (r) => numit(r, 'status-ignored')); }
// ================================================================ V. text from a bundle, made visible
{ const C = (...n) => String.fromCodePoint(...n);
const ascunse = [0x00, 0x09, 0x0a, 0x0d, 0x1b, 0x1f, 0x7f, 0x80, 0x9b, 0x9f, 0x200e, 0x200f, 0x2028, 0x2029, 0x202a, 0x202e, 0x2066, 0x2069, 0xfeff], lasate = [0x20, 0x7e, 0xa0, 0xe9, 0x200d, 0x2027, 0x202f, 0x2065, 0x206a, 0xfefe, 0x1f600];
const totAscuns = ascunse.every((n) => { const v = D.visible('a' + C(n) + 'b'); return v === 'a' + (n < 256 ? '\\x' + n.toString(16).padStart(2, '0') : '\\u' + n.toString(16).padStart(4, '0')) + 'b'; });
const totLasat = lasate.every((n) => D.visible('a' + C(n) + 'b') === 'a' + C(n) + 'b');
cer('V01', `visible(): each of ${ascunse.length} control characters (the edges of C0, DEL, C1, the marks of direction, the line and paragraph separators, the bidirectional controls, the byte order mark) becomes its visible form, each of ${lasate.length} neighbours of those ranges is left as it is, and a backslash is doubled so that the form cannot be forged`,
totAscuns && totLasat && D.visible('a\\x1b') === 'a\\\\x1b' && D.visible('plain text, 100%') === 'plain text, 100%' && D.visible(7) === '7'); }
// ================================================================ R. the form of each link, one field at a time (after a search for conditions without a row, 2026-10-06)
// Each row replaces ONE field of a link by values of the wrong form and has the link signed again by its signer with the published
// formulas: only the form check stands between such a link and a verdict. Every value must be refused for exactly the form rule.
spune('R. the form of each link, one field at a time, each signed again by its signer');
const pune = (st, cale, v) => { let o = st; for (const k of cale.slice(0, -1)) o = o[k]; const k = cale[cale.length - 1]; if (v === undefined) delete o[k]; else o[k] = v; };
const scurtV = (v) => (v === undefined ? 'removed' : JSON.stringify(v).length > 22 ? JSON.stringify(v).slice(0, 19) + '...' : JSON.stringify(v));
const LUNG = 'M'.repeat(257);
function forme(prefix, regula, ce, construieste, cazuri) {
cazuri.forEach(([camp, cale, valori], i) => { const rele = [];
for (const v of valori) { const [b, o] = construieste((st) => pune(st, cale, v)); const r = judeca(b, o); if (!(r.verdict === 'INVALID' && la(r.rules) === regula && r.subject === null)) rele.push(`${scurtV(v)}: ${r.verdict} [${r.rules}]`); }
rand(`${prefix}${String(i + 1).padStart(2, '0')}`, `ATAC: ${ce}: ${camp} (${valori.map(scurtV).join(', ')})`, rele.length ? 'ROSU' : 'VERDE', rele.join('; '), [regula], 'attack'); });
}
// the statement itself, or the whole link, replaced (nothing to sign again)
function intreg(id, regula, ce, construieste, valori) { const rele = [];
for (const v of valori) { const [b, o] = construieste(v); const r = judeca(b, o); if (!(r.verdict === 'INVALID' && la(r.rules) === regula && r.subject === null)) rele.push(`${scurtV(v)}: ${r.verdict} [${r.rules}]`); }
rand(id, `ATAC: ${ce} (${valori.map(scurtV).join(', ')})`, rele.length ? 'ROSU' : 'VERDE', rele.join('; '), [regula], 'attack'); }
const numaiM = (m) => [D.bundle({ manufacture: m }), O({ statusRecords: [] })];
intreg('RM00', 'manufacture-form', 'the statement of the manufacture credential is not an object', (v) => numaiM({ statement: v, signature: cred.signature }), [null, 'x', 7, []]);
forme('RM', 'manufacture-form', 'the manufacture credential', (f) => numaiM(reCred(cred, mfr, f)), [
['its kind', ['kind'], ['aere-credential-v2', undefined]], ['its version', ['v'], [2, '1', undefined]],
['the issuer is not an object', ['issuer'], [null, 'x', []]], ['the holder is not an object', ['holder'], [null, 'x', []]],
['the list of claims under selective disclosure is not a list', ['sd'], [null, 'x', {}]], ['the claims are not an object', ['claims'], [null, 'x', []]],
['the device id claim is not a text', ['claims', 'deviceId'], ['', 7, undefined]], ['the model is not a text of 1 to 256 characters', ['claims', 'model'], ['', 7, ['AX-200'], LUNG]],
['the serial is not a text of 1 to 256 characters', ['claims', 'serial'], ['', 7, LUNG]], ['the device key is not a text', ['claims', 'deviceKey'], [7, null, undefined]]]);
{ const r = judeca(...numaiM(reCred(cred, mfr, (st) => { st.claims.model = 'M'.repeat(256); })));
cer('RM11', 'a model of exactly 256 characters, the longest that is read, is read (nothing refuses the credential)', r.valid && r.rules.length === 0 && r.subject.model.length === 256, `${r.verdict} [${r.rules}]`); }
intreg('RM12', 'manufacture-signature', 'the signature of the manufacture credential is not an object', (v) => numaiM({ statement: cred.statement, signature: v }), [null, 7, 'x', []]);
intreg('RM13', 'manufacture-signature', 'a half of the manufacturer\'s signature is not a text', (v) => numaiM({ statement: cred.statement, signature: { ...cred.signature, ed25519: v } }), [7, null, ['x']]);
const cuF = (fw) => [B({ firmware: fw }), O()];
intreg('RF00', 'firmware-form', 'the statement of the release is not an object', (v) => cuF({ statement: v, signature: fw2.signature }), [null, 'x', 7, []]);
forme('RF', 'firmware-form', 'the firmware release', (f) => cuF(reSemn('firmware', fw2, mfr, f)), [
['its kind', ['kind'], ['aere-device-firmware-v0', undefined]], ['its version', ['v'], [2, '1']],
['the model is not a text of 1 to 256 characters', ['model'], ['', 7, ['AX-200'], LUNG]], ['the version label is not a text of 1 to 256 characters', ['version'], ['', 7, LUNG]],
['the sequence is not a whole number from 0 up', ['sequence'], ['2', 1.5, -1, null]], ['the digest is not 0x and 64 hex digits in lower case', ['digest'], [7, 'abc', '0x' + 'AB'.repeat(32), [fw2.statement.digest], undefined]],
['the signer is not an object', ['signer'], [null, 'x', []]], ['the hash of the Proof of Software attestation is not a hash', ['softwareAttestationHash'], [7, 'abc', null, [POS.statementHash]]]]);
{ const fw0 = D.releaseFirmware({ signer: mfr, model: 'AX-200', version: '0.9.0', sequence: 0, image: IMG1, now: cand(-25 * ZI) });
valid('RF09', 'a release with sequence 0 under a policy whose floor is 0: zero is a sequence and a floor like any other', B({ firmware: fw0, attestation: atest({ firmware: fw0 }) }), O({ firmwarePolicies: [politica({ minSequence: 0 })] }), (r) => r.subject.firmware.sequence === 0); }
intreg('RF10', 'firmware-signature', 'the signature of the release is absent or is not an object', (v) => cuF({ statement: fw2.statement, signature: v }), [null, 7, 'x', []]);
const cuT = (t) => [D.bundle({ manufacture: cred, transfers: [t] }), O({ statusRecords: [] })];
intreg('RT00', 'transfer-form', 'the transfer, or its statement, is not an object', cuT, [null, 'x', 7, { statement: null, signature: t1.signature }, { statement: 'x', signature: t1.signature }]);
forme('RT', 'transfer-form', 'a transfer', (f) => cuT(reSemn('transfer', t1, mfr, f)), [
['its kind', ['kind'], [undefined]], ['its version', ['v'], [2, '1']], ['the device id is not a text', ['deviceId'], [7, null]], ['its position is not a whole number', ['seq'], ['1', 1.5, null]],
['the hash of the link before is not a text', ['prev'], [7, null]], ['who gives it is not an object', ['from'], [null, 'x', []]], ['who receives it is not an object', ['to'], [null, 'x', []]]]);
intreg('RT08', 'transfer-signature', 'the signature of a transfer is absent or is not an object', (v) => cuT({ statement: t1.statement, signature: v }), [null, 7, 'x', []]);
const cuX = (x) => [B({ attestation: x }), O()];
intreg('RA00', 'attestation-form', 'the statement of the attestation is not an object', (v) => cuX({ statement: v, signature: X1.signature }), [null, 'x', 7, []]);
forme('RA', 'attestation-form', 'the attestation', (f) => cuX(reAtest(X1, dev, f)), [
['its kind', ['kind'], ['aere-device-attestation-v0', undefined]], ['its version', ['v'], [2, '1']], ['the device id is not a text', ['deviceId'], [7, null]], ['the key is not a text', ['key'], [7, null]],
['the nonce is not a text of 1 to 256 characters', ['nonce'], ['', 7, LUNG]], ['the audience is not a text of 1 to 256 characters', ['audience'], ['', 7, LUNG]], ['its time is not a text', ['at'], [7, null]],
['the firmware it names is not an object', ['firmware'], [null, 'x', []]], ['the digest it attests is not 0x and 64 hex digits', ['firmware', 'digest'], [7, 'abc', [fw2.statement.digest]]], ['the sequence it attests is not a whole number', ['firmware', 'sequence'], ['2', 1.5]],
['the ownership head is not 0x and 64 hex digits', ['ownership'], [7, 'abc', [cap1]]], ['the action is not an action name', ['action'], [7, '', 'Door Unlock', ['telemetry.publish']]]]);
intreg('RA13', 'attestation-form', 'the signature of the attestation is not an object', (v) => cuX({ statement: X1.statement, signature: v }), [null, 7, 'x', []]);
const cuZ = (a) => [B({ authorization: a }), O()];
intreg('RZ00', 'authorization-form', 'the statement of the authorization is not an object', (v) => cuZ({ statement: v, signature: A1.signature }), [null, 'x', 7, []]);
forme('RZ', 'authorization-form', 'the authorization', (f) => cuZ(reSemn('authorization', A1, op, f)), [
['its kind', ['kind'], ['aere-device-authorization-v0', undefined]], ['its version', ['v'], [2, '1']], ['the device id is not a text', ['deviceId'], [7, null]], ['the ownership head is not a text', ['ownership'], [7, null]],
['the owner is not an object', ['owner'], [null, 'x', []]],
['the actions are not a non-empty list of action names, sorted, each once', ['actions'], ['x', [], [7], ['Door Unlock'], [['telemetry.publish']], ['telemetry.publish', 'door.unlock'], ['door.unlock', 'door.unlock', 'telemetry.publish']]]]);
intreg('RZ07', 'authorization-signature', 'the signature of the authorization is absent or is not an object', (v) => cuZ({ statement: A1.statement, signature: v }), [null, 7, 'x', []]);
{ const b = D.bundle({ manufacture: cred, firmware: fw2, authorization: aut(mfr, cap0), attestation: atest({ ownership: cap0 }) }); delete b.transfers;
valid('RB01', 'a bundle with no `transfers` member at all is a device never transferred (the same verdict as with an empty list)', b, O({ statusRecords: [stare(mfr, 'active', 1, null)] }), (r) => r.subject.transfers === 0 && r.subject.owner === mfr.id); }
// ================================================================ T. the edge of every interval, and every tolerance
spune('T. the edges of the intervals: an ending includes its last instant, a beginning has the tolerance and no more');
const primit = (id, title, b, o, extra = () => true) => { const r = judeca(b, o); cer(id, title, r.valid && r.rules.length === 0 && extra(r), `${r.verdict} [${r.rules}]`); return r; };
primit('T01', 'the manufacture credential is valid until exactly the verifier\'s clock: its last instant is inside', ...numaiM(face({ validUntil: at(65) })));
primit('T02', 'the manufacture credential begins exactly 60 s after the verifier\'s clock: the edge of the tolerance is inside', ...numaiM(face({ validFrom: at(125) })));
valid('T03', 'the recall list is valid until exactly the verifier\'s clock: read as current', B(), O({ statusLists: [lista({ validUntil: at(65) })] }));
valid('T04', 'the recall list begins 30 s after the verifier\'s clock (a beginning has the tolerance): read as current', B(), O({ statusLists: [lista({ validFrom: at(95) })] }));
valid('T05', 'the recall list begins exactly 60 s after the verifier\'s clock: the edge of the tolerance is inside', B(), O({ statusLists: [lista({ validFrom: at(125) })] }));
partial('T06', 'the only recall list handed begins tomorrow: it says nothing about now', B(), O({ statusLists: [lista({ validFrom: at(ZI), validUntil: at(2 * ZI) })] }), ['manufacture-status-unread']);
valid('T07', 'the firmware policy is valid until exactly the verifier\'s clock: read as current', B(), O({ firmwarePolicies: [politica({ validUntil: at(65) })] }));
valid('T08', 'the firmware policy begins 30 s after the verifier\'s clock: read as current', B(), O({ firmwarePolicies: [politica({ validFrom: at(95) })] }));
valid('T09', 'the firmware policy begins exactly 60 s after the verifier\'s clock: the edge of the tolerance is inside', B(), O({ firmwarePolicies: [politica({ validFrom: at(125) })] }));
primit('T10', 'a transfer dated exactly 60 s before the manufacture credential was issued (two clocks that differ by the tolerance): accepted', ...cuT(D.transferOwnership({ from: mfr, to: op.public, manufacture: cred, now: cand(-30 * ZI - 60) })));
primit('T11', 'a transfer dated exactly 60 s after the verifier\'s clock: the edge of the tolerance is inside', ...cuT(D.transferOwnership({ from: mfr, to: op.public, manufacture: cred, now: cand(125) })));
{ const ta = D.transferOwnership({ from: mfr, to: op.public, manufacture: cred, now: cand(-10 * ZI) }), tb = D.transferOwnership({ from: op, to: cumparator.public, manufacture: cred, transfers: [ta], now: cand(-10 * ZI) });
primit('T12', 'two transfers dated the same instant: the second is not before the first', D.bundle({ manufacture: cred, transfers: [ta, tb] }), O({ statusRecords: [] })); }
valid('T13', 'an attestation dated exactly 60 s after the verifier\'s clock: the edge of the tolerance is inside', B({ attestation: atest({ now: cand(125) }) }), O());
valid('T14', 'the authorization ends exactly at the verifier\'s clock, which is also the time of the attestation: its last instant is inside, on both clocks', B({ authorization: aut(op, cap1, { notAfter: at(60) }) }), O({ now: at(60) }));
valid('T15', 'the authorization begins exactly 60 s after the verifier\'s clock and 60 s after the time of the attestation: the edge of the tolerance is inside, on both clocks', B({ authorization: aut(op, cap1, { notBefore: at(125) }), attestation: atest({ now: cand(65) }) }), O());
atac('T16', 'a retirement of the manufacturer dated exactly 60 s after the verifier\'s clock is in effect (the edge of the tolerance is inside)', B(), O({ statusRecords: [activ, stare(mfr, 'retired', 1, null, { now: cand(125) })] }), ['status-retired']);
valid('T17', 'the only "active" is valid until exactly the verifier\'s clock: current', B(), O({ statusRecords: [stare(op, 'active', 1, cap1, { validUntil: at(65) })] }), (r) => r.subject.state === 'active');
{ const sec = Math.floor((T0 + 60000) / 1000), ci = (o) => ({ statementHash: PLIC.statementHash, chainId: 28001, ...o });
valid('T18', 'the attestation is dated exactly 60 s after the moment its envelope was read on the chain: the edge of the tolerance is inside', B({ anchor: PLIC }), O({ anchors: [ci({ at: sec - 60 })] }), (r) => r.subject.witness.at === sec - 60);
{ const rele = []; for (const t of [0, -5, 1e11, 1.5]) { const r = judeca(B({ anchor: PLIC }), O({ anchors: [ci({ at: t })] })); if (!(r.verdict === 'PARTIAL' && la(r.unjudged.filter((c) => D.PARTIALE.includes(c))) === 'witness-unread' && r.subject.witness === null)) rele.push(`${t}: ${r.verdict} [${r.rules}] [${r.unjudged}]`); }
rand('T19', 'a reading whose time is 0, negative, 100000000000 or not whole is not a reading: not used, and said (each PARTIAL, witness-unread)', rele.length ? 'ROSU' : 'VERDE', rele.join('; '), [], 'partial'); }
valid('T20', 'two readings of one instant from two chains: the first in the list is the witness', B({ anchor: PLIC }), O({ anchors: [ci({ at: sec + 40 }), ci({ at: sec + 40, chainId: 7 })] }), (r) => r.subject.witness.chainId === 28001);
valid('T21', 'a reading that names no chain: the witness says null, not a chain', B({ anchor: PLIC }), O({ anchors: [{ statementHash: PLIC.statementHash, at: sec + 40 }] }), (r) => r.subject.witness.chainId === null && r.subject.witness.at === sec + 40); }
atac('T22', 'the manufacturer signed a validity written as a one-element list that holds a time: not a time', ...numaiM(reCred(cred, mfr, (st) => { st.validUntil = [at(3650 * ZI)]; })), ['unreadable']);
atac('T23', 'the manufacturer signed a validity that has the form of a time and is not a date (month 13): not a time', ...numaiM(reCred(cred, mfr, (st) => { st.validUntil = '2036-13-01T00:00:00Z'; })), ['unreadable']);
{ const arunca = (o) => { try { D.verifyDevice(B(), O(o)); return null; } catch (e) { return String(e.message); } };
const refuzate = [{ clockSkewS: 601 }, { clockSkewS: -1 }, { clockSkewS: 1.5 }, { clockSkewS: '60' }, { maxAgeS: 0 }, { maxAgeS: 1.5 }, { maxAgeS: '300' }, { trustedManufacturers: 'x' }, { trustedManufacturers: ['nobody'] }, { trustedManufacturers: [{ ed25519: 'x' }] }, { trustedManufacturers: [[mfr.id]] }, { statusRecords: 'x' }, { anchors: null }, { now: 'not a time' }];
const rele = refuzate.filter((o) => !/^verifyDevice: /.test(String(arunca(o)))).map((o) => JSON.stringify(o)), primite = [{ clockSkewS: 600 }, { clockSkewS: 0 }, { maxAgeS: 1 }].filter((o) => arunca(o) !== null).map((o) => JSON.stringify(o));
cer('T24', `the verifier's own configuration: ${refuzate.length} values that cannot be read each throw "verifyDevice: ..." (a tolerance of 601 s, negative, not whole or a text; an age of 0, not whole or a text; trusted manufacturers that are not a list, not ids, not keys; lists that are not lists; a clock that is not a time), and the edges are accepted (a tolerance of 600 s and of 0 s, an age of 1 s)`, !rele.length && !primite.length, `not refused: ${rele.join(' ')}; not accepted: ${primite.join(' ')}`); }
valid('T25', 'the verifier names the manufacturer it trusts by its public keys, not by its id', B(), O({ trustedManufacturers: [mfr.public] }));
// ================================================================ S. what the search for conditions without a row found (2026-10-06)
spune('S. conditions that had no row');
atac('S01', 'the only record is the manufacturer\'s retirement: the registry WAS read, and is not said to be unread', B(), O({ statusRecords: [stare(mfr, 'retired', 1, null)] }), ['status-retired'], (r) => !r.unjudged.includes('status-unread'));
atac('S02', 'the only record is the owner\'s suspension: the registry was read, and is not said to be unread', B(), O({ statusRecords: [stare(op, 'suspended', 1, cap1)] }), ['status-suspended'], (r) => !r.unjudged.includes('status-unread'));
{ const r = judeca(B(), O({ statusRecords: [stare(mfr, 'retired', 1, null), stare(op, 'retired', 2, cap1)] }));
cer('S03', 'retired by its manufacturer (first in the list) and by its owner: the row still names the retirement that is final, the manufacturer\'s', r.verdict === 'INVALID' && la(r.rules) === 'status-retired' && r.rows.some((x) => x.rule === 'status-retired' && x.pass === false && x.detail.includes(mfr.id) && x.detail.includes('never lifted')), `[${r.rules}] ${(r.rows.find((x) => x.rule === 'status-retired') || {}).detail}`, ['status-retired']); }
{ const r = judeca(B(), O({ statusRecords: [activ, stare(op, 'retired', 2, cap1)] }));
cer('S04', 'retired by its owner alone: the row names the owner and says how long that retirement holds (while it owns the device), not that it is final', r.verdict === 'INVALID' && la(r.rules) === 'status-retired' && r.rows.some((x) => x.rule === 'status-retired' && x.pass === false && x.detail.includes(op.id) && x.detail.includes('holds while it owns the device') && !x.detail.includes('never lifted')), `[${r.rules}] ${(r.rows.find((x) => x.rule === 'status-retired') || {}).detail}`, ['status-retired']); }
{ const faraSemnatar = clona(stare(op, 'retired', 2, cap1)); delete faraSemnatar.statement.by;
valid('S05', 'a status record with no signer at all (the member `by` is absent): ignored, and said so', B(), O({ statusRecords: [activ, faraSemnatar] }), (r) => numit(r, 'status-ignored', 'no signer')); }
partial('S06', 'a record of the manufacturer whose device id is a one-element list that holds the id: not a device id, and it may be meant for this device', B(), O({ statusRecords: [activ, stMana(mfr, 'retired', 1, null, { deviceId: [dev.deviceId] })] }), ['status-unread'], (r) => numit(r, 'status-unread', 'not a device id'));
{ const subAlt = (() => { const statement = { v: 1, kind: 'aere-device-status', deviceId: dev.deviceId, state: 'retired', seq: 1, ownership: null, by: { id: altMfr.id, keys: strain.public }, at: at(-600), validUntil: at(ZI) }; return { statement, signature: D.signLink('status', statement, strain) }; })();
valid('S07', 'a stranger signs with its own keys under the id of ANOTHER manufacturer the verifier trusts: the id is not the id of those keys, so it is a stranger\'s record (ignored), not a trusted manufacturer\'s', B(), O({ trustedManufacturers: [mfr.id, altMfr.id], statusRecords: [activ, subAlt] }), (r) => numit(r, 'status-ignored') && !r.unjudged.includes('status-other-manufacturer'));
const editat = clona(stMana(altMfr, 'active', 1, null)); editat.statement.state = 'retired';
valid('S08', 'a record of another trusted manufacturer edited after it was signed is not its record: ignored, and said so', B(), O({ trustedManufacturers: [mfr.id, altMfr.id], statusRecords: [activ, editat] }), (r) => numit(r, 'status-ignored') && !r.unjudged.includes('status-other-manufacturer')); }
atac('S09', 'no attestation in the bundle, and the manufacturer retired the device: the registry is judged for the device the manufacturer made', D.bundle({ manufacture: cred, firmware: fw2, transfers: [t1], authorization: A1 }), O({ statusRecords: [activ, stare(mfr, 'retired', 1, null)] }), ['status-retired']);
partial('S10', 'an authorization alone, with no chain and no attestation beside it: there is no device to judge it or the registry against, and nothing is refused', D.bundle({ authorization: A1 }), O(), ['attestation-missing', 'firmware-missing', 'manufacture-missing']);
partial('S11', 'a firmware release alone: the registry handed is about no device this bundle names, and is not said to be unread', D.bundle({ firmware: fw2 }), O(), ['attestation-missing', 'authorization-missing', 'manufacture-missing']);
atac('S12', 'an authorization and an attestation with no chain beside them: the authorization is of ANOTHER device than the one that attests', D.bundle({ authorization: D.authorize({ owner: op, deviceId: clonaDev.deviceId, ownership: cap1, actions: ACT, notBefore: at(-ZI), notAfter: at(ZI), now: cand(-ZI) }), attestation: X1 }), O({ statusRecords: [] }), ['authorization-device']);
atac('S13', 'a clone with its own key behind the real device\'s credential: the registry is still read for the device the manufacturer made (its "active" is seen)', B({ attestation: D.attest({ device: clonaDev, nonce: NONCE, audience: AUD, firmware: fw2, ownership: cap1, action: 'telemetry.publish', now: cand(60) }) }), O(), ['attestation-device'], (r) => !r.unjudged.includes('status-unread'));
atac('S14', 'the real device signs, with its own key, an attestation under ANOTHER device id', B({ attestation: reAtest(X1, dev, (st) => { st.deviceId = clonaDev.deviceId; }) }), O(), ['attestation-device', 'attestation-key']);
partial('S15', 'an envelope with no attestation in the bundle: the envelope is judged for itself (its hash), there is nothing to bind it to', D.bundle({ manufacture: cred, firmware: fw2, transfers: [t1], authorization: A1, anchor: PLIC }), O(), ['attestation-missing'], (r) => r.links.witness === 'PASSED');
atac('S16', 'the Ed25519 half of a transfer signature replaced by a stranger\'s (the ML-DSA-65 half is the owner\'s): the post-quantum half alone is not enough either', D.bundle({ manufacture: cred, transfers: [{ statement: t1.statement, signature: { ...t1.signature, ed25519: D.signLink('transfer', t1.statement, strain).ed25519 } }] }), O({ statusRecords: [] }), ['transfer-signature']);
{ const editata = clona(lista()); editata.statement.validUntil = at(2 * ZI);
partial('S17', 'the only recall list is the manufacturer\'s list edited after it was signed (its validity extended): no longer the manufacturer\'s list', B(), O({ statusLists: [editata] }), ['manufacture-status-unread']);
const altEmitent = (() => { const st = clona(lista().statement); st.issuer = { id: altMfr.id, keys: altMfr.public }; return { statement: st, signature: ID.signText('status-list', ID.canonical(st), mfr) }; })();
partial('S18', 'the only recall list is signed with the manufacturer\'s keys and names ANOTHER issuer: by its own statement it is not the manufacturer\'s list', B(), O({ statusLists: [altEmitent] }), ['manufacture-status-unread']); }
{ const editata = clona(politica()); editata.statement.minSequence = 0;
partial('S19', 'the only firmware policy is the manufacturer\'s policy edited after it was signed (its floor lowered): no longer the manufacturer\'s policy', B(), O({ firmwarePolicies: [editata] }), ['firmware-policy-unread']);
partial('S20', 'the only firmware policy is signed with the manufacturer\'s keys and names ANOTHER issuer', B(), O({ firmwarePolicies: [reSemn('firmware-policy', politica(), mfr, (st) => { st.issuer = { id: altMfr.id, keys: altMfr.public }; })] }), ['firmware-policy-unread']);
partial('S21', 'a signed policy whose retracted member is not a list: it cannot be read, and that is said (the bundle is not called unreadable)', B(), O({ firmwarePolicies: [reSemn('firmware-policy', politica(), mfr, (st) => { st.retracted = 'x'; })] }), ['firmware-policy-unread'], (r) => numit(r, 'firmware-policy-unread', 'not a digest'));
partial('S22', 'a signed policy whose retracted entry is a one-element list that holds a digest: not a digest', B(), O({ firmwarePolicies: [reSemn('firmware-policy', politica(), mfr, (st) => { st.retracted = [[fw1.statement.digest]]; })] }), ['firmware-policy-unread']);
valid('S23', 'the current policy retracts ANOTHER release: this one is not retracted', B(), O({ firmwarePolicies: [politica({ retracted: [fw1.statement.digest] })] })); }
{ const r = judeca(D.bundle({ manufacture: cred }), O({ statusRecords: [] })), r2 = judeca(D.bundle({ attestation: X1 }), O({ statusRecords: [] })), r1 = judeca(B(), O());
const nt = (x) => x.rows.filter((y) => y.name.includes('never transferred')).length;
cer('S24', 'what `links` says of each link: PASSED where rows were judged, NOT_SUPPLIED where the link is missing from the bundle, NOT_JUDGED where the registry was not read, ABSENT where there is nothing; and the row "never transferred" is there only for a device with a credential and no transfer',
JSON.stringify(r.links) === JSON.stringify({ manufacture: 'PASSED', firmware: 'NOT_SUPPLIED', ownership: 'PASSED', authorization: 'NOT_SUPPLIED', status: 'NOT_JUDGED', attestation: 'NOT_SUPPLIED', witness: 'ABSENT' }) && nt(r) === 1
&& r2.links.ownership === 'ABSENT' && r2.links.manufacture === 'NOT_SUPPLIED' && r2.links.attestation === 'PASSED' && nt(r2) === 0 && nt(r1) === 0 && r1.links.ownership === 'PASSED', JSON.stringify(r.links) + ' ' + JSON.stringify(r2.links)); }
{ const r = judeca(B(), O({ statusRecords: [activ, stare(strain, 'retired', 9, cap1)] })), r3 = judeca(D.bundle({ manufacture: cred, firmware: fw2, transfers: [t1, t2], authorization: aut(cumparator, cap2), attestation: atest({ ownership: cap2 }) }), O({ statusRecords: [stare(cumparator, 'active', 1, cap2)] }));
const num = (x, t) => x.rows.filter((y) => y.name.includes(t)).length;
cer('S25', 'every cause in `unjudged` is a declared cause (nothing else is listed there); and the rows of a chain say who gave each transfer: the manufacturer the first, the owner before the second', r.unjudged.length > 0 && r.unjudged.every((c) => D.NEJUDECATE.includes(c))
&& num(r3, 'transfer 1/2: given by the manufacturer') === 1 && num(r3, 'transfer 2/2: given by the owner before') === 1, `[${r.unjudged}]`); }
atac('S27', 'the only record is the owner\'s suspension, and that record has expired: a suspension does not expire, and the registry is not said to be unread', B(), O({ statusRecords: [stare(op, 'suspended', 1, cap1, { now: cand(-3 * ZI), validUntil: at(-ZI) })] }), ['status-suspended'], (r) => !r.unjudged.includes('status-unread'));
partial('S28', 'the envelope claims a notarization and the only reading handed for it is in milliseconds: ONE row says that reading cannot be read (the claim is not said twice)', B({ anchor: { ...PLIC, notarization: { chainId: 28001, firstSeen: 1 } } }), O({ anchors: [{ statementHash: PLIC.statementHash, at: T0 + 100000, chainId: 28001 }] }), ['witness-unread'], (r) => r.rows.filter((x) => x.unjudged === 'witness-unread').length === 1 && numit(r, 'witness-unread', 'milliseconds'));
{ let refuz = 0; for (const scop of ['credential', 'status-list', 'x', '']) { try { D.signLink(scop, A1.statement, op); } catch { refuz++; } }
cer('S29', 'the signing formula refuses a purpose that is not one of the six of this module (a signature of this module cannot be made for the purposes of another), and the verifying formula answers no for one', refuz === 4 && D.verifyLink('x', A1.statement, A1.signature, op.public) === false && D.verifyLink('authorization', A1.statement, A1.signature, op.public) === true); }
partial('S26', 'the readings handed hold null and an object that names no envelope: two things that are not readings, and that is said', B(), O({ anchors: [null, { at: 5 }] }), ['witness-unread'], (r) => numit(r, 'witness-unread', '2 of the 2'));
spune('C. malformed input is refused by name, never thrown at the reader');
atac('C72', 'the bundle is the JSON value null', null, O(), ['form']);
atac('C73', 'a file of another kind', { v: 1, kind: 'aere-presentation' }, O(), ['form']);
atac('C74', 'the manufacturer signed a credential whose validity is not a time', D.bundle({ manufacture: reCred(cred, mfr, (st) => { st.validUntil = 'next year'; }) }), O(), ['unreadable']);
if (!REPEDE) { let aruncate = 0, cazuri = 0; const b0 = B({ anchor: PLIC }); const cai = (x, p = []) => (x !== null && typeof x === 'object' ? [p, ...Object.keys(x).flatMap((k) => cai(x[k], [...p, Array.isArray(x) ? Number(k) : k]))] : [p]).filter((q) => q.length);
for (const p of cai(b0)) for (const v of ['remove', null, 'x', 7, [], {}]) { const c = clona(b0); let o = c; for (const k of p.slice(0, -1)) o = o[k]; const k = p[p.length - 1]; if (v === 'remove') { if (Array.isArray(o)) o.splice(k, 1); else delete o[k]; } else o[k] = clona(v);
cazuri++; try { const r = D.verifyDevice(c, O({ anchors: [{ statementHash: PLIC.statementHash, at: Math.floor(T0 / 1000) + 100 }] })); if (!['VALID', 'PARTIAL', 'INVALID'].includes(r.verdict)) aruncate++; } catch { aruncate++; } }
cer('C75', `${cazuri} damaged bundles (every path removed or replaced by null, a string, a number, a list, an object): each got a verdict, none threw`, cazuri > 800 && aruncate === 0, aruncate + ' thrown'); }
{ let a = 0; for (const o of [{ trustedManufacturers: 'x' }, { statusRecords: null }, { clockSkewS: 9999 }, { now: 'not a time' }]) { try { D.verifyDevice(B(), O(o)); } catch { a++; } }
cer('C76', 'a configuration of the VERIFIER that cannot be read throws (it is the verifier\'s mistake, not an invalid bundle)', a === 4); }
// ================================================================ D. keys, producers, the envelope under the reference verifier, the command line
spune('D. keys, producers, the envelope, the command line');
{ const f = D.exportDeviceKey(dev), k2 = D.importDeviceKey(clona(f)); let refuz = 0; try { D.importDeviceKey({ ...clona(f), publicKey: clonaDev.publicKey, deviceId: clonaDev.deviceId }); } catch { refuz++; }
cer('D01', 'a device key file round-trips, the private half is not enumerable, and a file with somebody else\'s public half is refused', k2.deviceId === dev.deviceId && !JSON.stringify(dev).includes(f.private) && Object.keys(dev).join() === 'deviceId,publicKey' && refuz === 1); }
{ let n = 0; const incearca = (f) => { try { f(); } catch { n++; } };
incearca(() => D.attest({ device: dev, nonce: '', audience: AUD, firmware: fw2, ownership: cap1, action: 'telemetry.publish' }));
incearca(() => D.releaseFirmware({ signer: mfr, model: 'AX-200', version: '2.0.0', sequence: 2, image: IMG1, softwareAttestation: POS }));
incearca(() => D.authorize({ owner: op, deviceId: dev.deviceId, ownership: cap1, actions: ['Door Unlock'], notAfter: at(ZI) }));
incearca(() => D.statusRecord({ by: op, deviceId: dev.deviceId, ownership: cap1, state: 'destroyed', seq: 1, validUntil: at(ZI) }));
incearca(() => D.deviceIdOf(crypto.generateKeyPairSync('ed25519').publicKey.export({ type: 'spki', format: 'der' }).toString('base64')));
incearca(() => D.statusRecord({ by: op, deviceId: dev.deviceId, state: 'retired', seq: 1, validUntil: at(ZI) })); // an owner's record with no ownership head: there is no default
incearca(() => D.statusRecord({ by: op, manufacture: cred, state: 'retired', seq: 1, validUntil: at(ZI) })); // an owner signing as the manufacturer
incearca(() => D.firmwarePolicy({ manufacturer: mfr, model: 'AX-200', retracted: [fw2.statement.digest.slice(2)], validUntil: at(ZI) })); // a retracted entry that is not a digest
const bune = [D.statusRecord({ by: op, deviceId: dev.deviceId, ownership: cap1, state: 'retired', seq: 1, validUntil: at(ZI) }).statement.ownership === cap1, D.statusRecord({ by: mfr, manufacture: cred, state: 'retired', seq: 1, validUntil: at(ZI) }).statement.ownership === null];
cer('D02', 'the producers refuse to write what the verifier would refuse or not apply (no nonce, an attestation that does not cover the image, an action that is not a name, an unknown state, a classical device key, an owner\'s status record with no ownership head, an owner signing as the manufacturer, a retracted entry that is not a digest), and write the two status records they should', n === 8 && bune.every(Boolean), n + ' refusals'); }
cer('D03', 'the Proof of Software fixture is an attestation of the firmware fixture (its statement hashes to its statementHash and lists the image digest)', D.softwareCovers(POS, fw2.statement.digest) && !D.softwareCovers(POS, fw1.statement.digest));
if (REPEDE) { /* not run */ } else if (!VERIFY || !fs.existsSync(VERIFY)) rand('D04', 'the AIP-23 reference verifier judges the device envelope (AERE_VERIFY_PROOF=<verify-proof.mjs>)', 'NEMASURAT', 'the reference verifier is not here');
else { const T = fs.mkdtempSync(path.join(os.tmpdir(), 'aere-device-')); try {
const f = path.join(T, 'e.json'); fs.writeFileSync(f, JSON.stringify(PLIC)); const r = spawnSync(process.execPath, [VERIFY, f, '--json'], { encoding: 'utf8' }); let j = null; try { j = JSON.parse(r.stdout); } catch { /* no verdict */ }
const e2 = clona(PLIC); e2.statement.deviceId = clonaDev.deviceId; fs.writeFileSync(f, JSON.stringify(e2)); const r2 = spawnSync(process.execPath, [VERIFY, f, '--json'], { encoding: 'utf8' }); let j2 = null; try { j2 = JSON.parse(r2.stdout); } catch { /* no verdict */ }
const niv = (x, n) => ((x && x.levels) || []).find((l) => l.level === n) || {};
cer('D04', 'the AIP-23 reference verifier judges the device envelope: integrity PASSED; the same envelope with the device id edited: integrity FAILED', niv(j, 'integrity').state === 'PASSED' && niv(j2, 'integrity').state === 'FAILED' && j2.verdict === 'INVALID', (r.stderr || '').slice(0, 120));
} finally { fs.rmSync(T, { recursive: true, force: true }); } }
if (!REPEDE) { // the command line, through files and processes only
const T = fs.mkdtempSync(path.join(os.tmpdir(), 'aere-device-cli-')); const cli = (...a) => spawnSync(process.execPath, [path.join(AICI, 'device-cli.mjs'), ...a], { cwd: T, encoding: 'utf8' });
try {
const pasi = [['new-identity', '--out', 'mfr.keys.json'], ['new-identity', '--out', 'owner.keys.json'], ['new-device', '--out', 'dev.key.json'],
['manufacture', '--manufacturer', 'mfr.keys.json', '--device', 'dev.key.json', '--model', 'AX-200', '--serial', 'SN-7', '--days', '3650', '--out', 'manufacture.json'],
['release', '--signer', 'mfr.keys.json', '--model', 'AX-200', '--version', '2.0.0', '--sequence', '2', '--image', path.join(AICI, 'fixturi', 'firmware-2.0.0.bin'), '--out', 'fw.json'],
['policy', '--manufacturer', 'mfr.keys.json', '--model', 'AX-200', '--min-sequence', '2', '--hours', '24', '--out', 'policy.json'],
['transfer', '--from', 'mfr.keys.json', '--to', 'owner.keys.json', '--manufacture', 'manufacture.json', '--out', 't1.json'],
['authorize', '--owner', 'owner.keys.json', '--manufacture', 'manufacture.json', '--transfer', 't1.json', '--action', 'telemetry.publish', '--hours', '24', '--out', 'auth.json'],
['status', '--by', 'owner.keys.json', '--manufacture', 'manufacture.json', '--transfer', 't1.json', '--state', 'active', '--seq', '1', '--hours', '24', '--out', 'status.json'],
['attest', '--device', 'dev.key.json', '--nonce', 'n-123', '--audience', 'https://gateway.example', '--firmware', 'fw.json', '--manufacture', 'manufacture.json', '--transfer', 't1.json', '--action', 'telemetry.publish', '--out', 'att.json'],
['bundle', '--manufacture', 'manufacture.json', '--firmware', 'fw.json', '--transfer', 't1.json', '--authorization', 'auth.json', '--attestation', 'att.json', '--out', 'bundle.json'],
['envelope', '--attestation', 'att.json', '--out', 'envelope.json']];
let rau = ''; for (const p of pasi) { const r = cli(...p); if (r.status !== 0) { rau = p[0] + ': ' + (r.stderr || r.stdout).trim().split('\n').slice(-1)[0]; break; } }
const mid = JSON.parse(fs.readFileSync(path.join(T, 'mfr.keys.json'), 'utf8')).id;
const comun = ['verify', '--bundle', 'bundle.json', '--audience', 'https://gateway.example', '--trust', mid, '--policy', 'policy.json', '--status', 'status.json', '--json'];
const v = cli(...comun, '--nonce', 'n-123'), v2 = cli(...comun), v3 = cli(...comun, '--nonce', 'n-999'); const j = (r) => { try { return JSON.parse(r.stdout); } catch { return {}; } };
const retras = cli('status', '--by', 'mfr.keys.json', '--manufacture', 'manufacture.json', '--state', 'retired', '--seq', '1', '--hours', '24', '--out', 'retired.json'); const v4 = cli(...comun, '--nonce', 'n-123', '--status', 'retired.json');
cer('D05', 'device-cli.mjs, through files and processes only: twelve commands build a bundle; verify exits 0 VALID, 2 PARTIAL without the nonce, 1 INVALID [nonce] with another nonce, 1 INVALID [status-retired] once the manufacturer\'s retirement is read',
!rau && v.status === 0 && j(v).verdict === 'VALID' && v2.status === 2 && j(v2).verdict === 'PARTIAL' && v3.status === 1 && la(j(v3).rules || []) === 'nonce' && retras.status === 0 && v4.status === 1 && la(j(v4).rules || []) === 'status-retired',
rau || `exits ${v.status} ${v2.status} ${v3.status} ${v4.status}: ${(v.stderr || '').trim().split('\n').slice(-1)[0]}`, ['nonce', 'status-retired']);
const fk = fs.readFileSync(path.join(T, 'dev.key.json'), 'utf8'); const iesiri = ['manufacture.json', 'fw.json', 'policy.json', 't1.json', 'auth.json', 'status.json', 'att.json', 'bundle.json', 'envelope.json'].map((f) => fs.readFileSync(path.join(T, f), 'utf8')).join('');
cer('D06', 'no private key leaves a key file: none of the nine files the commands wrote carries a private half', !iesiri.includes(JSON.parse(fk).private) && !/"private"/.test(iesiri));
// the owner retires the device (refused while it owns it), then sells it to a second identity: the retirement is NAMED by the command line, in words, and the verdict stands
const pasi2 = [['status', '--by', 'owner.keys.json', '--manufacture', 'manufacture.json', '--transfer', 't1.json', '--state', 'retired', '--seq', '2', '--hours', '24', '--out', 'retired-owner.json'],
['new-identity', '--out', 'owner2.keys.json'], ['transfer', '--from', 'owner.keys.json', '--to', 'owner2.keys.json', '--manufacture', 'manufacture.json', '--transfer', 't1.json', '--out', 't2.json'],
['authorize', '--owner', 'owner2.keys.json', '--manufacture', 'manufacture.json', '--transfer', 't1.json', '--transfer', 't2.json', '--action', 'telemetry.publish', '--hours', '24', '--out', 'auth2.json'],
['status', '--by', 'owner2.keys.json', '--manufacture', 'manufacture.json', '--transfer', 't1.json', '--transfer', 't2.json', '--state', 'active', '--seq', '1', '--hours', '24', '--out', 'status2.json'],
['attest', '--device', 'dev.key.json', '--nonce', 'n-456', '--audience', 'https://gateway.example', '--firmware', 'fw.json', '--manufacture', 'manufacture.json', '--transfer', 't1.json', '--transfer', 't2.json', '--action', 'telemetry.publish', '--out', 'att2.json'],
['bundle', '--manufacture', 'manufacture.json', '--firmware', 'fw.json', '--transfer', 't1.json', '--transfer', 't2.json', '--authorization', 'auth2.json', '--attestation', 'att2.json', '--out', 'bundle2.json']];
let rau2 = ''; for (const p of pasi2) { const r = cli(...p); if (r.status !== 0) { rau2 = p[0] + ': ' + (r.stderr || r.stdout).trim().split('\n').slice(-1)[0]; break; } }
const v5 = cli(...comun, '--nonce', 'n-123', '--status', 'retired-owner.json'); // before the sale: the owner's own retirement
const dupa = ['verify', '--bundle', 'bundle2.json', '--audience', 'https://gateway.example', '--trust', mid, '--policy', 'policy.json', '--nonce', 'n-456', '--status', 'status2.json', '--status', 'retired-owner.json'];
const v6 = cli(...dupa), v6j = cli(...dupa, '--json'); const oid = JSON.parse(fs.readFileSync(path.join(T, 'owner.keys.json'), 'utf8')).id; const s6 = j(v6j).subject || {};
cer('D07', 'device-cli.mjs: the owner\'s retirement is INVALID [status-retired] while it owns the device; after it sells the device, verify exits 0 VALID and SAYS, on a line of its own, that a previous owner\'s record was read and what it says',
!rau2 && v5.status === 1 && la(j(v5).rules || []) === 'status-retired' && v6.status === 0 && /^VALID$/m.test(v6.stdout) && /^noted, the verdict stands: (.*, )?status-prior-ownership$/m.test(v6.stdout) && v6.stdout.includes(`a previous owner (${oid}) signed 1 status record(s) that say: retired`)
&& v6j.status === 0 && (j(v6j).unjudged || []).includes('status-prior-ownership') && la((s6.priorOwnership || []).map((p) => `${p.owner} ${p.state}`)) === `${oid} retired`,
rau2 || `exits ${v5.status} ${v6.status} ${v6j.status}: ${(v6.stderr || v6.stdout || '').trim().split('\n').slice(-2).join(' | ')}`, ['status-retired']);
// ---- a file that holds a list: every element is handed over (the same two records in two files, in one list, in a registry object)
const citJ = (f) => JSON.parse(fs.readFileSync(path.join(T, f), 'utf8')), scrJ = (f, o) => fs.writeFileSync(path.join(T, f), typeof o === 'string' ? o : JSON.stringify(o));
const activF = citJ('status.json'), retrasF = citJ('retired-owner.json'), baza9 = comun.filter((x, i) => !(x === '--status' || comun[i - 1] === '--status')); // `comun` without its own --status
scrJ('registru.json', [activF, retrasF]); scrJ('registru-obiect.json', { records: [activF, retrasF] }); scrJ('registru-adanc.json', [activF, [{ records: [[retrasF]] }]]); scrJ('registru-alta-forma.json', { items: [activF, retrasF] });
const ver = (...a) => { const r = cli(...baza9, '--nonce', 'n-123', ...a); return [r.status, la(j(r).rules || []), la((j(r).unjudged || []).filter((c) => D.PARTIALE.includes(c)))].join(' '); };
const doua = ver('--status', 'status.json', '--status', 'retired-owner.json'), lista9 = ver('--status', 'registru.json'), obiect9 = ver('--status', 'registru-obiect.json'), adanc9 = ver('--status', 'registru-adanc.json'), alta9 = ver('--status', 'status.json', '--status', 'registru-alta-forma.json');
const polRetrage = cli('policy', '--manufacturer', 'mfr.keys.json', '--model', 'AX-200', '--min-sequence', '2', '--retract', citJ('fw.json').statement.digest, '--hours', '24', '--out', 'policy-retrage.json'); scrJ('politici.json', [citJ('policy.json'), citJ('policy-retrage.json')]);
const baza9p = baza9.filter((x, i) => !(x === '--policy' || baza9[i - 1] === '--policy')), pol9 = cli(...baza9p, '--nonce', 'n-123', '--status', 'status.json', '--policy', 'politici.json');
cer('D09', 'device-cli.mjs: a file that holds a LIST is read element by element. The owner\'s "active" and its retirement in two files, in one list, in { records: [...] } and nested deeper: INVALID [status-retired] each time (exit 1). In an object of a form that is not read ({ items: [...] }): PARTIAL status-unread (exit 2), never VALID. Two policies in one list, one retracting the release: INVALID [firmware-retracted]',
doua === '1 status-retired ' && lista9 === doua && obiect9 === doua && adanc9 === doua && alta9 === '2 status-unread' && polRetrage.status === 0 && pol9.status === 1 && la(j(pol9).rules || []) === 'firmware-retracted',
`two files [${doua}] list [${lista9}] object [${obiect9}] nested [${adanc9}] other form [${alta9}] policies ${pol9.status} [${(j(pol9).rules || [])}]`, ['firmware-retracted', 'status-retired']);
// ---- what a bundle wrote is not sent to the terminal: the BYTES of stdout are read, not its text
{ const E = String.fromCharCode(27), NL = String.fromCharCode(10);
const atacator = ID.generateKeys(), dz = D.generateDeviceKey(), acum = Date.now(), iso2 = (ms) => new Date(acum + ms).toISOString();
// the attacker makes its own chain (its own manufacturer, which the verifier does not trust): the model clears the screen, homes the cursor, writes a false answer, then conceals what follows
const MODEL = 'AX-200' + E + '[2J' + E + '[Hok every link of the chain' + NL + NL + 'VALID' + NL + E + '[8m';
const credZ = D.manufacture({ manufacturer: atacator, deviceKey: dz.publicKey, model: MODEL, serial: 'SN-1', validFrom: iso2(-86400000), validUntil: iso2(86400000 * 365), now: new Date(acum - 86400000) });
const fwZ = D.releaseFirmware({ signer: atacator, model: MODEL, version: '1', sequence: 1, image: Buffer.from('x'), now: new Date(acum - 3600000) }), capZ = D.ownershipHead(credZ);
scrJ('rau.json', D.bundle({ manufacture: credZ, firmware: fwZ, authorization: D.authorize({ owner: atacator, deviceId: dz.deviceId, ownership: capZ, actions: ['door.unlock'], notBefore: iso2(-3600000), notAfter: iso2(3600000), now: new Date(acum - 3600000) }),
attestation: D.attest({ device: dz, nonce: 'n-1', audience: 'https://gateway.example', firmware: fwZ, ownership: capZ, action: 'door.unlock', now: new Date(acum - 1000) }) }));
// and a status record nobody signed whose signer id carries new lines, a false verdict line, a C1 control and a line separator
scrJ('rau-stare.json', { statement: { v: 1, kind: 'aere-device-status', deviceId: activF.statement.deviceId, state: 'retired', seq: 1, ownership: null, by: { id: 'x)' + NL + NL + 'INVALID (refused by: status-retired)' + NL + E + '[8m' + String.fromCharCode(0x9b) + '2J' + String.fromCharCode(0x2028) + 'VALID' }, at: iso2(-600000), validUntil: iso2(86400000) }, signature: {} });
const brut = (...a) => spawnSync(process.execPath, [path.join(AICI, 'device-cli.mjs'), ...a], { cwd: T }); // no encoding: stdout is a Buffer
const r1 = brut('verify', '--bundle', 'rau.json', '--nonce', 'n-1', '--audience', 'https://gateway.example', '--trust', mid), r2 = brut(...comun.filter((x) => x !== '--json'), '--nonce', 'n-123', '--status', 'rau-stare.json');
const curat = (buf) => { for (let i = 0; i < buf.length; i++) { const o = buf[i]; if ((o < 0x20 && o !== 0x0a) || o === 0x7f) return false; if (o === 0xc2 && buf[i + 1] >= 0x80 && buf[i + 1] <= 0x9f) return false; if (o === 0xe2 && buf[i + 1] === 0x80 && (buf[i + 2] === 0xa8 || buf[i + 2] === 0xa9)) return false; } return true; };
const verdicte = (buf) => buf.toString('utf8').split(NL).filter((l) => /^(VALID|INVALID|PARTIAL)( |$)/.test(l)), t1b = r1.stdout.toString('utf8'), t2b = r2.stdout.toString('utf8');
cer('D10', 'device-cli.mjs: text a bundle or a registry file wrote is SHOWN, never sent to the terminal. Read as bytes, the output of verify holds no control byte but the line feeds the command itself writes (no ESC, no C1, no line separator), exactly one verdict line, and the hostile text in its visible form: for a manufacture model that clears the screen and writes "VALID" (the verdict stays INVALID, exit 1) and for an unsigned status record whose signer id writes a line "INVALID (...)" (the verdict stays VALID, exit 0)',
r1.status === 1 && r2.status === 0 && curat(r1.stdout) && curat(r2.stdout) && la(verdicte(r1.stdout)) === 'INVALID (refused by: manufacturer-trusted)' && la(verdicte(r2.stdout)) === 'VALID' && t1b.includes('AX-200\\x1b[2J\\x1b[Hok') && t1b.includes('\\x0a\\x0aVALID\\x0a\\x1b[8m') && t2b.includes('(x)\\x0a\\x0aINVALID (refused by: status-retired)\\x0a\\x1b[8m\\x9b2J\\u2028VALID)'),
`exits ${r1.status} ${r2.status}; clean ${curat(r1.stdout)} ${curat(r2.stdout)}; verdict lines [${verdicte(r1.stdout)}] [${verdicte(r2.stdout)}]`, ['manufacturer-trusted']);
const rj = spawnSync(process.execPath, [path.join(AICI, 'device-cli.mjs'), 'verify', '--bundle', 'rau.json', '--nonce', 'n-1', '--audience', 'https://gateway.example', '--trust', mid, '--json'], { cwd: T }); let pj = null; try { pj = JSON.parse(rj.stdout.toString('utf8')); } catch { /* not JSON */ }
cer('D10b', 'and --json stays JSON: it parses, its strings carry the model exactly as the bundle wrote it (escaped by JSON), and no ESC byte is in the output', pj !== null && pj.verdict === 'INVALID' && JSON.stringify(pj).includes('[2J') && pj.rows.some((x) => x.name.includes(MODEL)) && !rj.stdout.includes(27), '', ['manufacturer-trusted']); }
// ---- an input that cannot be read has an exit code of its own: 0, 1 and 2 are verdicts and nothing else
{ scrJ('stricat.json', '{ not json'); const faraVerdict = (r) => !/^(VALID|INVALID|PARTIAL)/m.test(r.stdout) && !/"verdict"/.test(r.stdout);
const lipsa = cli(...comun, '--nonce', 'n-123', '--status', 'lipsa.json'), stricat = cli(...comun, '--nonce', 'n-123', '--status', 'stricat.json'), ceas = cli('verify', '--bundle', 'bundle.json', '--now', 'not-a-time', '--status', 'status.json'), fara = cli('verify'), alta = cli('no-such-command');
const prod = cli('status', '--by', 'owner.keys.json', '--manufacture', 'manufacture.json', '--transfer', 't1.json', '--state', 'destroyed', '--seq', '1', '--hours', '24', '--out', 'x.json');
cer('D11', 'device-cli.mjs: 0, 1 and 2 are verdicts only. A status file that is missing, one that is not JSON, a clock that is not a time, a missing --bundle and an unknown command each exit 3 with no verdict printed; a command other than verify that fails (a state that does not exist) exits 1 and writes no file',
[lipsa, stricat, ceas, fara, alta].every((r) => r.status === 3 && faraVerdict(r) && /device-cli: /.test(r.stderr)) && prod.status === 1 && !fs.existsSync(path.join(T, 'x.json')), `exits ${[lipsa, stricat, ceas, fara, alta, prod].map((r) => r.status)}`); }
} finally { fs.rmSync(T, { recursive: true, force: true }); } }
{ // the evidence recorded on testnet 28001 (proba-device-ancora-testnet.mjs: two envelopes notarized there, each read back by the published
// reference verifier under a post-quantum certified anchor), judged AGAIN, offline, by the verifier as it is now: the bundles, what
// the verifier brought and its two readings are in the file; the six verdicts must be the ones recorded. Nothing is sent or read.
const dir = path.join(AICI, 'dovezi-ancora-28001'); const f = fs.existsSync(dir) ? fs.readdirSync(dir).filter((x) => /^ancora-dispozitiv-.*\.json$/.test(x)).sort() : [];
const titlu = 'the bundles and the readings recorded on testnet 28001, judged again offline by the verifier of now, give the six verdicts recorded (VALID with the reading, PARTIAL without it, a post-dated attestation caught by the reading, another envelope and another reading refused)';
if (!f.length) rand('D08', titlu, 'NEMASURAT', 'no evidence file in dovezi-ancora-28001/', ['witness-envelope', 'witness-time']);
else { const rele = [];
for (const n of f) { const E = JSON.parse(fs.readFileSync(path.join(dir, n), 'utf8')); const H = E.honest, P = E.postDated, t1e = H.bundle.attestation.statement.at, t2e = P.bundle.attestation.statement.at;
const j = (b, now, anchors) => { const r = D.verifyDevice(b, { ...E.verifierBrought, now, ...(anchors ? { anchors } : {}) }); return r.verdict === 'INVALID' ? [r.verdict, r.rules] : r.verdict; };
const acum = { honestWithReading: j(H.bundle, t1e, [H.reading]), honestWithoutReading: j(H.bundle, t1e), postDatedWithoutChain: j({ ...P.bundle, anchor: D.proofOfDevice(P.bundle.attestation, buildProof) }, t2e),
postDatedWithReading: j(P.bundle, t2e, [P.reading]), otherEnvelope: j({ ...P.bundle, anchor: H.bundle.anchor }, t2e, [H.reading]), otherReading: j(H.bundle, t1e, [P.reading]) };
if (JSON.stringify(acum) !== JSON.stringify(E.verdicts) || E.network !== 'eip155:28001') rele.push(n + ': now ' + JSON.stringify(acum)); }
cer('D08', titlu + ` (${f.length} evidence file(s))`, !rele.length, rele.join(' | '), ['witness-envelope', 'witness-time']); } }
// ================================================================ E. every rule has a row
{ const acoperite = new Set(randuri.flatMap((r) => r.expects)); const fara = D.REGULI.filter((r) => !acoperite.has(r));
cer('E01', `every rule the verifier can name (${D.REGULI.length}) is expected by at least one attack row`, !fara.length, 'without a row: ' + fara.join(', ')); }
const verzi = randuri.filter((r) => r.state === 'VERDE').length, rosii = randuri.filter((r) => r.state === 'ROSU').length, nem = randuri.length - verzi - rosii;
spune(`\ndevice identity: ${verzi} VERDE, ${rosii} ROSU, ${nem} NEMASURAT (of ${randuri.length} rows)` + (REPEDE ? ' -- FAST MODE: the command line, the reference verifier and the damaged bundles were NOT run' : ''));
if (JSON_OUT) console.log(JSON.stringify({ rows: randuri, verzi, rosii, nemasurat: nem, fast: REPEDE }));
process.exitCode = rosii ? 1 : nem || REPEDE ? 2 : 0;