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?
Use SDK 2.2.0. 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 | 50 |
| Enrolment | Your proof-of-possession message | 2 |
| Webhooks | Incoming notification digest, destination, application, time, signature and key purpose | See the published index |
Webhook vectors use their supplied verification time and raw bytes. Compare the expected accept or reject result before using a verifier with real deliveries.
/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).
- Build the signature base from
request.method,request.authority,request.path,request.canonicalQuery(the query without its?),request.anisDate,request.nonce,request.idempotencyKeyand the body inrequest.bodyBase64, withkey.keyIdas the key id. - Compare your
Content-Digestwithexpected.contentDigest, yourSignature-Inputwithexpected.signatureInput, and your base withexpected.signatureBaseUtf8— byte for byte. - Sign your base with the test private key (
key.privateKeyPkcs8Base64) and check thatkey.publicJwkverifies 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 and request.signature), 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
inside its window must be accepted, an answer created before its key’s notBefore must be rejected, and a key
document carrying a private member must be refused whole.
RS-112 tests an active key and RS-113 a retired key with created < notBefore: both must be rejected as
key_not_yet_valid.
The answer vectors test the verification rules. Some use a read of the profile as their example answer; Anis does not sign profile answers — it signs the answers of these calls (orders, reveals, enrolment, the signature self-check, application suspension and key revocation), and those are the answers your verifier applies these rules to.
Testing the enrolment proof
Each enrolment vector gives what the key submission returned (keySubmissionResult) and the test key (key).
- Build the proof message from
keySubmissionResultand compare it withexpected.proofMessageUtf8— and the challenge’s hash withexpected.challengeHashHex. - Sign it with the test key;
exampleSignatureis one valid signature, andkey.publicJwkverifies both. expected.proofRequestis 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.
Checking the v2 answer profile
SDK 2.0.1 and later require tag="anis-response-v2". The covered order is "@status", "content-digest", "x-request-id", then "signature-input";req and "signature";req when the request was signed, then location, retry-after, idempotency-replayed and cache-control when present. "signature";req is the request’s raw signature field exactly as received.
Test status, notBefore and retiredAt in keys[] and the revoked[] list: a compromised key is rejected as key_compromised; a retired key with created > retiredAt as key_retired; any key — active, next or retired — with created < notBefore as key_not_yet_valid. A missing or different tag is profile_unexpected; a different covered list is covered_components_mismatch. Test the exact time boundaries too.
Retired keys remain listed for 90 days after retiredAt. Anis announces a compromise within 24 hours by email and in revoked[], where the entry remains for 90 days. Follow the verification rules; the vector copy must match the v2 profile before you use it to certify your client.