draft-ietf-wimse-http-signature

Source files on GitHub

In one line: a profile of HTTP Message Signatures in which a workload signs its HTTP request (and optionally the response) with the key bound to its Workload Identity Token, so the proof survives TLS-terminating proxies.

What this is

Workloads that call each other over HTTP need to authenticate. Mutual TLS is one option, but many deployments prefer application-layer protection, either because they lack Certification Authority infrastructure or because a call crosses several TLS hops (Section 1). This draft defines such an application-layer option as a profile of HTTP Message Signatures ([RFC9421]). The caller sends its Workload Identity Token (WIT) and signs the request with the private key that matches the key in the WIT, which proves possession of that key. The signature covers the method, path, query, selected headers and a Content-Digest of any body, and carries three new signature parameters: wimse-aud for the intended recipient, wimse-sign-response to require a signed response, and wimse-req-nonce to bind a response to its request. The draft does not sign @authority (except for CONNECT), so proxies that rewrite the authority do not break verification. The datatracker states in subject.json are Active, Publication Requested and Submitted to IESG for Publication.

How it works

Diagram
sequenceDiagram
    participant C as Client workload
    participant M as TLS-terminating middlebox
    participant S as Server workload
    C->>C: Build signature base from covered components and parameters
    C->>M: Request with Signature, Signature-Input and Workload-Identity-Token
    M->>S: Forward request over a new TLS connection
    S->>S: Validate WIT, then verify the signature with the WIT key
    S->>S: Check tag, time window, nonce and wimse-aud
    alt authentication fails
        S-->>C: 400 with problem type wimse-signature-invalid
    else signed response required
        S-->>M: Response signed with wimse-req-nonce set to the request nonce
        M-->>C: Forward response
        C->>C: Verify signature and compare wimse-req-nonce
    else signing not required
        S-->>C: Response, optionally signed
    end
  1. The client builds the signature base. Under Section 3 it covers @method, @path, @query and, when present, Content-Type, Content-Digest, Authorization, Txn-Token and Workload-Identity-Token. It sets created, expires, nonce, tag (the value wimse-workload-to-workload) and wimse-aud, and optionally wimse-sign-response. It must not set keyid or alg.
  2. The client sends the request with the WIT and the signature. The draft's own text says TLS-terminating middleboxes can see the cleartext WIT and signature (Section 5.2), but cannot change covered components without breaking verification.
  3. The server validates the WIT as specified in the workload credentials draft before validating the signature, and must not process the message further until it is fully validated (Section 3).
  4. The server finds the WIMSE signature by its tag value, not by label, and rejects the message if more than one signature carries that tag. It checks the validity window and checks wimse-aud against its trusted configuration using Simple String Comparison (Sections 3, 3.2).
  5. On failure the server returns 400 with the wimse-signature-invalid problem type and does not say which check failed (Section 3.6).
  6. If the request carried wimse-sign-response set to true, or local policy requires it, the server signs the response, covering @status, the request components with the req parameter, Content-Type, Content-Digest and Workload-Identity-Token, and sets wimse-req-nonce to the request's nonce (Sections 3.4, 3.5). If it cannot sign, it returns an error instead of an unsigned success.
  7. The client verifies a signed response and compares wimse-req-nonce with its own nonce. It must reject an unsigned response when it asked for signing (Section 3.5).

Use cases

  • Service-to-service calls that pass through a TLS-terminating load balancer or API gateway, where the end-to-end proof must still verify at the destination (Sections 1, 5.2).
  • Clients that must detect a stripped or substituted response, using wimse-sign-response together with wimse-req-nonce (Sections 3.3, 3.5, Appendix A.4).
  • Proxy tunnelling with the CONNECT method, where the profile now covers @authority instead of @path and @query (Section 3).
  • Deployments with several audience names for one service, which must list each acceptable string in the recipient's configuration because comparison is byte-exact (Section 3.2).
  • Implementers and testers who want to check conformance against the independent test vectors for this revision in issue #419.

