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- The client builds the signature base. Under Section 3 it covers
@method,@path,@queryand, when present, Content-Type, Content-Digest, Authorization, Txn-Token and Workload-Identity-Token. It setscreated,expires,nonce,tag(the valuewimse-workload-to-workload) andwimse-aud, and optionallywimse-sign-response. It must not setkeyidoralg. - 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.
- 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).
- The server finds the WIMSE signature by its
tagvalue, not by label, and rejects the message if more than one signature carries that tag. It checks the validity window and checkswimse-audagainst its trusted configuration using Simple String Comparison (Sections 3, 3.2). - On failure the server returns 400 with the
wimse-signature-invalidproblem type and does not say which check failed (Section 3.6). - If the request carried
wimse-sign-responseset to true, or local policy requires it, the server signs the response, covering@status, the request components with thereqparameter, Content-Type, Content-Digest and Workload-Identity-Token, and setswimse-req-nonceto the request's nonce (Sections 3.4, 3.5). If it cannot sign, it returns an error instead of an unsigned success. - The client verifies a signed response and compares
wimse-req-noncewith 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-responsetogether withwimse-req-nonce(Sections 3.3, 3.5, Appendix A.4). - Proxy tunnelling with the CONNECT method, where the profile now covers
@authorityinstead of@pathand@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.jsonrecords 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@queryvalues 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-audis 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
@methodand@authorityand MUST NOT cover@pathor@query. For a signed response to a CONNECT request,@method;reqand@authority;reqreplace the path and query components. The draft keeps omitting@authorityfor all other methods. @request-targetwording: 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-invalidand MUST NOT reveal which check failed. A server that is required to sign but does not support signing MUST give 501 withwimse-signed-response-unsupported. A server that supports signing but cannot sign this response MUST give 400 withwimse-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-audafter 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@authorityis 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 withwimse-sign-responseand 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-responseis authoritative over Accept-Signature, which cannot request weaker coverage than the profile (Section 3.5). The algorithm comes fromcnf.jwk.algin the WIT, never from analgparameter, 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-audis 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).@querymust 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 (abcd1111toabcd4444): 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
keyidandalg(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-audis 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
@queryis?flavor=vanillawith 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-audvariants (trailing slash, explicit default port), duplicatewimse-workload-to-workloadtags,keyidandalgpresent, 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
Hostin front of a server, send a signed request withwimse-sign-responseset to true, and check that verification still succeeds, that a server that cannot sign (streamed response) returns 400 withwimse-signed-response-unavailable, and that a server without signing support returns 501 withwimse-signed-response-unsupported.
Sources
- draft-ietf-wimse-http-signature-08
- WIMSE list: Test vectors for draft-ietf-wimse-http-signature-08, request for review
- WIMSE list: Re: Test vectors for draft-ietf-wimse-http-signature-08 (Yaron Sheffer)
- WIMSE list: Re: Test vectors for draft-ietf-wimse-http-signature-08 (vector author)
- WIMSE list: Review of draft-ietf-wimse-http-signature
- WIMSE list: Re: Review of draft-ietf-wimse-http-signature
- Issue #385: shepherd review
- Issue #417: editorial comments on the introduction
- Issue #419: test vectors for -08
- Issue #420: example nonces below the entropy minimum
- Draft source repository and issue tracker