Test vectors
Status: informative. Document version 1.0-draft-1. Every file in this folder except this page is generated by the reference implementation, and its build fails if any differs from what the code produces. Do not edit them by hand.
These vectors let an implementation check, offline and without an Autonym account, that it canonicalises, signs and verifies the A5.4 signed objects exactly as the specification requires: disclosures (A5.3), receipts (A9), purge instructions and acknowledgements (A10).
The keys are synthetic and published with their private parts. They were generated for these vectors and the media worked example only, belong to no provider or recipient, and must never be trusted anywhere else. Anyone can sign anything with them, so a signature they verify proves nothing outside these tests.
Files
| File | What it proves | Checks |
|---|---|---|
keys.json |
Two provider keys, one retired with exp, and one recipient key, each with its private part. key_sets gives their public parts as the provider's and the recipient's key sets, the form key-set.json defines; the retired key stays in the set with its exp. Each kid is the RFC 7638 thumbprint |
KEY-01 |
jcs.json |
RFC 8785 canonical JSON: member names sorted by their 16-bit code units, characters outside ASCII, escapes, integers and the number form, each with the exact canonical text, its UTF-8 in hexadecimal and its SHA-256; and inputs strict parsing refuses, including numbers beyond what binary64 holds | SIGN-01 |
disclosure.json |
Signed disclosures: one signed by the active provider key, one signed by the later-retired key before its exp, and the edge of the 60-second clock allowance |
SIGN-01, KEY-01 |
receipt.json |
A receipt hash chain across a key rotation, each receipt with its provider, the canonical request derived from the request as received, the hash of the disclosure it records and its hash input. Both verify a year later from the provider's published key set alone |
SIGN-01, LOG-01, LOG-02, KEY-01 |
purge.json |
The purge instruction token, a compact JWT signed by the provider, and the recipient's signed acknowledgement over its canonical JSON | SIGN-01, PURGE-01 |
negative.json |
Signed objects a verifier must reject, each breaking one A5.4 rule: tampering, duplicate members, numbers and strings that are not I-JSON, alg:none and HS256, unknown and ambiguous key IDs, crit and other header members, wrong typ, unusable keys, retired keys, expiry, time bounds, issuer binding and receipt hashes |
SIGN-01, KEY-01, LOG-01, LOG-02, PURGE-01 |
Using a vector
A detached-signature vector gives typ, the exact received text (the object with its proof), and verify: the key set (a name from key_sets in keys.json, or an inline key set when the vector needs one that key-set.json refuses), the trusted issuer and the verifier's clock reading now. Verify text as A5.4 describes and compare the outcome with expected: either valid: true with the kid and the signed time, or valid: false with a reason.
A positive vector also shows each step: the object without proof (unsigned), its canonical text (jcs) and that text's SHA-256, the protected_header and the detached jws. A receipt vector adds its hash input and chain link, its request as received with its canonical form, and the disclosure whose hash it records.
The purge instruction token is stored as its three parts in the RFC 7515 flattened JSON form; a recipient receives protected.payload.signature. Verify it at its iat with the provider's key set: the header has exactly alg, kid and typ:autonym-purge+jwt, and the payload is claims.
The reasons in negative.json and jcs.json are labels for the rule each vector breaks. An implementation need not use them; it must reject every negative vector.
| Reason | A5.4 rule |
|---|---|
duplicate_member, not_i_json, malformed_json |
Parse strictly before canonicalisation: repeated member names, in the object or its protected header; text that is not I-JSON; text that is not JSON |
malformed_proof |
The proof member and the detached JWS: unpadded base64url in its one canonical spelling |
critical_header, header |
No protected header member other than alg, kid and typ; crit named separately |
algorithm |
Only ES256 |
type |
typ compared exactly |
header_mismatch |
Proof metadata agrees with the protected header |
unknown_key, key_id |
Exactly one trusted key has the header's kid, and the kid is its key's thumbprint |
key |
The matching key follows key-set.json: no private member, no key_ops, no lifetime member other than exp, canonical spellings |
signature |
The ES256 signature over the canonical bytes; RFC 7518 section 3.4 makes it exactly 64 bytes |
retired |
The signed time is before the key's exp |
issuer |
The object's provider is the trusted issuer |
hash |
A receipt's hash is the A9 hash of its content |
time_bounds, not_yet_valid, expired |
A disclosure's deadline, issue time and expiry |
A verifier may also refuse a member named __proto__; no vector depends on whether it does.
What is not covered
The vectors cover canonical JSON and the signed objects listed above. They do not cover the signed export (A11.1), whose receipts are represented here only by the carried receipt in negative.json, the optional encrypted export (A11.2), purge-token rejections, or anything a provider must do at run time, such as checking current grant authority.
Stability
The files change only deliberately. Regenerating them reproduces the same bytes: the keys are kept, and because ES256 signatures are randomised, each signature is kept while it still verifies over its object, so only a change to a signed object signs it afresh. The reference implementation's tests pin each file's SHA-256, so a vector changes only by a deliberate edit. An implementation identifies the bytes it tested against by their digests in the edition manifest, and any change to a vector is recorded in the change history.
The reference implementation verifies every vector with its own verifier and, independently, with Python's jwcrypto and rfc8785 packages, reading only these files.
Checking the files
Every file of the test vectors, as published in edition 1.0-draft-1. Download them without changing them: the edition manifest identifies each file by its SHA-256, so a reformatted, minified or re-encoded copy is a different file.
| File | Bytes | SHA-256 |
|---|---|---|
disclosure.json |
22575 | 1a319ac2ba86f7ffc0725e0e7790552fff083d5c53c7a73dc23e39805ee9f765 |
jcs.json |
11872 | 0e6966918b3aa47a43672f36dd43c3b638f987738f9d0117a770c669847490de |
keys.json |
3849 | ee56df0c7b3033beba0a5e8a726ff636fee91f0ad1333701c98909a6a85bb835 |
negative.json |
87309 | 883f90b639fd77925f006f88a7d6e3b053bb3b8a0a3008fd15b50e472c20ea39 |
purge.json |
4386 | 452a0b7c1519094f99ba8a3d41c0a38f0c07df2f94d6df4dab74ff5946c0cac5 |
receipt.json |
12731 | a7b95204207f9cbfb45430a83092e4822dd43eca1e5b5ef4941d14f0eff62cee |
To fetch them and check every digest against the edition manifest, with curl, jq and sha256sum:
base=https://autonym.dev/downloads/autonym-standard/1.0-draft-1
for f in disclosure jcs keys negative purge receipt; do curl -sfO "$base/conformance/vectors/$f.json"; done
curl -sf "$base/manifest.json" | jq -r '.files[] | select(.path | test("^conformance/vectors/[^/]+\\.json$")) | "\(.sha256) \(.path | ltrimstr("conformance/vectors/"))"' | sha256sum -cEach file should print OK. Then verify every vector with your own JOSE and RFC 8785 implementation, as using a vector describes: each positive vector verifies with the key ID and signed time it gives, and each negative vector is rejected. The independent verifier described above ships with the reference implementation, which is not released yet, so it is not published here.
References to other standards and projects describe intended technical alignment only and do not imply endorsement or partnership.