Why This Matters Now

  • Trigger: subject.json records that three new issues target this draft: test vectors (#419), nonce entropy in examples (#420) and editorial comments (#417). The draft is also at Publication Requested and Submitted to IESG for Publication, so wording changes now are close to final.
  • Source evidence: The -08 diff adds a CONNECT exception, Simple String Comparison for wimse-aud, a 64-bit minimum nonce entropy, uniform error handling with three registered problem types, and a shepherd-review pass (Appendix B.1). On 6 November 2026 an individual participant posted a test vector set for -08 in issue #419 (6 positive and 22 negative cases, plus 3 observations) and announced it on the WIMSE list. After Yaron Sheffer's review on the issue and an independent run by another participant, the author regenerated P-01, P-02, P-06 and N-15 on 7 November, because their @query values lacked the leading ?. Kathleen Moriarty sent an editorial review of the introduction on 5 November, which Yaron Sheffer recorded as issue #417.
  • Expected impact: Verifiers can now be tested against shared inputs, and several behaviours that were SHOULD or implicit become MUST (nonce generation and entropy, error status codes, how wimse-aud is compared). The TLS requirement is not new: it was already a MUST in -07 and only moves from Section 5.1 to Section 3. Existing -07 implementations need to check the nonce, error handling, audience comparison and CONNECT paths. Section 4 (Implementation Status) lists two implementations with last-updated dates but no revision: Cofide (13 November 2025) and wimsey (8 September 2026). The Cofide repository describes itself as an implementation of draft-ietf-wimse-s2s-protocol, the predecessor document, and was last pushed on 14 November 2025, so it predates the -08 changes. On the draft's main branch after -08, the wimsey entry says it covers signed responses "as per version -08 of the draft" (PR #406).

What Changed

  • CONNECT exception (Section 3): senders MUST cover @method and @authority and MUST NOT cover @path or @query. For a signed response to a CONNECT request, @method;req and @authority;req replace the path and query components. The draft keeps omitting @authority for all other methods.
  • @request-target wording: the -07 sentence calling it NOT RECOMMENDED outside HTTP/1.1 is replaced by "covers @path and @query separately rather than @request-target, following Section 2.2.5 of [RFC9421]".
  • wimse-aud (Section 3.2): the recipient MUST match it against its trusted configuration with Simple String Comparison ([RFC3986] Section 6.2.1). A trailing slash or explicit default port does not match, and each acceptable variant must be configured. The recipient MUST NOT derive the audience from request fields such as Host.
  • Nonce (Section 3): senders MUST use a cryptographically secure random number generator and at least 64 bits of entropy, with 96 bits or more RECOMMENDED.
  • Time window (Section 3): recipients MUST reject signatures outside their validity window. This sentence replaces the MUST that was in Security Considerations.
  • TLS (Section 3): the MUST for a server-authenticated TLS connection and client hostname validation ([RFC9525]) moves from Section 5.1 to the protocol section. A new SHOULD points to BCP 195.
  • Content-Digest (Section 3) now cites [RFC9530], which is a new normative reference together with [RFC3986] and BCP 195.
  • Error handling (Section 3.6): authentication failure MUST give 400 with wimse-signature-invalid and MUST NOT reveal which check failed. A server that is required to sign but does not support signing MUST give 501 with wimse-signed-response-unsupported. A server that supports signing but cannot sign this response MUST give 400 with wimse-signed-response-unavailable. In -07 a failed verification only said that a status code "such as 400" is appropriate, and a server that could not provide a required signed response SHOULD return 400 or 501. Problem details bodies are SHOULD, unless the peer is known not to accept them.
  • wimse-sign-response (Section 3.3): when true, the server MUST sign or return an error.
  • IANA (Section 7.2): three HTTP Problem Types registrations are added.
  • Editorial restructuring: Security Goals move from the body (Section 6 in -07) to Appendix A, which reletters Document History (now Appendix B) and the DPoP comparison (now Appendix C). Privacy Considerations, a subsection of Security Considerations in -07, becomes its own Section 6 and shrinks to a pointer to the workload credentials draft. Several Section 5 MUSTs become descriptive text that points back to Section 3. The Section 3.1 algorithm text now says implementations "follow the algorithm requirements" of the workload credentials draft rather than "inherit the requirement that ES256 MUST be supported". The Section 3.2 and Section 3.3 "defines" sentences are reworded.
  • Compatibility and migration: a -07 sender that used a short or low-entropy nonce, a sender or recipient that compared wimse-aud after URI normalization, a server that returned 501 or 400 in some other way when it could not sign, and any CONNECT traffic need changes. Error responses now need the registered problem types unless the peer is known not to accept problem details.

Technical Analysis

  • Threat model implications: The WIT is not a bearer token, but a captured token and signature can be replayed within the signature lifetime (Section 5.1). Short expires, the nonce and an optional replay cache reduce this, and the draft says replay detection is ineffective if an attacker can choose which cluster member validates (Section 5.1, Appendix A.4). Middleboxes see the cleartext WIT and signature and can capture and replay them or change unsigned headers (Section 5.2). The draft says a middlebox cannot change covered components without detection, and that re-signing at an intermediary is out of scope. Binding the audience in a signed parameter instead of @authority is how the draft keeps verification working through authority rewriting (Sections 3, 5.1). Appendix A.4 says undetected deletion of a request and response pair is prevented only when the client required signing with wimse-sign-response and rejects an unsigned response, so server-side local-policy signing alone is opportunistic (Section 3.5).
  • Protocol flow implications: Validation order is fixed: WIT first, then the HTTP message signature, and no further processing before both pass (Section 3). The WIMSE signature is found by tag, which allows other signatures from intermediaries to coexist (Sections 3, 5.2). wimse-sign-response is authoritative over Accept-Signature, which cannot request weaker coverage than the profile (Section 3.5). The algorithm comes from cnf.jwk.alg in the WIT, never from an alg parameter, and the recipient rejects algorithms not acceptable under local policy (Section 3.1). Two error paths for signing are distinguished only by status code and problem type: 501 for unsupported and 400 for unavailable (Section 3.6).
  • Interop concerns: wimse-aud is compared as an exact string, so a sender that uses the default target URI "as known to the sender" must match a configured value byte for byte (Section 3.2). @query must be included even for an empty query, with the value ? (Section 3). Omitting the leading ? in a signature base was a real error in the first version of the issue #419 vectors. Content-Digest must be present and verified whenever a body exists. CONNECT requests and their responses use a different covered set (Section 3). Sections 3 and 3.6 send readers to "Section 5.1.4" of the workload credentials draft for WIT validation, but the cited revision (-02) has no Section 5.1.4; the validation procedure is Section 5.1.3 of workload-creds-03, posted after -08. The Section 3.7 examples use fixed nonces (abcd1111 to abcd4444): eight hex characters, at most 32 bits and not random, so they do not meet the Section 3 minimum and should not be copied (issue #420). The example files are unchanged since -07, and the independent run in issue #419 reports that the Figure 2 signature verifies over ?flavor=vanilla. Problem type URIs are in the IANA Problem Types registry space and require the registration in Section 7.2 to be completed (Section 7.2).

Open Questions

  • Verifier behaviour for keyid and alg (Section 3): the draft states "MUST NOT be used" for senders, and the draft text does not say whether a verifier rejects or ignores a message that carries them. The test vector author recorded "reject" (N-06 and N-07 in issue #419) and asked on the list. Yaron Sheffer, one of the two authors in the -08 header (with Joe Salowey), replied on 7 November 2026 with a personal view: "IMO a MUST NOT in any security RFC should be rejected by the recipient." This is one author's opinion on the list, not text the draft has adopted, and the draft text, including the main branch after -08, does not say so yet.
  • Example nonces (Section 3.7): issue #420, opened by Yaron Sheffer on 7 November 2026 from observation O-2 in #419, asks for an editorial note near the examples that the sample nonces are illustrative and below the entropy minimum, without changing the example generator. The issue is open and the note is not in the draft yet.
  • Nonce format (Section 3): the draft sets entropy bits but not an encoding or length for the nonce string, and does not say how a recipient could check entropy. After -08, the main branch also lets a client use a server-generated nonce from Accept-Signature when it meets the same requirements (PR #404, listed for -09).
  • CONNECT audience (Sections 3, 3.2): the default wimse-aud is the target URI without query or fragment, while a CONNECT target is in authority-form. The draft does not say what the default audience is for CONNECT.
  • Uniform 400 versus a distinct problem type (Section 3.6): the server MUST NOT reveal which check failed, yet a 400 for an unavailable signed response is a separate error. A client that does not accept problem details cannot tell the cases apart, and the draft does not discuss this.
  • Intermediary re-signing (Section 5.2): the draft leaves stripping and replacing the WIMSE signature to deployment policy.
  • Introduction wording (Section 1): Kathleen Moriarty's review (issue #417) proposes saying HTTP in the first sentence and explaining object-level authentication across multiple TLS hops. The issue is open with no decision recorded, and -08 Section 1 does not contain that text.
  • Replay cache scope (Sections 3, 5.1, Appendix A.4): what to remember and for how long is local policy, and replay to a different cluster member can succeed.
  • Appendix C still says future versions may decide whether response signing becomes mandatory.

Experiments

  • Lab 1: Rebuild the signature base for the Figure 2 request and Figure 4 response in Section 3.7.1 using an RFC 9421 library, verify the signatures with the public keys in the WITs, and check that @query is ?flavor=vanilla with the leading ?.
  • Lab 2: Run a verifier against the test vector set in issue #419, using the regenerated P-01, P-02, P-06 and N-15 signatures from the author's 7 November comment, and compare its accept and reject decisions with the expected outcomes, in particular wimse-aud variants (trailing slash, explicit default port), duplicate wimse-workload-to-workload tags, keyid and alg present, and the CONNECT cases. Partly done on 11 November 2026 (see the hands-on log): the 22 accept/reject vectors were run, with most negative inputs reconstructed from the issue's prose, and N-17 and N-20 are still open.
  • Lab 3: Put a TLS-terminating proxy that rewrites Host in front of a server, send a signed request with wimse-sign-response set to true, and check that verification still succeeds, that a server that cannot sign (streamed response) returns 400 with wimse-signed-response-unavailable, and that a server without signing support returns 501 with wimse-signed-response-unsupported.

Sources

Context

Trigger

The subject was picked as an active working-group draft with a new revision to diff: draft-ietf-wimse-http-signature-08. The pick note says three new issues target this draft: test vectors (#419), nonce entropy in examples (#420) and editorial comments (#417). The datatracker states are Active, Publication Requested and Submitted to IESG for Publication.

Why This Matters

The draft is close to the end of the working-group process, so the -08 changes are the last chance to settle behaviour before IESG review. -08 turns several loose points into MUSTs: nonce generation and entropy, the 400 and 501 error behaviour, and Simple String Comparison for wimse-aud. It registers three problem types, moves the existing TLS MUST from Security Considerations into Section 3, and adds a CONNECT exception. Independent test vectors written against -08 were posted in issue #419 and announced on the WIMSE list, and a reviewer sent comments on the introduction. Implementers of -07 need to check what changed, and anyone writing a verifier now has shared inputs to test against.

Open Questions

  • Whether a verifier rejects or ignores keyid and alg when present: the draft only says senders MUST NOT use them (Section 3). Yaron Sheffer's reply on the list (7 November 2026) favours rejecting, but the text does not say so.
  • What the default wimse-aud is for CONNECT, whose target is in authority-form (Sections 3, 3.2).
  • How to tell "unsupported" from "unavailable" signing when the peer does not accept problem details (Section 3.6).
  • When issue #420 lands: it asks for an editorial note that the Section 3.7 example nonces (such as abcd1111) are illustrative and below the 64-bit minimum. It is open.
  • Whether the introduction text proposed in Kathleen Moriarty's review (issue #417) is adopted: the issue is open with no decision, and -08 Section 1 does not contain it.

Spec notes

References

Normative references listed in -08 (Section 8.1):

  • BCP 195 (RFC 8996 and RFC 9325), new in -08.
  • draft-ietf-wimse-workload-creds-02 (Workload Identity Token). Sections 3 and 3.6 cite its "Section 5.1.4" for WIT validation, but -02 has no Section 5.1.4 (its Section 5.1.3 is the note on iss and key distribution). The validation procedure is Section 5.1.3 ("Validating the WIT") of workload-creds-03, posted on 30 October 2026, after -08.
  • RFC 2119 and RFC 8174 (key words).
  • RFC 3986 (Simple String Comparison, Section 6.2.1), new in -08.
  • RFC 7942 (Implementation Status section).
  • RFC 9110 (HTTP Semantics, including CONNECT in Section 9.3.6 and the target URI in Section 7.1).
  • RFC 9421 (HTTP Message Signatures).
  • RFC 9457 (Problem Details for HTTP APIs).
  • RFC 9525 (hostname validation, Section 6.3).
  • RFC 9530 (Digest Fields), new in -08.

Informative references (Section 8.2):

  • draft-ietf-oauth-transaction-tokens-11 (Txn-Token header).
  • draft-ietf-wimse-arch-08 (terminology and architecture). It was normative in -07 and is informative in -08: the draft source changes {{!I-D.ietf-wimse-arch}} to {{?I-D.ietf-wimse-arch}}. The reason is in the shepherd review (issue #385): "Is wimse-arch really normative? Expect informative; if normative, it is a downref."
  • draft-ietf-wimse-mutual-tls-02.
  • draft-ietf-wimse-wpt-02 (the DPoP-inspired alternative).
  • The IANA HTTP Message Signature registry.
  • RFC 9449 (DPoP).
  • RFC 9864 (the fully-specified Ed25519 identifier used in the examples).

The algorithm itself comes from cnf.jwk.alg in the WIT (Section 3.1). -08 sets no baseline of its own and defers to the workload credentials draft, where ES256 MUST be supported by general-purpose implementations and any IANA JOSE signature algorithm other than none, symmetric or encryption algorithms is allowed. Ed25519 is used in the examples but is not mandatory, and the deprecated EdDSA identifier is not excluded by either draft.

Key Changes

  • Section 3: CONNECT exception for requests and signed responses (@method and @authority instead of @path and @query).
  • Section 3: Content-Digest cites RFC 9530. Nonce needs a cryptographically secure random number generator and at least 64 bits of entropy (96 or more RECOMMENDED). Recipients MUST reject signatures outside the validity window. A server-authenticated TLS connection and client hostname validation are required in this section.
  • Section 3.1: algorithm baseline wording changed to "follow the algorithm requirements" of the workload credentials draft.
  • Section 3.2: wimse-aud is compared with Simple String Comparison against the recipient's trusted configuration, and the audience must not be derived from Host.
  • Section 3.3: wimse-sign-response set to true obliges the server to sign or return an error.
  • Section 3.6: 400 with wimse-signature-invalid for any authentication failure, with no indication of which check failed. 501 with wimse-signed-response-unsupported and 400 with wimse-signed-response-unavailable when required signing fails. Problem details are SHOULD.
  • Section 7.2: three HTTP Problem Types registrations.
  • Restructuring: Security Goals move from Section 6 to Appendix A, so Document History becomes Appendix B and the DPoP comparison Appendix C. Privacy Considerations, Section 5.3 in -07, becomes Section 6 and is reduced to a pointer. Section 5 MUSTs were restated as descriptive text that refers to Section 3.
  • Document history for -08 has three entries: the shepherd review (#385), the CONNECT exception (#368) and Simple String Comparison (#396). The nonce, TLS, error-handling, problem-type, Privacy, appendix and wimse-arch changes are not itemised there, but each matches a point in Justin Richer's shepherd notes in issue #385, which was closed on 20 October 2026 with every item ticked.
  • Metadata: expiry moved to 23 April 2027 and the second author's affiliation changed from Intuit to Independent.

Impact Analysis

  • Upstream: depends on RFC 9421 for the signature mechanics, on the WIMSE workload credentials draft for the WIT and its validation, and on RFC 9457 and the HTTP Problem Types registry for error bodies. The new reference to RFC 9530 affects how Content-Digest is produced.
  • Downstream: the Cofide (proof-of-concept) and wimsey (alpha) implementations listed in Section 4, and test authors using the issue #419 vector set, need to follow the -08 rules. Section 4 gives only last-updated dates (13 November 2025 and 8 September 2026). The Cofide repository says it implements draft-ietf-wimse-s2s-protocol and was last pushed on 14 November 2025, before -08. After -08 the main branch updates the wimsey entry to "signed responses as per version -08 of the draft" (PR #406, merged 23 October 2026). IANA must register three signature metadata parameters and three problem types.
  • Compatibility: -08 is not shown to change the wire format of a correct -07 message, except for CONNECT, but it tightens what senders must produce (CSPRNG nonces with at least 64 bits of entropy), how verifiers compare wimse-aud (exact string match) and what servers must return (400 or 501 with problem types). Rejecting signatures outside the validity window was already a MUST in -07 (then in Section 5.1). Senders that relied on normalized audience comparison, short nonces or other error codes can fail. The draft does not discuss a transition period.

Test Vector Re-run

On 11 November 2026 this repository ran the 28 vectors in issue #419 through a verifier written from the -08 text and RFC 9421, using the regenerated P-01, P-02, P-06 and N-15 values from the 7 November comment. The issue gives literal bytes only for the positive vectors and N-15. For the other negative vectors it describes a change to a positive vector, so the script builds those inputs itself, and they are this repository's reconstructions, not the issue's bytes. The script is vectors/verify_419.py and its output is vectors/verify_419.log. The steps to reproduce it are in the hands-on log. As a check on the verifier itself, it rebuilds the signature base and re-signs with the published test keys, and all 8 published signatures come out byte for byte the same (log section 1).

  • Inputs: P-01 to P-06 and N-15 are the published messages, and the script reproduces their 8 signatures byte for byte. N-04 (body swapped) and N-09 (later verification time) use a published message with its original signature, and N-14 is the published P-02 response with its signature removed. N-01, N-02, N-03, N-05, N-06, N-07, N-08, N-10, N-11, N-12 and N-13 apply the change and re-sign with the published test keys. N-21 is built from the issue's prose, which gives no values, and is signed the same way. N-16 to N-22 describe behaviour and are run as checks on the script's own sender, server and verifier logic.
  • Result: all 22 accept/reject vectors (P-01 to P-06, N-01 to N-15, N-21) got the expected decision from this verifier. N-16, N-18, N-19 and N-22 describe sender, server or verifier behaviour. The script runs them against its own nonce generator, response checker and signature selection, so a match there only shows that the expected outcomes agree with the -08 text as the script reads it. It says nothing about another implementation. N-17 (the error must not reveal which check failed) and N-20 (verify the WIT before doing anything else) cannot be judged from a single message and were not scored.
  • N-04: the issue says the changed body "also makes the signature fail". In the run it does not: only the body changes, Content-Digest keeps its original value, and that header is what the signature covers. The only failure the verifier reports is the digest recomputed from the received body (Section 3: "the receiver MUST compute the Content-Digest value for the message content received and compare it with the Content-Digest value provided in the message").
  • N-09: verification time T0+3600 equals WIT-A's exp, so the WIT expiry and the signature validity window fail at the same instant. The script repeats the check at T0+301, where only the window check fails and the message is still rejected.
  • N-15: the ES256 signature fails as an invalid signature under the Ed25519 key in cnf.jwk, and verifies under the svcC ES256 key. In a verifier that takes the key from cnf.jwk, the vector therefore never reaches the Section 3.1 "algorithm not allowed by local policy" check.
  • Expected outcomes the -08 text states only as sender rules: N-06 and N-07 (keyid and alg "MUST NOT be used"), N-01 (required components "MUST be signed"), and N-02 and N-21 (CONNECT senders "MUST NOT cover @path or @query"). In this verifier, switching off receive-side enforcement of those rules makes all five of these accept (N-01, N-02, N-06, N-07 and N-21), since their signatures are valid. Whether another verifier rejects them depends on how it reads these sender rules.
  • P-04 signs @authority as svcb.example.com:443. -08 Section 3 says "The @authority value is the host and port of the CONNECT target (Section 2.2.3 of RFC9421)". RFC 9421 Section 2.2.3 says "The component value MUST be normalized according to the rules provided in [HTTP], Section 4.2.3. Namely, the hostname is normalized to lowercase, and the default port is omitted." A CONNECT target in authority form has no scheme, and neither text says whether 443 counts as its default port, so implementations can disagree on this value.

Interop checklist

  • Request covers @method, @path and @query, and does not cover @authority for non-CONNECT methods (Section 3).
  • A request with no query still signs @query with the value ? (Section 3, RFC 9421 Section 2.2.7), and the signature base keeps the leading ? for non-empty queries such as ?flavor=vanilla.
  • For CONNECT, the request covers @method and @authority and not @path or @query, and the signed response covers @method;req and @authority;req and not @path;req or @query;req (Section 3).
  • Content-Type, Content-Digest, Authorization, Txn-Token and Workload-Identity-Token are covered when present, and Workload-Identity-Token is covered on every signed response (Section 3).
  • When a body exists, Content-Digest is present, computed per RFC 9530 over the received content, and compared by the receiver. A missing or mismatched value is rejected (Section 3).
  • created, expires, nonce and tag are present on requests and responses, tag equals wimse-workload-to-workload, and expires is short (Section 3).
  • keyid and alg are absent on send. The verifier behaviour when they are present is not in the draft, so record the choice made (reject or ignore). Yaron Sheffer's reply on the list favours reject, and the issue #419 vectors expect it (N-06, N-07) (Section 3).
  • The WIMSE signature is selected by tag, never by label. A message with two signatures carrying that tag is rejected, and a message with no such tag is treated as carrying no WIMSE signature (Section 3).
  • Nonces come from a cryptographically secure random number generator and carry at least 64 bits of entropy. Do not copy the fixed Section 3.7 nonces such as abcd1111 (Section 3, issue #420).
  • Signatures outside the validity window (created and expires) are rejected, with the clock skew policy documented, because the draft gives no tolerance value (Section 3).
  • The WIT is validated before the signature is checked, and nothing is processed until both pass (Section 3). -08 cites "Section 5.1.4" of workload-creds for this, which does not exist in the cited -02; use Section 5.1.3 ("Validating the WIT") of workload-creds-03.
  • The signature algorithm is taken from cnf.jwk.alg in the WIT, and a locally unacceptable algorithm is rejected (Section 3.1). ES256 verifies, since workload-creds requires general-purpose implementations to support it. Ed25519, used in the Section 3.7 examples, verifies if supported. Decide on purpose how the deprecated EdDSA identifier is handled, since neither draft forbids it.
  • wimse-aud matches a configured value by exact string comparison. A trailing slash and an explicit default port do not match. It is never derived from Host (Section 3.2).
  • The sender's default wimse-aud (target URI without query and fragment) matches the recipient's configured string, or a deployment-specific audience is used when a proxy rewrites the URI (Section 3.2).
  • wimse-sign-response is a Boolean parameter, written as true only when the client requires signing. The client rejects an unsigned response in that case and accepts an unsigned response otherwise (Sections 3.3, 3.5).
  • Every signed response sets wimse-req-nonce to the request's nonce, and the client rejects a missing or different value (Sections 3.4, 3.5).
  • When the client sends Accept-Signature, it lists at least the components the profile requires, and wimse-sign-response wins when the two conflict (Section 3.5).
  • Authentication failure returns 400 with the wimse-signature-invalid problem type, and the response does not reveal whether the WIT or the HTTP message signature failed. 401 is NOT RECOMMENDED for this (Section 3.6).
  • When signing is required but unsupported the server returns 501 with wimse-signed-response-unsupported. When signing is supported but not possible for this response (streamed, very large) it returns 400 with wimse-signed-response-unavailable. It never returns a successful unsigned response (Sections 3.5, 3.6).
  • A client that receives 400 or 501 from a signing failure handles both problem types, and also behaves sensibly when the peer sends no problem details body (Section 3.6).
  • The WIT and the signature travel only over server-authenticated TLS, with client hostname validation per RFC 9525 (Section 3).
  • A proxy that adds its own HTTP message signature does not break WIMSE signature selection, and a proxy that changes a covered component causes verification to fail (Section 5.2).
  • Replay-cache behaviour, if implemented, is documented as local policy, including what happens when a replay reaches a different cluster member (Sections 3, 5.1).
  • When using the issue #419 test vectors, take the P-01, P-02, P-06 and N-15 signatures from the author's 7 November comment. The issue body still shows the original values, computed over an @query without the leading ?.
  • For issue #419 N-04, expect the signature to stay valid and the message to fail only on the recomputed Content-Digest. A verifier that reports N-04 as a signature failure is reporting a failure the vector does not produce.
  • Record the receive-side choice for sender-only rules that #419 expects to be rejected: keyid/alg present (N-06, N-07), a required component missing (N-01), and @path covered on CONNECT (N-02, N-21). Also record whether CONNECT @authority keeps the explicit :443 (P-04).

Hands-on log

Environment

  • Runtime: Python 3.15.2 (printed on the first line of vectors/verify_419.log)
  • Tooling: cryptography 50.0.2 (same line) (Ed25519, ECDSA P-256 with RFC 6979 deterministic signing), gh to fetch the draft source
  • Versions: draft-ietf-wimse-http-signature-08 (tag draft-ietf-wimse-http-signature-08 in ietf-wg-wimse/draft-ietf-wimse-s2s-protocol), issue #419 vectors with the 7 November 2026 regenerated values

Steps

  1. Fetch the sigs-*.txt include files used by the Section 3.7 examples from the draft-ietf-wimse-http-signature-08 tag into a local includes/ directory (gh api repos/ietf-wg-wimse/draft-ietf-wimse-s2s-protocol/contents/<file>?ref=draft-ietf-wimse-http-signature-08 -H "Accept: application/vnd.github.raw").
  2. Run python3 -I vectors/verify_419.py includes/ > vectors/verify_419.log. The vector inputs and test keys from issue #419 are written into the script, so the include files are only needed for the Section 3.7 example check.
  3. Read the summary table at the end of the log, and the per-vector notes for N-01, N-04, N-06, N-07, N-09 and N-15.

Results

  • Section 0 of the log: every test key derives from SHA-256("s01-validation-vectors:<name>") (svcC reduced mod n-1, plus 1), and the three WITs verify under the issuer key.
  • Section 1: the script rebuilds all 8 published signatures (P-01 to P-05, both P-06 signatures, N-15) byte for byte.
  • Most negative vectors are this repository's reconstructions of the change the issue describes, not the issue's bytes. spec-notes lists which vectors used which inputs.
  • No vector disagreed with its expected outcome. 22 accept/reject vectors and 4 behaviour vectors matched. N-17 and N-20 were not scored. The findings are in spec-notes, "Test Vector Re-run".

Failure Modes

  • Taking the issue body's original P-01, P-02, P-06 and N-15 signatures instead of the 7 November values: they verify only over an @query without the leading ?.
  • Reporting N-04 as a signature failure: the signature is valid, and only the recomputed digest fails.

Next Actions

  • Score N-17 and N-20 against a real server, where the error body and the order of checks can be observed.
  • Check the WIT validation in the script against the workload credentials draft text. The script follows this repository's reading of it and has not been compared clause by clause.