471 lines
57 KiB
Markdown
471 lines
57 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`): 240 rules of 15 verifiers, 21 artifact kinds (since
|
|
2026-10-18: 239 when the Travel Rule and the compliance verifiers were enrolled, 240 with `identity.request` later that day; 213 rules of 13 verifiers and 17 kinds after the SD-JWT
|
|
verifiers earlier that day; 181 rules of 11 verifiers, 15 kinds before). By level: 188 rules are reported at `integrity`, 49 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` | 38: `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`, `.request` (since 2026-10-18: the presentation commits to the request the verifier received, OpenID4VP `transaction_data`) |
|
|
| `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` |
|
|
| `sdjwt-presentation` (an SD-JWT of IETF RFC 9901, as issued or with its Key Binding JWT, judged with what the verifier brings: the issuer key it chose, a public JWK, never one taken from the token; its clock; its audience and nonce; the issuer and the explicit type it expects; whether it requires key binding and `exp`) | `identity/sdjwt.mjs` (enrolled 2026-10-18) | 23: `sdjwt.form`, `.alg`, `.typ`, `.crit`, `.key`, `.signature`, `.issuer`, `.sd-alg`, `.disclosure-form`, `.disclosure-twice`, `.digest-twice`, `.disclosure-name`, `.claim-conflict`, `.unreferenced`, `.time`, `.kb-context`, `.kb-required`, `.kb-cnf`, `.kb-claims`, `.kb-audience`, `.kb-nonce`, `.kb-freshness`, `.kb-sd-hash` |
|
|
| `sdjwt-vc-presentation` (an SD-JWT VC of `draft-ietf-oauth-sd-jwt-vc`, judged as the SD-JWT above plus the profile: the credential type the verifier expects, and the Token Status List it fetched for the `status` the credential names) | `identity/sdjwt-vc.mjs` (enrolled 2026-10-18) | 9 of its own, on top of the 23 of `sdjwt`: `sdjwtvc.vct-expected`, `.vct`, `.vct-mismatch`, `.disclosed-protected`, `.form`, `.status-list`, `.status-index`, `.status-revoked`, `.status-unjudged` |
|
|
| `travel-rule-binding`, `travel-rule-message`, `travel-rule-receipt` (the Travel Rule between VASPs: the beneficiary's KEM key binding with its VASP presentation, judged by the originator with its id, its nonce, its clock, the registries it trusts and their status lists; the sealed IVMS101 message, judged by the beneficiary with its id, its own KEM key pair, which decrypts, its clock, its registries and lists and its replay store; the signed receipt, judged by the originator with the message it sent and the beneficiary it accepted) | `identity/travel-rule.mjs` (enrolled 2026-10-18) | 23: `travelrule.binding-form`, `.binding-id`, `.binding-signature`, `.binding-window`, `.vasp-presentation`, `.vasp-status`, `.vasp-identity`, `.vasp-claim`, `.message-form`, `.message-id`, `.message-signature`, `.message-recipient`, `.message-kem-keys`, `.message-freshness`, `.replay-store`, `.replay`, `.kem-alg`, `.kem-open`, `.ciphertext`, `.receipt-form`, `.receipt-from`, `.receipt-signature`, `.receipt-message` |
|
|
| `compliance-presentation` (a presentation of a credential judged against the verifier's compliance policy: the issuers it believes and the claims it requires, with the verifier's audience, nonce and clock, all required, and the status lists and revocations it holds) | `identity/conformitate.mjs` (enrolled 2026-10-18) | 3: `compliance.presentation`, `.status`, `.rule` |
|
|
|
|
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 `vectori/device-bundle/resold-after-retirement.valid.json` 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.
|
|
|
|
The SD-JWT verifier and the SD-JWT VC profile (enrolled 2026-10-18) stop at their first refusal and name its rule: an integrity refusal
|
|
leaves the authenticity level `NOT_MEASURED` (`not-reached`), an authenticity refusal leaves integrity so. A VC presentation whose
|
|
`status` the verifier waived (`requireStatus: false`) and did not judge is `PARTIAL` (`claim-not-checked`), like a credential whose
|
|
status list was not read. The profile imports `sdjwt.mjs` from its real file, so a planted copy of `sdjwt.mjs` is reached only through
|
|
the artifact `sdjwt-presentation`: no `sdjwt-vc-presentation` vector expects a rule of `sdjwt` (they all exercise them).
|
|
|
|
The Travel Rule verifier and the compliance verifier (enrolled 2026-10-18, the last two of the identity component; the file counts
|
|
nothing as not enrolled since) run the identity verifier on the VASP or KYC presentation inside and name its whole verdict as one rule
|
|
(`travelrule.vasp-presentation`, `compliance.presentation`); the identity rules themselves are enrolled under `identity-presentation`.
|
|
Both make every check they can reach and name every rule that refused, like the identity verifier; the Travel Rule verifier does not open
|
|
a message whose header it refused for any reason, so with only its authenticity refused (the originator's signature or id, the VASP
|
|
presentation) the integrity of the message is `NOT_MEASURED` (`not-reached`), and a binding or a receipt that cannot be read at all
|
|
(`binding-form`, `receipt-form`, `message-form` before the signature) leaves authenticity so. Their verdicts are the verifier's: a
|
|
Travel Rule party brings its registries, its clock, the lists it holds and (the beneficiary) its replay store and its KEM keys; a
|
|
compliance verifier brings its policy, its audience, its nonce and its clock. A compliance judgement under a policy that waives the
|
|
credential status (`requireStatus: false`), with no list read, is `PARTIAL` (`claim-not-checked`); a Travel Rule verdict is never
|
|
`PARTIAL`, since a VASP status that was not judged is a refusal there (`vasp-status`). An invalid presentation establishes no claim, so
|
|
the compliance verifier reports every rule of its policy not met as well (the verifier's reasons list said so before the enrolment, and
|
|
the vectors expect both); a credential from an issuer or a registry whose status list the verifier does not hold is refused for its
|
|
issuer and for its status at once.
|
|
|
|
## The vectors
|
|
|
|
`vectori/` holds 626 vectors (617 offline, 9 that read testnet 28001; 22 MB, since every device, identity and Travel Rule vector carries its ML-DSA-65 keys and signatures), listed with their digests in `vectori/INDEX.json`:
|
|
envelope 67, 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 69, device-bundle 194, sdjwt-presentation 54, sdjwt-vc-presentation 32, travel-rule-binding 19, travel-rule-message 26, travel-rule-receipt 9, compliance-presentation 12. By expected verdict: 87 `VALID`, 70 `PARTIAL`, 469 `INVALID`. None is typed by hand
|
|
(`node genereaza-vectori.mjs --verifica` regenerates them and compares byte for byte):
|
|
|
|
- the 17 AIP-23 kinds come from their producers on fixed inputs (the same envelopes as the published AIP-23 kind vectors; `secret-access`, the audit entry of the secrets service, since 2026-10-18); 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); since 2026-10-18 also an SD-JWT and an SD-JWT VC issued with AERE keys (an issuer, a holder, a
|
|
stranger: three hybrid key pairs each, made and discarded), presented to one verifier, the VC's Status List Tokens (clean, revoked,
|
|
suspended, 8 bits, for another uri, expired, by a stranger, typed JWT, without `iat`, too short, ML-DSA-65) and the forged siblings
|
|
only a key holder can make (a key binding JWT by a stranger or without its nonce, a digest signed twice, a nested `_sd_alg`, a
|
|
disclosure named `_sd`, a claim named `__proto__`, an algorithm confusion, a `status` behind a disclosure, and so on); since the same
|
|
day also one Travel Rule exchange (a registry, another registry, the paying VASP, the receiving VASP, a third VASP and a stranger: six
|
|
hybrid key pairs made and discarded; two hybrid KEM key pairs, X25519 + ML-KEM-768, of which the receiving VASP's is **kept whole in
|
|
the vectors, private half included**, since the beneficiary's private KEM keys are what opens a message: it was made for these vectors
|
|
and opens nothing else, and it is declared test material at the scene, in the form the secret gate of this repository reads, so that
|
|
the gate reports it as seen and would still block a private key without that declaration), with the forged siblings only a key holder
|
|
can make, each checked at generation to be refused for exactly the rule it was made for (a stranger's keys under a VASP's id, a binding
|
|
that expired or whose ML-KEM key is an X25519 key, a header addressed to another VASP, dated 400 s earlier, with another encapsulation
|
|
name, a truncated ML-KEM ciphertext, a flipped ciphertext byte or another amount, each signed again by the originator, a receipt by
|
|
another VASP); and one compliance exchange (an issuer, a holder, another issuer: three hybrid key pairs made and discarded; a KYC
|
|
credential with six disclosable claims, the issuer's status list, a policy of four rules, and the holder's presentations: enough
|
|
claims, too few, claims that break every rule, a credential from the other issuer, a revoked one);
|
|
- the SD-JWT vectors have two more sources that are fixed text: the example of RFC 9901 Section 5 with the key of its Appendix A.5
|
|
(`identity/fixturi-rfc9901.json`), and the tokens of `@sd-jwt/core` 0.23.0 and `@sd-jwt/sd-jwt-vc` 0.23.0 with the status lists of
|
|
`@owf/token-status-list` 0.5.0 (`identity/interop/fixturi-interop-*.json`), which the identity component derived from those
|
|
libraries' real output on 2026-10-18, with their public test keys.
|
|
|
|
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: 303 of the
|
|
469 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; a digest repeated in an SD-JWT must still be
|
|
signed by its issuer, a key binding JWT for another audience by its holder; a Travel Rule header addressed to another VASP, or with a
|
|
cut ML-KEM ciphertext, must still be signed by the originator; a presentation without a request must still be signed by its holder): the other 166 are made at
|
|
the producer, the commitments above the change recomputed with the published formulas, and every path that moved is declared
|
|
(`mutation.changed`). Twenty-four 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; a revoked VASP credential, or one from a registry
|
|
whose list the verifier does not hold, fails the VASP presentation and the VASP status at once; a message opened by another VASP is
|
|
not addressed to it and its originator's presentation was not made for it; a compliance presentation that is invalid for any reason
|
|
also establishes no claim, so its policy's rules are not met either).
|
|
|
|
## 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: 617 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, 557 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, `{ sdJwt, issuerKey, issuerAlg?, now, audience?, nonce?,
|
|
expectedIssuer?, expectedTyp?, requireKeyBinding?, requireExp?, maxAgeS? }` for an SD-JWT presentation and the same plus
|
|
`{ expectedVct, statusListJwt?, statusListKey?, statusListAlg?, requireStatus? }` for an SD-JWT VC, `{ binding, presentation,
|
|
registries, originatorId, nonce, now, statusLists?, maxAgeS? }` for a Travel Rule binding (`originatorId` and `nonce` are the
|
|
originator's own; without them the identity verifier would leave the audience or the nonce of the VASP presentation unjudged, and the
|
|
Travel Rule verifier refuses instead of accepting on trust: a defect this suite's malformed-input row found on 2026-10-18, fixed the
|
|
same day), `{ message, me, kem, registries, now,
|
|
statusLists?, seen?, maxAgeS? }` for a Travel Rule message (`kem` is the beneficiary's key file, `aere-travel-rule-kem-keys`, with
|
|
its private half; `seen` is the list of message ids already received, the replay store; without it nothing is opened), `{ receipt,
|
|
message, beneficiaryId }` for a Travel Rule receipt, and `{ presentation, policy, audience, nonce, now, statusLists?, revocations? }` for
|
|
a compliance presentation (`audience` and `nonce` required, as the module requires them): `now`, the verifier's clock, is required for
|
|
all of them but the receipt, 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 240 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 65 vectors it claims (measured 2026-10-18). 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: 561 vectors, among them every vector of
|
|
the trust chain, the audit log, the agents' ledger, the identity presentations, the device bundles, the SD-JWT presentations, the
|
|
Travel Rule artifacts and the compliance presentations. For those ten verifiers the suite has one implementation, ours (for SD-JWT,
|
|
the identity component measured our verifier against `@sd-jwt/core` and `@sd-jwt/sd-jwt-vc` outside this suite; those libraries are
|
|
not implementations of this suite's contract).
|
|
|
|
## How the suite itself is checked
|
|
|
|
Measured on 2026-10-18 (UTC), in the evening, after the identity verifier gained the rule `request` (a presentation committed to the
|
|
request the verifier received; the adversarial review of the secrets service) and its four vectors were derived from a new fresh
|
|
source (`identityRequest`; every other fresh source kept, so no other vector's input moved; the 65 earlier identity vectors changed
|
|
only in `exercises`, which now lists `identity.request`), on Node.js 24.14.1, offline, with the served reference verifier and with the
|
|
one of the development repository: `node proba-conformance.mjs` gives 12 rows green, 0 red, 1 not measured (the 9 vectors that read
|
|
testnet 28001). 240 rules of 15 verifiers, 626 vectors (`LA ZI`), every rule has a vector (240 of 240), every INVALID vector (469) is
|
|
its base with one operation (303) or one declared producer change (166), the published verifiers pass 617 of 617 offline vectors,
|
|
the independent Python implementation passes the 65 it claims and reports the other 561 not covered, 101,833 malformed inputs each got
|
|
a verdict. `node proba-conformance.mjs --control-negativ` (offline) gives 319 rows green, 0 red, 3 not measured (the three rules whose
|
|
vectors need the testnet): 237 of 240 rules proven load-bearing, `identity.request` among them (planted out, exactly its 2 vectors
|
|
fail). Before the change, the same commands on the untouched tree: 12 green, 0 red, 1 not measured; 318 green, 0 red, 3 not
|
|
measured (236 of 239). Not run after the change: `--online`.
|
|
|
|
Measured again on 2026-10-18 (UTC), later the same day, after the AIP-23 kind `secret-access` (the audit entry of the secrets service,
|
|
`pq-secrets/`) was added to proof-kinds and its three envelope vectors derived (Node.js 24.14.1, the reference verifier of the
|
|
development repository, offline): `node proba-conformance.mjs` gives 10 rows green, 2 red, 1 not measured. The vectors on disk are what
|
|
the generator derives (622, `LA ZI`); the 17 kind envelopes are the published kind vectors; every rule has a vector (239 of 239); every
|
|
artifact kind has a valid vector (21); every INVALID vector (467) is its base with one operation (302) or one declared producer change
|
|
(165); the published verifiers pass 613 of 613 offline vectors; the independent Python implementation passes all 65 it claims and the
|
|
other 557 are reported not covered; 100,825 malformed inputs and 183 malformed envelope files each got a verdict. The 9 vectors that
|
|
read testnet 28001 were not run (`--online`). The two red rows are the derivation of the `aip23-envelope` rules from the verifier's code
|
|
(its finality sites, "appears on 0 FAILED lines of the verifier") and `REGULI.json`, and they were red before this change, on the
|
|
untouched tree, in the same run form (measured first): the reference verifier was refactored earlier that day (`judge()` exported,
|
|
commit b2cf499c6) and the rule derivation has not followed it yet; the kind added here does not touch them.
|
|
|
|
Measured on 2026-10-18 (UTC), after the Travel Rule verifier and the compliance verifier were enrolled, the last two of the identity
|
|
component (Node.js 24.14.1; the reference verifier of the development repository; testnet 28001 read, nothing sent; the run is the one
|
|
on the final code, after the three fixes below):
|
|
|
|
- `node proba-conformance.mjs --online`: 12 rows green, 0 red. The rules and the vectors are what the code derives now: 239 rules of
|
|
15 verifiers, 619 vectors (19 of the kind `travel-rule-binding`, 26 of `travel-rule-message`, 9 of `travel-rule-receipt`, 12 of
|
|
`compliance-presentation`). Every rule has a vector that expects it (239 of 239); every artifact kind has a valid vector (21); every
|
|
invalid vector is its base with one operation (300) or one declared producer change (165); the published verifiers pass 619/619; the
|
|
independent implementation passes all 62 it claims and the other 557 are reported not covered; 100,825 malformed inputs given to the
|
|
module verifiers each got a verdict and none threw (4,766 still accepted, at 255 distinct paths; of the four new kinds only
|
|
`travel-rule-receipt` accepts damage, at 62 paths, all of them the originator's presentation and signature inside the message handed
|
|
with the receipt, which the receipt names by the hash of the signed header and the ciphertext and does not read; the binding, the
|
|
message and the compliance presentation accept none); 183 malformed envelope files given to the reference verifier each got a verdict.
|
|
- `node proba-conformance.mjs --control-negativ --online`: 321 rows green, 0 red: 239 of 239 rules (each of the 23 rules of the Travel
|
|
Rule verifier and the 3 of the compliance verifier planted out in turn fails exactly the vectors that expect it), 76 of 76 conditions
|
|
of the device verifier, six controls of the instruments (an implementation that calls everything valid fails 578 of the 610 offline
|
|
vectors).
|
|
- While the enrolment was being made, the measurement found three things, each fixed before this run: the malformed-input row showed
|
|
that `acceptBeneficiary` called without `originatorId` or without `nonce` accepted the beneficiary (the identity verifier leaves the
|
|
audience or the nonce of the VASP presentation unjudged, and the Travel Rule verifier did not require them judged: it refuses now; a
|
|
test of the identity component, a planting of its negative control and two vectors here); and the control showed, with a `*-form`
|
|
rule planted out, the vector whose artifact is the JSON value `null` **breaking** instead of failing, twice in the adapter (its
|
|
not-reached reason read the name of a stopping rule that was not there) and once in `verifyReceipt` (it read `accepted` from a
|
|
statement that was not there). A crash is not a refusal, and the control counted each as broken, not as a fail.
|
|
|
|
Measured on 2026-10-18 (UTC), when the SD-JWT verifier and the SD-JWT VC profile were enrolled (Node.js 24.14.1; the reference
|
|
verifier of the development repository; testnet 28001 read, nothing sent):
|
|
|
|
- `node proba-conformance.mjs --online`: 12 rows green, 0 red. The rules and the vectors are what the code derives now: 213 rules of
|
|
13 verifiers, 553 vectors (54 of the kind `sdjwt-presentation`, 32 of `sdjwt-vc-presentation`). Every rule has a vector that
|
|
expects it (213 of 213); every artifact kind has a valid vector (17); every invalid vector is its base with one operation (249) or
|
|
one declared producer change (155); the published verifiers pass 553/553; the independent implementation passes all 62 it claims
|
|
and the other 491 are reported not covered; 97,966 malformed inputs given to the module verifiers each got a verdict and none
|
|
threw (4,398 still accepted, at 193 distinct paths, 4 of them of `sdjwt-presentation` and 2 of `sdjwt-vc-presentation`: the
|
|
optional inputs of the verifier, such as the issuer it expects); 183 malformed envelope files given to the reference verifier each
|
|
got a verdict.
|
|
- `node proba-conformance.mjs --control-negativ --online`: 295 rows green, 0 red: 213 of 213 rules (each of the 23 rules of the
|
|
SD-JWT verifier and the 9 of the VC profile planted out in turn fails exactly the vectors that expect it), 76 of 76 conditions of
|
|
the device verifier, six controls of the instruments (an implementation that calls everything valid fails 512 of the 544 offline
|
|
vectors). While the enrolment was being made, the control found one thing and it was fixed before this measurement: with
|
|
`vct-expected` planted out, the VC verifier threw on the vector that names no expected type instead of refusing it by the next
|
|
rule, which the suite reported as a broken vector, not as a refusal.
|
|
|
|
Measured on 2026-10-14 (UTC), after the third review of the device verifier (Node.js 24.14.1; the AIP-23 reference verifier fetched
|
|
from aere.network/tools with its four sibling files and `npm install` done in an empty folder, as a stranger would; testnet 28001
|
|
read, nothing sent):
|
|
|
|
- `node proba-conformance.mjs --online`: 12 rows green, 0 red (in this published copy the comparison of the 16 kind envelopes with
|
|
the published AIP-23 kind vectors is printed as information; the twelfth row is green where their generator is). The rules and the
|
|
vectors are what the code derives now: 181 rules of 11 verifiers, 467 vectors. 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 (221) or one declared producer
|
|
change (116); the published verifiers pass 467/467; the independent implementation passes all 62 it claims and the other 405 are
|
|
reported not covered; 96,757 malformed inputs given to the module verifiers each got a verdict and none threw (one vector,
|
|
`device-bundle/chain-of-64.partial`, is larger than 300,000 characters and is not damaged this way); 183 malformed envelope files
|
|
given to the reference verifier each got a verdict.
|
|
- `node proba-conformance.mjs --control-negativ --online`: 263 rows green, 0 red: 181 of 181 rules, 76 of 76 conditions of the device
|
|
verifier (under them 96 distinct device vectors fail, each exactly under the conditions written for it), six controls of the
|
|
instruments (an implementation that calls everything valid fails 426 of the 458 offline vectors, every one that is not plainly valid).
|
|
|
|
The finer counts in the paragraphs below (how many plantings of the device verifier no vector reaches, how many device vectors the
|
|
purpose separation breaks at once, and the `--pe-loc` measurement) are those of the measurement of 2026-10-05; they were not counted
|
|
again on 2026-10-14.
|
|
|
|
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
|
|
626 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 240; the other
|
|
234 rules, among them every rule of the trust chain, the audit log, the agents' ledger, the identity verifier, the device
|
|
verifier, the SD-JWT verifiers, the Travel Rule verifier and the compliance 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 100,825 malformed inputs (measured 2026-10-18), 4,766 were still accepted, at 255 distinct paths (76 of them in device bundles, 62 in Travel Rule receipts: the originator's presentation and signature inside the message handed with a receipt, which the receipt does not read; on 2026-10-14 the count was 96,757 inputs, 4,280 accepted at 187 paths; the kinds that follow were listed on 2026-10-05, when the count was 81 and the device verifier had not had its third review: 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, and so are all five identity verifiers published beside these: the
|
|
presentation verifier, the SD-JWT verifier and the SD-JWT VC profile, the Travel Rule verifier and the compliance verifier (the last
|
|
two since 2026-10-18).
|
|
- **A Travel Rule verdict is the receiving party's, and the vectors carry one test decryption key.** A binding, a message or a receipt
|
|
is judged with what the originator or the beneficiary brings: the registries it trusts, the status lists it holds, its clock, its
|
|
nonce, its replay store, its KEM keys. `VALID` for a message says that it opened for this beneficiary, with these keys, on this
|
|
clock, that it was not in the replay store handed, and that the originator's VASP status was judged on the lists handed; it does not
|
|
say who a registry is in the world, nor that the IVMS101 inside is true or complete (the module does not validate the schema). The
|
|
beneficiary's KEM key pair in the message vectors is test material made at generation, declared so at the scene, and opens only the
|
|
messages of these vectors; nothing else in this suite holds a private key. The record of an exchange (an AIP-23 `compliance` envelope
|
|
without personal data) is judged as the artifact `envelope`, not here.
|
|
- **A compliance verdict is the verifier's policy applied to one presentation.** It says that the presentation was valid for that
|
|
verifier and that the claims it established satisfy the policy's rules, with the status judged on the lists handed when the policy
|
|
requires it. It is not a zero-knowledge proof (a shown claim is shown whole), not an AML or sanctions screening, and not legal
|
|
compliance with anything; a policy that cannot be defined is the verifier's error (the module throws, by design), not a verdict.
|
|
- **The SD-JWT vectors say what two libraries agree on, on these inputs.** The tokens of `@sd-jwt/core` and `@sd-jwt/sd-jwt-vc` in
|
|
the SD-JWT vectors are the fixtures the identity component derived from those libraries' real output on 2026-10-18 (their versions
|
|
are in the vector sources); the expected verdicts are those of our verifiers on them, which the identity component measured against
|
|
the libraries' own verdicts in both directions. A verifier that resolves the issuer key from `iss` or `x5c`, fetches the status list
|
|
or the type metadata, or takes an untyped token by default, is judged here on inputs where all of that is already brought.
|
|
- **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).
|