Expand description
wimsey-httpsig — the WIMSE HTTP Message Signatures transport binding.
Target spec: draft-ietf-wimse-http-signature-06, a profile of RFC 9421.
The calling workload signs the outgoing HTTP request — including the header
that carries its WIT — with its proof-of-possession key, so an intermediary
can read but not tamper with the covered components. The receiver recovers
the key from the WIT’s cnf claim and verifies the signature.
This crate implements the RFC 9421 signature base (Section 2.5) for the
derived components @method, @authority, @path, @query and
@request-target plus header fields, signs with Ed25519, and serializes the
Signature-Input and Signature fields. The signature base is verified
byte-for-byte against the RFC’s worked example.
§The WIMSE profile
Section 3 of the draft narrows RFC 9421 considerably. Set
VerifyConfig::wimse_profile to enforce it, or call
check_request_profile directly:
@methodand@request-targetMUST be covered, along withContent-Type,Content-Digest,Authorization,Txn-TokenandWorkload-Identity-Tokenwhenever the message carries them.created,expires,nonceandtagMUST all be present, withtagequal toWIMSE_TAGand a tightexpireswindow (minutes, not hours).wimse-audMUST be present on a request, naming the service the signature is for. A verifier binds itself to that audience withVerifyConfig::expected_audience.keyidandalgMUST NOT be used: the key travels in the WIT and itscnfJWK pins the algorithm, so repeating either would only add confusion.
The profile is off by default, so the crate can also be driven as a plain RFC 9421 implementation.
§Caller responsibilities and limitations
- Verifying a signature proves only that the covered components were signed.
Set
VerifyConfig::required_componentsto demand the components you care about. - Covering
content-digestprotects only the header string. To bind the body, also callverify_content_digestover the received body. - Exactly one signature per
Signature/Signature-Inputfield is supported. @authorityis lowercased but its default port is not stripped; pass a normalized authority.- Response signing is supported: sign an
HttpExchangerather than anHttpRequest, enforceVerifyConfig::wimse_response_profile, and check the returned nonce withVerifyConfig::expected_req_nonce. - Replay defense is the caller’s: this crate checks that a
nonceis present but does not remember the ones it has seen.
use wimsey_httpsig::{
content_digest_sha256, sign, verify, verify_content_digest, Component, HttpRequest,
SignatureParams, SigningKey, VerifyConfig, WIMSE_TAG,
};
let pop_key = SigningKey::from_ed25519_seed(&[5u8; 32]);
let body = br#"{"hello":"world"}"#;
let request = HttpRequest {
method: "POST".to_owned(),
authority: "service.example".to_owned(),
path: "/transfer".to_owned(),
query: None,
headers: vec![
("Content-Digest".to_owned(), content_digest_sha256(body)),
("Workload-Identity-Token".to_owned(), "eyJ0eXAi.wit.value".to_owned()),
],
};
let components = vec![
Component::Method,
Component::RequestTarget,
Component::header("content-digest"),
Component::header("workload-identity-token"),
];
let params = SignatureParams {
created: Some(1_700_000_000),
expires: Some(1_700_000_300),
nonce: Some("abcd1111".to_owned()),
tag: Some(WIMSE_TAG.to_owned()),
wimse_aud: Some("https://service.example/transfer".to_owned()),
..SignatureParams::default()
};
let signed = sign(&request, &components, ¶ms, "wimse", &pop_key).unwrap();
// The receiver enforces the profile, pins the audience it answers to, and
// binds the body by checking the content-digest against it.
let config = VerifyConfig {
now: Some(1_700_000_030),
required_components: components.clone(),
wimse_profile: true,
expected_audience: Some("https://service.example/transfer".to_owned()),
..VerifyConfig::default()
};
let verified =
verify(&request, &signed.signature_input, &signed.signature, &pop_key.verifying_key(), &config)
.unwrap();
assert_eq!(verified.label, "wimse");
assert!(verify_content_digest("sha-256=:invalid:", body) == false);Structs§
- Http
Exchange - A response together with the request it answers.
- Http
Request - A minimal HTTP request, sufficient to derive RFC 9421 component values.
- Http
Response - A minimal HTTP response, sufficient to derive RFC 9421 component values.
- Signature
Params - RFC 9421 signature parameters, serialized after the covered-component list.
- Signed
Signature - The header field values produced by
sign. - Verified
Signature - The outcome of a successful
verify. - Verify
Config - Options controlling
verify.
Enums§
- Algorithm
- A JOSE signature algorithm this workspace can produce and verify.
- Component
- A covered component of an HTTP message signature.
- Http
SigError - An error returned while signing or verifying an HTTP message signature.
- Signing
Key - A private key that can sign.
- Verifying
Key - A public key that can verify.
Constants§
- ALG
- The signature algorithm name this crate emits and accepts (RFC 9421 Section 3.3.6).
- WIMSE_
LABEL - The signature label the draft recommends when a message carries a single signature.
- WIMSE_
TAG - The
tagvalue identifying a WIMSE workload-to-workload signature.
Traits§
- Component
Source - Something a signature base can read covered component values from.
Functions§
- check_
request_ profile - Errors unless
paramssatisfies the WIMSE profile for a request signature (Section 3 ofdraft-ietf-wimse-http-signature). - check_
response_ profile - Errors unless
paramssatisfies the WIMSE profile for a response signature (Section 3 ofdraft-ietf-wimse-http-signature). - content_
digest_ sha256 - Computes a
Content-Digestfield value overbodyusing SHA-256, in the RFC 9530 dictionary formsha-256=:<base64>:. - response_
components - The components the WIMSE profile requires a response signature to cover, given the headers the response actually carries.
- sign
- Signs
requestovercomponents, producingSignature-InputandSignaturefield values underlabel. - signature_
base - Builds the RFC 9421 signature base for
requestovercomponentswithparams. - verify
- Verifies an HTTP message signature on
request. - verify_
content_ digest - Checks a
Content-Digestheader value againstbodyfor the SHA-256 form.