307 lines
35 KiB
Markdown
307 lines
35 KiB
Markdown
# Conformance suite for the AIP-23 proof kinds
|
|
|
|
Every component beside this one ships its own verifier and its own way of saying no: the AIP-23 reference verifier prints four
|
|
levels, the payment modules return `reasons`, the market judge `motive`, the dataset module `ok`, the audit log and the agents'
|
|
ledger a refusal text, the identity verifier and the device verifier a list of labelled rows. This folder adds the one place that
|
|
says, in the same form for all of them, **this artifact passes or does not, at level N, for reason M**, and a set of vectors that any
|
|
implementation, in any language, can be run against.
|
|
|
|
It adds no verification rule. A verdict here is derived from the verdict of the published verifier of that artifact; the rule ids
|
|
are the ones those verifiers name, read from their code.
|
|
|
|
## The one verdict form
|
|
|
|
```json
|
|
{ "v": 1, "kind": "aere-conformance-verdict", "artifact": "payment-receipt", "verdict": "INVALID",
|
|
"levels": [
|
|
{ "level": "integrity", "state": "FAILED", "rules": ["receipt.chain"], "reason": "the transaction on chain is not this payment ..." },
|
|
{ "level": "authenticity", "state": "NOT_MEASURED", "rules": [], "cause": "absent", "reason": "the envelope carries no signature" },
|
|
{ "level": "anchoring", "state": "NOT_MEASURED", "rules": [], "cause": "absent", "reason": "no notarization claimed and no --rpc given" },
|
|
{ "level": "finality", "state": "NOT_MEASURED", "rules": [], "cause": "absent", "reason": "no notarization claimed and no --rpc given" } ],
|
|
"failed": ["receipt.chain"] }
|
|
```
|
|
|
|
| level | what it says |
|
|
|---|---|
|
|
| `integrity` | the artifact is what it says: its hashes bind its content, and the rules it declares (a policy, deadlines, a challenge window, Merkle paths, a payout) hold when recomputed from the evidence supplied |
|
|
| `authenticity` | the signature the artifact carries verifies, with a key of the scheme declared, and the identity it names is the one derived from that key (an ML-DSA signature on an AIP-23 envelope, the ML-DSA-65 signature of an audit head, the ML-DSA-65 key and signatures of an agent's ledger; the hybrid Ed25519 + ML-DSA-65 signatures of a credential, of its presentation and of each delegation link, both halves required; the same hybrid signatures on a device's manufacture credential, firmware release, transfers, authorization and status records, and the ML-DSA-65 signature of the device on its attestation) |
|
|
| `anchoring` | its `statementHash` (or, for a trust chain, the head of its ledger) is recorded in the notary contract, under a state root that a post-quantum anchor covers |
|
|
| `finality` | that anchor's post-quantum certificate verifies. The finality is the one of the chain the artifact was notarized on |
|
|
|
|
Each level is `PASSED`, `FAILED` or `NOT_MEASURED`, with the reason; a level nobody judged says why (`cause`). The verdict is
|
|
`INVALID` when a level failed; `PARTIAL` when nothing failed but something the artifact **claims** was not measured (a notarization
|
|
claim not checked because the run was offline, a signature scheme the verifier cannot check, a timeline that is not terminal, a
|
|
presentation judged without the verifier's audience, nonce or trusted issuers, a credential whose status list was not read, a
|
|
device bundle with a link missing or a registry nobody read);
|
|
`VALID` otherwise, and only with integrity passed. A claim that was not checked is never `VALID`. `failed` is the sorted list of the
|
|
rule ids that refused; the suite compares it as an exact set.
|
|
|
|
A receipt or a market timeline that carries an AIP-23 envelope gets that envelope judged too, by the reference verifier, so one
|
|
verdict covers the artifact: the receipt recorded on testnet 28001 comes out with integrity, anchoring and finality `PASSED` in one
|
|
verdict (`vectori/payment-receipt/company-p1-28001.valid.json`).
|
|
|
|
## What is enrolled
|
|
|
|
`REGULI.json` is derived from the code (`node deriva-reguli.mjs --verifica`): 181 rules of 11 verifiers, 15 artifact kinds. By level:
|
|
142 rules are reported at `integrity`, 36 at `authenticity`, 3 at `anchoring` (one of them, `envelope.finality`, is reported at
|
|
`anchoring` or at `finality` by the place that refused).
|
|
|
|
| artifact kind | judged by | rules |
|
|
|---|---|---|
|
|
| `envelope` (every AIP-23 kind) | the AIP-23 reference verifier, run as the program it is (`AERE_VERIFY_PROOF=<verify-proof.mjs>`, published at aere.network/tools) | 4: `envelope.form`, `.integrity`, `.signature`, `.finality` |
|
|
| `payment-risk` (a payment presented as allowed under a declared policy) | `payments/risc.mjs` | 6: `risk.form`, `.asset`, `.counterparty`, `.rate`, `.limit`, `.window` |
|
|
| `payment-receipt` | `payments/chitanta.mjs` | 5: `receipt.binding`, `.amount`, `.policy`, `.risk`, `.chain` |
|
|
| `market-timeline` | `market-settlement/piata.mjs` | 10: `market.form`, `.complete`, `.posting`, `.deadline`, `.window`, `.arbiter`, `.payout`, `.result`, `.replay`, `.proof` |
|
|
| `dataset-record`, `-answer`, `-consistency`, `-sample` | `verifiable-data/baza.mjs` | 10: `dataset.form`, `.commitment`, `.field`, `.inclusion`, `.index`, `.range`, `.order`, `.completeness`, `.consistency`, `.sample` |
|
|
| `ai-trust-bundle` (the trust chain of an AI result) | `ai-trust/lant-de-incredere.mjs` | 21: `aitrust.form`, `.identity-mismatch`, `.identity-key`, `.version-attestation`, `.version-artifact-mismatch`, `.version-label-mismatch`, `.runtime-record`, `.runtime-artifact-mismatch`, `.runtime-after-output`, `.audit-log-broken`, `.runtime-not-in-audit-log`, `.audit-head`, `.witness-head`, `.policy-hash`, `.ledger-invalid`, `.output-not-in-ledger`, `.action-refused`, `.ledger-time-mismatch`, `.policy`, `.provenance-envelope`, `.provenance-finality` |
|
|
| `audit-log` (a hash-chained log, alone or against an attested head, signed or not) | `verify-layer/sidecar.mjs` | 13: `auditlog.entry`, `.seq`, `.chain`, `.hash`, `.head-form`, `.head-hash`, `.head-unsigned`, `.head-scheme`, `.head-key`, `.head-signature`, `.head-signer`, `.head-short`, `.head-rewritten` |
|
|
| `audit-inclusion`, `audit-consistency` (RFC 9162 proofs over that log) | `verify-layer/jurnal-arbore.mjs` | 12: `audittree.proof-form`, `.head-form`, `.head-hash`, `.inclusion-index`, `.inclusion-entry`, `.inclusion-envelope`, `.inclusion-leaf`, `.inclusion-path`, `.inclusion-head`, `.consistency-start`, `.consistency-end`, `.consistency` |
|
|
| `agent-ledger` (an agent's signed ledger under its policy) | `agents/agent-ledger.mjs` | 16: `agents.ledger-form`, `.policy`, `.other-policy`, `.session`, `.key`, `.identity`, `.anchor`, `.seq`, `.chain`, `.body`, `.signature`, `.hash`, `.time`, `.revocation`, `.approvals`, `.decision` |
|
|
| `identity-presentation` (a credential shown by its holder or by the last delegate of a chain, judged with what the verifier brings: its audience, its nonce, its clock, the issuers it trusts, the status lists and the revocations it was handed) | `identity/identity.mjs` | 37: `identity.form`, `.issuer-id`, `.holder-id`, `.credential-signature`, `.issuer-trusted`, `.credential-window`, `.status-list`, `.credential-revoked`, `.sd-distinct`, `.disclosure-readable`, `.disclosure-signed`, `.disclosure-once`, `.disclosure-plain`, `.claim-name`, `.binding-credential`, `.binding-disclosures`, `.binding-delegations`, `.presenter`, `.presentation-signature`, `.audience`, `.nonce`, `.freshness`, `.chain-length`, `.delegation-form`, `.delegation-ids`, `.delegation-from`, `.delegation-parent`, `.delegation-signature`, `.delegation-scope`, `.delegation-window`, `.delegation-depth`, `.delegation-narrows`, `.delegation-credential`, `.delegation-claims`, `.delegation-audience`, `.delegation-revoked`, `.unreadable` |
|
|
| `device-bundle` (the chain of a device: the manufacturer's credential for the device key, a firmware release, the ownership transfers, the owner's authorization, the device's attestation and its AIP-23 envelope, judged with what the verifier brings: its clock, nonce and audience, the manufacturers it trusts, the recall lists, firmware policies and status records it read, its own reading of the notarized envelope) | `device-identity/device.mjs` | 47: `device.form`, `.manufacture-form`, `.manufacturer-id`, `.manufacture-holder`, `.manufacture-signature`, `.manufacturer-trusted`, `.manufacture-window`, `.manufacture-status-list`, `.manufacture-revoked`, `.device-key`, `.device-id`, `.firmware-form`, `.firmware-signer`, `.firmware-signature`, `.firmware-model`, `.firmware-retracted`, `.firmware-rollback`, `.firmware-software`, `.chain-length`, `.transfer-form`, `.transfer-device`, `.transfer-ids`, `.transfer-from`, `.transfer-prev`, `.transfer-signature`, `.transfer-time`, `.authorization-form`, `.authorization-device`, `.authorization-owner`, `.authorization-ownership`, `.authorization-signature`, `.authorization-window`, `.authorization-action`, `.status-retired`, `.status-suspended`, `.attestation-form`, `.attestation-key`, `.attestation-device`, `.attestation-signature`, `.audience`, `.nonce`, `.freshness`, `.attestation-firmware`, `.attestation-ownership`, `.witness-envelope`, `.witness-time`, `.unreadable` |
|
|
|
|
A module verifier exports the list of rules it can name (`REGULI`; its helper throws on any other) and marks its rules in one of two
|
|
ways, both read by the derivation: a marker on every check of a rule (nine modules), or the rule named at every call of the
|
|
verifier's one refusal helper (`ai-trust`). The derivation requires declared == marked == mapped here. A rule added to a verifier and
|
|
not mapped is a refusal of the derivation; a mapped rule without a vector is a refusal of the probe. For the reference verifier, a
|
|
published program this suite does not edit, the rules are its level names, and each of the six places where its `finality` level
|
|
can refuse is classified as `anchoring` or `finality` (a seventh place would be a refusal of the derivation).
|
|
|
|
The identity verifier does not stop at its first refusal: it makes every check it can reach and names every rule that refused, so a
|
|
vector may expect several. What it could not judge because the verifier did not bring it (no audience, no nonce, no trusted issuer,
|
|
the status list the credential names not handed or not current) leaves the verdict `PARTIAL`, never `VALID`; the cause is named
|
|
`not-supplied` (added to the verdict form on 2026-10-05).
|
|
|
|
The device verifier (enrolled on 2026-10-05) works the same way. A link that is not in the bundle, the verifier's nonce, audience
|
|
or trusted manufacturers not brought, a recall list, a firmware policy or the status registry not read, a recall list, a policy or
|
|
a status record that is signed by who may give it and cannot be read, a notarization the envelope claims and the verifier did not
|
|
read, a hardware root (which that verifier cannot judge): each leaves the verdict `PARTIAL`. The adapter refuses to run if the
|
|
causes the module declares as keeping its verdict from `VALID` are not exactly the ones mapped here, and if the verdict of the
|
|
suite is not the verdict of the module. The readings of a notarized envelope in these vectors are inputs (what a verifier would
|
|
bring), not readings of a chain: the module's own probe reads testnet 28001.
|
|
|
|
Four things the device verifier **names without changing its verdict**, and what a `VALID` then does and does not say: a status
|
|
record of a **previous owner** (it does not bind the owner of now: a seller could date a retirement before the sale after it;
|
|
the manufacturer's retirement is final, an owner's lasts as long as its ownership), a status record dated in the verifier's
|
|
future, a status record of a stranger, a credential that names no recall list. The one verdict form has no field for them: the
|
|
adapter writes them in the reason of the integrity level, and the vectors compare verdicts, rules and level states, not reasons.
|
|
So `device-bundle/resold-after-retirement.valid` is `VALID` here, and a verifier that drops the naming still passes it; that the
|
|
naming is there is measured by the device component's own probe, not by this suite.
|
|
|
|
**Not enrolled**, counted in the same file: three verifiers of the identity component that sit beside the enrolled one or on top of
|
|
it: SD-JWT (26 calls of its refusal helper), Travel Rule (20 places that add a reason) and compliance (3). Their refusals are texts,
|
|
not rule ids. Their own probes and negative controls stay what checks them.
|
|
|
|
## The vectors
|
|
|
|
`vectori/` holds 422 vectors (413 offline, 9 that read testnet 28001; 16 MB, since every device and identity vector carries its ML-DSA-65 keys and signatures), listed with their digests in `vectori/INDEX.json`:
|
|
envelope 64, payment-risk 11, payment-receipt 10, market-timeline 26, dataset 22, ai-trust-bundle 24, audit-log 19, audit-inclusion
|
|
10, audit-consistency 4, agent-ledger 18, identity-presentation 65, device-bundle 149. By expected verdict: 59 `VALID`, 47 `PARTIAL`, 316 `INVALID`. None is typed by hand
|
|
(`node genereaza-vectori.mjs --verifica` regenerates them and compares byte for byte):
|
|
|
|
- the 16 AIP-23 kinds come from their producers on fixed inputs (the same envelopes as the published AIP-23 kind vectors); the audit
|
|
log, its heads and its inclusion and consistency proofs come from the verify-layer producers on fixed inputs;
|
|
- the receipt, the five market timelines, the notarized dataset commitment and one trust chain of an AI result are the evidence
|
|
recorded on testnet 28001 on 2026-10-02 beside those components;
|
|
- `surse-proaspete.json` holds what needed a key, randomness or the chain, made once by `--proaspat` (which only adds what is
|
|
missing). Every key was generated and discarded in that call: a tree head signed with an ML-DSA-65 key and the same head "signed"
|
|
with an Ed25519 key that declares itself ml-dsa-65; an audit head signed by an operator key, by another key, and by a classical
|
|
key; the ledger of an agent (a tool call, a payment, a payment refused above the limit, a tool call) with the forged siblings
|
|
that only the agent's key can make (an empty approvals list, a false decision, a revocation entry without a revocation) and one
|
|
written with a classical key; three trust chains of a fresh agent (allowed, refused by the policy, dated apart); the presentations of one identity credential
|
|
(an issuer, a holder, a phone, a session key, a stranger and an impostor issuer: six hybrid key pairs), its status lists, its
|
|
delegations and revocations, with the forged siblings that only a key holder can make (a statement changed and signed again by
|
|
the issuer, the holder or a delegate) and one credential from an issuer whose "ML-DSA-65" key is an Ed25519 key; the chain of one
|
|
device (a manufacturer, two owners, a second identity the first owner sells to, a stranger, another manufacturer: six hybrid key
|
|
pairs; the device key and a clone's key, ML-DSA-65), sold once, sold on, and bought back, its recall lists, firmware policies and
|
|
status records, with the forged siblings that only a key holder can make and a device key that is an Ed25519 key (the firmware
|
|
image and its Proof of Software attestation are the fixtures of the device component; `--proaspat --refa device` makes this one
|
|
source anew when the producers of the device component change, and every other vector stays byte for byte); an envelope with
|
|
random content that nobody notarized; a dataset of 16 records with random salts; and the transaction of the recorded receipt read
|
|
from the chain (two read calls).
|
|
|
|
An `INVALID` vector names its `base` and its `mutation`: **one operation** (set, remove or add at one JSON path) applied to the input
|
|
of a vector that is not invalid, and it must be refused for exactly the rules in `expected.failed`, not merely refused: 209 of the
|
|
316 are made this way. Where the rule sits under a hash or a signature, one place is not reachable (changing an amount under the
|
|
instruction hash trips the binding first; a false decision in a ledger must still be signed by the agent; a disclosure the issuer never signed must still be signed for by
|
|
the holder; an authorization by a previous owner must still be signed by that owner): the other 107 are made at
|
|
the producer, the commitments above the change recomputed with the published formulas, and every path that moved is declared
|
|
(`mutation.changed`). Eleven vectors expect more than one rule: rules that guard the same field cannot be separated (a looser policy shown
|
|
under a receipt also makes the risk verdict not reproduce; a Proof of AI envelope that is changed is also no longer the one the
|
|
ledger links; a delegation chain cut after it was signed for is also not presented by its last delegate; a manufacture credential
|
|
whose signature is changed is also no longer the link the first transfer names; a clone that claims the device's id with another
|
|
key breaks both the id derivation and the match with the manufacturer's credential).
|
|
|
|
## Run it
|
|
|
|
```
|
|
AERE_VERIFY_PROOF=<verify-proof.mjs from aere.network/tools, with verify-anchor.mjs, mpt.mjs, falcon512.mjs, registry-v3.mjs and package.json beside it and npm install done>
|
|
node conformance.mjs --impl "node implementare-aere.mjs" # the published verifiers behind the one verdict form: 413 offline vectors
|
|
node conformance.mjs --impl "node implementare-aere.mjs" --online # and the 9 that read testnet 28001 (nothing is sent)
|
|
node conformance.mjs --impl "python3 implementare-integritate.py" # an independent implementation: 62 vectors, 360 reported NOT COVERED
|
|
node conformance.mjs --impl "<your command>" # yours
|
|
node implementare-aere.mjs <artifact.json> [--online] # one artifact { artifact, input }: its verdict
|
|
```
|
|
|
|
For the artifact `envelope` the input is `{ "envelope": <any JSON value> }`, or `{ "raw": "<the text of the file>" }` when the file is
|
|
not JSON at all: a file that is not an envelope is refused at the form level, by name, like any other. The other inputs are the ones
|
|
the vectors show (`{ log, head?, signer? }` for an audit log, `{ ledger, policy }` for an agent's ledger, `{ bundle }` for a trust
|
|
chain, `{ presentation, now, audience?, nonce?, trustedIssuers?, statusLists?, revocations?, maxAgeS? }` for a presentation of a
|
|
credential, `{ bundle, now, nonce?, audience?, trustedManufacturers?, statusLists?, firmwarePolicies?, statusRecords?,
|
|
softwareAttestation?, anchors?, maxAgeS? }` for a device bundle: `now`, the verifier's clock, is required for both, since a verdict
|
|
on the clock of the machine that runs a vector would not reproduce).
|
|
|
|
An implementation is a command that answers three calls with JSON on stdout: `--capabilities` (`{ artifacts, rules, online, batch }`:
|
|
what it claims to judge), `<vector.json> [--online]` (the verdict above for the file's `{ artifact, input }`), and, if it says
|
|
`batch`, `--batch <list.json>` (one verdict per line). A vector is scored only if the implementation claims its artifact kind and
|
|
every rule the vector exercises; otherwise it is reported `NOT COVERED`, never passed. A scored vector passes when the verdict, the
|
|
exact set of failed rules and the state of each of the four levels are the expected ones. No output is `BROKEN`: a crash is not a
|
|
refusal. Without `--online` an implementation must not open a connection.
|
|
|
|
`implementare-integritate.py` is the second implementation: Python, standard library only, written from the rules and not from the
|
|
JavaScript. It claims 6 of the 181 rules (the form and the statement hash of an envelope; the form, the commitment, the salted field
|
|
commitments and the RFC 9162 inclusion of a dataset record) and passes all 62 vectors it claims. It cannot check an ML-DSA signature
|
|
or read a chain, says so in its capabilities, and the runner reports the rest as not covered: 360 vectors, among them every vector of
|
|
the trust chain, the audit log, the agents' ledger, the identity presentations and the device bundles. For those six verifiers the
|
|
suite has one implementation, ours.
|
|
|
|
## How the suite itself is checked
|
|
|
|
Measured on 2026-10-05 (Node.js 24.14.1, Python 3.14.4, testnet 28001 read):
|
|
|
|
- `node proba-conformance.mjs --online`: every row green, 11 in this copy (a twelfth, the comparison of the 16 kind envelopes with
|
|
the published AIP-23 kind vectors, runs where their generator is, in the development repository: 12/12 there, and here it is
|
|
printed as information). The rules and the vectors are what the code derives now; every rule has a vector that expects it (181 of
|
|
181); every artifact kind has a valid vector (15); every invalid vector is its base with one operation (209) or one declared
|
|
producer change (107); the published verifiers pass 422/422; the independent implementation passes all 62 it claims; and 70,358
|
|
malformed inputs (every path of every offline vector that is not invalid removed, or replaced by null, a string, a number, a list,
|
|
an object) given to the module verifiers each got a verdict, none threw. That row covers the modules of nine verifiers; the
|
|
trust chain verifier is not in it (each case would run the reference verifier several times). The reference verifier, a program,
|
|
has a row of its own: 183 malformed envelope files (every path of a signed tree head and of a notarized envelope removed or
|
|
replaced; files that hold null, a number, a string, true, a list, an empty object, text that is not JSON, nothing) are given to
|
|
it, and each gets a verdict. That row contacts no public endpoint: the program goes to the chain whenever a file claims a
|
|
notarization, so a file that still carries the claim is run against a closed local endpoint and its finality is not measured. Without `--online` the nine chain vectors are reported unmeasured and the exit code is 2.
|
|
- `node proba-conformance.mjs --control-negativ --online`: 263 rows green: 181 of 181 rules, 76 of 76 conditions of the device
|
|
verifier, six controls of the instruments. Each rule is planted out of its verifier, one at a time, in
|
|
a copy (every check that carries its marker made true at run time; for `ai-trust`, its refusal helper made to drop the refusals of
|
|
that rule; for the reference verifier, the level made to pass); the suite is run against the planted verifier; the vectors that
|
|
expect the rule, **and only they**, must fail, and none may break. A rule taken out whole says nothing about each condition of
|
|
a check made of several: every vector of the rule fails, whichever condition it was written for. So, for the device verifier,
|
|
the conditions are then planted out **one at a time**: 76 of 76. The plantings are those of that verifier's own negative control,
|
|
read from its `plantari-device.mjs` and not typed again here (a credential that has not begun, each condition of the
|
|
authorization window, the position of a transfer, the id of who gives one, the sequence and the digest the device attests, the
|
|
model, the validity and the version of a policy, a date ahead of the verifier's clock, a signature in another base64 text, whose
|
|
status record counts, and so on); under each, exactly the vectors written for that condition must fail (82 vectors in all). Of
|
|
the 88 plantings that control makes in the verifier, 11 are reached by no vector and are not in this list: four are of the
|
|
producers and two of the key files, which a vector does not exercise; four are of what the verifier names without changing its
|
|
verdict, which the verdict form does not carry; and one empties the list of causes that keep a verdict from `VALID`, under which
|
|
the adapter refuses to run at all (every device vector breaks, none passes). One more, the purpose separation of signatures,
|
|
fails 123 of the 149 device vectors at once and is not a condition of one check. The other verifiers have no such list: for them
|
|
the control shows that each **rule** is load-bearing, and `--pe-loc` below measures their checks. Then six controls of the
|
|
instruments: an implementation that
|
|
calls everything valid fails every vector that is not plainly valid (381 of the 413 offline vectors); the coverage gate goes red when one rule's vector
|
|
is taken away; a rule added to a verifier and not mapped is refused by the derivation; a judge that throws again turns the
|
|
malformed-input row red; a reference verifier that throws again turns the malformed-envelope row red; no planted copy is left
|
|
behind.
|
|
- `node proba-conformance.mjs --pe-loc` is a measurement, not a gate: each of the 209 marked checks of the module verifiers
|
|
planted out alone (the trust chain verifier has no per-check markers and is not in this count). For 174 of them a vector
|
|
then fails (all 40 checks of the identity verifier and all 47 of the device verifier among them). The others are checks whose vector trips a second check of the same rule at the same time, so removing one alone
|
|
changes nothing (the three checks of `market.replay`, the three of `agents.anchor`, the run and neighbour checks of
|
|
`dataset.completeness`), or checks no vector reaches (a dispute timed out before the arbitration timeout; five of the six checks
|
|
of `agents.time`). The control above shows that each **rule** is load-bearing, not each of its checks.
|
|
|
|
The suite found defects in the verifiers it enrols, on 2026-10-03, while they were being enrolled; all are fixed and each is now a
|
|
vector or a probe row:
|
|
|
|
- **three verifiers accepted a classical key under a post-quantum name.** The AIP-23 reference verifier reported a signature that
|
|
declared `ml-dsa-65` and was made with an Ed25519, Ed448, RSA or P-256 key as `signature PASSED: ml-dsa-65: verifies`; the audit
|
|
log verifier accepted a head "signed" the same way as a head signed with ML-DSA-65; the agents' ledger verifier did not require
|
|
the agent key to be an ML-DSA-65 key, so the ledger of a "post-quantum identity" could be written and signed with an Ed25519 key
|
|
and verify. In each, the key was never required to be a key of the scheme declared. Each is now a refusal by name
|
|
(`envelope/signed-head.signature-classical-key`, `audit-log/signed-head.head-scheme-classical-key`,
|
|
`agent-ledger/four-entries.key-classical`). The audit head has a second vector since 2026-10-05,
|
|
`audit-log/signed-head-any-signer.head-scheme-classical-key`, the same head with no signer expected: with the scheme rule taken
|
|
out of the verifier, the first is still refused, for its signer, and this one is accepted;
|
|
- the reference verifier exited with an exception on a file holding the JSON value `null`, let a statement that is a list pass its
|
|
form level, and reported a malformed ML-DSA signature (no key, a key that cannot be parsed) as unmeasured "needs OpenSSL 3.5" on a
|
|
runtime that has it; each is now a refusal by name, and a file that is not JSON is refused at the form level instead of ending
|
|
the run;
|
|
- a malformed payment made the risk module throw instead of refusing by name; malformed dataset answers made the dataset verifier
|
|
throw;
|
|
- the market judge accepted a timeline from which the submission event had been withheld, which skipped the challenge-window rule
|
|
(the judge now requires every event the job state implies: `market.complete`);
|
|
- the market judge took the time an execution envelope was notarized from the envelope's own claim, so a claim written earlier than
|
|
the truth passed. The time now comes from the reference verifier's reading under the anchor, and the claim is only compared with
|
|
it (`market-timeline/s1-28001.proof-claim-early`).
|
|
|
|
On 2026-10-05, while the identity verifier was being enrolled, one more, fixed the same day:
|
|
|
|
- **a fault of the issuer's keys was also blamed on the holder.** When the issuer keys in a credential could not be read (for
|
|
example an Ed25519 key in the field of the ML-DSA-65 key), the identity verifier refused the credential, as it must, and also
|
|
reported that the holder id is not the id of its keys: the two ids were derived in one step, and the first failure left the
|
|
second underived. The verdict was `INVALID` either way; the list of rules named one that had not been broken. Each id is now
|
|
derived on its own, and the vector `identity-presentation/direct.issuer-classical-key` expects exactly the two rules of the
|
|
issuer. That vector also measures that the identity verifier never accepted a classical key in the ML-DSA-65 field: its key
|
|
type was checked before this suite.
|
|
|
|
## What it is not, and what is not measured
|
|
|
|
- **Not a certification, not an audit, not a mark.** Passing says that an implementation agrees with the published verifiers on these
|
|
422 inputs. It does not say either of them is correct, and no third party has reviewed the verifiers, the vectors or this suite.
|
|
- **The vectors and the verifiers have the same authors.** The expected verdicts were written by the people who wrote the verifiers.
|
|
The second implementation is independent in code and language, not in authorship, and it covers 6 rules of 181; the other
|
|
175 rules, among them every rule of the trust chain, the audit log, the agents' ledger, the identity verifier and the device
|
|
verifier, are judged here by one implementation.
|
|
- **Finality is the chain's.** The level says that the post-quantum certificate of the covering anchor verified on the chain where the
|
|
artifact was notarized. Every notarized vector here is on chain 28001, a testnet; nothing in this suite ran on mainnet.
|
|
- **Five of the six finality refusals have no vector.** The reference verifier can refuse at its finality level in six places; the
|
|
vectors reach one (a notarization claimed that the chain does not hold). The others need an endpoint that lies or an anchor that
|
|
does not verify: they are exercised by that verifier's own probe, through a local proxy that lies to it, not by these vectors.
|
|
- **The chain vectors depend on the testnet's state.** The notary contract is open: anyone can notarize the fresh envelope's hash on
|
|
28001, and `envelope/fresh-28001.*` then change verdict. The runner would show it; `--proaspat` makes a new one.
|
|
- **Integrity is over the evidence supplied.** A market timeline and the transaction facts of a receipt are the caller's reading of the
|
|
chain; this suite does not certify that reading under an anchor. The market judge now refuses a timeline that lacks an event the
|
|
job state implies; it does not prove that the job state shown is the chain's.
|
|
- **Offline, a notarization time is only a claim.** With the chain, `market.proof` takes the time an execution envelope was first
|
|
seen from the reference verifier's reading under the anchor and refuses a claim that differs from it. Offline there is no such
|
|
reading: the claim is all there is, the verdict is `PARTIAL`, and a `certified` field that an artifact brings itself is dropped.
|
|
- **Authenticity is what each verifier checks.** The reference verifier checks ML-DSA signatures on envelopes, the audit log verifier the
|
|
ML-DSA-65 signature of a head, the ledger verifier the agent's ML-DSA-65 key and signatures, each with a key that must be of the
|
|
scheme declared; the identity verifier the hybrid Ed25519 + ML-DSA-65 signatures of a credential, a presentation and each
|
|
delegation link (both halves, each with a key of its type). A hybrid signature on an AIP-23 envelope is still reported
|
|
unmeasured by the reference verifier, and the verdict is `PARTIAL`.
|
|
- **Most times in a trust chain and in a ledger are claims.** A trust chain without a witness is `PARTIAL`: every time in it is the
|
|
statement of whoever holds the keys. Three trust-chain vectors read a ledger head notarized on testnet 28001. No vector gives an
|
|
agent's ledger an anchor that verifies: `agents.anchor` is reached only through an anchor that is refused, and five of the six
|
|
checks of `agents.time` have no vector.
|
|
- **An identity verdict is the verifier's.** A presentation is judged on the clock, the audience, the nonce, the trusted issuers, the
|
|
status lists and the revocations in the vector input: what a verifier brings decides the verdict. `VALID` for a delegated
|
|
presentation says that no revocation handed to the verifier has taken effect, not that none exists; a revocation that cannot be
|
|
attributed to the giver of the link or to the holder is ignored; a credential that names no status list cannot be revoked by
|
|
its issuer and is still `VALID`. Every time in these vectors is the statement of a key holder: no vector notarizes a credential
|
|
or a delegation. Nothing here says who an issuer is in the world.
|
|
- **A device verdict is the verifier's too, and the device key is a software key.** A bundle is judged on the clock, the nonce, the
|
|
audience, the trusted manufacturers, the recall lists, the firmware policies, the status records and the readings in the vector
|
|
input. `VALID` says that every link was brought and that what was read revokes nothing, not that nothing was withheld. No vector,
|
|
and nothing in the device verifier, checks a hardware root: a device key is a key in software, and an attestation says what the
|
|
holder of that key claims to run. A Proof of Software attestation handed with a bundle is checked for being the one the release
|
|
names and for listing the image digest; its own signature is not verified there. An owner's retirement of a device does not
|
|
survive a sale: after a transfer it is named and not applied (the manufacturer's is final). A retirement by an owner that holds
|
|
across a sale needs a witness of when it and the transfer were signed, which is not built; every time in a transfer, an
|
|
authorization and a status record of these vectors is the statement of the key holder that signed it.
|
|
- **What a damaged artifact can still pass.** Of the 70,358 malformed inputs, 3,889 were still accepted, at 192 distinct paths (81 of them in device bundles: a status record that decided nothing, such as a suspension already lifted, a stranger's record, a record not in effect yet or one of a previous owner; a recall list or a firmware policy that could not be read and stood beside a clean one, which damaged is no longer signed by the manufacturer and counts like one that was not handed; the envelope and its notarization claim, which are optional; and the signature of a Proof of Software attestation, which the device verifier does not read): parts a
|
|
verifier treats as optional (the transaction facts of a receipt, the envelope of a timeline, the head of an audit log), a policy
|
|
field removed that falls back to its default, a log or a ledger without its last entry (still a valid shorter one), a revocation that was not in effect (damaged, it is
|
|
ignored as before), and fields no rule reads
|
|
(`node proba-conformance.mjs --acceptate` lists them). A policy that cannot be defined makes the risk module throw, by design. Of
|
|
the 183 malformed envelope files given to the reference verifier (the probe's own row, measured at every run), 16 are still
|
|
called `VALID`: an envelope with its signature or its notarization claim taken off (an unsigned, unnotarized envelope), and an
|
|
envelope whose outer `v` or `kind` was damaged. Those two fields are outside the statement hash and the reference verifier does
|
|
not read them; the statement carries its own `kind`, which is under the hash. (This file used to quote 526 malformed envelopes,
|
|
a figure of 2026-10-03 from a run whose script was not kept; the row above replaces it.)
|
|
- **Not part of AIP-23.** The rule ids, the four levels and the verdict form are this suite's reading of the published verifiers;
|
|
AIP-23 defines the envelope. The device verifier is enrolled. Of the identity verifiers published beside these, the presentation verifier is enrolled; SD-JWT,
|
|
Travel Rule and compliance are not.
|
|
- **The Python implementation** writes numbers the way JavaScript does only for integers and plain decimals, and sorts keys by code
|
|
point (JavaScript's order for keys in the Basic Multilingual Plane).
|