From 082b89c5bde7b8a22d22d9b8485a43615f993f1f Mon Sep 17 00:00:00 2001 From: Sakina Roufid Date: Sat, 29 Aug 2026 03:58:59 -0400 Subject: [PATCH] fix(signatures): cover ucp-agent and signature-agent when signing The REST request signing pseudocode builds a component list that leaves out two components the verification pseudocode requires, so a signer written from it produces signatures the spec's own verifier rejects with coverage_insufficient. Three problems in the same block: * `ucp_agent` is tested on the component line but is not a parameter of `sign_rest_request`, and `ucp-agent` is missing from the headers passed to `build_signature_base`. UCP-Agent is required on every request, so verification always demands `ucp-agent` coverage. * `signature-agent` is never added, although the component table, WBA Interop step 3 and the verification routine all require it whenever the Signature-Agent header is present. * the body check reads `body`, but the parameter is `body_bytes`. Add the two parameters, append `signature-agent` with the `;key` label used by the worked example above, and pass both headers through to the signature base. No normative requirement changes here; this only makes the example agree with the component table, the WBA Interop rules and the verification routine. The conformance suite currently skips grading `ucp-agent` coverage because of the same class of inconsistency elsewhere in the spec (#659). --- docs/specification/signatures.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/specification/signatures.md b/docs/specification/signatures.md index 88683e25b..982008950 100644 --- a/docs/specification/signatures.md +++ b/docs/specification/signatures.md @@ -450,7 +450,8 @@ verification. **Signature Generation:** ```text -sign_rest_request(method, path, query, body_bytes, idempotency_key, private_key, kid): +sign_rest_request(method, path, query, body_bytes, ucp_agent, signature_agent, + idempotency_key, private_key, kid): // 1. Compute body digest (if body present) if body_bytes: digest = sha256(body_bytes) // Hash raw bytes, no canonicalization @@ -459,9 +460,11 @@ sign_rest_request(method, path, query, body_bytes, idempotency_key, private_key, // 2. Build component list components = ["@method", "@authority", "@path"] if query: components.append("@query") + // WBA-shape: bind the key source, tagged with the signature label + if signature_agent: components.append("signature-agent;key=\"sig1\"") if ucp_agent: components.append("ucp-agent") if idempotency_key: components.append("idempotency-key") - if body: components.extend(["content-digest", "content-type"]) + if body_bytes: components.extend(["content-digest", "content-type"]) // 3. Build signature base (RFC 9421) signature_base = build_signature_base( @@ -470,6 +473,8 @@ sign_rest_request(method, path, query, body_bytes, idempotency_key, private_key, path=path, query=query, headers={ + "ucp-agent": ucp_agent, + "signature-agent": signature_agent, "idempotency-key": idempotency_key, "content-digest": digest_header, "content-type": "application/json"