// Writes the vectors in contracts/vectors/ from the seller's own code, with throwaway keys. // // Positive vectors are what the seller issues today — a quote JWS, a receipt JWS, the ledger // entries of one search and one checkout — and what it accepts: a request envelope, a key // attestation (JWT-SVID signed) and a cart mandate, each with the public key that verifies // it, so a second implementation can verify these before it verifies anything live. They // are generated IN THIS REPOSITORY; the contracts plan (architecture v0.2 §8) asks for one // positive example per message type encoded independently of the seller's code, and these // are the reference to encode against, not that independent encoding. // // Negative vectors R-21, R-22 and R-23 are tokens built to be refused, with the key set they // were presented against and the expected reason code. // // node contracts/generate-vectors.mjs import { mkdir, writeFile } from 'node:fs/promises'; import { mkdtemp } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { createApp } from '../src/app.mjs'; import { mintAuthorization, publicJwks, thumbprint } from '../src/authorization.mjs'; import { loadConfig } from '../src/config.mjs'; import { b64url, hmac, sha256 } from '../src/shared.mjs'; const out = new URL('./vectors/', import.meta.url); await mkdir(new URL('./positive/', out), { recursive: true }); await mkdir(new URL('./negative/', out), { recursive: true }); const temp = await mkdtemp(join(tmpdir(), 'agent-bench-vectors-')); const now = () => Math.floor(Date.now() / 1000); async function keypair() { const pair = await crypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, true, ['sign', 'verify']); const priv = await crypto.subtle.exportKey('jwk', pair.privateKey); const pub = await crypto.subtle.exportKey('jwk', pair.publicKey); const kid = await thumbprint(pub); const key = await crypto.subtle.importKey('jwk', { ...priv, key_ops: ['sign'], ext: false }, { name: 'ECDSA', namedCurve: 'P-256' }, false, ['sign']); return { priv: { ...priv, kid }, pub: { kty: pub.kty, crv: pub.crv, x: pub.x, y: pub.y, kid }, kid, sign: async (header, payload) => { const input = `${b64url(JSON.stringify(header))}.${b64url(JSON.stringify(payload))}`; return `${input}.${b64url(new Uint8Array(await crypto.subtle.sign({ name: 'ECDSA', hash: 'SHA-256' }, key, new TextEncoder().encode(input))))}`; } }; } const SELLER = 'https://travel.example.test'; const AUTHORITY = 'https://authority.example.test'; const BUYER_DOMAIN = 'buyer.example.test'; const buyerSpiffe = `spiffe://${BUYER_DOMAIN}/agent/i-1`; const issuer = await keypair(); // the self-issued authorization issuer (demo scripts) const authority = await keypair(); // the external authority const buyerKey = await keypair(); // the buyer's stable key const buyerIssuer = await keypair(); // the buyer's JWT-SVID issuing key (cut-order fallback signer) const env = { DEMO_MODE: 'simulation', AUTHORIZATION_PUBLIC_JWKS: JSON.stringify(publicJwks([issuer.pub])), AUTHORIZATION_ISSUER: AUTHORITY, AUTHORIZATION_AUDIENCE: SELLER, MERCHANT_DOMAIN: 'travel.example.test', MERCHANT_ID: 'demo-travel-seller', CURRENCY: 'EUR', PUBLIC_BASE_URL: SELLER, MANDATE_ISSUERS: AUTHORITY, AUDIT_LOG_PATH: join(temp, 'ledger.ndjson'), INSTANCE_KEY_PATH: join(temp, 'instance.json') }; // The buyer's and the authority's published documents, as the seller fetches them. const attestation = await buyerIssuer.sign({ alg: 'ES256', typ: 'key-attestation+jwt', kid: buyerIssuer.kid }, { iss: buyerSpiffe, sub: buyerKey.kid, iat: now(), exp: now() + 3600, cnf: { jwk: { kty: buyerKey.pub.kty, crv: buyerKey.pub.crv, x: buyerKey.pub.x, y: buyerKey.pub.y } }, spiffe_id: buyerSpiffe }); const buyerBundle = { keys: [{ ...buyerIssuer.pub, alg: 'ES256', use: 'jwt-svid' }] }; const authorityJwks = publicJwks([authority.pub]); const statuses = new Map(); const fetchImpl = async (url) => { const target = String(url); const json = (body, status = 200) => ({ ok: status < 300, status, json: async () => body }); if (target === `https://${BUYER_DOMAIN}/.well-known/agent-keys.json`) return json({ attestations: [attestation] }); if (target === `https://${BUYER_DOMAIN}/.well-known/spiffe-bundle.json`) return json(buyerBundle); if (target === `${AUTHORITY}/.well-known/jwks.json`) return json(authorityJwks); if (target.startsWith(`${AUTHORITY}/mandates/`)) { const jti = decodeURIComponent(target.slice(`${AUTHORITY}/mandates/`.length)); return json({ jti, status: statuses.get(jti) || 'unknown' }, statuses.has(jti) ? 200 : 404); } return json({}, 404); }; const config = loadConfig(env); const minter = loadConfig({ ...env, AUTHORIZATION_PRIVATE_JWK: JSON.stringify(issuer.priv) }); const app = createApp(config, { fetch: fetchImpl }); const correlation = crypto.randomUUID(); // --- the self-issued path: what the demo scripts do today ----------------------------------- const claims = { iss: AUTHORITY, aud: SELLER, sub: 'person-7f3a', jti: crypto.randomUUID(), exp: now() + 600, scope: ['commerce:search', 'commerce:purchase'], grant: { merchant_id: 'demo-travel-seller', currency: 'EUR', max_amount: 30000, approved_at: new Date().toISOString() } }; const token = await mintAuthorization(minter, claims); const post = (path, body, bearer = token) => app.route({ method: 'POST', path, headers: { authorization: `Bearer ${bearer}`, 'content-type': 'application/json' }, rawBody: JSON.stringify(body), body, requestId: crypto.randomUUID() }); const search = (await post('/v1/commerce/flight-search', { origin: 'MAD', destination: 'LHR', departure_date: '2026-10-12', correlation_id: correlation })).body; const option = search.options[0]; const checkout = (await post('/v1/commerce/checkout', { quote_id: option.id, quote_hash: option.quote_hash, merchant_id: 'demo-travel-seller', currency: 'EUR', correlation_id: correlation })).body; const identity = (await app.route({ method: 'GET', path: '/.well-known/spiffe-svid.json', headers: {}, rawBody: '', body: {} })).body; // --- the signed-envelope path: what the buyer and the authority will do ------------------- const envelopeCorrelation = crypto.randomUUID(); async function envelopeCall(path, body) { const rawBody = JSON.stringify(body); const payload = { iss: buyerSpiffe, aud: SELLER, iat: now(), nonce: crypto.randomUUID(), correlation_id: envelopeCorrelation, method: 'POST', path, body_sha256: sha256(rawBody) }; const jws = await buyerKey.sign({ alg: 'ES256', typ: 'request+jwt', kid: buyerKey.kid }, payload); const result = await app.route({ method: 'POST', path, headers: { authorization: `Bearer ${jws}`, 'content-type': 'application/json' }, rawBody, body, requestId: crypto.randomUUID() }); return { jws, payload, rawBody, result }; } const envSearch = await envelopeCall('/v1/commerce/flight-search', { origin: 'MAD', destination: 'LHR', departure_date: '2026-10-12', correlation_id: envelopeCorrelation }); const envQuote = envSearch.result.body.options[1]; const mandateJti = crypto.randomUUID(); statuses.set(mandateJti, 'active'); const mandatePayload = { iss: AUTHORITY, sub: 'person-7f3a', act: { sub: buyerSpiffe }, aud: SELLER, merchant_id: 'demo-travel-seller', action: 'purchase', amount: { value: envQuote.total, currency: envQuote.currency }, quote_hash: envQuote.quote_hash, nbf: now() - 5, exp: now() + 300, jti: mandateJti, cnf: { jkt: buyerKey.kid }, policy_version: '2026-09-10', stage: 'cart' }; const mandate = await authority.sign({ alg: 'ES256', typ: 'mandate+jwt', kid: authority.kid }, mandatePayload); const envCheckout = await envelopeCall('/v1/commerce/checkout', { quote_id: envQuote.id, quote_hash: envQuote.quote_hash, merchant_id: 'demo-travel-seller', currency: 'EUR', correlation_id: envelopeCorrelation, mandate_jws: mandate }); if (envCheckout.result.status !== 200) throw new Error(`envelope checkout did not pass: ${JSON.stringify(envCheckout.result.body)}`); const ledger = (await app.route({ method: 'GET', path: '/ledger', headers: {}, rawBody: '', body: {} })).body; const write = (name, data) => writeFile(new URL(name, out), JSON.stringify(data, null, 2) + '\n'); const generated = { generated_at: new Date().toISOString(), generated_by: 'agent-bench contracts/generate-vectors.mjs (generated in the seller repository; see README)' }; await write('positive/quote.json', { ...generated, description: 'A quote+jwt from search_flights, the instance key that signed it, and the quote_hash over its payload octets. Verify the JWS with verifying_jwk; recompute quote_hash as SHA-256 hex over the base64url-decoded payload.', verifying_jwk: identity.public_jwk, instance_public_jwk: identity.public_jwk, instance_spiffe_id: identity.spiffe_id, jws: option.quote_jws, quote_jws: option.quote_jws, quote_hash: option.quote_hash, search_response: search }); await write('positive/receipt.json', { ...generated, description: 'A receipt+jwt from create_checkout on the self-issued path, signed by the same instance key, citing the quote hash, the (simulated) payment intent and the ledger entry.', verifying_jwk: identity.public_jwk, instance_public_jwk: identity.public_jwk, jws: checkout.receipt_jws, receipt_jws: checkout.receipt_jws, checkout_response: checkout }); await write('positive/ledger.json', { ...generated, description: 'The seller ledger after a self-issued search and checkout and an envelope search and checkout: quote.created (four), payment_intent.created (two), checkout.completed (two, each naming its rail and citing the intent entry and the receipt), a hash chain over JCS, each entry signed by the instance key. Recompute every hash and verify every signature with instance_public_jwk. The envelope entries name the buyer instance and, at checkout, the person (hashed), the authority and the mandate jti.', canonicalization: ledger.canonicalization, instance_public_jwk: identity.public_jwk, signer: ledger.signer.spiffe_id, events: ledger.events }); await write('positive/request-envelope.json', { ...generated, description: 'A request+jwt over the checkout body below, signed by the buyer stable key; verify with verifying_jwk (= the key the attestation vouches for), then check body_sha256 against SHA-256 of raw_body.', verifying_jwk: buyerKey.pub, jws: envCheckout.jws, payload: envCheckout.payload, raw_body: envCheckout.rawBody, seller_response: envCheckout.result.body }); await write('positive/key-attestation.json', { ...generated, description: 'A key-attestation+jwt for the buyer stable key, signed by the buyer domain\'s JWT-SVID issuing key (the cut-order fallback signer; the X509-SVID form carries x5c instead). Verify with verifying_jwk, which is the use: jwt-svid entry of spiffe_bundle; then check that sub is the thumbprint of cnf.jwk.', verifying_jwk: buyerIssuer.pub, jws: attestation, agent_keys: { attestations: [attestation] }, spiffe_bundle: buyerBundle }); await write('positive/mandate.json', { ...generated, description: 'A cart-stage mandate+jwt bound to the quote below and to the buyer key, signed by the authority; verify with verifying_jwk (= the authority key set entry). The seller accepted it: seller_response.', verifying_jwk: authority.pub, jws: mandate, payload: mandatePayload, authority_jwks: authorityJwks, quote: envQuote, seller_response: envCheckout.result.body }); // Negative vectors. Each is presented against the same key set and must be refused with // the expected code before any signature is checked. const keySet = publicJwks([issuer.pub]); const body = b64url(JSON.stringify(claims)); const negative = [ { id: 'R-21', name: 'alg: none', token: `${b64url(JSON.stringify({ alg: 'none', typ: 'JWT', kid: issuer.pub.kid }))}.${body}.${b64url('x')}`, expected: { code: 'header_forbidden', step: 2, entry: 'request.refused' } }, { id: 'R-22', name: 'HS256 with the public key as the secret', token: (() => { const h = b64url(JSON.stringify({ alg: 'HS256', typ: 'JWT', kid: issuer.pub.kid })); return `${h}.${body}.${b64url(hmac(JSON.stringify(issuer.pub), `${h}.${body}`))}`; })(), expected: { code: 'header_forbidden', step: 2, entry: 'request.refused' } }, { id: 'R-23', name: 'jku pointing at a rogue-controlled key set', token: await issuer.sign({ alg: 'ES256', typ: 'JWT', kid: issuer.pub.kid, jku: 'https://rogue.example/jwks.json' }, claims), expected: { code: 'header_forbidden', step: 2, entry: 'request.refused' } } ]; for (const vector of negative) { let observed; try { await app.route({ method: 'POST', path: '/v1/commerce/flight-search', headers: { authorization: `Bearer ${vector.token}`, 'content-type': 'application/json' }, rawBody: '{}', body: { origin: 'MAD', destination: 'LHR', departure_date: '2026-10-12' } }); observed = 'accepted'; } catch (error) { observed = { code: error.code, step: error.step }; } await write(`negative/${vector.id}.json`, { ...generated, ...vector, key_set: keySet, observed_by_seller: observed }); } console.log(`Vectors written to ${out.pathname}`);