Signing test vectors

A signature is right to the byte or it is refused, and a refusal never says which byte. These vectors let you find the byte yourself: each one is a real request, answer or enrolment proof with the exact text that was signed and the exact result expected.

Using the .NET SDK?

It already passes every vector. This page is for a client written by hand — or by an AI assistant.

Test keys only

The keys in these files are test keys — never Anis’s or any partner’s real keys — and partners.anis.ly is an example address. Use them to test, never to enrol.

What is here

Set What it tests Vectors
Requests Your signature base, byte for byte, for every kind of signed call 9
Answers Your answer checks: which answers to accept and which to throw away 39
Enrolment Your proof-of-possession message 2

/vectors/index.json lists every vector with its title, its file and — for answers — whether it must be accepted or rejected. The same index names the signed parts of each kind of call.

Testing request signing

Each request vector carries the request (request), the signature’s times (signature.created, signature.expires), the test key (key) and what must come out (expected).

  1. Build the signature base from request.method, request.authority, request.path, request.canonicalQuery (the query without its ?), request.anisDate, request.nonce, request.idempotencyKey and the body in request.bodyBase64, with key.keyId as the key id.
  2. Compare your Content-Digest with expected.contentDigest, your Signature-Input with expected.signatureInput, and your base with expected.signatureBaseUtf8byte for byte.
  3. Sign your base with the test private key (key.privateKeyPkcs8Base64) and check that key.publicJwk verifies it. An ECDSA signature is different every time, so compare the base, never the signature bytes.

RQ-03 gives the host as a caller might type it (request.authorityAsGiven) and as it must be signed (request.authority, lower-case). RQ-06 is a reveal: no body at all, so the digest of zero bytes.

Testing answer verification

Each answer vector carries the answer (response.status, response.headers, response.bodyBase64), the Signature-Input of the request it answers (request.signatureInput), Anis’s published keys at that moment (signingKeys) and the time to use as your clock (verifyAt, Unix seconds — use it, not the real clock).

Your verifier must reach expected.outcome: accept or reject. For a rejection, expected.reason says why; your own error names may differ, so match the outcome and use the reason to understand a mismatch. signatureBaseForDebugging is the base Anis signed.

Most answers come in families: one that must be accepted (RS-001-…) and changed copies of it (RS-001-…-M01 onward) that must each be rejected — one body byte changed, a rewritten digest, a DER signature, a signed part missing, a time out of the window. Others test the published keys: an answer signed by the next or the retiring key must be accepted, and a key document carrying a private member must be refused whole.

Testing the enrolment proof

Each enrolment vector gives what the key submission returned (keySubmissionResult) and the test key (key).

  1. Build the proof message from keySubmissionResult and compare it with expected.proofMessageUtf8 — and the challenge’s hash with expected.challengeHashHex.
  2. Sign it with the test key; exampleSignature is one valid signature, and key.publicJwk verifies both.
  3. expected.proofRequest is the body to send.

After the vectors pass

Call Check your signature with your real key. It runs every check Anis runs on a signed call, reports what Anis saw, and changes nothing.