···1313 | Bearer_form of { token : string }
1414 (** RFC 6750 Section 2.2: Bearer token in form-encoded body *)
1515 | Digest of { username : string; password : string }
1616+ | Signature of Signature.config
1717+ (** RFC 9421: HTTP Message Signatures *)
1618 | Custom of (Headers.t -> Headers.t)
17191820(** Digest authentication challenge parsed from WWW-Authenticate header *)
···3335let bearer ~token = Bearer { token }
34363537let digest ~username ~password = Digest { username; password }
3838+3939+let signature config = Signature config
36403741let custom f = Custom f
3842···5054 | Bearer _ -> "Bearer"
5155 | Bearer_form _ -> "Bearer (form)"
5256 | Digest _ -> "Digest"
5757+ | Signature _ -> "Signature"
5358 | Custom _ -> "Custom"
54595560(** Check if auth type requires HTTPS (per RFC 7617/6750).
5656- Basic, Bearer, and Digest send credentials that can be intercepted. *)
6161+ Basic, Bearer, and Digest send credentials that can be intercepted.
6262+ Signature does not strictly require HTTPS as it provides its own integrity. *)
5763let requires_https = function
5864 | Basic _ | Bearer _ | Bearer_form _ | Digest _ -> true
5959- | No_auth | Custom _ -> false
6565+ | No_auth | Signature _ | Custom _ -> false
60666167(** Validate that sensitive authentication is used over HTTPS.
6268 Per RFC 7617 Section 4 (Basic) and RFC 6750 Section 5.1 (Bearer):
···96102 | Digest { username; password = _ } ->
97103 Log.debug (fun m -> m "Digest auth configured for user: %s (requires server challenge)" username);
98104 (* Digest auth requires server challenge first, handled elsewhere *)
105105+ headers
106106+ | Signature _ ->
107107+ Log.debug (fun m -> m "Signature auth configured (requires request context)");
108108+ (* Signature auth requires request context (method, URI) to compute.
109109+ Handled separately in request flow via apply_signature. *)
99110 headers
100111 | Custom f ->
101112 Log.debug (fun m -> m "Applying custom authentication handler");
···378389(** Check if stale=true in digest challenge, indicating password is still valid.
379390 Per RFC 7616: If stale=true, the client should retry with same credentials
380391 using the new nonce. If stale=false or not present, credentials are wrong. *)
381381-let digest_is_stale challenge = challenge.stale392392+let digest_is_stale challenge = challenge.stale
393393+394394+(** {1 HTTP Message Signatures (RFC 9421)} *)
395395+396396+let is_signature = function
397397+ | Signature _ -> true
398398+ | _ -> false
399399+400400+let get_signature_config = function
401401+ | Signature config -> Some config
402402+ | _ -> None
403403+404404+(** Apply HTTP Message Signature to headers given request context.
405405+ This computes and adds the Signature-Input and Signature headers.
406406+407407+ @param method_ The HTTP method
408408+ @param uri The request URI
409409+ @param headers The headers to sign (and add signature to)
410410+ @param auth The authentication configuration (must be [Signature])
411411+ @return Updated headers with signature, or original headers if not Signature auth *)
412412+let apply_signature ~method_ ~uri ~headers auth =
413413+ match auth with
414414+ | Signature config ->
415415+ let context = Signature.Context.request ~method_ ~uri ~headers in
416416+ (match Signature.sign ~config ~context ~headers with
417417+ | Ok signed_headers ->
418418+ Log.debug (fun m -> m "Applied HTTP message signature");
419419+ signed_headers
420420+ | Error e ->
421421+ Log.err (fun m -> m "Failed to apply HTTP message signature: %s"
422422+ (Signature.sign_error_to_string e));
423423+ headers)
424424+ | _ -> headers
+43-1
lib/auth.mli
···4848 Note: SHA-512-256 is not supported as it requires special initialization
4949 vectors not available in standard libraries. *)
50505151+val signature : Signature.config -> t
5252+(** HTTP Message Signatures (RFC 9421).
5353+5454+ Creates cryptographic signatures over HTTP message components.
5555+ The signature covers selected headers and derived values like
5656+ the method, path, and authority.
5757+5858+ Use {!Signature.config} to create the configuration:
5959+ {[
6060+ let key = Signature.Key.ed25519 ~priv:... ~pub:... in
6161+ let config = Signature.config ~key ~keyid:"my-key" () in
6262+ let auth = Auth.signature config
6363+ ]}
6464+6565+ The signature is computed and added when the request is made,
6666+ as it requires the full request context (method, URI, headers). *)
6767+5168val bearer_form : token:string -> t
5269(** Bearer token in form-encoded body (RFC 6750 Section 2.2).
5370···170187 Per RFC 7616 Section 3.2.2: If stale=true, the nonce is expired but the
171188 credentials are still valid. The client should retry with the same
172189 credentials using the new nonce. If stale=false or not present, the
173173- credentials themselves are wrong. *)190190+ credentials themselves are wrong. *)
191191+192192+(** {1 HTTP Message Signatures (RFC 9421)} *)
193193+194194+val is_signature : t -> bool
195195+(** [is_signature auth] returns [true] if [auth] is HTTP Message Signature authentication. *)
196196+197197+val get_signature_config : t -> Signature.config option
198198+(** [get_signature_config auth] returns [Some config] if [auth] is HTTP Message
199199+ Signature authentication, [None] otherwise. *)
200200+201201+val apply_signature :
202202+ method_:Method.t ->
203203+ uri:Uri.t ->
204204+ headers:Headers.t ->
205205+ t ->
206206+ Headers.t
207207+(** [apply_signature ~method_ ~uri ~headers auth] applies HTTP Message Signature
208208+ to [headers] if [auth] is Signature authentication. Returns the headers with
209209+ [Signature-Input] and [Signature] headers added.
210210+211211+ This function computes the signature based on the request context and adds
212212+ the appropriate headers per RFC 9421.
213213+214214+ If [auth] is not Signature authentication, returns [headers] unchanged.
215215+ If signature computation fails, logs an error and returns [headers] unchanged. *)
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** RFC 8941 Structured Fields (minimal subset for HTTP Signatures) *)
77+88+(* Result monad syntax for cleaner parsing *)
99+let ( let* ) = Result.bind
1010+1111+type item =
1212+ | String of string
1313+ | Token of string
1414+ | Integer of int64
1515+ | Decimal of float
1616+ | Boolean of bool
1717+ | Byte_seq of string
1818+1919+type parameters = (string * item) list
2020+2121+type inner_list = (item * parameters) list * parameters
2222+2323+type list_member =
2424+ | Item of item * parameters
2525+ | Inner_list of inner_list
2626+2727+type dictionary = (string * list_member) list
2828+2929+(* Convenience constructors *)
3030+let string_item s = String s
3131+let token_item s = Token s
3232+let integer_item i = Integer i
3333+let byte_seq_item s = Byte_seq s
3434+let bool_item b = Boolean b
3535+3636+(* Serialization *)
3737+3838+let item_to_string = function
3939+ | String s ->
4040+ (* Escape backslashes and quotes *)
4141+ let escaped = String.to_seq s
4242+ |> Seq.flat_map (fun c ->
4343+ if c = '\\' || c = '"' then List.to_seq ['\\'; c]
4444+ else Seq.return c)
4545+ |> String.of_seq
4646+ in
4747+ "\"" ^ escaped ^ "\""
4848+ | Token s -> s
4949+ | Integer i -> Int64.to_string i
5050+ | Decimal f -> Printf.sprintf "%g" f
5151+ | Boolean true -> "?1"
5252+ | Boolean false -> "?0"
5353+ | Byte_seq s ->
5454+ ":" ^ Base64.encode_string s ^ ":"
5555+5656+let parameters_to_string params =
5757+ String.concat "" (List.map (fun (k, v) ->
5858+ match v with
5959+ | Boolean true -> ";" ^ k
6060+ | _ -> ";" ^ k ^ "=" ^ item_to_string v
6161+ ) params)
6262+6363+let inner_list_to_string (items, params) =
6464+ let item_strs = List.map (fun (item, item_params) ->
6565+ item_to_string item ^ parameters_to_string item_params
6666+ ) items in
6767+ "(" ^ String.concat " " item_strs ^ ")" ^ parameters_to_string params
6868+6969+let dictionary_to_string dict =
7070+ String.concat ", " (List.map (fun (k, member) ->
7171+ match member with
7272+ | Item (Boolean true, params) ->
7373+ k ^ parameters_to_string params
7474+ | Item (item, params) ->
7575+ k ^ "=" ^ item_to_string item ^ parameters_to_string params
7676+ | Inner_list il ->
7777+ k ^ "=" ^ inner_list_to_string il
7878+ ) dict)
7979+8080+(* Parsing helpers *)
8181+8282+let is_alpha c = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
8383+let is_digit c = c >= '0' && c <= '9'
8484+let is_lcalpha c = c >= 'a' && c <= 'z'
8585+let is_tchar c =
8686+ is_alpha c || is_digit c ||
8787+ c = '!' || c = '#' || c = '$' || c = '%' || c = '&' || c = '\'' ||
8888+ c = '*' || c = '+' || c = '-' || c = '.' || c = '^' || c = '_' ||
8989+ c = '`' || c = '|' || c = '~'
9090+9191+type parser_state = {
9292+ input : string;
9393+ mutable pos : int;
9494+}
9595+9696+let peek s = if s.pos < String.length s.input then Some s.input.[s.pos] else None
9797+let advance s = s.pos <- s.pos + 1
9898+let _consume s = let c = s.input.[s.pos] in advance s; c
9999+let skip_sp s = while peek s = Some ' ' do advance s done
100100+let skip_ows s = while peek s = Some ' ' || peek s = Some '\t' do advance s done
101101+102102+let parse_token s =
103103+ let start = s.pos in
104104+ match peek s with
105105+ | Some c when is_alpha c || c = '*' ->
106106+ advance s;
107107+ while match peek s with
108108+ | Some c -> is_tchar c || c = ':' || c = '/'
109109+ | None -> false
110110+ do advance s done;
111111+ Ok (String.sub s.input start (s.pos - start))
112112+ | _ -> Error "Expected token"
113113+114114+let parse_key s =
115115+ let start = s.pos in
116116+ match peek s with
117117+ | Some c when is_lcalpha c || c = '*' ->
118118+ advance s;
119119+ while match peek s with
120120+ | Some c -> is_lcalpha c || is_digit c || c = '_' || c = '-' || c = '.' || c = '*'
121121+ | None -> false
122122+ do advance s done;
123123+ Ok (String.sub s.input start (s.pos - start))
124124+ | _ -> Error "Expected key"
125125+126126+let parse_integer s =
127127+ let start = s.pos in
128128+ let neg = if peek s = Some '-' then (advance s; true) else false in
129129+ if not (match peek s with Some c -> is_digit c | None -> false) then
130130+ Error "Expected integer"
131131+ else begin
132132+ while match peek s with Some c -> is_digit c | None -> false do advance s done;
133133+ let str = String.sub s.input start (s.pos - start) in
134134+ match Int64.of_string_opt str with
135135+ | Some i -> Ok (if neg then Int64.neg i else i)
136136+ | None -> Error "Integer overflow"
137137+ end
138138+139139+let parse_decimal s =
140140+ let start = s.pos in
141141+ let _ = if peek s = Some '-' then advance s in
142142+ while match peek s with Some c -> is_digit c | None -> false do advance s done;
143143+ if peek s = Some '.' then begin
144144+ advance s;
145145+ while match peek s with Some c -> is_digit c | None -> false do advance s done
146146+ end;
147147+ let str = String.sub s.input start (s.pos - start) in
148148+ match float_of_string_opt str with
149149+ | Some f -> Ok f
150150+ | None -> Error "Invalid decimal"
151151+152152+let parse_string s =
153153+ if peek s <> Some '"' then Error "Expected string"
154154+ else begin
155155+ advance s;
156156+ let buf = Buffer.create 32 in
157157+ let rec loop () =
158158+ match peek s with
159159+ | None -> Error "Unterminated string"
160160+ | Some '"' -> advance s; Ok (Buffer.contents buf)
161161+ | Some '\\' ->
162162+ advance s;
163163+ (match peek s with
164164+ | Some c when c = '\\' || c = '"' ->
165165+ Buffer.add_char buf c; advance s; loop ()
166166+ | _ -> Error "Invalid escape")
167167+ | Some c ->
168168+ if Char.code c >= 0x20 && Char.code c <= 0x7e then begin
169169+ Buffer.add_char buf c; advance s; loop ()
170170+ end else Error "Invalid character in string"
171171+ in
172172+ loop ()
173173+ end
174174+175175+let parse_byte_seq s =
176176+ if peek s <> Some ':' then Error "Expected byte sequence"
177177+ else begin
178178+ advance s;
179179+ let start = s.pos in
180180+ while match peek s with
181181+ | Some c -> c <> ':'
182182+ | None -> false
183183+ do advance s done;
184184+ if peek s <> Some ':' then Error "Unterminated byte sequence"
185185+ else begin
186186+ let b64 = String.sub s.input start (s.pos - start) in
187187+ advance s;
188188+ match Base64.decode b64 with
189189+ | Ok decoded -> Ok decoded
190190+ | Error (`Msg msg) -> Error msg
191191+ end
192192+ end
193193+194194+let parse_boolean s =
195195+ if peek s <> Some '?' then Error "Expected boolean"
196196+ else begin
197197+ advance s;
198198+ match peek s with
199199+ | Some '1' -> advance s; Ok true
200200+ | Some '0' -> advance s; Ok false
201201+ | _ -> Error "Expected ?0 or ?1"
202202+ end
203203+204204+let rec parse_bare_item s =
205205+ match peek s with
206206+ | Some '"' -> Result.map (fun x -> String x) (parse_string s)
207207+ | Some ':' -> Result.map (fun x -> Byte_seq x) (parse_byte_seq s)
208208+ | Some '?' -> Result.map (fun x -> Boolean x) (parse_boolean s)
209209+ | Some c when c = '-' || is_digit c ->
210210+ (* Could be integer or decimal, peek ahead *)
211211+ let start = s.pos in
212212+ let _ = if peek s = Some '-' then advance s in
213213+ while match peek s with Some c -> is_digit c | None -> false do advance s done;
214214+ if peek s = Some '.' then begin
215215+ s.pos <- start;
216216+ Result.map (fun x -> Decimal x) (parse_decimal s)
217217+ end else begin
218218+ s.pos <- start;
219219+ Result.map (fun x -> Integer x) (parse_integer s)
220220+ end
221221+ | Some c when is_alpha c || c = '*' ->
222222+ Result.map (fun x -> Token x) (parse_token s)
223223+ | _ -> Error "Expected bare item"
224224+225225+and parse_parameters s =
226226+ let rec loop acc =
227227+ if peek s = Some ';' then begin
228228+ advance s;
229229+ skip_sp s;
230230+ match parse_key s with
231231+ | Error _ -> Ok (List.rev acc)
232232+ | Ok key ->
233233+ if peek s = Some '=' then begin
234234+ advance s;
235235+ match parse_bare_item s with
236236+ | Ok v -> loop ((key, v) :: acc)
237237+ | Error e -> Error e
238238+ end else
239239+ loop ((key, Boolean true) :: acc)
240240+ end else Ok (List.rev acc)
241241+ in
242242+ loop []
243243+244244+let parse_item s =
245245+ let* item = parse_bare_item s in
246246+ let* params = parse_parameters s in
247247+ Ok (item, params)
248248+249249+let parse_inner_list_items s =
250250+ let rec loop acc =
251251+ skip_sp s;
252252+ match peek s with
253253+ | Some ')' -> advance s; Ok (List.rev acc)
254254+ | Some _ ->
255255+ let* item = parse_item s in
256256+ loop (item :: acc)
257257+ | None -> Error "Unterminated inner list"
258258+ in
259259+ loop []
260260+261261+let parse_inner_list str =
262262+ let s = { input = str; pos = 0 } in
263263+ skip_sp s;
264264+ if peek s <> Some '(' then Error "Expected '('"
265265+ else begin
266266+ advance s;
267267+ let* items = parse_inner_list_items s in
268268+ let* params = parse_parameters s in
269269+ Ok (items, params)
270270+ end
271271+272272+let parse_list_member s =
273273+ if peek s = Some '(' then begin
274274+ advance s;
275275+ let* items = parse_inner_list_items s in
276276+ let* params = parse_parameters s in
277277+ Ok (Inner_list (items, params))
278278+ end else
279279+ let* (item, params) = parse_item s in
280280+ Ok (Item (item, params))
281281+282282+let parse_dictionary str =
283283+ let s = { input = str; pos = 0 } in
284284+ let rec loop acc =
285285+ skip_ows s;
286286+ if s.pos >= String.length s.input then Ok (List.rev acc)
287287+ else
288288+ let* key = parse_key s in
289289+ let* member =
290290+ if peek s = Some '=' then (advance s; parse_list_member s)
291291+ else
292292+ let* params = parse_parameters s in
293293+ Ok (Item (Boolean true, params))
294294+ in
295295+ skip_ows s;
296296+ if peek s = Some ',' then (advance s; skip_ows s);
297297+ loop ((key, member) :: acc)
298298+ in
299299+ loop []
+80
lib/sf.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** RFC 8941 Structured Fields (minimal subset for HTTP Signatures)
77+88+ This module implements the subset of
99+ {{:https://datatracker.ietf.org/doc/html/rfc8941}RFC 8941} Structured Fields
1010+ required for HTTP Message Signatures (RFC 9421).
1111+1212+ Only Dictionary and Inner List types are fully supported, as these are
1313+ used by the Signature-Input and Signature headers. *)
1414+1515+(** {1 Bare Items} *)
1616+1717+type item =
1818+ | String of string (** Quoted string: "value" *)
1919+ | Token of string (** Unquoted token: value *)
2020+ | Integer of int64 (** Integer: 123 *)
2121+ | Decimal of float (** Decimal: 1.23 *)
2222+ | Boolean of bool (** Boolean: ?1 or ?0 *)
2323+ | Byte_seq of string (** Byte sequence: :base64: *)
2424+2525+val item_to_string : item -> string
2626+(** Serialize an item to its Structured Field representation. *)
2727+2828+(** {1 Parameters} *)
2929+3030+type parameters = (string * item) list
3131+(** Parameters are key-value pairs attached to items. *)
3232+3333+val parameters_to_string : parameters -> string
3434+(** Serialize parameters (e.g., [;key=value;key2]). *)
3535+3636+(** {1 Inner Lists} *)
3737+3838+type inner_list = (item * parameters) list * parameters
3939+(** An inner list is a list of parameterized items with list-level parameters.
4040+ Format: [(item1;param item2);listparam=value] *)
4141+4242+val inner_list_to_string : inner_list -> string
4343+(** Serialize an inner list. *)
4444+4545+val parse_inner_list : string -> (inner_list, string) result
4646+(** Parse an inner list from its string representation. *)
4747+4848+(** {1 List Members} *)
4949+5050+type list_member =
5151+ | Item of item * parameters
5252+ | Inner_list of inner_list
5353+5454+(** {1 Dictionaries} *)
5555+5656+type dictionary = (string * list_member) list
5757+(** A dictionary is an ordered map of string keys to list members. *)
5858+5959+val dictionary_to_string : dictionary -> string
6060+(** Serialize a dictionary to its Structured Field representation. *)
6161+6262+val parse_dictionary : string -> (dictionary, string) result
6363+(** Parse a dictionary from its string representation. *)
6464+6565+(** {1 Convenience Functions} *)
6666+6767+val string_item : string -> item
6868+(** Create a string item. *)
6969+7070+val token_item : string -> item
7171+(** Create a token item. *)
7272+7373+val integer_item : int64 -> item
7474+(** Create an integer item. *)
7575+7676+val byte_seq_item : string -> item
7777+(** Create a byte sequence item (raw bytes, will be base64 encoded). *)
7878+7979+val bool_item : bool -> item
8080+(** Create a boolean item. *)
+969
lib/signature.ml
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** RFC 9421 HTTP Message Signatures *)
77+88+let src = Logs.Src.create "requests.signature" ~doc:"HTTP Message Signatures"
99+module Log = (val Logs.src_log src : Logs.LOG)
1010+1111+(* Result monad syntax for cleaner error handling *)
1212+let ( let* ) = Result.bind
1313+1414+(* ========================================================================= *)
1515+(* Algorithms *)
1616+(* ========================================================================= *)
1717+1818+module Algorithm = struct
1919+ type t = [
2020+ | `Rsa_pss_sha512
2121+ | `Rsa_v1_5_sha256
2222+ | `Hmac_sha256
2323+ | `Ecdsa_p256_sha256
2424+ | `Ecdsa_p384_sha384
2525+ | `Ed25519
2626+ ]
2727+2828+ let to_string : t -> string = function
2929+ | `Rsa_pss_sha512 -> "rsa-pss-sha512"
3030+ | `Rsa_v1_5_sha256 -> "rsa-v1_5-sha256"
3131+ | `Hmac_sha256 -> "hmac-sha256"
3232+ | `Ecdsa_p256_sha256 -> "ecdsa-p256-sha256"
3333+ | `Ecdsa_p384_sha384 -> "ecdsa-p384-sha384"
3434+ | `Ed25519 -> "ed25519"
3535+3636+ let of_string : string -> t option = function
3737+ | "rsa-pss-sha512" -> Some `Rsa_pss_sha512
3838+ | "rsa-v1_5-sha256" -> Some `Rsa_v1_5_sha256
3939+ | "hmac-sha256" -> Some `Hmac_sha256
4040+ | "ecdsa-p256-sha256" -> Some `Ecdsa_p256_sha256
4141+ | "ecdsa-p384-sha384" -> Some `Ecdsa_p384_sha384
4242+ | "ed25519" -> Some `Ed25519
4343+ | _ -> None
4444+4545+ let of_string_exn s =
4646+ match of_string s with
4747+ | Some a -> a
4848+ | None -> invalid_arg ("Unknown algorithm: " ^ s)
4949+end
5050+5151+(* ========================================================================= *)
5252+(* Components *)
5353+(* ========================================================================= *)
5454+5555+module Component = struct
5656+ type derived = [
5757+ | `Method
5858+ | `Authority
5959+ | `Path
6060+ | `Query
6161+ | `Query_param of string
6262+ | `Target_uri
6363+ | `Status
6464+ | `Request_target
6565+ ]
6666+6767+ type param = [
6868+ | `Sf
6969+ | `Key of string
7070+ | `Bs
7171+ | `Tr
7272+ | `Req
7373+ ]
7474+7575+ type t = [
7676+ | `Derived of derived * param list
7777+ | `Field of string * param list
7878+ ]
7979+8080+ (* Constructors *)
8181+ let method_ : t = `Derived (`Method, [])
8282+ let authority : t = `Derived (`Authority, [])
8383+ let path : t = `Derived (`Path, [])
8484+ let query : t = `Derived (`Query, [])
8585+ let query_param name : t = `Derived (`Query_param name, [])
8686+ let target_uri : t = `Derived (`Target_uri, [])
8787+ let status : t = `Derived (`Status, [])
8888+ let request_target : t = `Derived (`Request_target, [])
8989+9090+ let field name : t = `Field (String.lowercase_ascii name, [])
9191+ let field_sf name : t = `Field (String.lowercase_ascii name, [`Sf])
9292+ let field_bs name : t = `Field (String.lowercase_ascii name, [`Bs])
9393+ let field_key name ~key : t = `Field (String.lowercase_ascii name, [`Key key])
9494+ let field_req name : t = `Field (String.lowercase_ascii name, [`Req])
9595+9696+ let content_type = field "content-type"
9797+ let content_length = field "content-length"
9898+ let content_digest = field "content-digest"
9999+ let date = field "date"
100100+ let host = field "host"
101101+102102+ let derived_to_string : derived -> string = function
103103+ | `Method -> "@method"
104104+ | `Authority -> "@authority"
105105+ | `Path -> "@path"
106106+ | `Query -> "@query"
107107+ | `Query_param _ -> "@query-param"
108108+ | `Target_uri -> "@target-uri"
109109+ | `Status -> "@status"
110110+ | `Request_target -> "@request-target"
111111+112112+ let param_to_sf : param -> string * Sf.item = function
113113+ | `Sf -> ("sf", Sf.Boolean true)
114114+ | `Key k -> ("key", Sf.String k)
115115+ | `Bs -> ("bs", Sf.Boolean true)
116116+ | `Tr -> ("tr", Sf.Boolean true)
117117+ | `Req -> ("req", Sf.Boolean true)
118118+119119+ let param_to_string : param -> string = function
120120+ | `Sf -> ";sf"
121121+ | `Key k -> ";key=\"" ^ k ^ "\""
122122+ | `Bs -> ";bs"
123123+ | `Tr -> ";tr"
124124+ | `Req -> ";req"
125125+126126+ let to_identifier : t -> string = function
127127+ | `Derived (d, params) ->
128128+ let base = derived_to_string d in
129129+ let name_param = match d with
130130+ | `Query_param name -> ";name=\"" ^ name ^ "\""
131131+ | _ -> ""
132132+ in
133133+ "\"" ^ base ^ "\"" ^ name_param ^ String.concat "" (List.map param_to_string params)
134134+ | `Field (name, params) ->
135135+ "\"" ^ name ^ "\"" ^ String.concat "" (List.map param_to_string params)
136136+137137+ let to_sf_item : t -> Sf.item * Sf.parameters = function
138138+ | `Derived (d, params) ->
139139+ let base = derived_to_string d in
140140+ let sf_params = List.map param_to_sf params in
141141+ let sf_params = match d with
142142+ | `Query_param name -> ("name", Sf.String name) :: sf_params
143143+ | _ -> sf_params
144144+ in
145145+ (Sf.String base, sf_params)
146146+ | `Field (name, params) ->
147147+ let sf_params = List.map param_to_sf params in
148148+ (Sf.String name, sf_params)
149149+150150+ let of_identifier str =
151151+ (* Parse component identifier like "@method" or "content-type;sf" *)
152152+ let parse_params rest =
153153+ let rec loop acc s =
154154+ if String.length s = 0 then Ok (List.rev acc)
155155+ else if s.[0] = ';' then
156156+ let s = String.sub s 1 (String.length s - 1) in
157157+ if String.length s >= 2 && String.sub s 0 2 = "sf" then
158158+ loop (`Sf :: acc) (String.sub s 2 (String.length s - 2))
159159+ else if String.length s >= 2 && String.sub s 0 2 = "bs" then
160160+ loop (`Bs :: acc) (String.sub s 2 (String.length s - 2))
161161+ else if String.length s >= 2 && String.sub s 0 2 = "tr" then
162162+ loop (`Tr :: acc) (String.sub s 2 (String.length s - 2))
163163+ else if String.length s >= 3 && String.sub s 0 3 = "req" then
164164+ loop (`Req :: acc) (String.sub s 3 (String.length s - 3))
165165+ else if String.length s >= 4 && String.sub s 0 4 = "key=" then
166166+ let s = String.sub s 4 (String.length s - 4) in
167167+ if String.length s > 0 && s.[0] = '"' then
168168+ match String.index_from_opt s 1 '"' with
169169+ | Some idx ->
170170+ let key = String.sub s 1 (idx - 1) in
171171+ loop (`Key key :: acc) (String.sub s (idx + 1) (String.length s - idx - 1))
172172+ | None -> Error "Unterminated key parameter"
173173+ else Error "Expected quoted key value"
174174+ else if String.length s >= 5 && String.sub s 0 5 = "name=" then
175175+ (* Skip name parameter, handled separately for query-param *)
176176+ let s = String.sub s 5 (String.length s - 5) in
177177+ if String.length s > 0 && s.[0] = '"' then
178178+ match String.index_from_opt s 1 '"' with
179179+ | Some idx ->
180180+ loop acc (String.sub s (idx + 1) (String.length s - idx - 1))
181181+ | None -> Error "Unterminated name parameter"
182182+ else Error "Expected quoted name value"
183183+ else Error ("Unknown parameter: " ^ s)
184184+ else Error ("Unexpected character: " ^ s)
185185+ in
186186+ loop [] rest
187187+ in
188188+ (* Extract name parameter for query-param *)
189189+ let extract_name s =
190190+ match String.index_opt s ';' with
191191+ | None -> (s, None)
192192+ | Some idx ->
193193+ let rest = String.sub s idx (String.length s - idx) in
194194+ if String.length rest >= 6 &&
195195+ String.sub rest 0 6 = ";name=" then
196196+ let after = String.sub rest 6 (String.length rest - 6) in
197197+ if String.length after > 0 && after.[0] = '"' then
198198+ match String.index_from_opt after 1 '"' with
199199+ | Some end_idx ->
200200+ let name = String.sub after 1 (end_idx - 1) in
201201+ let remaining = String.sub s 0 idx ^
202202+ String.sub after (end_idx + 1) (String.length after - end_idx - 1) in
203203+ (remaining, Some name)
204204+ | None -> (s, None)
205205+ else (s, None)
206206+ else (s, None)
207207+ in
208208+ let (ident, query_name) = extract_name str in
209209+ match String.index_opt ident ';' with
210210+ | None ->
211211+ if String.length ident > 0 && ident.[0] = '@' then
212212+ match ident with
213213+ | "@method" -> Ok method_
214214+ | "@authority" -> Ok authority
215215+ | "@path" -> Ok path
216216+ | "@query" -> Ok query
217217+ | "@query-param" ->
218218+ (match query_name with
219219+ | Some n -> Ok (query_param n)
220220+ | None -> Error "@query-param requires name parameter")
221221+ | "@target-uri" -> Ok target_uri
222222+ | "@status" -> Ok status
223223+ | "@request-target" -> Ok request_target
224224+ | _ -> Error ("Unknown derived component: " ^ ident)
225225+ else Ok (field ident)
226226+ | Some idx ->
227227+ let name = String.sub ident 0 idx in
228228+ let params_str = String.sub ident idx (String.length ident - idx) in
229229+ match parse_params params_str with
230230+ | Error e -> Error e
231231+ | Ok params ->
232232+ if String.length name > 0 && name.[0] = '@' then
233233+ match name with
234234+ | "@method" -> Ok (`Derived (`Method, params))
235235+ | "@authority" -> Ok (`Derived (`Authority, params))
236236+ | "@path" -> Ok (`Derived (`Path, params))
237237+ | "@query" -> Ok (`Derived (`Query, params))
238238+ | "@query-param" ->
239239+ (match query_name with
240240+ | Some n -> Ok (`Derived (`Query_param n, params))
241241+ | None -> Error "@query-param requires name parameter")
242242+ | "@target-uri" -> Ok (`Derived (`Target_uri, params))
243243+ | "@status" -> Ok (`Derived (`Status, params))
244244+ | "@request-target" -> Ok (`Derived (`Request_target, params))
245245+ | _ -> Error ("Unknown derived component: " ^ name)
246246+ else Ok (`Field (name, params))
247247+end
248248+249249+(* ========================================================================= *)
250250+(* Parameters *)
251251+(* ========================================================================= *)
252252+253253+module Params = struct
254254+ type t = {
255255+ created : Ptime.t option;
256256+ expires : Ptime.t option;
257257+ nonce : string option;
258258+ alg : Algorithm.t option;
259259+ keyid : string option;
260260+ tag : string option;
261261+ }
262262+263263+ let empty = {
264264+ created = None;
265265+ expires = None;
266266+ nonce = None;
267267+ alg = None;
268268+ keyid = None;
269269+ tag = None;
270270+ }
271271+272272+ let created time t = { t with created = Some time }
273273+ let expires time t = { t with expires = Some time }
274274+ let nonce value t = { t with nonce = Some value }
275275+ let alg algorithm t = { t with alg = Some algorithm }
276276+ let keyid id t = { t with keyid = Some id }
277277+ let tag value t = { t with tag = Some value }
278278+279279+ let get_created t = t.created
280280+ let get_expires t = t.expires
281281+ let get_nonce t = t.nonce
282282+ let get_alg t = t.alg
283283+ let get_keyid t = t.keyid
284284+ let get_tag t = t.tag
285285+286286+ let ptime_to_unix t =
287287+ Int64.of_float (Ptime.to_float_s t)
288288+289289+ let to_sf_params t =
290290+ List.filter_map Fun.id [
291291+ Option.map (fun c -> ("created", Sf.Integer (ptime_to_unix c))) t.created;
292292+ Option.map (fun e -> ("expires", Sf.Integer (ptime_to_unix e))) t.expires;
293293+ Option.map (fun n -> ("nonce", Sf.String n)) t.nonce;
294294+ Option.map (fun a -> ("alg", Sf.String (Algorithm.to_string a))) t.alg;
295295+ Option.map (fun k -> ("keyid", Sf.String k)) t.keyid;
296296+ Option.map (fun tag -> ("tag", Sf.String tag)) t.tag;
297297+ ]
298298+299299+ let of_sf_params params =
300300+ let get_int k =
301301+ match List.assoc_opt k params with
302302+ | Some (Sf.Integer i) -> Some (Ptime.of_float_s (Int64.to_float i))
303303+ | _ -> None
304304+ in
305305+ let get_string k =
306306+ match List.assoc_opt k params with
307307+ | Some (Sf.String s) -> Some s
308308+ | Some (Sf.Token s) -> Some s
309309+ | _ -> None
310310+ in
311311+ {
312312+ created = Option.join (get_int "created");
313313+ expires = Option.join (get_int "expires");
314314+ nonce = get_string "nonce";
315315+ alg = Option.bind (get_string "alg") Algorithm.of_string;
316316+ keyid = get_string "keyid";
317317+ tag = get_string "tag";
318318+ }
319319+end
320320+321321+(* ========================================================================= *)
322322+(* Key Material *)
323323+(* ========================================================================= *)
324324+325325+module Key = struct
326326+ type private_key =
327327+ | Symmetric_priv of string
328328+ | Ed25519_priv of string
329329+ | P256_priv of Mirage_crypto_ec.P256.Dsa.priv
330330+ | P384_priv of Mirage_crypto_ec.P384.Dsa.priv
331331+ | Rsa_priv of Mirage_crypto_pk.Rsa.priv
332332+333333+ type public_key =
334334+ | Symmetric_pub of string
335335+ | Ed25519_pub of string
336336+ | P256_pub of Mirage_crypto_ec.P256.Dsa.pub
337337+ | P384_pub of Mirage_crypto_ec.P384.Dsa.pub
338338+ | Rsa_pub of Mirage_crypto_pk.Rsa.pub
339339+340340+ type t = {
341341+ priv : private_key option;
342342+ pub : public_key option;
343343+ }
344344+345345+ let symmetric secret = {
346346+ priv = Some (Symmetric_priv secret);
347347+ pub = Some (Symmetric_pub secret);
348348+ }
349349+350350+ let ed25519 ~priv ~pub = {
351351+ priv = Some (Ed25519_priv priv);
352352+ pub = Some (Ed25519_pub pub);
353353+ }
354354+355355+ let ed25519_priv priv = {
356356+ priv = Some (Ed25519_priv priv);
357357+ pub = None;
358358+ }
359359+360360+ let ed25519_pub pub = {
361361+ priv = None;
362362+ pub = Some (Ed25519_pub pub);
363363+ }
364364+365365+ let p256 ~priv = {
366366+ priv = Some (P256_priv priv);
367367+ pub = Some (P256_pub (Mirage_crypto_ec.P256.Dsa.pub_of_priv priv));
368368+ }
369369+370370+ let p256_pub pub = {
371371+ priv = None;
372372+ pub = Some (P256_pub pub);
373373+ }
374374+375375+ let p384 ~priv = {
376376+ priv = Some (P384_priv priv);
377377+ pub = Some (P384_pub (Mirage_crypto_ec.P384.Dsa.pub_of_priv priv));
378378+ }
379379+380380+ let p384_pub pub = {
381381+ priv = None;
382382+ pub = Some (P384_pub pub);
383383+ }
384384+385385+ let rsa ~priv = {
386386+ priv = Some (Rsa_priv priv);
387387+ pub = Some (Rsa_pub (Mirage_crypto_pk.Rsa.pub_of_priv priv));
388388+ }
389389+390390+ let rsa_pub pub = {
391391+ priv = None;
392392+ pub = Some (Rsa_pub pub);
393393+ }
394394+395395+ let can_sign t = Option.is_some t.priv
396396+ let can_verify t = Option.is_some t.pub
397397+398398+ let algorithm t : Algorithm.t option =
399399+ match t.priv, t.pub with
400400+ | Some (Symmetric_priv _), _ | _, Some (Symmetric_pub _) -> Some `Hmac_sha256
401401+ | Some (Ed25519_priv _), _ | _, Some (Ed25519_pub _) -> Some `Ed25519
402402+ | Some (P256_priv _), _ | _, Some (P256_pub _) -> Some `Ecdsa_p256_sha256
403403+ | Some (P384_priv _), _ | _, Some (P384_pub _) -> Some `Ecdsa_p384_sha384
404404+ | Some (Rsa_priv _), _ | _, Some (Rsa_pub _) -> Some `Rsa_pss_sha512
405405+ | None, None -> None
406406+end
407407+408408+(* ========================================================================= *)
409409+(* Context *)
410410+(* ========================================================================= *)
411411+412412+module Context = struct
413413+ type request_ctx = {
414414+ method_ : Method.t;
415415+ uri : Uri.t;
416416+ headers : Headers.t;
417417+ }
418418+419419+ type response_ctx = {
420420+ status : int;
421421+ headers : Headers.t;
422422+ request : request_ctx option;
423423+ }
424424+425425+ type t = [
426426+ | `Request of request_ctx
427427+ | `Response of response_ctx
428428+ ]
429429+430430+ let request ~method_ ~uri ~headers : t =
431431+ `Request { method_; uri; headers }
432432+433433+ let response ~status ~headers ?request () : t =
434434+ let req_ctx = match request with
435435+ | Some (`Request r) -> Some r
436436+ | Some (`Response _) -> None
437437+ | None -> None
438438+ in
439439+ `Response { status; headers; request = req_ctx }
440440+end
441441+442442+(* ========================================================================= *)
443443+(* Content-Digest *)
444444+(* ========================================================================= *)
445445+446446+module Content_digest = struct
447447+ type algorithm = [ `Sha256 | `Sha512 ]
448448+449449+ let compute ~algorithm ~body =
450450+ match algorithm with
451451+ | `Sha256 ->
452452+ let hash = Digestif.SHA256.digest_string body in
453453+ let b64 = Base64.encode_string (Digestif.SHA256.to_raw_string hash) in
454454+ "sha-256=:" ^ b64 ^ ":"
455455+ | `Sha512 ->
456456+ let hash = Digestif.SHA512.digest_string body in
457457+ let b64 = Base64.encode_string (Digestif.SHA512.to_raw_string hash) in
458458+ "sha-512=:" ^ b64 ^ ":"
459459+460460+ let add ~algorithm ~body headers =
461461+ let value = compute ~algorithm ~body in
462462+ Headers.set `Content_digest value headers
463463+464464+ let verify ~header ~body =
465465+ (* Extract base64 value from :base64: format *)
466466+ let extract_b64 rest =
467467+ if String.length rest >= 2 && rest.[0] = ':' then
468468+ let end_idx = String.rindex rest ':' in
469469+ if end_idx > 0 then Some (String.sub rest 1 (end_idx - 1))
470470+ else None
471471+ else None
472472+ in
473473+ (* Verify a single digest entry *)
474474+ let verify_one part =
475475+ let part = String.trim part in
476476+ let try_algorithm ~prefix ~hash_fn ~name =
477477+ if String.length part > String.length prefix &&
478478+ String.sub part 0 (String.length prefix) = prefix then
479479+ let rest = String.sub part (String.length prefix)
480480+ (String.length part - String.length prefix) in
481481+ match extract_b64 rest with
482482+ | None -> Some (Error "Invalid Content-Digest format")
483483+ | Some b64 ->
484484+ match Base64.decode b64 with
485485+ | Error _ -> Some (Error "Invalid base64 in Content-Digest")
486486+ | Ok expected ->
487487+ let actual = hash_fn body in
488488+ if Eqaf.equal expected actual then Some (Ok ())
489489+ else Some (Error ("Content-Digest mismatch (" ^ name ^ ")"))
490490+ else None
491491+ in
492492+ match try_algorithm ~prefix:"sha-256="
493493+ ~hash_fn:Digestif.SHA256.(fun s -> to_raw_string (digest_string s))
494494+ ~name:"sha-256" with
495495+ | Some r -> r
496496+ | None ->
497497+ match try_algorithm ~prefix:"sha-512="
498498+ ~hash_fn:Digestif.SHA512.(fun s -> to_raw_string (digest_string s))
499499+ ~name:"sha-512" with
500500+ | Some r -> r
501501+ | None -> Error ("Unknown Content-Digest algorithm: " ^ part)
502502+ in
503503+ let parts = String.split_on_char ',' header in
504504+ let results = List.map verify_one parts in
505505+ if List.exists Result.is_ok results then Ok ()
506506+ else match results with
507507+ | [] -> Error "Empty Content-Digest header"
508508+ | err :: _ -> err
509509+end
510510+511511+(* ========================================================================= *)
512512+(* Signature Base Construction *)
513513+(* ========================================================================= *)
514514+515515+let resolve_component (ctx : Context.t) (component : Component.t) =
516516+ let get_headers : Context.t -> Headers.t = function
517517+ | `Request r -> r.Context.headers
518518+ | `Response r -> r.Context.headers
519519+ in
520520+ let get_request_headers : Context.t -> Headers.t option = function
521521+ | `Request r -> Some r.Context.headers
522522+ | `Response (r : Context.response_ctx) -> Option.map (fun (req : Context.request_ctx) -> req.headers) r.request
523523+ in
524524+ match component with
525525+ | `Derived (d, params) ->
526526+ let has_req = List.mem `Req params in
527527+ let target_ctx : Context.t = if has_req then
528528+ match ctx with
529529+ | `Response { request = Some req; _ } -> `Request req
530530+ | _ -> ctx
531531+ else ctx
532532+ in
533533+ (match d with
534534+ | `Method ->
535535+ (match target_ctx with
536536+ | `Request r -> Ok (Method.to_string r.method_)
537537+ | `Response _ -> Error "Cannot resolve @method on response")
538538+ | `Authority ->
539539+ (match target_ctx with
540540+ | `Request r ->
541541+ let host = Uri.host r.uri |> Option.value ~default:"" in
542542+ let port = Uri.port r.uri in
543543+ let authority = match port with
544544+ | Some p when p <> 80 && p <> 443 -> host ^ ":" ^ string_of_int p
545545+ | _ -> host
546546+ in
547547+ Ok (String.lowercase_ascii authority)
548548+ | `Response _ -> Error "Cannot resolve @authority on response")
549549+ | `Path ->
550550+ (match target_ctx with
551551+ | `Request r ->
552552+ let path = Uri.path r.uri in
553553+ Ok (if path = "" then "/" else path)
554554+ | `Response _ -> Error "Cannot resolve @path on response")
555555+ | `Query ->
556556+ (match target_ctx with
557557+ | `Request r ->
558558+ let query = Uri.query r.uri in
559559+ let encoded = Uri.encoded_of_query query in
560560+ Ok ("?" ^ encoded)
561561+ | `Response _ -> Error "Cannot resolve @query on response")
562562+ | `Query_param name ->
563563+ (match target_ctx with
564564+ | `Request r ->
565565+ (match Uri.get_query_param r.uri name with
566566+ | Some v -> Ok v
567567+ | None -> Error ("Missing query parameter: " ^ name))
568568+ | `Response _ -> Error "Cannot resolve @query-param on response")
569569+ | `Target_uri ->
570570+ (match target_ctx with
571571+ | `Request r -> Ok (Uri.to_string r.uri)
572572+ | `Response _ -> Error "Cannot resolve @target-uri on response")
573573+ | `Status ->
574574+ (match ctx with
575575+ | `Response r -> Ok (string_of_int r.status)
576576+ | `Request _ -> Error "Cannot resolve @status on request")
577577+ | `Request_target ->
578578+ (match target_ctx with
579579+ | `Request r ->
580580+ let path = Uri.path r.uri in
581581+ let path = if path = "" then "/" else path in
582582+ let query = Uri.query r.uri in
583583+ if query = [] then Ok path
584584+ else Ok (path ^ "?" ^ Uri.encoded_of_query query)
585585+ | `Response _ -> Error "Cannot resolve @request-target on response"))
586586+ | `Field (name, params) ->
587587+ let has_req = List.mem `Req params in
588588+ let headers = if has_req then get_request_headers ctx else Some (get_headers ctx) in
589589+ match headers with
590590+ | None -> Error ("Cannot resolve request-bound field: " ^ name)
591591+ | Some hdrs ->
592592+ let name_typed = Header_name.of_string name in
593593+ match Headers.get name_typed hdrs with
594594+ | None -> Error ("Missing header: " ^ name)
595595+ | Some value ->
596596+ (* Apply bs (byte sequence) parameter if present *)
597597+ if List.mem `Bs params then
598598+ Ok (":" ^ Base64.encode_string value ^ ":")
599599+ else
600600+ Ok value
601601+602602+let build_signature_base ~(components : Component.t list) ~(params : Params.t) (ctx : Context.t) =
603603+ let component_lines = List.map (fun c ->
604604+ match resolve_component ctx c with
605605+ | Error e -> Error e
606606+ | Ok value ->
607607+ let id = Component.to_identifier c in
608608+ Ok (id ^ ": " ^ value)
609609+ ) components in
610610+ (* Check for errors *)
611611+ let errors = List.filter_map (function Error e -> Some e | Ok _ -> None) component_lines in
612612+ if errors <> [] then Error (List.hd errors)
613613+ else
614614+ let lines = List.filter_map (function Ok l -> Some l | Error _ -> None) component_lines in
615615+ (* Build @signature-params line *)
616616+ let component_items = List.map Component.to_sf_item components in
617617+ let sf_params = Params.to_sf_params params in
618618+ let sig_params_il : Sf.inner_list = (component_items, sf_params) in
619619+ let sig_params_str = Sf.inner_list_to_string sig_params_il in
620620+ let sig_params_line = "\"@signature-params\": " ^ sig_params_str in
621621+ let base = String.concat "\n" (lines @ [sig_params_line]) in
622622+ Log.debug (fun m -> m "Signature base:\n%s" base);
623623+ Ok base
624624+625625+(* ========================================================================= *)
626626+(* Cryptographic Operations *)
627627+(* ========================================================================= *)
628628+629629+(* RSA-PSS requires a functor instantiation *)
630630+module Rsa_pss_sha512 = Mirage_crypto_pk.Rsa.PSS(Digestif.SHA512)
631631+632632+let sign_bytes ~(alg : Algorithm.t) ~(key : Key.t) (data : string) =
633633+ match alg, key.priv with
634634+ | `Hmac_sha256, Some (Key.Symmetric_priv secret) ->
635635+ let mac = Digestif.SHA256.hmac_string ~key:secret data in
636636+ Ok (Digestif.SHA256.to_raw_string mac)
637637+638638+ | `Ed25519, Some (Key.Ed25519_priv priv) ->
639639+ (match Mirage_crypto_ec.Ed25519.priv_of_octets priv with
640640+ | Error _ -> Error "Invalid Ed25519 private key"
641641+ | Ok priv_key ->
642642+ let sig_ = Mirage_crypto_ec.Ed25519.sign ~key:priv_key data in
643643+ Ok sig_)
644644+645645+ | `Ecdsa_p256_sha256, Some (Key.P256_priv priv) ->
646646+ let hash = Digestif.SHA256.(to_raw_string (digest_string data)) in
647647+ let (r, s) = Mirage_crypto_ec.P256.Dsa.sign ~key:priv hash in
648648+ (* Concatenate r and s for raw signature format *)
649649+ Ok (r ^ s)
650650+651651+ | `Ecdsa_p384_sha384, Some (Key.P384_priv priv) ->
652652+ let hash = Digestif.SHA384.(to_raw_string (digest_string data)) in
653653+ let (r, s) = Mirage_crypto_ec.P384.Dsa.sign ~key:priv hash in
654654+ Ok (r ^ s)
655655+656656+ | `Rsa_pss_sha512, Some (Key.Rsa_priv priv) ->
657657+ let hash = Digestif.SHA512.(to_raw_string (digest_string data)) in
658658+ let sig_ = Rsa_pss_sha512.sign ~key:priv (`Digest hash) in
659659+ Ok sig_
660660+661661+ | `Rsa_v1_5_sha256, Some (Key.Rsa_priv priv) ->
662662+ let hash = Digestif.SHA256.(to_raw_string (digest_string data)) in
663663+ let sig_ = Mirage_crypto_pk.Rsa.PKCS1.sign ~hash:`SHA256 ~key:priv (`Digest hash) in
664664+ Ok sig_
665665+666666+ | alg, None -> Error ("Missing private key for " ^ Algorithm.to_string alg)
667667+ | _, _ -> Error "Key type mismatch for algorithm"
668668+669669+let verify_bytes ~(alg : Algorithm.t) ~(key : Key.t) ~(signature : string) (data : string) =
670670+ match alg, key.pub with
671671+ | `Hmac_sha256, Some (Key.Symmetric_pub secret) ->
672672+ let expected = Digestif.SHA256.hmac_string ~key:secret data in
673673+ let expected_str = Digestif.SHA256.to_raw_string expected in
674674+ if Eqaf.equal signature expected_str then Ok ()
675675+ else Error "HMAC signature mismatch"
676676+677677+ | `Ed25519, Some (Key.Ed25519_pub pub) ->
678678+ (match Mirage_crypto_ec.Ed25519.pub_of_octets pub with
679679+ | Error _ -> Error "Invalid Ed25519 public key"
680680+ | Ok pub_key ->
681681+ let valid = Mirage_crypto_ec.Ed25519.verify ~key:pub_key signature ~msg:data in
682682+ if valid then Ok () else Error "Ed25519 signature verification failed")
683683+684684+ | `Ecdsa_p256_sha256, Some (Key.P256_pub pub) ->
685685+ let hash = Digestif.SHA256.(to_raw_string (digest_string data)) in
686686+ (* Split signature into r and s (each 32 bytes for P-256) *)
687687+ if String.length signature <> 64 then Error "Invalid P-256 signature length"
688688+ else
689689+ let r = String.sub signature 0 32 in
690690+ let s = String.sub signature 32 32 in
691691+ let valid = Mirage_crypto_ec.P256.Dsa.verify ~key:pub (r, s) hash in
692692+ if valid then Ok () else Error "ECDSA P-256 signature verification failed"
693693+694694+ | `Ecdsa_p384_sha384, Some (Key.P384_pub pub) ->
695695+ let hash = Digestif.SHA384.(to_raw_string (digest_string data)) in
696696+ (* Split signature into r and s (each 48 bytes for P-384) *)
697697+ if String.length signature <> 96 then Error "Invalid P-384 signature length"
698698+ else
699699+ let r = String.sub signature 0 48 in
700700+ let s = String.sub signature 48 48 in
701701+ let valid = Mirage_crypto_ec.P384.Dsa.verify ~key:pub (r, s) hash in
702702+ if valid then Ok () else Error "ECDSA P-384 signature verification failed"
703703+704704+ | `Rsa_pss_sha512, Some (Key.Rsa_pub pub) ->
705705+ let hash = Digestif.SHA512.(to_raw_string (digest_string data)) in
706706+ let valid = Rsa_pss_sha512.verify ~key:pub ~signature (`Digest hash) in
707707+ if valid then Ok () else Error "RSA-PSS signature verification failed"
708708+709709+ | `Rsa_v1_5_sha256, Some (Key.Rsa_pub pub) ->
710710+ let hash = Digestif.SHA256.(to_raw_string (digest_string data)) in
711711+ let hashp = function `SHA256 -> true | _ -> false in
712712+ let valid = Mirage_crypto_pk.Rsa.PKCS1.verify ~hashp ~key:pub
713713+ ~signature (`Digest hash) in
714714+ if valid then Ok () else Error "RSA-PKCS1 signature verification failed"
715715+716716+ | alg, None -> Error ("Missing public key for " ^ Algorithm.to_string alg)
717717+ | _, _ -> Error "Key type mismatch for algorithm"
718718+719719+(* ========================================================================= *)
720720+(* Configuration *)
721721+(* ========================================================================= *)
722722+723723+type config = {
724724+ key : Key.t;
725725+ keyid : string option;
726726+ components : Component.t list;
727727+ tag : string option;
728728+ include_created : bool;
729729+ label : string;
730730+}
731731+732732+let default_components = [
733733+ Component.method_;
734734+ Component.authority;
735735+ Component.path;
736736+]
737737+738738+let config ~key ?keyid ?(components = default_components) ?tag
739739+ ?(include_created = true) ?(label = "sig1") () =
740740+ { key; keyid; components; tag; include_created; label }
741741+742742+(* ========================================================================= *)
743743+(* Signing *)
744744+(* ========================================================================= *)
745745+746746+type sign_error = [
747747+ | `Key_algorithm_mismatch of string
748748+ | `Missing_private_key
749749+ | `Component_resolution_error of string
750750+ | `Crypto_error of string
751751+]
752752+753753+let sign_error_to_string : sign_error -> string = function
754754+ | `Key_algorithm_mismatch s -> "Key/algorithm mismatch: " ^ s
755755+ | `Missing_private_key -> "Missing private key"
756756+ | `Component_resolution_error s -> "Component resolution error: " ^ s
757757+ | `Crypto_error s -> "Cryptographic error: " ^ s
758758+759759+let sign ~(config : config) ~(context : Context.t) ~(headers : Headers.t) =
760760+ if not (Key.can_sign config.key) then Error `Missing_private_key
761761+ else
762762+ let alg = Key.algorithm config.key |> Option.value ~default:`Ed25519 in
763763+ let params =
764764+ Params.empty
765765+ |> (fun p -> if config.include_created then Params.created (Ptime_clock.now ()) p else p)
766766+ |> Option.fold ~none:Fun.id ~some:Params.keyid config.keyid
767767+ |> Option.fold ~none:Fun.id ~some:Params.tag config.tag
768768+ |> Params.alg alg
769769+ in
770770+ let* base = Result.map_error (fun e -> `Component_resolution_error e)
771771+ (build_signature_base ~components:config.components ~params context) in
772772+ let* sig_bytes = Result.map_error (fun e -> `Crypto_error e)
773773+ (sign_bytes ~alg ~key:config.key base) in
774774+ (* Build headers *)
775775+ let component_items = List.map Component.to_sf_item config.components in
776776+ let sf_params = Params.to_sf_params params in
777777+ let sig_input_il : Sf.inner_list = (component_items, sf_params) in
778778+ let sig_input_header = Sf.dictionary_to_string [(config.label, Sf.Inner_list sig_input_il)] in
779779+ let sig_header = Sf.dictionary_to_string [(config.label, Sf.Item (Sf.Byte_seq sig_bytes, []))] in
780780+ Log.debug (fun m -> m "Signature-Input: %s" sig_input_header);
781781+ Log.debug (fun m -> m "Signature: %s" sig_header);
782782+ Ok (headers
783783+ |> Headers.set `Signature_input sig_input_header
784784+ |> Headers.set `Signature sig_header)
785785+786786+let sign_with_digest ~(config : config) ~(context : Context.t) ~(headers : Headers.t)
787787+ ~(body : string) ~(digest_algorithm : Content_digest.algorithm) =
788788+ (* Add Content-Digest header *)
789789+ let headers = Content_digest.add ~algorithm:digest_algorithm ~body headers in
790790+ (* Add content-digest to components if not already present *)
791791+ let has_content_digest = List.exists (function
792792+ | `Field ("content-digest", _) -> true
793793+ | _ -> false
794794+ ) config.components in
795795+ let components = if has_content_digest then config.components
796796+ else config.components @ [Component.content_digest]
797797+ in
798798+ let config = { config with components } in
799799+ sign ~config ~context ~headers
800800+801801+(* ========================================================================= *)
802802+(* Verification *)
803803+(* ========================================================================= *)
804804+805805+type verify_error = [
806806+ | `Missing_signature_header
807807+ | `Missing_signature_input_header
808808+ | `Invalid_signature_input of string
809809+ | `Signature_label_not_found of string
810810+ | `Key_algorithm_mismatch of string
811811+ | `Missing_public_key
812812+ | `Component_resolution_error of string
813813+ | `Signature_mismatch
814814+ | `Signature_expired
815815+ | `Required_component_missing of string
816816+ | `Crypto_error of string
817817+]
818818+819819+let verify_error_to_string : verify_error -> string = function
820820+ | `Missing_signature_header -> "Missing Signature header"
821821+ | `Missing_signature_input_header -> "Missing Signature-Input header"
822822+ | `Invalid_signature_input s -> "Invalid Signature-Input: " ^ s
823823+ | `Signature_label_not_found s -> "Signature label not found: " ^ s
824824+ | `Key_algorithm_mismatch s -> "Key/algorithm mismatch: " ^ s
825825+ | `Missing_public_key -> "Missing public key"
826826+ | `Component_resolution_error s -> "Component resolution error: " ^ s
827827+ | `Signature_mismatch -> "Signature verification failed"
828828+ | `Signature_expired -> "Signature has expired"
829829+ | `Required_component_missing s -> "Required component missing from signature: " ^ s
830830+ | `Crypto_error s -> "Cryptographic error: " ^ s
831831+832832+type verify_result = {
833833+ label : string;
834834+ keyid : string option;
835835+ created : Ptime.t option;
836836+ expires : Ptime.t option;
837837+ verified_components : Component.t list;
838838+}
839839+840840+let verify ~(key : Key.t) ?label ?max_age ?required_components
841841+ ~(context : Context.t) ~(headers : Headers.t) () =
842842+ (* Helper to require a value or return an error *)
843843+ let require opt err = match opt with Some x -> Ok x | None -> Error err in
844844+ (* Parse components from SF items *)
845845+ let parse_components items =
846846+ List.filter_map (fun (item, item_params) ->
847847+ match item with
848848+ | Sf.String s ->
849849+ let params_str = String.concat "" (List.map (fun (k, v) ->
850850+ match v with
851851+ | Sf.Boolean true -> ";" ^ k
852852+ | Sf.String s -> ";" ^ k ^ "=\"" ^ s ^ "\""
853853+ | _ -> ";" ^ k ^ "=" ^ Sf.item_to_string v
854854+ ) item_params) in
855855+ Result.to_option (Component.of_identifier (s ^ params_str))
856856+ | _ -> None
857857+ ) items
858858+ in
859859+ (* Check required components are present *)
860860+ let check_required components =
861861+ match required_components with
862862+ | None -> Ok ()
863863+ | Some req ->
864864+ let missing = List.find_opt (fun rc ->
865865+ not (List.exists (fun c ->
866866+ Component.to_identifier c = Component.to_identifier rc
867867+ ) components)
868868+ ) req in
869869+ match missing with
870870+ | Some m -> Error (`Required_component_missing (Component.to_identifier m))
871871+ | None -> Ok ()
872872+ in
873873+ (* Check signature hasn't expired *)
874874+ let check_expiration sig_params =
875875+ match sig_params.Params.expires with
876876+ | Some exp when Ptime.is_earlier exp ~than:(Ptime_clock.now ()) ->
877877+ Error `Signature_expired
878878+ | _ -> Ok ()
879879+ in
880880+ (* Check signature isn't too old *)
881881+ let check_max_age sig_params =
882882+ match max_age, sig_params.Params.created with
883883+ | Some age, Some created ->
884884+ let now = Ptime_clock.now () in
885885+ (match Ptime.add_span created age with
886886+ | Some limit when Ptime.is_earlier limit ~than:now -> Error `Signature_expired
887887+ | _ -> Ok ())
888888+ | _ -> Ok ()
889889+ in
890890+ (* Main verification flow using let* *)
891891+ if not (Key.can_verify key) then Error `Missing_public_key
892892+ else
893893+ let* sig_header = require (Headers.get `Signature headers) `Missing_signature_header in
894894+ let* sig_input_header = require (Headers.get `Signature_input headers) `Missing_signature_input_header in
895895+ let* sig_dict = Result.map_error (fun e -> `Invalid_signature_input ("Signature: " ^ e))
896896+ (Sf.parse_dictionary sig_header) in
897897+ let* sig_input_dict = Result.map_error (fun e -> `Invalid_signature_input ("Signature-Input: " ^ e))
898898+ (Sf.parse_dictionary sig_input_header) in
899899+ let target_label = label |> Option.value ~default:(
900900+ match sig_dict with (l, _) :: _ -> l | [] -> "sig1"
901901+ ) in
902902+ let* sig_member = require (List.assoc_opt target_label sig_dict)
903903+ (`Signature_label_not_found target_label) in
904904+ let* sig_bytes = match sig_member with
905905+ | Sf.Item (Sf.Byte_seq s, _) -> Ok s
906906+ | _ -> Error (`Invalid_signature_input "Expected byte sequence")
907907+ in
908908+ let* input_member = require (List.assoc_opt target_label sig_input_dict)
909909+ (`Signature_label_not_found target_label) in
910910+ let* (items, params) = match input_member with
911911+ | Sf.Inner_list il -> Ok il
912912+ | _ -> Error (`Invalid_signature_input "Expected inner list")
913913+ in
914914+ let components = parse_components items in
915915+ let sig_params = Params.of_sf_params params in
916916+ let alg = match sig_params.Params.alg, Key.algorithm key with
917917+ | Some a, _ -> a
918918+ | None, Some a -> a
919919+ | None, None -> `Ed25519
920920+ in
921921+ let* () = check_required components in
922922+ let* () = check_expiration sig_params in
923923+ let* () = check_max_age sig_params in
924924+ let* base = Result.map_error (fun e -> `Component_resolution_error e)
925925+ (build_signature_base ~components ~params:sig_params context) in
926926+ let* () = Result.map_error (fun e -> `Crypto_error e)
927927+ (verify_bytes ~alg ~key ~signature:sig_bytes base) in
928928+ Ok {
929929+ label = target_label;
930930+ keyid = sig_params.Params.keyid;
931931+ created = sig_params.Params.created;
932932+ expires = sig_params.Params.expires;
933933+ verified_components = components;
934934+ }
935935+936936+let verify_all ~key_resolver ?max_age ~(context : Context.t) ~(headers : Headers.t) () =
937937+ match Headers.get `Signature headers, Headers.get `Signature_input headers with
938938+ | None, _ -> Error `Missing_signature_header
939939+ | _, None -> Error `Missing_signature_input_header
940940+ | Some sig_header, Some sig_input_header ->
941941+ match Sf.parse_dictionary sig_header, Sf.parse_dictionary sig_input_header with
942942+ | Error e, _ -> Error (`Invalid_signature_input e)
943943+ | _, Error e -> Error (`Invalid_signature_input e)
944944+ | Ok sig_dict, Ok sig_input_dict ->
945945+ let results = List.filter_map (fun (label, _) ->
946946+ (* Get keyid from signature-input *)
947947+ let keyid = match List.assoc_opt label sig_input_dict with
948948+ | Some (Sf.Inner_list (_, params)) ->
949949+ (match List.assoc_opt "keyid" params with
950950+ | Some (Sf.String k) -> Some k
951951+ | _ -> None)
952952+ | _ -> None
953953+ in
954954+ match keyid with
955955+ | None -> None
956956+ | Some kid ->
957957+ match key_resolver kid with
958958+ | None -> None
959959+ | Some key ->
960960+ match verify ~key ~label ?max_age ~context ~headers () with
961961+ | Ok r -> Some (Ok r)
962962+ | Error e -> Some (Error e)
963963+ ) sig_dict in
964964+ let successes = List.filter_map (function Ok r -> Some r | _ -> None) results in
965965+ let errors = List.filter_map (function Error e -> Some e | _ -> None) results in
966966+ if successes <> [] then Ok successes
967967+ else match errors with
968968+ | e :: _ -> Error e
969969+ | [] -> Error (`Signature_label_not_found "no signatures found")
+453
lib/signature.mli
···11+(*---------------------------------------------------------------------------
22+ Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved.
33+ SPDX-License-Identifier: ISC
44+ ---------------------------------------------------------------------------*)
55+66+(** RFC 9421 HTTP Message Signatures
77+88+ This module implements {{:https://datatracker.ietf.org/doc/html/rfc9421}RFC 9421}
99+ HTTP Message Signatures, enabling cryptographic signing and verification of
1010+ HTTP request and response messages.
1111+1212+ {2 Overview}
1313+1414+ HTTP Message Signatures provide a mechanism to create and verify cryptographic
1515+ signatures on HTTP messages. The signature covers specific message components
1616+ (headers, derived values) and metadata (timestamps, key identifiers).
1717+1818+ {2 Example Usage}
1919+2020+ {[
2121+ (* Create a signing configuration *)
2222+ let key = Signature.Key.ed25519
2323+ ~priv:(Base64.decode_exn "private-key-bytes")
2424+ ~pub:(Base64.decode_exn "public-key-bytes") in
2525+ let config = Signature.config
2626+ ~key
2727+ ~keyid:"my-key-id"
2828+ ~components:[Signature.Component.method_; Signature.Component.authority]
2929+ () in
3030+3131+ (* Sign headers *)
3232+ let ctx = Signature.Context.request ~method_:`GET ~uri:(Uri.of_string "https://example.com/") ~headers in
3333+ let signed_headers = Signature.sign ~config ~context:ctx ~headers |> Result.get_ok in
3434+ ]}
3535+3636+ {2 References}
3737+ {ul
3838+ {- {{:https://datatracker.ietf.org/doc/html/rfc9421}RFC 9421} - HTTP Message Signatures}
3939+ {- {{:https://datatracker.ietf.org/doc/html/rfc9530}RFC 9530} - Digest Fields}} *)
4040+4141+(** {1 Algorithms} *)
4242+4343+module Algorithm : sig
4444+ (** Signature algorithms per
4545+ {{:https://datatracker.ietf.org/doc/html/rfc9421#section-3.3}RFC 9421 Section 3.3}. *)
4646+4747+ type t = [
4848+ | `Rsa_pss_sha512 (** RSASSA-PSS using SHA-512 *)
4949+ | `Rsa_v1_5_sha256 (** RSASSA-PKCS1-v1_5 using SHA-256 *)
5050+ | `Hmac_sha256 (** HMAC using SHA-256 *)
5151+ | `Ecdsa_p256_sha256 (** ECDSA using P-256 curve with SHA-256 *)
5252+ | `Ecdsa_p384_sha384 (** ECDSA using P-384 curve with SHA-384 *)
5353+ | `Ed25519 (** EdDSA using curve25519 *)
5454+ ]
5555+5656+ val to_string : t -> string
5757+ (** [to_string alg] returns the algorithm identifier string. *)
5858+5959+ val of_string : string -> t option
6060+ (** [of_string s] parses an algorithm identifier string. *)
6161+6262+ val of_string_exn : string -> t
6363+ (** [of_string_exn s] parses an algorithm identifier or raises [Invalid_argument]. *)
6464+end
6565+6666+(** {1 Message Components} *)
6767+6868+module Component : sig
6969+ (** Message components that can be included in signatures per
7070+ {{:https://datatracker.ietf.org/doc/html/rfc9421#section-2}RFC 9421 Section 2}. *)
7171+7272+ (** {2 Derived Components}
7373+7474+ Derived components are computed from message context, not raw headers.
7575+ Per {{:https://datatracker.ietf.org/doc/html/rfc9421#section-2.2}Section 2.2}. *)
7676+7777+ type derived = [
7878+ | `Method (** [@method] - HTTP request method *)
7979+ | `Authority (** [@authority] - Target host (host:port) *)
8080+ | `Path (** [@path] - Request target path *)
8181+ | `Query (** [@query] - Query string with leading [?] *)
8282+ | `Query_param of string (** [@query-param;name="..."] - Individual query parameter *)
8383+ | `Target_uri (** [@target-uri] - Full target URI *)
8484+ | `Status (** [@status] - Response status code (responses only) *)
8585+ | `Request_target (** [@request-target] - Deprecated form *)
8686+ ]
8787+8888+ (** {2 Component Parameters}
8989+9090+ Parameters that modify component behavior per
9191+ {{:https://datatracker.ietf.org/doc/html/rfc9421#section-2.1}Section 2.1}. *)
9292+9393+ type param = [
9494+ | `Sf (** Strict structured field serialization *)
9595+ | `Key of string (** Dictionary member selection *)
9696+ | `Bs (** Byte sequence wrapping *)
9797+ | `Tr (** Trailer field designation *)
9898+ | `Req (** Request-bound component (for response signatures) *)
9999+ ]
100100+101101+ (** A component identifier, either derived or a header field. *)
102102+ type t = [
103103+ | `Derived of derived * param list
104104+ | `Field of string * param list
105105+ ]
106106+107107+ (** {2 Constructors} *)
108108+109109+ val method_ : t
110110+ (** The [@method] derived component. *)
111111+112112+ val authority : t
113113+ (** The [@authority] derived component. *)
114114+115115+ val path : t
116116+ (** The [@path] derived component. *)
117117+118118+ val query : t
119119+ (** The [@query] derived component. *)
120120+121121+ val query_param : string -> t
122122+ (** [query_param name] creates a [@query-param;name="..."] component. *)
123123+124124+ val target_uri : t
125125+ (** The [@target-uri] derived component. *)
126126+127127+ val status : t
128128+ (** The [@status] derived component (for responses). *)
129129+130130+ val request_target : t
131131+ (** The [@request-target] derived component (deprecated). *)
132132+133133+ val field : string -> t
134134+ (** [field name] creates a header field component (lowercased). *)
135135+136136+ val field_sf : string -> t
137137+ (** [field_sf name] creates a header field with strict structured field serialization. *)
138138+139139+ val field_bs : string -> t
140140+ (** [field_bs name] creates a header field with byte sequence wrapping. *)
141141+142142+ val field_key : string -> key:string -> t
143143+ (** [field_key name ~key] creates a header field selecting a dictionary member. *)
144144+145145+ val field_req : string -> t
146146+ (** [field_req name] creates a request-bound header field (for responses). *)
147147+148148+ (** {2 Common Fields} *)
149149+150150+ val content_type : t
151151+ (** The [content-type] header field. *)
152152+153153+ val content_length : t
154154+ (** The [content-length] header field. *)
155155+156156+ val content_digest : t
157157+ (** The [content-digest] header field (RFC 9530). *)
158158+159159+ val date : t
160160+ (** The [date] header field. *)
161161+162162+ val host : t
163163+ (** The [host] header field. *)
164164+165165+ (** {2 Serialization} *)
166166+167167+ val to_identifier : t -> string
168168+ (** [to_identifier c] returns the component identifier string. *)
169169+170170+ val of_identifier : string -> (t, string) result
171171+ (** [of_identifier s] parses a component identifier. *)
172172+end
173173+174174+(** {1 Signature Parameters} *)
175175+176176+module Params : sig
177177+ (** Signature parameters per
178178+ {{:https://datatracker.ietf.org/doc/html/rfc9421#section-2.3}RFC 9421 Section 2.3}. *)
179179+180180+ type t
181181+ (** Signature parameters. *)
182182+183183+ val empty : t
184184+ (** Empty parameters. *)
185185+186186+ val created : Ptime.t -> t -> t
187187+ (** [created time params] sets the creation timestamp. *)
188188+189189+ val expires : Ptime.t -> t -> t
190190+ (** [expires time params] sets the expiration timestamp. *)
191191+192192+ val nonce : string -> t -> t
193193+ (** [nonce value params] sets a unique nonce. *)
194194+195195+ val alg : Algorithm.t -> t -> t
196196+ (** [alg algorithm params] sets the algorithm identifier. *)
197197+198198+ val keyid : string -> t -> t
199199+ (** [keyid id params] sets the key identifier. *)
200200+201201+ val tag : string -> t -> t
202202+ (** [tag value params] sets an application-specific tag. *)
203203+204204+ val get_created : t -> Ptime.t option
205205+ val get_expires : t -> Ptime.t option
206206+ val get_nonce : t -> string option
207207+ val get_alg : t -> Algorithm.t option
208208+ val get_keyid : t -> string option
209209+ val get_tag : t -> string option
210210+end
211211+212212+(** {1 Key Material} *)
213213+214214+module Key : sig
215215+ (** Cryptographic key material for signing and verification. *)
216216+217217+ type t
218218+ (** A key (may contain private key, public key, or both). *)
219219+220220+ (** {2 Symmetric Keys} *)
221221+222222+ val symmetric : string -> t
223223+ (** [symmetric secret] creates a symmetric key for HMAC algorithms. *)
224224+225225+ (** {2 Ed25519 Keys} *)
226226+227227+ val ed25519 : priv:string -> pub:string -> t
228228+ (** [ed25519 ~priv ~pub] creates an Ed25519 key pair.
229229+ Both [priv] and [pub] should be raw 32-byte keys. *)
230230+231231+ val ed25519_priv : string -> t
232232+ (** [ed25519_priv priv] creates an Ed25519 private key (for signing only). *)
233233+234234+ val ed25519_pub : string -> t
235235+ (** [ed25519_pub pub] creates an Ed25519 public key (for verification only). *)
236236+237237+ (** {2 ECDSA P-256 Keys} *)
238238+239239+ val p256 : priv:Mirage_crypto_ec.P256.Dsa.priv -> t
240240+ (** [p256 ~priv] creates a P-256 key from the private key
241241+ (public key derived automatically). *)
242242+243243+ val p256_pub : Mirage_crypto_ec.P256.Dsa.pub -> t
244244+ (** [p256_pub pub] creates a P-256 public key (for verification only). *)
245245+246246+ (** {2 ECDSA P-384 Keys} *)
247247+248248+ val p384 : priv:Mirage_crypto_ec.P384.Dsa.priv -> t
249249+ (** [p384 ~priv] creates a P-384 key from the private key. *)
250250+251251+ val p384_pub : Mirage_crypto_ec.P384.Dsa.pub -> t
252252+ (** [p384_pub pub] creates a P-384 public key (for verification only). *)
253253+254254+ (** {2 RSA Keys} *)
255255+256256+ val rsa : priv:Mirage_crypto_pk.Rsa.priv -> t
257257+ (** [rsa ~priv] creates an RSA key from the private key. *)
258258+259259+ val rsa_pub : Mirage_crypto_pk.Rsa.pub -> t
260260+ (** [rsa_pub pub] creates an RSA public key (for verification only). *)
261261+262262+ (** {2 Key Properties} *)
263263+264264+ val can_sign : t -> bool
265265+ (** [can_sign key] returns [true] if the key can be used for signing. *)
266266+267267+ val can_verify : t -> bool
268268+ (** [can_verify key] returns [true] if the key can be used for verification. *)
269269+270270+ val algorithm : t -> Algorithm.t option
271271+ (** [algorithm key] returns the algorithm associated with the key, if known. *)
272272+end
273273+274274+(** {1 Signing Context} *)
275275+276276+module Context : sig
277277+ (** Context for resolving message components. *)
278278+279279+ type request_ctx = {
280280+ method_ : Method.t;
281281+ uri : Uri.t;
282282+ headers : Headers.t;
283283+ }
284284+285285+ type response_ctx = {
286286+ status : int;
287287+ headers : Headers.t;
288288+ request : request_ctx option;
289289+ }
290290+291291+ type t = [
292292+ | `Request of request_ctx
293293+ | `Response of response_ctx
294294+ ]
295295+ (** Message context (request or response). *)
296296+297297+ val request :
298298+ method_:Method.t ->
299299+ uri:Uri.t ->
300300+ headers:Headers.t ->
301301+ t
302302+ (** [request ~method_ ~uri ~headers] creates a request context. *)
303303+304304+ val response :
305305+ status:int ->
306306+ headers:Headers.t ->
307307+ ?request:t ->
308308+ unit ->
309309+ t
310310+ (** [response ~status ~headers ?request ()] creates a response context.
311311+ The optional [request] context is used for request-bound components. *)
312312+end
313313+314314+(** {1 Content-Digest} *)
315315+316316+module Content_digest : sig
317317+ (** RFC 9530 Content-Digest support.
318318+319319+ {{:https://datatracker.ietf.org/doc/html/rfc9530}RFC 9530} defines the
320320+ Content-Digest header for message body integrity. *)
321321+322322+ type algorithm = [ `Sha256 | `Sha512 ]
323323+324324+ val compute : algorithm:algorithm -> body:string -> string
325325+ (** [compute ~algorithm ~body] returns the Content-Digest header value. *)
326326+327327+ val add : algorithm:algorithm -> body:string -> Headers.t -> Headers.t
328328+ (** [add ~algorithm ~body headers] adds a Content-Digest header. *)
329329+330330+ val verify : header:string -> body:string -> (unit, string) result
331331+ (** [verify ~header ~body] verifies a Content-Digest header value. *)
332332+end
333333+334334+(** {1 Signing Configuration} *)
335335+336336+type config
337337+(** Configuration for signing requests. *)
338338+339339+val config :
340340+ key:Key.t ->
341341+ ?keyid:string ->
342342+ ?components:Component.t list ->
343343+ ?tag:string ->
344344+ ?include_created:bool ->
345345+ ?label:string ->
346346+ unit ->
347347+ config
348348+(** [config ~key ?keyid ?components ?tag ?include_created ?label ()]
349349+ creates a signing configuration.
350350+351351+ @param key The signing key.
352352+ @param keyid Key identifier (included in signature parameters).
353353+ @param components Components to sign. Default: [[\@method; \@authority; \@path]].
354354+ @param tag Application-specific tag.
355355+ @param include_created Include creation timestamp. Default: [true].
356356+ @param label Signature label for dictionary key. Default: ["sig1"]. *)
357357+358358+val default_components : Component.t list
359359+(** Default components to sign: [[\@method; \@authority; \@path]]. *)
360360+361361+(** {1 Signing} *)
362362+363363+type sign_error = [
364364+ | `Key_algorithm_mismatch of string
365365+ | `Missing_private_key
366366+ | `Component_resolution_error of string
367367+ | `Crypto_error of string
368368+]
369369+370370+val sign_error_to_string : sign_error -> string
371371+(** [sign_error_to_string err] returns a human-readable error message. *)
372372+373373+val sign :
374374+ config:config ->
375375+ context:Context.t ->
376376+ headers:Headers.t ->
377377+ (Headers.t, sign_error) result
378378+(** [sign ~config ~context ~headers] signs the message and returns
379379+ headers with [Signature-Input] and [Signature] headers added. *)
380380+381381+val sign_with_digest :
382382+ config:config ->
383383+ context:Context.t ->
384384+ headers:Headers.t ->
385385+ body:string ->
386386+ digest_algorithm:Content_digest.algorithm ->
387387+ (Headers.t, sign_error) result
388388+(** [sign_with_digest ~config ~context ~headers ~body ~digest_algorithm]
389389+ computes Content-Digest, adds it to headers, and signs.
390390+ The [content-digest] component is automatically added to signed components. *)
391391+392392+(** {1 Verification} *)
393393+394394+type verify_error = [
395395+ | `Missing_signature_header
396396+ | `Missing_signature_input_header
397397+ | `Invalid_signature_input of string
398398+ | `Signature_label_not_found of string
399399+ | `Key_algorithm_mismatch of string
400400+ | `Missing_public_key
401401+ | `Component_resolution_error of string
402402+ | `Signature_mismatch
403403+ | `Signature_expired
404404+ | `Required_component_missing of string
405405+ | `Crypto_error of string
406406+]
407407+408408+val verify_error_to_string : verify_error -> string
409409+(** [verify_error_to_string err] returns a human-readable error message. *)
410410+411411+type verify_result = {
412412+ label : string;
413413+ keyid : string option;
414414+ created : Ptime.t option;
415415+ expires : Ptime.t option;
416416+ verified_components : Component.t list;
417417+}
418418+(** Successful verification result. *)
419419+420420+val verify :
421421+ key:Key.t ->
422422+ ?label:string ->
423423+ ?max_age:Ptime.Span.t ->
424424+ ?required_components:Component.t list ->
425425+ context:Context.t ->
426426+ headers:Headers.t ->
427427+ unit ->
428428+ (verify_result, verify_error) result
429429+(** [verify ~key ?label ?max_age ?required_components ~context ~headers]
430430+ verifies a signature on the message.
431431+432432+ @param key The verification key.
433433+ @param label Signature label to verify. Default: first signature found.
434434+ @param max_age Maximum age of signature. If [created] is present and older
435435+ than [max_age], verification fails.
436436+ @param required_components Components that must be signed. Verification
437437+ fails if any are missing from the signature.
438438+ @param context Message context for component resolution.
439439+ @param headers Headers containing [Signature] and [Signature-Input]. *)
440440+441441+val verify_all :
442442+ key_resolver:(string -> Key.t option) ->
443443+ ?max_age:Ptime.Span.t ->
444444+ context:Context.t ->
445445+ headers:Headers.t ->
446446+ unit ->
447447+ (verify_result list, verify_error) result
448448+(** [verify_all ~key_resolver ?max_age ~context ~headers] verifies all
449449+ signatures on a message.
450450+451451+ @param key_resolver Function to resolve keys by [keyid]. Called for
452452+ each signature with its [keyid] parameter.
453453+ @param max_age Maximum age for all signatures. *)
···11+
22+33+44+55+Internet Engineering Task Force (IETF) A. Backman, Ed.
66+Request for Comments: 9421 Amazon
77+Category: Standards Track J. Richer, Ed.
88+ISSN: 2070-1721 Bespoke Engineering
99+ M. Sporny
1010+ Digital Bazaar
1111+ February 2024
1212+1313+1414+ HTTP Message Signatures
1515+1616+Abstract
1717+1818+ This document describes a mechanism for creating, encoding, and
1919+ verifying digital signatures or message authentication codes over
2020+ components of an HTTP message. This mechanism supports use cases
2121+ where the full HTTP message may not be known to the signer and where
2222+ the message may be transformed (e.g., by intermediaries) before
2323+ reaching the verifier. This document also describes a means for
2424+ requesting that a signature be applied to a subsequent HTTP message
2525+ in an ongoing HTTP exchange.
2626+2727+Status of This Memo
2828+2929+ This is an Internet Standards Track document.
3030+3131+ This document is a product of the Internet Engineering Task Force
3232+ (IETF). It represents the consensus of the IETF community. It has
3333+ received public review and has been approved for publication by the
3434+ Internet Engineering Steering Group (IESG). Further information on
3535+ Internet Standards is available in Section 2 of RFC 7841.
3636+3737+ Information about the current status of this document, any errata,
3838+ and how to provide feedback on it may be obtained at
3939+ https://www.rfc-editor.org/info/rfc9421.
4040+4141+Copyright Notice
4242+4343+ Copyright (c) 2024 IETF Trust and the persons identified as the
4444+ document authors. All rights reserved.
4545+4646+ This document is subject to BCP 78 and the IETF Trust's Legal
4747+ Provisions Relating to IETF Documents
4848+ (https://trustee.ietf.org/license-info) in effect on the date of
4949+ publication of this document. Please review these documents
5050+ carefully, as they describe your rights and restrictions with respect
5151+ to this document. Code Components extracted from this document must
5252+ include Revised BSD License text as described in Section 4.e of the
5353+ Trust Legal Provisions and are provided without warranty as described
5454+ in the Revised BSD License.
5555+5656+Table of Contents
5757+5858+ 1. Introduction
5959+ 1.1. Conventions and Terminology
6060+ 1.2. Requirements
6161+ 1.3. HTTP Message Transformations
6262+ 1.4. Application of HTTP Message Signatures
6363+ 2. HTTP Message Components
6464+ 2.1. HTTP Fields
6565+ 2.1.1. Strict Serialization of HTTP Structured Fields
6666+ 2.1.2. Dictionary Structured Field Members
6767+ 2.1.3. Binary-Wrapped HTTP Fields
6868+ 2.1.4. Trailer Fields
6969+ 2.2. Derived Components
7070+ 2.2.1. Method
7171+ 2.2.2. Target URI
7272+ 2.2.3. Authority
7373+ 2.2.4. Scheme
7474+ 2.2.5. Request Target
7575+ 2.2.6. Path
7676+ 2.2.7. Query
7777+ 2.2.8. Query Parameters
7878+ 2.2.9. Status Code
7979+ 2.3. Signature Parameters
8080+ 2.4. Signing Request Components in a Response Message
8181+ 2.5. Creating the Signature Base
8282+ 3. HTTP Message Signatures
8383+ 3.1. Creating a Signature
8484+ 3.2. Verifying a Signature
8585+ 3.2.1. Enforcing Application Requirements
8686+ 3.3. Signature Algorithms
8787+ 3.3.1. RSASSA-PSS Using SHA-512
8888+ 3.3.2. RSASSA-PKCS1-v1_5 Using SHA-256
8989+ 3.3.3. HMAC Using SHA-256
9090+ 3.3.4. ECDSA Using Curve P-256 DSS and SHA-256
9191+ 3.3.5. ECDSA Using Curve P-384 DSS and SHA-384
9292+ 3.3.6. EdDSA Using Curve edwards25519
9393+ 3.3.7. JSON Web Signature (JWS) Algorithms
9494+ 4. Including a Message Signature in a Message
9595+ 4.1. The Signature-Input HTTP Field
9696+ 4.2. The Signature HTTP Field
9797+ 4.3. Multiple Signatures
9898+ 5. Requesting Signatures
9999+ 5.1. The Accept-Signature Field
100100+ 5.2. Processing an Accept-Signature
101101+ 6. IANA Considerations
102102+ 6.1. HTTP Field Name Registration
103103+ 6.2. HTTP Signature Algorithms Registry
104104+ 6.2.1. Registration Template
105105+ 6.2.2. Initial Contents
106106+ 6.3. HTTP Signature Metadata Parameters Registry
107107+ 6.3.1. Registration Template
108108+ 6.3.2. Initial Contents
109109+ 6.4. HTTP Signature Derived Component Names Registry
110110+ 6.4.1. Registration Template
111111+ 6.4.2. Initial Contents
112112+ 6.5. HTTP Signature Component Parameters Registry
113113+ 6.5.1. Registration Template
114114+ 6.5.2. Initial Contents
115115+ 7. Security Considerations
116116+ 7.1. General Considerations
117117+ 7.1.1. Skipping Signature Verification
118118+ 7.1.2. Use of TLS
119119+ 7.2. Message Processing and Selection
120120+ 7.2.1. Insufficient Coverage
121121+ 7.2.2. Signature Replay
122122+ 7.2.3. Choosing Message Components
123123+ 7.2.4. Choosing Signature Parameters and Derived Components
124124+ over HTTP Fields
125125+ 7.2.5. Signature Labels
126126+ 7.2.6. Multiple Signature Confusion
127127+ 7.2.7. Collision of Application-Specific Signature Tag
128128+ 7.2.8. Message Content
129129+ 7.3. Cryptographic Considerations
130130+ 7.3.1. Cryptography and Signature Collision
131131+ 7.3.2. Key Theft
132132+ 7.3.3. Symmetric Cryptography
133133+ 7.3.4. Key Specification Mixup
134134+ 7.3.5. Non-deterministic Signature Primitives
135135+ 7.3.6. Key and Algorithm Specification Downgrades
136136+ 7.3.7. Signing Signature Values
137137+ 7.4. Matching Signature Parameters to the Target Message
138138+ 7.4.1. Modification of Required Message Parameters
139139+ 7.4.2. Matching Values of Covered Components to Values in the
140140+ Target Message
141141+ 7.4.3. Message Component Source and Context
142142+ 7.4.4. Multiple Message Component Contexts
143143+ 7.5. HTTP Processing
144144+ 7.5.1. Processing Invalid HTTP Field Names as Derived
145145+ Component Names
146146+ 7.5.2. Semantically Equivalent Field Values
147147+ 7.5.3. Parsing Structured Field Values
148148+ 7.5.4. HTTP Versions and Component Ambiguity
149149+ 7.5.5. Canonicalization Attacks
150150+ 7.5.6. Non-List Field Values
151151+ 7.5.7. Padding Attacks with Multiple Field Values
152152+ 7.5.8. Ambiguous Handling of Query Elements
153153+ 8. Privacy Considerations
154154+ 8.1. Identification through Keys
155155+ 8.2. Signatures do not provide confidentiality
156156+ 8.3. Oracles
157157+ 8.4. Required Content
158158+ 9. References
159159+ 9.1. Normative References
160160+ 9.2. Informative References
161161+ Appendix A. Detecting HTTP Message Signatures
162162+ Appendix B. Examples
163163+ B.1. Example Keys
164164+ B.1.1. Example RSA Key
165165+ B.1.2. Example RSA-PSS Key
166166+ B.1.3. Example ECC P-256 Test Key
167167+ B.1.4. Example Ed25519 Test Key
168168+ B.1.5. Example Shared Secret
169169+ B.2. Test Cases
170170+ B.2.1. Minimal Signature Using rsa-pss-sha512
171171+ B.2.2. Selective Covered Components Using rsa-pss-sha512
172172+ B.2.3. Full Coverage Using rsa-pss-sha512
173173+ B.2.4. Signing a Response Using ecdsa-p256-sha256
174174+ B.2.5. Signing a Request Using hmac-sha256
175175+ B.2.6. Signing a Request Using ed25519
176176+ B.3. TLS-Terminating Proxies
177177+ B.4. HTTP Message Transformations
178178+ Acknowledgements
179179+ Authors' Addresses
180180+181181+1. Introduction
182182+183183+ Message integrity and authenticity are security properties that are
184184+ critical to the secure operation of many HTTP applications.
185185+ Application developers typically rely on the transport layer to
186186+ provide these properties, by operating their application over TLS
187187+ [TLS]. However, TLS only guarantees these properties over a single
188188+ TLS connection, and the path between the client and application may
189189+ be composed of multiple independent TLS connections (for example, if
190190+ the application is hosted behind a TLS-terminating gateway or if the
191191+ client is behind a TLS Inspection appliance). In such cases, TLS
192192+ cannot guarantee end-to-end message integrity or authenticity between
193193+ the client and application. Additionally, some operating
194194+ environments present obstacles that make it impractical to use TLS
195195+ (such as the presentation of client certificates from a browser) or
196196+ to use features necessary to provide message authenticity.
197197+ Furthermore, some applications require the binding of a higher-level
198198+ application-specific key to the HTTP message, separate from any TLS
199199+ certificates in use. Consequently, while TLS can meet message
200200+ integrity and authenticity needs for many HTTP-based applications, it
201201+ is not a universal solution.
202202+203203+ Additionally, many applications need to be able to generate and
204204+ verify signatures despite incomplete knowledge of the HTTP message as
205205+ seen on the wire, due to the use of libraries, proxies, or
206206+ application frameworks that alter or hide portions of the message
207207+ from the application at the time of signing or verification. These
208208+ applications need a means to protect the parts of the message that
209209+ are most relevant to the application without having to violate
210210+ layering and abstraction.
211211+212212+ Finally, object-based signature mechanisms such as JSON Web Signature
213213+ [JWS] require the intact conveyance of the exact information that was
214214+ signed. When applying such technologies to an HTTP message, elements
215215+ of the HTTP message need to be duplicated in the object payload
216216+ either directly or through the inclusion of a hash. This practice
217217+ introduces complexity, since the repeated information needs to be
218218+ carefully checked for consistency when the signature is verified.
219219+220220+ This document defines a mechanism for providing end-to-end integrity
221221+ and authenticity for components of an HTTP message by using a
222222+ detached signature on HTTP messages. The mechanism allows
223223+ applications to create digital signatures or message authentication
224224+ codes (MACs) over only the components of the message that are
225225+ meaningful and appropriate for the application. Strict
226226+ canonicalization rules ensure that the verifier can verify the
227227+ signature even if the message has been transformed in many of the
228228+ ways permitted by HTTP.
229229+230230+ The signing mechanism described in this document consists of three
231231+ parts:
232232+233233+ * A common nomenclature and canonicalization rule set for the
234234+ different protocol elements and other components of HTTP messages,
235235+ used to create the signature base (Section 2).
236236+237237+ * Algorithms for generating and verifying signatures over HTTP
238238+ message components using this signature base through the
239239+ application of cryptographic primitives (Section 3).
240240+241241+ * A mechanism for attaching a signature and related metadata to an
242242+ HTTP message and for parsing attached signatures and metadata from
243243+ HTTP messages. To facilitate this, this document defines the
244244+ "Signature-Input" and "Signature" fields (Section 4).
245245+246246+ This document also provides a mechanism for negotiating the use of
247247+ signatures in one or more subsequent messages via the "Accept-
248248+ Signature" field (Section 5). This optional negotiation mechanism
249249+ can be used along with opportunistic or application-driven message
250250+ signatures by either party.
251251+252252+ The mechanisms defined in this document are important tools that can
253253+ be used to build an overall security mechanism for an application.
254254+ This toolkit provides some powerful capabilities but is not
255255+ sufficient in creating an overall security story. In particular, the
256256+ requirements listed in Section 1.4 and the security considerations
257257+ discussed in Section 7 are of high importance to all implementors of
258258+ this specification. For example, this specification does not define
259259+ a means to directly cover HTTP message content (defined in
260260+ Section 6.4 of [HTTP]); rather, it relies on the Digest specification
261261+ [DIGEST] to provide a hash of the message content, as discussed in
262262+ Section 7.2.8.
263263+264264+1.1. Conventions and Terminology
265265+266266+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
267267+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
268268+ "OPTIONAL" in this document are to be interpreted as described in
269269+ BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all
270270+ capitals, as shown here.
271271+272272+ The terms "HTTP message", "HTTP request", "HTTP response", "target
273273+ URI", "gateway", "header field", "intermediary", "request target",
274274+ "trailer field", "sender", "method", and "recipient" are used as
275275+ defined in [HTTP].
276276+277277+ For brevity, the term "signature" on its own is used in this document
278278+ to refer to both digital signatures (which use asymmetric
279279+ cryptography) and keyed MACs (which use symmetric cryptography).
280280+ Similarly, the verb "sign" refers to the generation of either a
281281+ digital signature or keyed MAC over a given signature base. The
282282+ qualified term "digital signature" refers specifically to the output
283283+ of an asymmetric cryptographic signing operation.
284284+285285+ This document uses the following terminology from Section 3 of
286286+ [STRUCTURED-FIELDS] to specify data types: List, Inner List,
287287+ Dictionary, Item, String, Integer, Byte Sequence, and Boolean.
288288+289289+ This document defines several string constructions using ABNF [ABNF]
290290+ and uses the following ABNF rules: VCHAR, SP, DQUOTE, and LF. This
291291+ document uses the following ABNF rules from [STRUCTURED-FIELDS]: sf-
292292+ string, inner-list, and parameters. This document uses the following
293293+ ABNF rules from [HTTP] and [HTTP/1.1]: field-content, obs-fold, and
294294+ obs-text.
295295+296296+ In addition to those listed above, this document uses the following
297297+ terms:
298298+299299+ HTTP Message Signature:
300300+ A digital signature or keyed MAC that covers one or more portions
301301+ of an HTTP message. Note that a given HTTP message can contain
302302+ multiple HTTP message signatures.
303303+304304+ Signer:
305305+ The entity that is generating or has generated an HTTP message
306306+ signature. Note that multiple entities can act as signers and
307307+ apply separate HTTP message signatures to a given HTTP message.
308308+309309+ Verifier:
310310+ An entity that is verifying or has verified an HTTP message
311311+ signature against an HTTP message. Note that an HTTP message
312312+ signature may be verified multiple times, potentially by different
313313+ entities.
314314+315315+ HTTP Message Component:
316316+ A portion of an HTTP message that is capable of being covered by
317317+ an HTTP message signature.
318318+319319+ Derived Component:
320320+ An HTTP message component derived from the HTTP message through
321321+ the use of a specified algorithm or process. See Section 2.2.
322322+323323+ HTTP Message Component Name:
324324+ A String that identifies an HTTP message component's source, such
325325+ as a field name or derived component name.
326326+327327+ HTTP Message Component Identifier:
328328+ The combination of an HTTP message component name and any
329329+ parameters. This combination uniquely identifies a specific HTTP
330330+ message component with respect to a particular HTTP message
331331+ signature and the HTTP message it applies to.
332332+333333+ HTTP Message Component Value:
334334+ The value associated with a given component identifier within the
335335+ context of a particular HTTP message. Component values are
336336+ derived from the HTTP message and are usually subject to a
337337+ canonicalization process.
338338+339339+ Covered Components:
340340+ An ordered set of HTTP message component identifiers for fields
341341+ (Section 2.1) and derived components (Section 2.2) that indicates
342342+ the set of message components covered by the signature, never
343343+ including the @signature-params identifier itself. The order of
344344+ this set is preserved and communicated between the signer and
345345+ verifier to facilitate reconstruction of the signature base.
346346+347347+ Signature Base:
348348+ The sequence of bytes generated by the signer and verifier using
349349+ the covered components set and the HTTP message. The signature
350350+ base is processed by the cryptographic algorithm to produce or
351351+ verify the HTTP message signature.
352352+353353+ HTTP Message Signature Algorithm:
354354+ A cryptographic algorithm that describes the signing and
355355+ verification process for the signature, defined in terms of the
356356+ HTTP_SIGN and HTTP_VERIFY primitives described in Section 3.3.
357357+358358+ Key Material:
359359+ The key material required to create or verify the signature. The
360360+ key material is often identified with an explicit key identifier,
361361+ allowing the signer to indicate to the verifier which key was
362362+ used.
363363+364364+ Creation Time:
365365+ A timestamp representing the point in time that the signature was
366366+ generated, as asserted by the signer.
367367+368368+ Expiration Time:
369369+ A timestamp representing the point in time after which the
370370+ signature should no longer be accepted by the verifier, as
371371+ asserted by the signer.
372372+373373+ Target Message:
374374+ The HTTP message to which an HTTP message signature is applied.
375375+376376+ Signature Context:
377377+ The data source from which the HTTP message component values are
378378+ drawn. The context includes the target message and any additional
379379+ information the signer or verifier might have, such as the full
380380+ target URI of a request or the related request message for a
381381+ response.
382382+383383+ The term "UNIX timestamp" refers to what Section 4.16 of [POSIX.1]
384384+ calls "seconds since the Epoch".
385385+386386+ This document contains non-normative examples of partial and complete
387387+ HTTP messages. Some examples use a single trailing backslash (\) to
388388+ indicate line wrapping for long values, as per [RFC8792]. The \
389389+ character and leading spaces on wrapped lines are not part of the
390390+ value.
391391+392392+1.2. Requirements
393393+394394+ HTTP permits, and sometimes requires, intermediaries to transform
395395+ messages in a variety of ways. This can result in a recipient
396396+ receiving a message that is not bitwise-equivalent to the message
397397+ that was originally sent. In such a case, the recipient will be
398398+ unable to verify integrity protections over the raw bytes of the
399399+ sender's HTTP message, as verifying digital signatures or MACs
400400+ requires both signer and verifier to have the exact same signature
401401+ base. Since the exact raw bytes of the message cannot be relied upon
402402+ as a reliable source for a signature base, the signer and verifier
403403+ have to independently create the signature base from their respective
404404+ versions of the message, via a mechanism that is resilient to safe
405405+ changes that do not alter the meaning of the message.
406406+407407+ For a variety of reasons, it is impractical to strictly define what
408408+ constitutes a safe change versus an unsafe one. Applications use
409409+ HTTP in a wide variety of ways and may disagree on whether a
410410+ particular piece of information in a message (e.g., the message
411411+ content, the method, or a particular header field) is relevant.
412412+ Thus, a general-purpose solution needs to provide signers with some
413413+ degree of control over which message components are signed.
414414+415415+ HTTP applications may be running in environments that do not provide
416416+ complete access to or control over HTTP messages (such as a web
417417+ browser's JavaScript environment) or may be using libraries that
418418+ abstract away the details of the protocol (such as the Java HTTP
419419+ Client (HttpClient) library
420420+ (https://openjdk.java.net/groups/net/httpclient/intro.html)). These
421421+ applications need to be able to generate and verify signatures
422422+ despite incomplete knowledge of the HTTP message.
423423+424424+1.3. HTTP Message Transformations
425425+426426+ As mentioned earlier, HTTP explicitly permits, and in some cases
427427+ requires, implementations to transform messages in a variety of ways.
428428+ Implementations are required to tolerate many of these
429429+ transformations. What follows is a non-normative and non-exhaustive
430430+ list of transformations that could occur under HTTP, provided as
431431+ context:
432432+433433+ * Reordering of fields with different field names (Section 5.3 of
434434+ [HTTP]).
435435+436436+ * Combination of fields with the same field name (Section 5.2 of
437437+ [HTTP]).
438438+439439+ * Removal of fields listed in the Connection header field
440440+ (Section 7.6.1 of [HTTP]).
441441+442442+ * Addition of fields that indicate control options (Section 7.6.1 of
443443+ [HTTP]).
444444+445445+ * Addition or removal of a transfer coding (Section 7.7 of [HTTP]).
446446+447447+ * Addition of fields such as Via (Section 7.6.3 of [HTTP]) and
448448+ Forwarded (Section 4 of [RFC7239]).
449449+450450+ * Conversion between different versions of HTTP (e.g., HTTP/1.x to
451451+ HTTP/2, or vice versa).
452452+453453+ * Changes in case (e.g., "Origin" to "origin") of any case-
454454+ insensitive components such as field names, request URI scheme, or
455455+ host.
456456+457457+ * Changes to the request target and authority that, when applied
458458+ together, do not result in a change to the message's target URI,
459459+ as defined in Section 7.1 of [HTTP].
460460+461461+ Additionally, there are some transformations that are either
462462+ deprecated or otherwise not allowed but that could still occur in the
463463+ wild. These transformations can still be handled without breaking
464464+ the signature; they include such actions as:
465465+466466+ * Use, addition, or removal of leading or trailing whitespace in a
467467+ field value.
468468+469469+ * Use, addition, or removal of obs-fold in field values (Section 5.2
470470+ of [HTTP/1.1]).
471471+472472+ We can identify these types of transformations as transformations
473473+ that should not prevent signature verification, even when performed
474474+ on message components covered by the signature. Additionally, all
475475+ changes to components not covered by the signature should not prevent
476476+ signature verification.
477477+478478+ Some examples of these kinds of transformations, and the effect they
479479+ have on the message signature, are found in Appendix B.4.
480480+481481+ Other transformations, such as parsing and reserializing the field
482482+ values of a covered component or changing the value of a derived
483483+ component, can cause a signature to no longer validate against a
484484+ target message. Applications of this specification need to take care
485485+ to ensure that the transformations expected by the application are
486486+ adequately handled by the choice of covered components.
487487+488488+1.4. Application of HTTP Message Signatures
489489+490490+ HTTP message signatures are designed to be a general-purpose tool
491491+ applicable in a wide variety of circumstances and applications. In
492492+ order to properly and safely apply HTTP message signatures, an
493493+ application or profile of this specification MUST specify, at a
494494+ minimum, all of the following items:
495495+496496+ * The set of component identifiers (Section 2) and signature
497497+ parameters (Section 2.3) that are expected and required to be
498498+ included in the covered components list. For example, an
499499+ authorization protocol could mandate that the Authorization field
500500+ be covered to protect the authorization credentials and mandate
501501+ that the signature parameters contain a created parameter
502502+ (Section 2.3), while an API expecting semantically relevant HTTP
503503+ message content could require the Content-Digest field defined in
504504+ [DIGEST] to be present and covered as well as mandate a value for
505505+ the tag parameter (Section 2.3) that is specific to the API being
506506+ protected.
507507+508508+ * The expected Structured Field types [STRUCTURED-FIELDS] of any
509509+ required or expected covered component fields or parameters.
510510+511511+ * A means of retrieving the key material used to verify the
512512+ signature. An application will usually use the keyid parameter of
513513+ the signature parameters (Section 2.3) and define rules for
514514+ resolving a key from there, though the appropriate key could be
515515+ known from other means such as preregistration of a signer's key.
516516+517517+ * The set of allowable signature algorithms to be used by signers
518518+ and accepted by verifiers.
519519+520520+ * A means of determining that the signature algorithm used to verify
521521+ the signature is appropriate for the key material and context of
522522+ the message. For example, the process could use the alg parameter
523523+ of the signature parameters (Section 2.3) to state the algorithm
524524+ explicitly, derive the algorithm from the key material, or use
525525+ some preconfigured algorithm agreed upon by the signer and
526526+ verifier.
527527+528528+ * A means of determining that a given key and algorithm used for a
529529+ signature are appropriate for the context of the message. For
530530+ example, a server expecting only ECDSA signatures should know to
531531+ reject any RSA signatures, or a server expecting asymmetric
532532+ cryptography should know to reject any symmetric cryptography.
533533+534534+ * A means of determining the context for derivation of message
535535+ components from an HTTP message and its application context.
536536+ While this is normally the target HTTP message itself, the context
537537+ could include additional information known to the application
538538+ through configuration, such as an external hostname.
539539+540540+ * If binding between a request and response is needed using the
541541+ mechanism provided in Section 2.4, all elements of the request
542542+ message and the response message that would be required to provide
543543+ properties of such a binding.
544544+545545+ * The error messages and codes that are returned from the verifier
546546+ to the signer when the signature is invalid, the key material is
547547+ inappropriate, the validity time window is out of specification, a
548548+ component value cannot be calculated, or any other errors occur
549549+ during the signature verification process. For example, if a
550550+ signature is being used as an authentication mechanism, an HTTP
551551+ status code of 401 (Unauthorized) or 403 (Forbidden) could be
552552+ appropriate. If the response is from an HTTP API, a response with
553553+ an HTTP status code such as 400 (Bad Request) could include more
554554+ details [RFC7807] [RFC9457], such as an indicator that the wrong
555555+ key material was used.
556556+557557+ When choosing these parameters, an application of HTTP message
558558+ signatures has to ensure that the verifier will have access to all
559559+ required information needed to recreate the signature base. For
560560+ example, a server behind a reverse proxy would need to know the
561561+ original request URI to make use of the derived component @target-
562562+ uri, even though the apparent target URI would be changed by the
563563+ reverse proxy (see also Section 7.4.3). Additionally, an application
564564+ using signatures in responses would need to ensure that clients
565565+ receiving signed responses have access to all the signed portions of
566566+ the message, including any portions of the request that were signed
567567+ by the server using the req ("request-response") parameter
568568+ (Section 2.4).
569569+570570+ Details regarding this kind of profiling are within the purview of
571571+ the application and outside the scope of this specification; however,
572572+ some additional considerations are discussed in Section 7. In
573573+ particular, when choosing the required set of component identifiers,
574574+ care has to be taken to make sure that the coverage is sufficient for
575575+ the application, as discussed in Sections 7.2.1 and 7.2.8. This
576576+ specification defines only part of a full security system for an
577577+ application. When building a complete security system based on this
578578+ tool, it is important to perform a security analysis of the entire
579579+ system, of which HTTP message signatures is a part. Historical
580580+ systems, such as AWS Signature Version 4 [AWS-SIGv4], can provide
581581+ inspiration and examples of how to apply similar mechanisms to an
582582+ application, though review of such historical systems does not negate
583583+ the need for a security analysis of an application of HTTP message
584584+ signatures.
585585+586586+2. HTTP Message Components
587587+588588+ In order to allow signers and verifiers to establish which components
589589+ are covered by a signature, this document defines component
590590+ identifiers for components covered by an HTTP message signature, a
591591+ set of rules for deriving and canonicalizing the values associated
592592+ with these component identifiers from the HTTP message, and the means
593593+ for combining these canonicalized values into a signature base.
594594+595595+ The signature context for deriving these values MUST be accessible to
596596+ both the signer and the verifier of the message. The context MUST be
597597+ the same across all components in a given signature. For example, it
598598+ would be an error to use the raw query string for the @query derived
599599+ component but combined query and form parameters for the @query-param
600600+ derived component. For more considerations regarding the message
601601+ component context, see Section 7.4.3.
602602+603603+ A component identifier is composed of a component name and any
604604+ parameters associated with that name. Each component name is either
605605+ an HTTP field name (Section 2.1) or a registered derived component
606606+ name (Section 2.2). The possible parameters for a component
607607+ identifier are dependent on the component identifier. The "HTTP
608608+ Signature Component Parameters" registry, which catalogs all possible
609609+ parameters, is defined in Section 6.5.
610610+611611+ Within a single list of covered components, each component identifier
612612+ MUST occur only once. One component identifier is distinct from
613613+ another if the component name differs or if any of the parameters
614614+ differ for the same component name. Multiple component identifiers
615615+ having the same component name MAY be included if they have
616616+ parameters that make them distinct, such as "foo";bar and "foo";baz.
617617+ The order of parameters MUST be preserved when processing a component
618618+ identifier (such as when parsing during verification), but the order
619619+ of parameters is not significant when comparing two component
620620+ identifiers for equality checks. That is to say, "foo";bar;baz
621621+ cannot be in the same message as "foo";baz;bar, since these two
622622+ component identifiers are equivalent, but a system processing one
623623+ form is not allowed to transform it into the other form.
624624+625625+ The component value associated with a component identifier is defined
626626+ by the identifier itself. Component values MUST NOT contain newline
627627+ (\n) characters. Some HTTP message components can undergo
628628+ transformations that change the bitwise value without altering the
629629+ meaning of the component's value (for example, when combining field
630630+ values). Message component values therefore need to be canonicalized
631631+ before they are signed, to ensure that a signature can be verified
632632+ despite such intermediary transformations. This document defines
633633+ rules for each component identifier that transform the identifier's
634634+ associated component value into such a canonical form.
635635+636636+ The following sections define component identifier names, their
637637+ parameters, their associated values, and the canonicalization rules
638638+ for their values. The method for combining message components into
639639+ the signature base is defined in Section 2.5.
640640+641641+2.1. HTTP Fields
642642+643643+ The component name for an HTTP field is the lowercased form of its
644644+ field name as defined in Section 5.1 of [HTTP]. While HTTP field
645645+ names are case insensitive, implementations MUST use lowercased field
646646+ names (e.g., content-type, date, etag) when using them as component
647647+ names.
648648+649649+ The component value for an HTTP field is the field value for the
650650+ named field as defined in Section 5.5 of [HTTP]. The field value
651651+ MUST be taken from the named header field of the target message
652652+ unless this behavior is overridden by additional parameters and
653653+ rules, such as the req and tr flags, below. For most fields, the
654654+ field value is an ASCII string as recommended by [HTTP], and the
655655+ component value is exactly that string. Other encodings could exist
656656+ in some implementations, and all non-ASCII field values MUST be
657657+ encoded to ASCII before being added to the signature base. The bs
658658+ parameter, as described in Section 2.1.3, provides a method for
659659+ wrapping such problematic field values.
660660+661661+ Unless overridden by additional parameters and rules, HTTP field
662662+ values MUST be combined into a single value as defined in Section 5.2
663663+ of [HTTP] to create the component value. Specifically, HTTP fields
664664+ sent as multiple fields MUST be combined by concatenating the values
665665+ using a single comma and a single space as a separator ("," + " ").
666666+ Note that intermediaries are allowed to combine values of HTTP fields
667667+ with any amount of whitespace between the commas, and if this
668668+ behavior is not accounted for by the verifier, the signature can
669669+ fail, since the signer and verifier will see a different component
670670+ value in their respective signature bases. For robustness, it is
671671+ RECOMMENDED that signed messages include only a single instance of
672672+ any field covered under the signature, particularly with the value
673673+ for any list-based fields serialized using the algorithm below. This
674674+ approach increases the chances of the field value remaining untouched
675675+ through intermediaries. Where that approach is not possible and
676676+ multiple instances of a field need to be sent separately, it is
677677+ RECOMMENDED that signers and verifiers process any list-based fields
678678+ taking all individual field values and combining them based on the
679679+ strict algorithm below, to counter possible intermediary behavior.
680680+ When the field in question is a Structured Field of type List or
681681+ Dictionary, this effect can be accomplished more directly by
682682+ requiring the strict Structured Field serialization of the field
683683+ value, as described in Section 2.1.1.
684684+685685+ Note that some HTTP fields, such as Set-Cookie [COOKIE], do not
686686+ follow a syntax that allows for the combination of field values in
687687+ this manner (such that the combined output is unambiguous from
688688+ multiple inputs). Even though the component value is never parsed by
689689+ the message signature process and is used only as part of the
690690+ signature base (Section 2.5), caution needs to be taken when
691691+ including such fields in signatures, since the combined value could
692692+ be ambiguous. The bs parameter, as described in Section 2.1.3,
693693+ provides a method for wrapping such problematic fields. See
694694+ Section 7.5.6 for more discussion regarding this issue.
695695+696696+ If the correctly combined value is not directly available for a given
697697+ field by an implementation, the following algorithm will produce
698698+ canonicalized results for list-based fields:
699699+700700+ 1. Create an ordered list of the field values of each instance of
701701+ the field in the message, in the order they occur (or will occur)
702702+ in the message.
703703+704704+ 2. Strip leading and trailing whitespace from each item in the list.
705705+ Note that since HTTP field values are not allowed to contain
706706+ leading and trailing whitespace, this would be a no-op in a
707707+ compliant implementation.
708708+709709+ 3. Remove any obsolete line folding within the line, and replace it
710710+ with a single space (" "), as discussed in Section 5.2 of
711711+ [HTTP/1.1]. Note that this behavior is specific to HTTP/1.1 and
712712+ does not apply to other versions of the HTTP specification, which
713713+ do not allow internal line folding.
714714+715715+ 4. Concatenate the list of values with a single comma (",") and a
716716+ single space (" ") between each item.
717717+718718+ The resulting string is the component value for the field.
719719+720720+ Note that some HTTP fields have values with multiple valid
721721+ serializations that have equivalent semantics, such as allowing case-
722722+ insensitive values that intermediaries could change. Applications
723723+ signing and processing such fields MUST consider how to handle the
724724+ values of such fields to ensure that the signer and verifier can
725725+ derive the same value, as discussed in Section 7.5.2.
726726+727727+ The following are non-normative examples of component values for
728728+ header fields, given the following example HTTP message fragment:
729729+730730+ Host: www.example.com
731731+ Date: Tue, 20 Apr 2021 02:07:56 GMT
732732+ X-OWS-Header: Leading and trailing whitespace.
733733+ X-Obs-Fold-Header: Obsolete
734734+ line folding.
735735+ Cache-Control: max-age=60
736736+ Cache-Control: must-revalidate
737737+ Example-Dict: a=1, b=2;x=1;y=2, c=(a b c)
738738+739739+ The following example shows the component values for these example
740740+ header fields, presented using the signature base format defined in
741741+ Section 2.5:
742742+743743+ "host": www.example.com
744744+ "date": Tue, 20 Apr 2021 02:07:56 GMT
745745+ "x-ows-header": Leading and trailing whitespace.
746746+ "x-obs-fold-header": Obsolete line folding.
747747+ "cache-control": max-age=60, must-revalidate
748748+ "example-dict": a=1, b=2;x=1;y=2, c=(a b c)
749749+750750+ Empty HTTP fields can also be signed when present in a message. The
751751+ canonicalized value is the empty string. This means that the
752752+ following empty header field, with (SP) indicating a single trailing
753753+ space character before the empty field value:
754754+755755+ X-Empty-Header:(SP)
756756+757757+ is serialized by the signature base generation algorithm
758758+ (Section 2.5) with an empty string value following the colon and
759759+ space added after the component identifier.
760760+761761+ "x-empty-header":(SP)
762762+763763+ Any HTTP field component identifiers MAY have the following
764764+ parameters in specific circumstances, each described in detail in
765765+ their own sections:
766766+767767+ sf A Boolean flag indicating that the component value is serialized
768768+ using strict encoding of the Structured Field value
769769+ (Section 2.1.1).
770770+771771+ key A String parameter used to select a single member value from a
772772+ Dictionary Structured Field (Section 2.1.2).
773773+774774+ bs A Boolean flag indicating that individual field values are
775775+ encoded using Byte Sequence data structures before being combined
776776+ into the component value (Section 2.1.3).
777777+778778+ req A Boolean flag for signed responses indicating that the
779779+ component value is derived from the request that triggered this
780780+ response message and not from the response message directly. Note
781781+ that this parameter can also be applied to any derived component
782782+ identifiers that target the request (Section 2.4).
783783+784784+ tr A Boolean flag indicating that the field value is taken from the
785785+ trailers of the message as defined in Section 6.5 of [HTTP]. If
786786+ this flag is absent, the field value is taken from the header
787787+ fields of the message as defined in Section 6.3 of [HTTP]
788788+ (Section 2.1.4).
789789+790790+ Multiple parameters MAY be specified together, though some
791791+ combinations are redundant or incompatible. For example, the sf
792792+ parameter's functionality is already covered when the key parameter
793793+ is used on a Dictionary item, since key requires strict serialization
794794+ of the value. The bs parameter, which requires the raw bytes of the
795795+ field values from the message, is not compatible with the use of the
796796+ sf or key parameters, which require the parsed data structures of the
797797+ field values after combination.
798798+799799+ Additional parameters can be defined in the "HTTP Signature Component
800800+ Parameters" registry established in Section 6.5.
801801+802802+2.1.1. Strict Serialization of HTTP Structured Fields
803803+804804+ If the value of an HTTP field is known by the application to be a
805805+ Structured Field type (as defined in [STRUCTURED-FIELDS] or its
806806+ extensions or updates) and the expected type of the Structured Field
807807+ is known, the signer MAY include the sf parameter in the component
808808+ identifier. If this parameter is included with a component
809809+ identifier, the HTTP field value MUST be serialized using the formal
810810+ serialization rules specified in Section 4 of [STRUCTURED-FIELDS] (or
811811+ the applicable formal serialization section of its extensions or
812812+ updates) applicable to the type of the HTTP field. Note that this
813813+ process will replace any optional internal whitespace with a single
814814+ space character, among other potential transformations of the value.
815815+816816+ If multiple field values occur within a message, these values MUST be
817817+ combined into a single List or Dictionary structure before
818818+ serialization.
819819+820820+ If the application does not know the type of the field or does not
821821+ know how to serialize the type of the field, the use of this flag
822822+ will produce an error. As a consequence, the signer can only
823823+ reliably sign fields using this flag when the verifier's system knows
824824+ the type as well.
825825+826826+ For example, the following Dictionary field is a valid serialization:
827827+828828+ Example-Dict: a=1, b=2;x=1;y=2, c=(a b c)
829829+830830+ If included in the signature base without parameters, its value would
831831+ be:
832832+833833+ "example-dict": a=1, b=2;x=1;y=2, c=(a b c)
834834+835835+ However, if the sf parameter is added, the value is reserialized as
836836+ follows:
837837+838838+ "example-dict";sf: a=1, b=2;x=1;y=2, c=(a b c)
839839+840840+ The resulting string is used as the component value; see Section 2.1.
841841+842842+2.1.2. Dictionary Structured Field Members
843843+844844+ If a given field is known by the application to be a Dictionary
845845+ Structured Field, an individual member in the value of that
846846+ Dictionary is identified by using the parameter key and the
847847+ Dictionary member key as a String value.
848848+849849+ If multiple field values occur within a message, these values MUST be
850850+ combined into a single Dictionary structure before serialization.
851851+852852+ An individual member value of a Dictionary Structured Field is
853853+ canonicalized by applying the serialization algorithm described in
854854+ Section 4.1.2 of [STRUCTURED-FIELDS] on the member_value and its
855855+ parameters, not including the Dictionary key itself. Specifically,
856856+ the value is serialized as an Item or Inner List (the two possible
857857+ values of a Dictionary member), with all parameters and possible
858858+ subfields serialized using the strict serialization rules defined in
859859+ Section 4 of [STRUCTURED-FIELDS] (or the applicable section of its
860860+ extensions or updates).
861861+862862+ Each parameterized key for a given field MUST NOT appear more than
863863+ once in the signature base. Parameterized keys MAY appear in any
864864+ order in the signature base, regardless of the order they occur in
865865+ the source Dictionary.
866866+867867+ If a Dictionary key is named as a covered component but it does not
868868+ occur in the Dictionary, this MUST cause an error in the signature
869869+ base generation.
870870+871871+ The following are non-normative examples of canonicalized values for
872872+ Dictionary Structured Field members, given the following example
873873+ header field, whose value is known by the application to be a
874874+ Dictionary:
875875+876876+ Example-Dict: a=1, b=2;x=1;y=2, c=(a b c), d
877877+878878+ The following example shows canonicalized values for different
879879+ component identifiers of this field, presented using the signature
880880+ base format discussed in Section 2.5:
881881+882882+ "example-dict";key="a": 1
883883+ "example-dict";key="d": ?1
884884+ "example-dict";key="b": 2;x=1;y=2
885885+ "example-dict";key="c": (a b c)
886886+887887+ Note that the value for key="c" has been reserialized according to
888888+ the strict member_value algorithm, and the value for key="d" has been
889889+ serialized as a Boolean value.
890890+891891+2.1.3. Binary-Wrapped HTTP Fields
892892+893893+ If the value of the HTTP field in question is known by the
894894+ application to cause problems with serialization, particularly with
895895+ the combination of multiple values into a single line as discussed in
896896+ Section 7.5.6, the signer SHOULD include the bs parameter in a
897897+ component identifier to indicate that the values of the field need to
898898+ be wrapped as binary structures before being combined.
899899+900900+ If this parameter is included with a component identifier, the
901901+ component value MUST be calculated using the following algorithm:
902902+903903+ 1. Let the input be the ordered set of values for a field, in the
904904+ order they appear in the message.
905905+906906+ 2. Create an empty List for accumulating processed field values.
907907+908908+ 3. For each field value in the set:
909909+910910+ 3.1. Strip leading and trailing whitespace from the field value.
911911+ Note that since HTTP field values are not allowed to
912912+ contain leading and trailing whitespace, this would be a
913913+ no-op in a compliant implementation.
914914+915915+ 3.2. Remove any obsolete line folding within the line, and
916916+ replace it with a single space (" "), as discussed in
917917+ Section 5.2 of [HTTP/1.1]. Note that this behavior is
918918+ specific to [HTTP/1.1] and does not apply to other versions
919919+ of the HTTP specification.
920920+921921+ 3.3. Encode the bytes of the resulting field value as a Byte
922922+ Sequence. Note that most fields are restricted to ASCII
923923+ characters, but other octets could be included in the value
924924+ in some implementations.
925925+926926+ 3.4. Add the Byte Sequence to the List accumulator.
927927+928928+ 4. The intermediate result is a List of Byte Sequence values.
929929+930930+ 5. Follow the strict serialization of a List as described in
931931+ Section 4.1.1 of [STRUCTURED-FIELDS], and return this output.
932932+933933+ For example, the following field with internal commas prevents the
934934+ distinct field values from being safely combined:
935935+936936+ Example-Header: value, with, lots
937937+ Example-Header: of, commas
938938+939939+ In our example, the same field can be sent with a semantically
940940+ different single value:
941941+942942+ Example-Header: value, with, lots, of, commas
943943+944944+ Both of these versions are treated differently by the application.
945945+ However, if included in the signature base without parameters, the
946946+ component value would be the same in both cases:
947947+948948+ "example-header": value, with, lots, of, commas
949949+950950+ However, if the bs parameter is added, the two separate instances are
951951+ encoded and serialized as follows:
952952+953953+ "example-header";bs: :dmFsdWUsIHdpdGgsIGxvdHM=:, :b2YsIGNvbW1hcw==:
954954+955955+ For the single-instance field above, the encoding with the bs
956956+ parameter is:
957957+958958+ "example-header";bs: :dmFsdWUsIHdpdGgsIGxvdHMsIG9mLCBjb21tYXM=:
959959+960960+ This component value is distinct from the multiple-instance field
961961+ above, preventing a collision that could potentially be exploited.
962962+963963+2.1.4. Trailer Fields
964964+965965+ If the signer wants to include a trailer field in the signature, the
966966+ signer MUST include the tr Boolean parameter to indicate that the
967967+ value MUST be taken from the trailer fields and not from the header
968968+ fields.
969969+970970+ For example, given the following message:
971971+972972+ HTTP/1.1 200 OK
973973+ Content-Type: text/plain
974974+ Transfer-Encoding: chunked
975975+ Trailer: Expires
976976+977977+ 4
978978+ HTTP
979979+ 7
980980+ Message
981981+ a
982982+ Signatures
983983+ 0
984984+ Expires: Wed, 9 Nov 2022 07:28:00 GMT
985985+986986+ The signer decides to add both the Trailer header field and the
987987+ Expires trailer field to the signature base, along with the status
988988+ code derived component:
989989+990990+ "@status": 200
991991+ "trailer": Expires
992992+ "expires";tr: Wed, 9 Nov 2022 07:28:00 GMT
993993+994994+ If a field is available as both a header and a trailer in a message,
995995+ both values MAY be signed, but the values MUST be signed separately.
996996+ The values of header fields and trailer fields of the same name MUST
997997+ NOT be combined for purposes of the signature.
998998+999999+ Since trailer fields could be merged into the header fields or
10001000+ dropped entirely by intermediaries as per Section 6.5.1 of [HTTP], it
10011001+ is NOT RECOMMENDED to include trailers in the signature unless the
10021002+ signer knows that the verifier will have access to the values of the
10031003+ trailers as sent.
10041004+10051005+2.2. Derived Components
10061006+10071007+ In addition to HTTP fields, there are a number of different
10081008+ components that can be derived from the control data, signature
10091009+ context, or other aspects of the HTTP message being signed. Such
10101010+ derived components can be included in the signature base by defining
10111011+ a component name, possible parameters, message targets, and the
10121012+ derivation method for its component value.
10131013+10141014+ Derived component names MUST start with the "at" (@) character. This
10151015+ differentiates derived component names from HTTP field names, which
10161016+ cannot contain the @ character as per Section 5.1 of [HTTP].
10171017+ Processors of HTTP message signatures MUST treat derived component
10181018+ names separately from field names, as discussed in Section 7.5.1.
10191019+10201020+ This specification defines the following derived components:
10211021+10221022+ @method The method used for a request (Section 2.2.1).
10231023+10241024+ @target-uri The full target URI for a request (Section 2.2.2).
10251025+10261026+ @authority The authority of the target URI for a request
10271027+ (Section 2.2.3).
10281028+10291029+ @scheme The scheme of the target URI for a request (Section 2.2.4).
10301030+10311031+ @request-target The request target (Section 2.2.5).
10321032+10331033+ @path The absolute path portion of the target URI for a request
10341034+ (Section 2.2.6).
10351035+10361036+ @query The query portion of the target URI for a request
10371037+ (Section 2.2.7).
10381038+10391039+ @query-param A parsed and encoded query parameter of the target URI
10401040+ for a request (Section 2.2.8).
10411041+10421042+ @status The status code for a response (Section 2.2.9).
10431043+10441044+ Additional derived component names are defined in the "HTTP Signature
10451045+ Derived Component Names" registry (Section 6.4).
10461046+10471047+ Derived component values are taken from the context of the target
10481048+ message for the signature. This context includes information about
10491049+ the message itself, such as its control data, as well as any
10501050+ additional state and context held by the signer or verifier. In
10511051+ particular, when signing a response, the signer can include any
10521052+ derived components from the originating request by using the req
10531053+ parameter (Section 2.4).
10541054+10551055+ request: Values derived from, and results applied to, an HTTP
10561056+ request message as described in Section 3.4 of [HTTP]. If the
10571057+ target message of the signature is a response, derived components
10581058+ that target request messages can be included by using the req
10591059+ parameter as defined in Section 2.4.
10601060+10611061+ response: Values derived from, and results applied to, an HTTP
10621062+ response message as described in Section 3.4 of [HTTP].
10631063+10641064+ request, response: Values derived from, and results applied to,
10651065+ either a request message or a response message.
10661066+10671067+ A derived component definition MUST define all target message types
10681068+ to which it can be applied.
10691069+10701070+ Derived component values MUST be limited to printable characters and
10711071+ spaces and MUST NOT contain any newline characters. Derived
10721072+ component values MUST NOT start or end with whitespace characters.
10731073+10741074+2.2.1. Method
10751075+10761076+ The @method derived component refers to the HTTP method of a request
10771077+ message. The component value is canonicalized by taking the value of
10781078+ the method as a string. Note that the method name is case sensitive
10791079+ as per [HTTP], Section 9.1. While conventionally standardized method
10801080+ names are uppercase [ASCII], no transformation to the input method
10811081+ value's case is performed.
10821082+10831083+ For example, the following request message:
10841084+10851085+ POST /path?param=value HTTP/1.1
10861086+ Host: www.example.com
10871087+10881088+ would result in the following @method component value:
10891089+10901090+ POST
10911091+10921092+ and the following signature base line:
10931093+10941094+ "@method": POST
10951095+10961096+2.2.2. Target URI
10971097+10981098+ The @target-uri derived component refers to the target URI of a
10991099+ request message. The component value is the target URI of the
11001100+ request ([HTTP], Section 7.1), assembled from all available URI
11011101+ components, including the authority.
11021102+11031103+ For example, the following message sent over HTTPS:
11041104+11051105+ POST /path?param=value HTTP/1.1
11061106+ Host: www.example.com
11071107+11081108+ would result in the following @target-uri component value:
11091109+11101110+ https://www.example.com/path?param=value
11111111+11121112+ and the following signature base line:
11131113+11141114+ "@target-uri": https://www.example.com/path?param=value
11151115+11161116+2.2.3. Authority
11171117+11181118+ The @authority derived component refers to the authority component of
11191119+ the target URI of the HTTP request message, as defined in [HTTP],
11201120+ Section 7.2. In HTTP/1.1, this is usually conveyed using the Host
11211121+ header field, while in HTTP/2 and HTTP/3 it is conveyed using the
11221122+ :authority pseudo-header. The value is the fully qualified authority
11231123+ component of the request, comprised of the host and, optionally, port
11241124+ of the request target, as a string. The component value MUST be
11251125+ normalized according to the rules provided in [HTTP], Section 4.2.3.
11261126+ Namely, the hostname is normalized to lowercase, and the default port
11271127+ is omitted.
11281128+11291129+ For example, the following request message:
11301130+11311131+ POST /path?param=value HTTP/1.1
11321132+ Host: www.example.com
11331133+11341134+ would result in the following @authority component value:
11351135+11361136+ www.example.com
11371137+11381138+ and the following signature base line:
11391139+11401140+ "@authority": www.example.com
11411141+11421142+ The @authority derived component SHOULD be used instead of signing
11431143+ the Host header field directly. See Section 7.2.4.
11441144+11451145+2.2.4. Scheme
11461146+11471147+ The @scheme derived component refers to the scheme of the target URL
11481148+ of the HTTP request message. The component value is the scheme as a
11491149+ lowercase string as defined in [HTTP], Section 4.2. While the scheme
11501150+ itself is case insensitive, it MUST be normalized to lowercase for
11511151+ inclusion in the signature base.
11521152+11531153+ For example, the following request message sent over plain HTTP:
11541154+11551155+ POST /path?param=value HTTP/1.1
11561156+ Host: www.example.com
11571157+11581158+ would result in the following @scheme component value:
11591159+11601160+ http
11611161+11621162+ and the following signature base line:
11631163+11641164+ "@scheme": http
11651165+11661166+2.2.5. Request Target
11671167+11681168+ The @request-target derived component refers to the full request
11691169+ target of the HTTP request message, as defined in [HTTP],
11701170+ Section 7.1. The component value of the request target can take
11711171+ different forms, depending on the type of request, as described
11721172+ below.
11731173+11741174+ For HTTP/1.1, the component value is equivalent to the request target
11751175+ portion of the request line. However, this value is more difficult
11761176+ to reliably construct in other versions of HTTP. Therefore, it is
11771177+ NOT RECOMMENDED that this component be used when versions of HTTP
11781178+ other than 1.1 might be in use.
11791179+11801180+ The origin form value is a combination of the absolute path and query
11811181+ components of the request URL.
11821182+11831183+ For example, the following request message:
11841184+11851185+ POST /path?param=value HTTP/1.1
11861186+ Host: www.example.com
11871187+11881188+ would result in the following @request-target component value:
11891189+11901190+ /path?param=value
11911191+11921192+ and the following signature base line:
11931193+11941194+ "@request-target": /path?param=value
11951195+11961196+ The following request to an HTTP proxy with the absolute-form value,
11971197+ containing the fully qualified target URI:
11981198+11991199+ GET https://www.example.com/path?param=value HTTP/1.1
12001200+12011201+ would result in the following @request-target component value:
12021202+12031203+ https://www.example.com/path?param=value
12041204+12051205+ and the following signature base line:
12061206+12071207+ "@request-target": https://www.example.com/path?param=value
12081208+12091209+ The following CONNECT request with an authority-form value,
12101210+ containing the host and port of the target:
12111211+12121212+ CONNECT www.example.com:80 HTTP/1.1
12131213+ Host: www.example.com
12141214+12151215+ would result in the following @request-target component value:
12161216+12171217+ www.example.com:80
12181218+12191219+ and the following signature base line:
12201220+12211221+ "@request-target": www.example.com:80
12221222+12231223+ The following OPTIONS request message with the asterisk-form value,
12241224+ containing a single asterisk (*) character:
12251225+12261226+ OPTIONS * HTTP/1.1
12271227+ Host: www.example.com
12281228+12291229+ would result in the following @request-target component value:
12301230+12311231+ *
12321232+12331233+ and the following signature base line:
12341234+12351235+ "@request-target": *
12361236+12371237+2.2.6. Path
12381238+12391239+ The @path derived component refers to the target path of the HTTP
12401240+ request message. The component value is the absolute path of the
12411241+ request target defined by [URI], with no query component and no
12421242+ trailing question mark (?) character. The value is normalized
12431243+ according to the rules provided in [HTTP], Section 4.2.3. Namely, an
12441244+ empty path string is normalized as a single slash (/) character.
12451245+ Path components are represented by their values before decoding any
12461246+ percent-encoded octets, as described in the simple string comparison
12471247+ rules provided in Section 6.2.1 of [URI].
12481248+12491249+ For example, the following request message:
12501250+12511251+ GET /path?param=value HTTP/1.1
12521252+ Host: www.example.com
12531253+12541254+ would result in the following @path component value:
12551255+12561256+ /path
12571257+12581258+ and the following signature base line:
12591259+12601260+ "@path": /path
12611261+12621262+2.2.7. Query
12631263+12641264+ The @query derived component refers to the query component of the
12651265+ HTTP request message. The component value is the entire normalized
12661266+ query string defined by [URI], including the leading ? character.
12671267+ The value is read using the simple string comparison rules provided
12681268+ in Section 6.2.1 of [URI]. Namely, percent-encoded octets are not
12691269+ decoded.
12701270+12711271+ For example, the following request message:
12721272+12731273+ GET /path?param=value&foo=bar&baz=bat%2Dman HTTP/1.1
12741274+ Host: www.example.com
12751275+12761276+ would result in the following @query component value:
12771277+12781278+ ?param=value&foo=bar&baz=bat%2Dman
12791279+12801280+ and the following signature base line:
12811281+12821282+ "@query": ?param=value&foo=bar&baz=bat%2Dman
12831283+12841284+ The following request message:
12851285+12861286+ POST /path?queryString HTTP/1.1
12871287+ Host: www.example.com
12881288+12891289+ would result in the following @query component value:
12901290+12911291+ ?queryString
12921292+12931293+ and the following signature base line:
12941294+12951295+ "@query": ?queryString
12961296+12971297+ Just like including an empty path component, the signer can include
12981298+ an empty query component to indicate that this component is not used
12991299+ in the message. If the query string is absent from the request
13001300+ message, the component value is the leading ? character alone:
13011301+13021302+ ?
13031303+13041304+ resulting in the following signature base line:
13051305+13061306+ "@query": ?
13071307+13081308+2.2.8. Query Parameters
13091309+13101310+ If the query portion of a request target URI uses HTML form
13111311+ parameters in the format defined in Section 5 ("application/
13121312+ x-www-form-urlencoded") of [HTMLURL], the @query-param derived
13131313+ component allows addressing of these individual query parameters.
13141314+ The query parameters MUST be parsed according to Section 5.1
13151315+ ("application/x-www-form-urlencoded parsing") of [HTMLURL], resulting
13161316+ in a list of (nameString, valueString) tuples. The REQUIRED name
13171317+ parameter of each component identifier contains the encoded
13181318+ nameString of a single query parameter as a String value. The
13191319+ component value of a single named parameter is the encoded
13201320+ valueString of that single query parameter. Several different named
13211321+ query parameters MAY be included in the covered components. Single
13221322+ named parameters MAY occur in any order in the covered components,
13231323+ regardless of the order they occur in the query string.
13241324+13251325+ The value of the name parameter and the component value of a single
13261326+ named parameter are calculated via the following process:
13271327+13281328+ 1. Parse the nameString or valueString of the named query parameter
13291329+ defined by Section 5.1 ("application/x-www-form-urlencoded
13301330+ parsing") of [HTMLURL]; this is the value after percent-encoded
13311331+ octets are decoded.
13321332+13331333+ 2. Encode the nameString or valueString using the "percent-encode
13341334+ after encoding" process defined by Section 5.2 ("application/
13351335+ x-www-form-urlencoded serializing") of [HTMLURL]; this results in
13361336+ an ASCII string [ASCII].
13371337+13381338+ 3. Output the ASCII string.
13391339+13401340+ Note that the component value does not include any leading question
13411341+ mark (?) characters, equals sign (=) characters, or separating
13421342+ ampersand (&) characters. Named query parameters with an empty
13431343+ valueString have an empty string as the component value. Note that
13441344+ due to inconsistencies in implementations, some query parameter
13451345+ parsing libraries drop such empty values.
13461346+13471347+ If a query parameter is named as a covered component but it does not
13481348+ occur in the query parameters, this MUST cause an error in the
13491349+ signature base generation.
13501350+13511351+ For example, for the following request:
13521352+13531353+ GET /path?param=value&foo=bar&baz=batman&qux= HTTP/1.1
13541354+ Host: www.example.com
13551355+13561356+ Indicating the baz, qux, and param named query parameters would
13571357+ result in the following @query-param component values:
13581358+13591359+ _baz_: batman
13601360+13611361+ _qux_: an empty string
13621362+13631363+ _param_: value
13641364+13651365+ and the following signature base lines, with (SP) indicating a single
13661366+ trailing space character before the empty component value:
13671367+13681368+ "@query-param";name="baz": batman
13691369+ "@query-param";name="qux":(SP)
13701370+ "@query-param";name="param": value
13711371+13721372+ This derived component has some limitations. Specifically, the
13731373+ algorithms provided in Section 5 ("application/
13741374+ x-www-form-urlencoded") of [HTMLURL] only support query parameters
13751375+ using percent-escaped UTF-8 encoding. Other encodings are not
13761376+ supported. Additionally, multiple instances of a named parameter are
13771377+ not reliably supported in the wild. If a parameter name occurs
13781378+ multiple times in a request, the named query parameter MUST NOT be
13791379+ included. If multiple parameters are common within an application,
13801380+ it is RECOMMENDED to sign the entire query string using the @query
13811381+ component identifier defined in Section 2.2.7.
13821382+13831383+ The encoding process allows query parameters that include newlines or
13841384+ other problematic characters in their values, or with alternative
13851385+ encodings such as using the plus (+) character to represent spaces.
13861386+ For the query parameters in this message:
13871387+13881388+ NOTE: '\' line wrapping per RFC 8792
13891389+13901390+ GET /parameters?var=this%20is%20a%20big%0Amultiline%20value&\
13911391+ bar=with+plus+whitespace&fa%C3%A7ade%22%3A%20=something HTTP/1.1
13921392+ Host: www.example.com
13931393+ Date: Tue, 20 Apr 2021 02:07:56 GMT
13941394+13951395+ The resulting values are encoded as follows:
13961396+13971397+ "@query-param";name="var": this%20is%20a%20big%0Amultiline%20value
13981398+ "@query-param";name="bar": with%20plus%20whitespace
13991399+ "@query-param";name="fa%C3%A7ade%22%3A%20": something
14001400+14011401+ If the encoding were not applied, the resultant values would be:
14021402+14031403+ "@query-param";name="var": this is a big
14041404+ multiline value
14051405+ "@query-param";name="bar": with plus whitespace
14061406+ "@query-param";name="façade\": ": something
14071407+14081408+ This base string contains characters that violate the constraints on
14091409+ component names and values and is therefore invalid.
14101410+14111411+2.2.9. Status Code
14121412+14131413+ The @status derived component refers to the three-digit numeric HTTP
14141414+ status code of a response message as defined in [HTTP], Section 15.
14151415+ The component value is the serialized three-digit integer of the HTTP
14161416+ status code, with no descriptive text.
14171417+14181418+ For example, the following response message:
14191419+14201420+ HTTP/1.1 200 OK
14211421+ Date: Fri, 26 Mar 2010 00:05:00 GMT
14221422+14231423+ would result in the following @status component value:
14241424+14251425+ 200
14261426+14271427+ and the following signature base line:
14281428+14291429+ "@status": 200
14301430+14311431+ The @status component identifier MUST NOT be used in a request
14321432+ message.
14331433+14341434+2.3. Signature Parameters
14351435+14361436+ HTTP message signatures have metadata properties that provide
14371437+ information regarding the signature's generation and verification,
14381438+ consisting of the ordered set of covered components and the ordered
14391439+ set of parameters, where the parameters include a timestamp of
14401440+ signature creation, identifiers for verification key material, and
14411441+ other utilities. This metadata is represented by a special message
14421442+ component in the signature base for signature parameters; this
14431443+ special message component is treated slightly differently from other
14441444+ message components. Specifically, the signature parameters message
14451445+ component is REQUIRED as the last line of the signature base
14461446+ (Section 2.5), and the component identifier MUST NOT be enumerated
14471447+ within the set of covered components for any signature, including
14481448+ itself.
14491449+14501450+ The signature parameters component name is @signature-params.
14511451+14521452+ The signature parameters component value is the serialization of the
14531453+ signature parameters for this signature, including the covered
14541454+ components ordered set with all associated parameters. These
14551455+ parameters include any of the following:
14561456+14571457+ created: Creation time as a UNIX timestamp value of type Integer.
14581458+ Sub-second precision is not supported. The inclusion of this
14591459+ parameter is RECOMMENDED.
14601460+14611461+ expires: Expiration time as a UNIX timestamp value of type Integer.
14621462+ Sub-second precision is not supported.
14631463+14641464+ nonce: A random unique value generated for this signature as a
14651465+ String value.
14661466+14671467+ alg: The HTTP message signature algorithm from the "HTTP Signature
14681468+ Algorithms" registry, as a String value.
14691469+14701470+ keyid: The identifier for the key material as a String value.
14711471+14721472+ tag: An application-specific tag for the signature as a String
14731473+ value. This value is used by applications to help identify
14741474+ signatures relevant for specific applications or protocols.
14751475+14761476+ Additional parameters can be defined in the "HTTP Signature Metadata
14771477+ Parameters" registry (Section 6.3). Note that the parameters are not
14781478+ in any general order, but once an ordering is chosen for a given set
14791479+ of parameters, it cannot be changed without altering the signature
14801480+ parameters value.
14811481+14821482+ The signature parameters component value is serialized as a
14831483+ parameterized Inner List using the rules provided in Section 4 of
14841484+ [STRUCTURED-FIELDS] as follows:
14851485+14861486+ 1. Let the output be an empty string.
14871487+14881488+ 2. Determine an order for the component identifiers of the covered
14891489+ components, not including the @signature-params component
14901490+ identifier itself. Once this order is chosen, it cannot be
14911491+ changed. This order MUST be the same order as that used in
14921492+ creating the signature base (Section 2.5).
14931493+14941494+ 3. Serialize the component identifiers of the covered components,
14951495+ including all parameters, as an ordered Inner List of String
14961496+ values according to Section 4.1.1.1 of [STRUCTURED-FIELDS]; then,
14971497+ append this to the output. Note that the component identifiers
14981498+ can include their own parameters, and these parameters are
14991499+ ordered sets. Once an order is chosen for a component's
15001500+ parameters, the order cannot be changed.
15011501+15021502+ 4. Determine an order for any signature parameters. Once this order
15031503+ is chosen, it cannot be changed.
15041504+15051505+ 5. Append the parameters to the Inner List in order according to
15061506+ Section 4.1.1.2 of [STRUCTURED-FIELDS], skipping parameters that
15071507+ are not available or not used for this message signature.
15081508+15091509+ 6. The output contains the signature parameters component value.
15101510+15111511+ Note that the Inner List serialization from Section 4.1.1.1 of
15121512+ [STRUCTURED-FIELDS] is used for the covered component value instead
15131513+ of the List serialization from Section 4.1.1 of [STRUCTURED-FIELDS]
15141514+ in order to facilitate parallelism with this value's inclusion in the
15151515+ Signature-Input field, as discussed in Section 4.1.
15161516+15171517+ This example shows the serialized component value for the parameters
15181518+ of an example message signature:
15191519+15201520+ NOTE: '\' line wrapping per RFC 8792
15211521+15221522+ ("@target-uri" "@authority" "date" "cache-control")\
15231523+ ;keyid="test-key-rsa-pss";alg="rsa-pss-sha512";\
15241524+ created=1618884475;expires=1618884775
15251525+15261526+ Note that an HTTP message could contain multiple signatures
15271527+ (Section 4.3), but only the signature parameters used for a single
15281528+ signature are included in a given signature parameters entry.
15291529+15301530+2.4. Signing Request Components in a Response Message
15311531+15321532+ When a request message results in a signed response message, the
15331533+ signer can include portions of the request message in the signature
15341534+ base by adding the req parameter to the component identifier.
15351535+15361536+ req A Boolean flag indicating that the component value is derived
15371537+ from the request that triggered this response message and not from
15381538+ the response message directly.
15391539+15401540+ This parameter can be applied to both HTTP fields and derived
15411541+ components that target the request, with the same semantics. The
15421542+ component value for a message component using this parameter is
15431543+ calculated in the same manner as it is normally, but data is pulled
15441544+ from the request message instead of the target response message to
15451545+ which the signature is applied.
15461546+15471547+ Note that the same component name MAY be included with and without
15481548+ the req parameter in a single signature base, indicating the same
15491549+ named component from both the request message and the response
15501550+ message.
15511551+15521552+ The req parameter MAY be combined with other parameters as
15531553+ appropriate for the component identifier, such as the key parameter
15541554+ for a Dictionary field.
15551555+15561556+ For example, when serving a response for this request:
15571557+15581558+ NOTE: '\' line wrapping per RFC 8792
15591559+15601560+ POST /foo?param=Value&Pet=dog HTTP/1.1
15611561+ Host: example.com
15621562+ Date: Tue, 20 Apr 2021 02:07:55 GMT
15631563+ Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+T\
15641564+ aPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:
15651565+ Content-Type: application/json
15661566+ Content-Length: 18
15671567+15681568+ {"hello": "world"}
15691569+15701570+ This would result in the following unsigned response message:
15711571+15721572+ NOTE: '\' line wrapping per RFC 8792
15731573+15741574+ HTTP/1.1 503 Service Unavailable
15751575+ Date: Tue, 20 Apr 2021 02:07:56 GMT
15761576+ Content-Type: application/json
15771577+ Content-Length: 62
15781578+ Content-Digest: sha-512=:0Y6iCBzGg5rZtoXS95Ijz03mslf6KAMCloESHObfwn\
15791579+ HJDbkkWWQz6PhhU9kxsTbARtY2PTBOzq24uJFpHsMuAg==:
15801580+15811581+ {"busy": true, "message": "Your call is very important to us"}
15821582+15831583+ The server signs the response with its own key, including the @status
15841584+ code and several header fields in the covered components. While this
15851585+ covers a reasonable amount of the response for this application, the
15861586+ server additionally includes several components derived from the
15871587+ original request message that triggered this response. In this
15881588+ example, the server includes the method, authority, path, and content
15891589+ digest from the request in the covered components of the response.
15901590+ The Content-Digest for both the request and the response is included
15911591+ under the response signature. For the application in this example,
15921592+ the query is deemed not to be relevant to the response and is
15931593+ therefore not covered. Other applications would make different
15941594+ decisions based on application needs, as discussed in Section 1.4.
15951595+15961596+ The signature base for this example is:
15971597+15981598+ NOTE: '\' line wrapping per RFC 8792
15991599+16001600+ "@status": 503
16011601+ "content-digest": sha-512=:0Y6iCBzGg5rZtoXS95Ijz03mslf6KAMCloESHObf\
16021602+ wnHJDbkkWWQz6PhhU9kxsTbARtY2PTBOzq24uJFpHsMuAg==:
16031603+ "content-type": application/json
16041604+ "@authority";req: example.com
16051605+ "@method";req: POST
16061606+ "@path";req: /foo
16071607+ "content-digest";req: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A\
16081608+ 2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:
16091609+ "@signature-params": ("@status" "content-digest" "content-type" \
16101610+ "@authority";req "@method";req "@path";req "content-digest";req)\
16111611+ ;created=1618884479;keyid="test-key-ecc-p256"
16121612+16131613+ The signed response message is:
16141614+16151615+ NOTE: '\' line wrapping per RFC 8792
16161616+16171617+ HTTP/1.1 503 Service Unavailable
16181618+ Date: Tue, 20 Apr 2021 02:07:56 GMT
16191619+ Content-Type: application/json
16201620+ Content-Length: 62
16211621+ Content-Digest: sha-512=:0Y6iCBzGg5rZtoXS95Ijz03mslf6KAMCloESHObfwn\
16221622+ HJDbkkWWQz6PhhU9kxsTbARtY2PTBOzq24uJFpHsMuAg==:
16231623+ Signature-Input: reqres=("@status" "content-digest" "content-type" \
16241624+ "@authority";req "@method";req "@path";req "content-digest";req)\
16251625+ ;created=1618884479;keyid="test-key-ecc-p256"
16261626+ Signature: reqres=:dMT/A/76ehrdBTD/2Xx8QuKV6FoyzEP/I9hdzKN8LQJLNgzU\
16271627+ 4W767HK05rx1i8meNQQgQPgQp8wq2ive3tV5Ag==:
16281628+16291629+ {"busy": true, "message": "Your call is very important to us"}
16301630+16311631+ Note that the ECDSA signature algorithm in use here is non-
16321632+ deterministic, meaning that a different signature value will be
16331633+ created every time the algorithm is run. The signature value
16341634+ provided here can be validated against the given keys, but newly
16351635+ generated signature values are not expected to match the example.
16361636+ See Section 7.3.5.
16371637+16381638+ Since the component values from the request are not repeated in the
16391639+ response message, the requester MUST keep the original message
16401640+ component values around long enough to validate the signature of the
16411641+ response that uses this component identifier parameter. In most
16421642+ cases, this means the requester needs to keep the original request
16431643+ message around, since the signer could choose to include any portions
16441644+ of the request in its response, according to the needs of the
16451645+ application. Since it is possible for an intermediary to alter a
16461646+ request message before it is processed by the server, applications
16471647+ need to take care not to sign such altered values, as the client
16481648+ would not be able to validate the resulting signature.
16491649+16501650+ It is also possible for a server to create a signed response in
16511651+ response to a signed request. For this example of a signed request:
16521652+16531653+ NOTE: '\' line wrapping per RFC 8792
16541654+16551655+ POST /foo?param=Value&Pet=dog HTTP/1.1
16561656+ Host: example.com
16571657+ Date: Tue, 20 Apr 2021 02:07:55 GMT
16581658+ Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+T\
16591659+ aPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:
16601660+ Content-Type: application/json
16611661+ Content-Length: 18
16621662+ Signature-Input: sig1=("@method" "@authority" "@path" "@query" \
16631663+ "content-digest" "content-type" "content-length")\
16641664+ ;created=1618884475;keyid="test-key-rsa-pss"
16651665+ Signature: sig1=:e8UJ5wMiRaonlth5ERtE8GIiEH7Akcr493nQ07VPNo6y3qvjdK\
16661666+ t0fo8VHO8xXDjmtYoatGYBGJVlMfIp06eVMEyNW2I4vN7XDAz7m5v1108vGzaDljr\
16671667+ d0H8+SJ28g7bzn6h2xeL/8q+qUwahWA/JmC8aOC9iVnwbOKCc0WSrLgWQwTY6VLp4\
16681668+ 2Qt7jjhYT5W7/wCvfK9A1VmHH1lJXsV873Z6hpxesd50PSmO+xaNeYvDLvVdZlhtw\
16691669+ 5PCtUYzKjHqwmaQ6DEuM8udRjYsoNqp2xZKcuCO1nKc0V3RjpqMZLuuyVbHDAbCzr\
16701670+ 0pg2d2VM/OC33JAU7meEjjaNz+d7LWPg==:
16711671+16721672+ {"hello": "world"}
16731673+16741674+ The server could choose to sign portions of this response, including
16751675+ several portions of the request, resulting in this signature base:
16761676+16771677+ NOTE: '\' line wrapping per RFC 8792
16781678+16791679+ "@status": 503
16801680+ "content-digest": sha-512=:0Y6iCBzGg5rZtoXS95Ijz03mslf6KAMCloESHObf\
16811681+ wnHJDbkkWWQz6PhhU9kxsTbARtY2PTBOzq24uJFpHsMuAg==:
16821682+ "content-type": application/json
16831683+ "@authority";req: example.com
16841684+ "@method";req: POST
16851685+ "@path";req: /foo
16861686+ "@query";req: ?param=Value&Pet=dog
16871687+ "content-digest";req: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A\
16881688+ 2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:
16891689+ "content-type";req: application/json
16901690+ "content-length";req: 18
16911691+ "@signature-params": ("@status" "content-digest" "content-type" \
16921692+ "@authority";req "@method";req "@path";req "@query";req \
16931693+ "content-digest";req "content-type";req "content-length";req)\
16941694+ ;created=1618884479;keyid="test-key-ecc-p256"
16951695+16961696+ and the following signed response:
16971697+16981698+ NOTE: '\' line wrapping per RFC 8792
16991699+17001700+ HTTP/1.1 503 Service Unavailable
17011701+ Date: Tue, 20 Apr 2021 02:07:56 GMT
17021702+ Content-Type: application/json
17031703+ Content-Length: 62
17041704+ Content-Digest: sha-512=:0Y6iCBzGg5rZtoXS95Ijz03mslf6KAMCloESHObfwn\
17051705+ HJDbkkWWQz6PhhU9kxsTbARtY2PTBOzq24uJFpHsMuAg==:
17061706+ Signature-Input: reqres=("@status" "content-digest" "content-type" \
17071707+ "@authority";req "@method";req "@path";req "@query";req \
17081708+ "content-digest";req "content-type";req "content-length";req)\
17091709+ ;created=1618884479;keyid="test-key-ecc-p256"
17101710+ Signature: reqres=:C73J41GVKc+TYXbSobvZf0CmNcptRiWN+NY1Or0A36ISg6ym\
17111711+ dRN6ZgR2QfrtopFNzqAyv+CeWrMsNbcV2Ojsgg==:
17121712+17131713+ {"busy": true, "message": "Your call is very important to us"}
17141714+17151715+ Note that the ECDSA signature algorithm in use here is non-
17161716+ deterministic, meaning that a different signature value will be
17171717+ created every time the algorithm is run. The signature value
17181718+ provided here can be validated against the given keys, but newly
17191719+ generated signature values are not expected to match the example.
17201720+ See Section 7.3.5.
17211721+17221722+ Applications signing a response to a signed request SHOULD sign all
17231723+ of the components of the request signature value to provide
17241724+ sufficient coverage and protection against a class of collision
17251725+ attacks, as discussed in Section 7.3.7. The server in this example
17261726+ has included all components listed in the Signature-Input field of
17271727+ the client's signature on the request in the response signature, in
17281728+ addition to components of the response.
17291729+17301730+ While it is syntactically possible to include the Signature and
17311731+ Signature-Input fields of the request message in the signature
17321732+ components of a response to a message using this mechanism, this
17331733+ practice is NOT RECOMMENDED. This is because signatures of
17341734+ signatures do not provide transitive coverage of covered components
17351735+ as one might expect, and the practice is susceptible to several
17361736+ attacks as discussed in Section 7.3.7. An application that needs to
17371737+ signal successful processing or receipt of a signature would need to
17381738+ carefully specify alternative mechanisms for sending such a signal
17391739+ securely.
17401740+17411741+ The response signature can only ever cover what is included in the
17421742+ request message when using this flag. Consequently, if an
17431743+ application needs to include the message content of the request under
17441744+ the signature of its response, the client needs to include a means
17451745+ for covering that content, such as a Content-Digest field. See the
17461746+ discussion in Section 7.2.8 for more information.
17471747+17481748+ The req parameter MUST NOT be used for any component in a signature
17491749+ that targets a request message.
17501750+17511751+2.5. Creating the Signature Base
17521752+17531753+ The signature base is an ASCII string [ASCII] containing the
17541754+ canonicalized HTTP message components covered by the signature. The
17551755+ input to the signature base creation algorithm is the ordered set of
17561756+ covered component identifiers and their associated values, along with
17571757+ any additional signature parameters discussed in Section 2.3.
17581758+17591759+ Component identifiers are serialized using the strict serialization
17601760+ rules defined by [STRUCTURED-FIELDS], Section 4. The component
17611761+ identifier has a component name, which is a String Item value
17621762+ serialized using the sf-string ABNF rule. The component identifier
17631763+ MAY also include defined parameters that are serialized using the
17641764+ parameters ABNF rule. The signature parameters line defined in
17651765+ Section 2.3 follows this same pattern, but the component identifier
17661766+ is a String Item with a fixed value and no parameters, and the
17671767+ component value is always an Inner List with optional parameters.
17681768+17691769+ Note that this means the serialization of the component name itself
17701770+ is encased in double quotes, with parameters following as a
17711771+ semicolon-separated list, such as "cache-control", "@authority",
17721772+ "@signature-params", or "example-dictionary";key="foo".
17731773+17741774+ The output is the ordered set of bytes that form the signature base,
17751775+ which conforms to the following ABNF:
17761776+17771777+ signature-base = *( signature-base-line LF ) signature-params-line
17781778+ signature-base-line = component-identifier ":" SP
17791779+ ( derived-component-value / *field-content )
17801780+ ; no obs-fold nor obs-text
17811781+ component-identifier = component-name parameters
17821782+ component-name = sf-string
17831783+ derived-component-value = *( VCHAR / SP )
17841784+ signature-params-line = DQUOTE "@signature-params" DQUOTE
17851785+ ":" SP inner-list
17861786+17871787+ To create the signature base, the signer or verifier concatenates
17881788+ entries for each component identifier in the signature's covered
17891789+ components (including their parameters) using the following
17901790+ algorithm. All errors produced as described MUST fail the algorithm
17911791+ immediately, without outputting a signature base.
17921792+17931793+ 1. Let the output be an empty string.
17941794+17951795+ 2. For each message component item in the covered components set (in
17961796+ order):
17971797+17981798+ 2.1. If the component identifier (including its parameters) has
17991799+ already been added to the signature base, produce an error.
18001800+18011801+ 2.2. Append the component identifier for the covered component
18021802+ serialized according to the component-identifier ABNF rule.
18031803+ Note that this serialization places the component name in
18041804+ double quotes and appends any parameters outside of the
18051805+ quotes.
18061806+18071807+ 2.3. Append a single colon (:).
18081808+18091809+ 2.4. Append a single space (" ").
18101810+18111811+ 2.5. Determine the component value for the component identifier.
18121812+18131813+ * If the component identifier has a parameter that is not
18141814+ understood, produce an error.
18151815+18161816+ * If the component identifier has parameters that are
18171817+ mutually incompatible with one another, such as bs and
18181818+ sf, produce an error.
18191819+18201820+ * If the component identifier contains the req parameter
18211821+ and the target message is a request, produce an error.
18221822+18231823+ * If the component identifier contains the req parameter
18241824+ and the target message is a response, the context for
18251825+ the component value is the related request message of
18261826+ the target response message. Otherwise, the context for
18271827+ the component value is the target message.
18281828+18291829+ * If the component name starts with an "at" (@) character,
18301830+ derive the component's value from the message according
18311831+ to the specific rules defined for the derived component,
18321832+ as provided in Section 2.2, including processing of any
18331833+ known valid parameters. If the derived component name
18341834+ is unknown or the value cannot be derived, produce an
18351835+ error.
18361836+18371837+ * If the component name does not start with an "at" (@)
18381838+ character, canonicalize the HTTP field value as
18391839+ described in Section 2.1, including processing of any
18401840+ known valid parameters. If the field cannot be found in
18411841+ the message or the value cannot be obtained in the
18421842+ context, produce an error.
18431843+18441844+ 2.6. Append the covered component's canonicalized component
18451845+ value.
18461846+18471847+ 2.7. Append a single newline (\n).
18481848+18491849+ 3. Append the signature parameters component (Section 2.3) according
18501850+ to the signature-params-line rule as follows:
18511851+18521852+ 3.1. Append the component identifier for the signature
18531853+ parameters serialized according to the component-identifier
18541854+ rule, i.e., the exact value "@signature-params" (including
18551855+ double quotes).
18561856+18571857+ 3.2. Append a single colon (:).
18581858+18591859+ 3.3. Append a single space (" ").
18601860+18611861+ 3.4. Append the signature parameters' canonicalized component
18621862+ values as defined in Section 2.3, i.e., Inner List
18631863+ Structured Field values with parameters.
18641864+18651865+ 4. Produce an error if the output string contains any non-ASCII
18661866+ characters [ASCII].
18671867+18681868+ 5. Return the output string.
18691869+18701870+ If covered components reference a component identifier that cannot be
18711871+ resolved to a component value in the message, the implementation MUST
18721872+ produce an error and not create a signature base. Such situations
18731873+ include, but are not limited to, the following:
18741874+18751875+ * The signer or verifier does not understand the derived component
18761876+ name.
18771877+18781878+ * The component name identifies a field that is not present in the
18791879+ message or whose value is malformed.
18801880+18811881+ * The component identifier includes a parameter that is unknown or
18821882+ does not apply to the component identifier to which it is
18831883+ attached.
18841884+18851885+ * The component identifier indicates that a Structured Field
18861886+ serialization is used (via the sf parameter), but the field in
18871887+ question is known to not be a Structured Field or the type of
18881888+ Structured Field is not known to the implementation.
18891889+18901890+ * The component identifier is a Dictionary member identifier that
18911891+ references a field that is not present in the message, that is not
18921892+ a Dictionary Structured Field, or whose value is malformed.
18931893+18941894+ * The component identifier is a Dictionary member identifier or a
18951895+ named query parameter identifier that references a member that is
18961896+ not present in the component value or whose value is malformed.
18971897+ For example, the identifier is "example-dict";key="c", and the
18981898+ value of the Example-Dict header field is a=1, b=2, which does not
18991899+ have the c value.
19001900+19011901+ In the following non-normative example, the HTTP message being signed
19021902+ is the following request:
19031903+19041904+ NOTE: '\' line wrapping per RFC 8792
19051905+19061906+ POST /foo?param=Value&Pet=dog HTTP/1.1
19071907+ Host: example.com
19081908+ Date: Tue, 20 Apr 2021 02:07:55 GMT
19091909+ Content-Type: application/json
19101910+ Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+T\
19111911+ aPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:
19121912+ Content-Length: 18
19131913+19141914+ {"hello": "world"}
19151915+19161916+ The covered components consist of the @method, @authority, and @path
19171917+ derived components followed by the Content-Digest, Content-Length,
19181918+ and Content-Type HTTP header fields, in order. The signature
19191919+ parameters consist of a creation timestamp of 1618884473 and a key
19201920+ identifier of test-key-rsa-pss. Note that no explicit alg parameter
19211921+ is given here, since the verifier is known by the application to use
19221922+ the RSA-PSS algorithm based on the identified key. The signature
19231923+ base for this message with these parameters is:
19241924+19251925+ NOTE: '\' line wrapping per RFC 8792
19261926+19271927+ "@method": POST
19281928+ "@authority": example.com
19291929+ "@path": /foo
19301930+ "content-digest": sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX\
19311931+ +TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:
19321932+ "content-length": 18
19331933+ "content-type": application/json
19341934+ "@signature-params": ("@method" "@authority" "@path" \
19351935+ "content-digest" "content-length" "content-type")\
19361936+ ;created=1618884473;keyid="test-key-rsa-pss"
19371937+19381938+ Figure 1: Non-normative Example Signature Base
19391939+19401940+ Note that the example signature base above does not include the final
19411941+ newline that ends the displayed example, nor do other example
19421942+ signature bases displayed elsewhere in this specification.
19431943+19441944+3. HTTP Message Signatures
19451945+19461946+ An HTTP message signature is a signature over a string generated from
19471947+ a subset of the components of an HTTP message in addition to metadata
19481948+ about the signature itself. When successfully verified against an
19491949+ HTTP message, an HTTP message signature provides cryptographic proof
19501950+ that the message is semantically equivalent to the message for which
19511951+ the signature was generated, with respect to the subset of message
19521952+ components that was signed.
19531953+19541954+3.1. Creating a Signature
19551955+19561956+ Creation of an HTTP message signature is a process that takes as its
19571957+ input the signature context (including the target message) and the
19581958+ requirements for the application. The output is a signature value
19591959+ and set of signature parameters that can be communicated to the
19601960+ verifier by adding them to the message.
19611961+19621962+ In order to create a signature, a signer MUST apply the following
19631963+ algorithm:
19641964+19651965+ 1. The signer chooses an HTTP signature algorithm and key material
19661966+ for signing from the set of potential signing algorithms. The
19671967+ set of potential algorithms is determined by the application and
19681968+ is out of scope for this document. The signer MUST choose key
19691969+ material that is appropriate for the signature's algorithm and
19701970+ that conforms to any requirements defined by the algorithm, such
19711971+ as key size or format. The mechanism by which the signer chooses
19721972+ the algorithm and key material is out of scope for this document.
19731973+19741974+ 2. The signer sets the signature's creation time to the current
19751975+ time.
19761976+19771977+ 3. If applicable, the signer sets the signature's expiration time
19781978+ property to the time at which the signature is to expire. The
19791979+ expiration is a hint to the verifier, expressing the time at
19801980+ which the signer is no longer willing to vouch for the signature.
19811981+ An appropriate expiration length, and the processing requirements
19821982+ of this parameter, are application specific.
19831983+19841984+ 4. The signer creates an ordered set of component identifiers
19851985+ representing the message components to be covered by the
19861986+ signature and attaches signature metadata parameters to this set.
19871987+ The serialized value of this set is later used as the value of
19881988+ the Signature-Input field as described in Section 4.1.
19891989+19901990+ * Once an order of covered components is chosen, the order MUST
19911991+ NOT change for the life of the signature.
19921992+19931993+ * Each covered component identifier MUST be either (1) an HTTP
19941994+ field (Section 2.1) in the signature context or (2) a derived
19951995+ component listed in Section 2.2 or in the "HTTP Signature
19961996+ Derived Component Names" registry.
19971997+19981998+ * Signers of a request SHOULD include some or all of the message
19991999+ control data in the covered components, such as the @method,
20002000+ @authority, @target-uri, or some combination thereof.
20012001+20022002+ * Signers SHOULD include the created signature metadata
20032003+ parameter to indicate when the signature was created.
20042004+20052005+ * The @signature-params derived component identifier MUST NOT be
20062006+ present in the list of covered component identifiers. The
20072007+ derived component is required to always be the last line in
20082008+ the signature base, ensuring that a signature always covers
20092009+ its own metadata and the metadata cannot be substituted.
20102010+20112011+ * Further guidance on what to include in this set and in what
20122012+ order is out of scope for this document.
20132013+20142014+ 5. The signer creates the signature base using these parameters and
20152015+ the signature base creation algorithm (Section 2.5).
20162016+20172017+ 6. The signer uses the HTTP_SIGN primitive function to sign the
20182018+ signature base with the chosen signing algorithm using the key
20192019+ material chosen by the signer. The HTTP_SIGN primitive and
20202020+ several concrete applications of signing algorithms are defined
20212021+ in Section 3.3.
20222022+20232023+ 7. The byte array output of the signature function is the HTTP
20242024+ message signature output value to be included in the Signature
20252025+ field as defined in Section 4.2.
20262026+20272027+ For example, given the HTTP message and signature parameters in the
20282028+ example in Section 2.5, the example signature base is signed with the
20292029+ test-key-rsa-pss key (see Appendix B.1.2) and the RSASSA-PSS
20302030+ algorithm described in Section 3.3.1, giving the following message
20312031+ signature output value, encoded in Base64:
20322032+20332033+ NOTE: '\' line wrapping per RFC 8792
20342034+20352035+ HIbjHC5rS0BYaa9v4QfD4193TORw7u9edguPh0AW3dMq9WImrlFrCGUDih47vAxi4L2\
20362036+ YRZ3XMJc1uOKk/J0ZmZ+wcta4nKIgBkKq0rM9hs3CQyxXGxHLMCy8uqK488o+9jrptQ\
20372037+ +xFPHK7a9sRL1IXNaagCNN3ZxJsYapFj+JXbmaI5rtAdSfSvzPuBCh+ARHBmWuNo1Uz\
20382038+ VVdHXrl8ePL4cccqlazIJdC4QEjrF+Sn4IxBQzTZsL9y9TP5FsZYzHvDqbInkTNigBc\
20392039+ E9cKOYNFCn4D/WM7F6TNuZO9EgtzepLWcjTymlHzK7aXq6Am6sfOrpIC49yXjj3ae6H\
20402040+ RalVc/g==
20412041+20422042+ Figure 2: Non-normative Example Signature Value
20432043+20442044+ Note that the RSA-PSS algorithm in use here is non-deterministic,
20452045+ meaning that a different signature value will be created every time
20462046+ the algorithm is run. The signature value provided here can be
20472047+ validated against the given keys, but newly generated signature
20482048+ values are not expected to match the example. See Section 7.3.5.
20492049+20502050+3.2. Verifying a Signature
20512051+20522052+ Verification of an HTTP message signature is a process that takes as
20532053+ its input the signature context (including the target message,
20542054+ particularly its Signature and Signature-Input fields) and the
20552055+ requirements for the application. The output of the verification is
20562056+ either a positive verification or an error.
20572057+20582058+ In order to verify a signature, a verifier MUST apply the following
20592059+ algorithm:
20602060+20612061+ 1. Parse the Signature and Signature-Input fields as described in
20622062+ Sections 4.1 and 4.2, and extract the signatures to be verified
20632063+ and their labels.
20642064+20652065+ 1.1. If there is more than one signature value present,
20662066+ determine which signature should be processed for this
20672067+ message based on the policy and configuration of the
20682068+ verifier. If an applicable signature is not found, produce
20692069+ an error.
20702070+20712071+ 1.2. If the chosen Signature field value does not have a
20722072+ corresponding Signature-Input field value (i.e., one with
20732073+ the same label), produce an error.
20742074+20752075+ 2. Parse the values of the chosen Signature-Input field as a
20762076+ parameterized Inner List to get the ordered list of covered
20772077+ components and the signature parameters for the signature to be
20782078+ verified.
20792079+20802080+ 3. Parse the value of the corresponding Signature field to get the
20812081+ byte array value of the signature to be verified.
20822082+20832083+ 4. Examine the signature parameters to confirm that the signature
20842084+ meets the requirements described in this document, as well as any
20852085+ additional requirements defined by the application such as which
20862086+ message components are required to be covered by the signature
20872087+ (Section 3.2.1).
20882088+20892089+ 5. Determine the verification key material for this signature. If
20902090+ the key material is known through external means such as static
20912091+ configuration or external protocol negotiation, the verifier will
20922092+ use the applicable technique to obtain the key material from this
20932093+ external knowledge. If the key is identified in the signature
20942094+ parameters, the verifier will dereference the key identifier to
20952095+ appropriate key material to use with the signature. The verifier
20962096+ has to determine the trustworthiness of the key material for the
20972097+ context in which the signature is presented. If a key is
20982098+ identified that the verifier does not know or trust for this
20992099+ request or that does not match something preconfigured, the
21002100+ verification MUST fail.
21012101+21022102+ 6. Determine the algorithm to apply for verification:
21032103+21042104+ 6.1. Start with the set of allowable algorithms known to the
21052105+ application. If any of the following steps select an
21062106+ algorithm that is not in this set, the signature validation
21072107+ fails.
21082108+21092109+ 6.2. If the algorithm is known through external means such as
21102110+ static configuration or external protocol negotiation, the
21112111+ verifier will use that algorithm.
21122112+21132113+ 6.3. If the algorithm can be determined from the keying
21142114+ material, such as through an algorithm field on the key
21152115+ value itself, the verifier will use that algorithm.
21162116+21172117+ 6.4. If the algorithm is explicitly stated in the signature
21182118+ parameters using a value from the "HTTP Signature
21192119+ Algorithms" registry, the verifier will use the referenced
21202120+ algorithm.
21212121+21222122+ 6.5. If the algorithm is specified in more than one location
21232123+ (e.g., a combination of static configuration, the algorithm
21242124+ signature parameter, and the key material itself), the
21252125+ resolved algorithms MUST be the same. If the algorithms
21262126+ are not the same, the verifier MUST fail the verification.
21272127+21282128+ 7. Use the received HTTP message and the parsed signature parameters
21292129+ to recreate the signature base, using the algorithm defined in
21302130+ Section 2.5. The value of the @signature-params input is the
21312131+ value of the Signature-Input field for this signature serialized
21322132+ according to the rules described in Section 2.3. Note that this
21332133+ does not include the signature's label from the Signature-Input
21342134+ field.
21352135+21362136+ 8. If the key material is appropriate for the algorithm, apply the
21372137+ appropriate HTTP_VERIFY cryptographic verification algorithm to
21382138+ the signature, recalculated signature base, key material, and
21392139+ signature value. The HTTP_VERIFY primitive and several concrete
21402140+ algorithms are defined in Section 3.3.
21412141+21422142+ 9. The results of the verification algorithm function are the final
21432143+ results of the cryptographic verification function.
21442144+21452145+ If any of the above steps fail or produce an error, the signature
21462146+ validation fails.
21472147+21482148+ For example, verifying the signature with the label sig1 of the
21492149+ following message with the test-key-rsa-pss key (see Appendix B.1.2)
21502150+ and the RSASSA-PSS algorithm described in Section 3.3.1:
21512151+21522152+ NOTE: '\' line wrapping per RFC 8792
21532153+21542154+ POST /foo?param=Value&Pet=dog HTTP/1.1
21552155+ Host: example.com
21562156+ Date: Tue, 20 Apr 2021 02:07:55 GMT
21572157+ Content-Type: application/json
21582158+ Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+T\
21592159+ aPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:
21602160+ Content-Length: 18
21612161+ Signature-Input: sig1=("@method" "@authority" "@path" \
21622162+ "content-digest" "content-length" "content-type")\
21632163+ ;created=1618884473;keyid="test-key-rsa-pss"
21642164+ Signature: sig1=:HIbjHC5rS0BYaa9v4QfD4193TORw7u9edguPh0AW3dMq9WImrl\
21652165+ FrCGUDih47vAxi4L2YRZ3XMJc1uOKk/J0ZmZ+wcta4nKIgBkKq0rM9hs3CQyxXGxH\
21662166+ LMCy8uqK488o+9jrptQ+xFPHK7a9sRL1IXNaagCNN3ZxJsYapFj+JXbmaI5rtAdSf\
21672167+ SvzPuBCh+ARHBmWuNo1UzVVdHXrl8ePL4cccqlazIJdC4QEjrF+Sn4IxBQzTZsL9y\
21682168+ 9TP5FsZYzHvDqbInkTNigBcE9cKOYNFCn4D/WM7F6TNuZO9EgtzepLWcjTymlHzK7\
21692169+ aXq6Am6sfOrpIC49yXjj3ae6HRalVc/g==:
21702170+21712171+ {"hello": "world"}
21722172+21732173+ With the additional requirements that at least the method, authority,
21742174+ path, content-digest, content-length, and content-type entries be
21752175+ signed, and that the signature creation timestamp be recent enough at
21762176+ the time of verification, the verification passes.
21772177+21782178+3.2.1. Enforcing Application Requirements
21792179+21802180+ The verification requirements specified in this document are intended
21812181+ as a baseline set of restrictions that are generally applicable to
21822182+ all use cases. Applications using HTTP message signatures MAY impose
21832183+ requirements above and beyond those specified by this document, as
21842184+ appropriate for their use case.
21852185+21862186+ Some non-normative examples of additional requirements an application
21872187+ might define are:
21882188+21892189+ * Requiring a specific set of header fields to be signed (e.g.,
21902190+ Authorization, Content-Digest).
21912191+21922192+ * Enforcing a maximum signature age from the time of the created
21932193+ timestamp.
21942194+21952195+ * Rejecting signatures past the expiration time in the expires
21962196+ timestamp. Note that the expiration time is a hint from the
21972197+ signer and that a verifier can always reject a signature ahead of
21982198+ its expiration time.
21992199+22002200+ * Prohibiting certain signature metadata parameters, such as runtime
22012201+ algorithm signaling with the alg parameter when the algorithm is
22022202+ determined from the key information.
22032203+22042204+ * Ensuring successful dereferencing of the keyid parameter to valid
22052205+ and appropriate key material.
22062206+22072207+ * Prohibiting the use of certain algorithms or mandating the use of
22082208+ a specific algorithm.
22092209+22102210+ * Requiring keys to be of a certain size (e.g., 2048 bits vs. 1024
22112211+ bits).
22122212+22132213+ * Enforcing uniqueness of the nonce parameter.
22142214+22152215+ * Requiring an application-specific value for the tag parameter.
22162216+22172217+ Application-specific requirements are expected and encouraged. When
22182218+ an application defines additional requirements, it MUST enforce them
22192219+ during the signature verification process, and signature verification
22202220+ MUST fail if the signature does not conform to the application's
22212221+ requirements.
22222222+22232223+ Applications MUST enforce the requirements defined in this document.
22242224+ Regardless of use case, applications MUST NOT accept signatures that
22252225+ do not conform to these requirements.
22262226+22272227+3.3. Signature Algorithms
22282228+22292229+ An HTTP message signature MUST use a cryptographic digital signature
22302230+ or MAC method that is appropriate for the key material, environment,
22312231+ and needs of the signer and verifier. This specification does not
22322232+ strictly limit the available signature algorithms, and any signature
22332233+ algorithm that meets these basic requirements MAY be used by an
22342234+ application of HTTP message signatures.
22352235+22362236+ For each signing method, HTTP_SIGN takes as its input the signature
22372237+ base defined in Section 2.5 as a byte array (M) and the signing key
22382238+ material (Ks), and outputs the resultant signature as a byte array
22392239+ (S):
22402240+22412241+ HTTP_SIGN (M, Ks) -> S
22422242+22432243+ For each verification method, HTTP_VERIFY takes as its input the
22442244+ regenerated signature base defined in Section 2.5 as a byte array
22452245+ (M), the verification key material (Kv), and the presented signature
22462246+ to be verified as a byte array (S), and outputs the verification
22472247+ result (V) as a Boolean:
22482248+22492249+ HTTP_VERIFY (M, Kv, S) -> V
22502250+22512251+ The following sections contain several common signature algorithms
22522252+ and demonstrate how these cryptographic primitives map to the
22532253+ HTTP_SIGN and HTTP_VERIFY definitions above. Which method to use can
22542254+ be communicated through the explicit algorithm (alg) signature
22552255+ parameter (Section 2.3), by reference to the key material, or through
22562256+ mutual agreement between the signer and verifier. Signature
22572257+ algorithms selected using the alg parameter MUST use values from the
22582258+ "HTTP Signature Algorithms" registry (Section 6.2).
22592259+22602260+3.3.1. RSASSA-PSS Using SHA-512
22612261+22622262+ To sign using this algorithm, the signer applies the RSASSA-PSS-SIGN
22632263+ (K, M) function defined in [RFC8017] with the signer's private
22642264+ signing key (K) and the signature base (M) (Section 2.5). The mask
22652265+ generation function is MGF1 as specified in [RFC8017] with a hash
22662266+ function of SHA-512 [RFC6234]. The salt length (sLen) is 64 bytes.
22672267+ The hash function (Hash) SHA-512 [RFC6234] is applied to the
22682268+ signature base to create the digest content to which the digital
22692269+ signature is applied. The resulting signed content byte array (S) is
22702270+ the HTTP message signature output used in Section 3.1.
22712271+22722272+ To verify using this algorithm, the verifier applies the RSASSA-PSS-
22732273+ VERIFY ((n, e), M, S) function [RFC8017] using the public key portion
22742274+ of the verification key material (n, e) and the signature base (M)
22752275+ recreated as described in Section 3.2. The mask generation function
22762276+ is MGF1 as specified in [RFC8017] with a hash function of SHA-512
22772277+ [RFC6234]. The salt length (sLen) is 64 bytes. The hash function
22782278+ (Hash) SHA-512 [RFC6234] is applied to the signature base to create
22792279+ the digest content to which the verification function is applied.
22802280+ The verifier extracts the HTTP message signature to be verified (S)
22812281+ as described in Section 3.2. The results of the verification
22822282+ function indicate whether the signature presented is valid.
22832283+22842284+ Note that the output of the RSASSA-PSS algorithm is non-
22852285+ deterministic; therefore, it is not correct to recalculate a new
22862286+ signature on the signature base and compare the results to an
22872287+ existing signature. Instead, the verification algorithm defined here
22882288+ needs to be used. See Section 7.3.5.
22892289+22902290+ The use of this algorithm can be indicated at runtime using the rsa-
22912291+ pss-sha512 value for the alg signature parameter.
22922292+22932293+3.3.2. RSASSA-PKCS1-v1_5 Using SHA-256
22942294+22952295+ To sign using this algorithm, the signer applies the RSASSA-
22962296+ PKCS1-V1_5-SIGN (K, M) function defined in [RFC8017] with the
22972297+ signer's private signing key (K) and the signature base (M)
22982298+ (Section 2.5). The hash SHA-256 [RFC6234] is applied to the
22992299+ signature base to create the digest content to which the digital
23002300+ signature is applied. The resulting signed content byte array (S) is
23012301+ the HTTP message signature output used in Section 3.1.
23022302+23032303+ To verify using this algorithm, the verifier applies the RSASSA-
23042304+ PKCS1-V1_5-VERIFY ((n, e), M, S) function [RFC8017] using the public
23052305+ key portion of the verification key material (n, e) and the signature
23062306+ base (M) recreated as described in Section 3.2. The hash function
23072307+ SHA-256 [RFC6234] is applied to the signature base to create the
23082308+ digest content to which the verification function is applied. The
23092309+ verifier extracts the HTTP message signature to be verified (S) as
23102310+ described in Section 3.2. The results of the verification function
23112311+ indicate whether the signature presented is valid.
23122312+23132313+ The use of this algorithm can be indicated at runtime using the rsa-
23142314+ v1_5-sha256 value for the alg signature parameter.
23152315+23162316+3.3.3. HMAC Using SHA-256
23172317+23182318+ To sign and verify using this algorithm, the signer applies the HMAC
23192319+ function [RFC2104] with the shared signing key (K) and the signature
23202320+ base (text) (Section 2.5). The hash function SHA-256 [RFC6234] is
23212321+ applied to the signature base to create the digest content to which
23222322+ the HMAC is applied, giving the signature result.
23232323+23242324+ For signing, the resulting value is the HTTP message signature output
23252325+ used in Section 3.1.
23262326+23272327+ For verification, the verifier extracts the HTTP message signature to
23282328+ be verified (S) as described in Section 3.2. The output of the HMAC
23292329+ function is compared bytewise to the value of the HTTP message
23302330+ signature, and the results of the comparison determine the validity
23312331+ of the signature presented.
23322332+23332333+ The use of this algorithm can be indicated at runtime using the hmac-
23342334+ sha256 value for the alg signature parameter.
23352335+23362336+3.3.4. ECDSA Using Curve P-256 DSS and SHA-256
23372337+23382338+ To sign using this algorithm, the signer applies the ECDSA signature
23392339+ algorithm defined in [FIPS186-5] using curve P-256 with the signer's
23402340+ private signing key and the signature base (Section 2.5). The hash
23412341+ SHA-256 [RFC6234] is applied to the signature base to create the
23422342+ digest content to which the digital signature is applied (M). The
23432343+ signature algorithm returns two integer values: r and s. These are
23442344+ both encoded as big-endian unsigned integers, zero-padded to 32
23452345+ octets each. These encoded values are concatenated into a single
23462346+ 64-octet array consisting of the encoded value of r followed by the
23472347+ encoded value of s. The resulting concatenation of (r, s) is a byte
23482348+ array of the HTTP message signature output used in Section 3.1.
23492349+23502350+ To verify using this algorithm, the verifier applies the ECDSA
23512351+ signature algorithm defined in [FIPS186-5] using the public key
23522352+ portion of the verification key material and the signature base
23532353+ recreated as described in Section 3.2. The hash function SHA-256
23542354+ [RFC6234] is applied to the signature base to create the digest
23552355+ content to which the signature verification function is applied (M).
23562356+ The verifier extracts the HTTP message signature to be verified (S)
23572357+ as described in Section 3.2. This value is a 64-octet array
23582358+ consisting of the encoded values of r and s concatenated in order.
23592359+ These are both encoded as big-endian unsigned integers, zero-padded
23602360+ to 32 octets each. The resulting signature value (r, s) is used as
23612361+ input to the signature verification function. The results of the
23622362+ verification function indicate whether the signature presented is
23632363+ valid.
23642364+23652365+ Note that the output of ECDSA signature algorithms is non-
23662366+ deterministic; therefore, it is not correct to recalculate a new
23672367+ signature on the signature base and compare the results to an
23682368+ existing signature. Instead, the verification algorithm defined here
23692369+ needs to be used. See Section 7.3.5.
23702370+23712371+ The use of this algorithm can be indicated at runtime using the
23722372+ ecdsa-p256-sha256 value for the alg signature parameter.
23732373+23742374+3.3.5. ECDSA Using Curve P-384 DSS and SHA-384
23752375+23762376+ To sign using this algorithm, the signer applies the ECDSA signature
23772377+ algorithm defined in [FIPS186-5] using curve P-384 with the signer's
23782378+ private signing key and the signature base (Section 2.5). The hash
23792379+ SHA-384 [RFC6234] is applied to the signature base to create the
23802380+ digest content to which the digital signature is applied (M). The
23812381+ signature algorithm returns two integer values: r and s. These are
23822382+ both encoded as big-endian unsigned integers, zero-padded to 48
23832383+ octets each. These encoded values are concatenated into a single
23842384+ 96-octet array consisting of the encoded value of r followed by the
23852385+ encoded value of s. The resulting concatenation of (r, s) is a byte
23862386+ array of the HTTP message signature output used in Section 3.1.
23872387+23882388+ To verify using this algorithm, the verifier applies the ECDSA
23892389+ signature algorithm defined in [FIPS186-5] using the public key
23902390+ portion of the verification key material and the signature base
23912391+ recreated as described in Section 3.2. The hash function SHA-384
23922392+ [RFC6234] is applied to the signature base to create the digest
23932393+ content to which the signature verification function is applied (M).
23942394+ The verifier extracts the HTTP message signature to be verified (S)
23952395+ as described in Section 3.2. This value is a 96-octet array
23962396+ consisting of the encoded values of r and s concatenated in order.
23972397+ These are both encoded as big-endian unsigned integers, zero-padded
23982398+ to 48 octets each. The resulting signature value (r, s) is used as
23992399+ input to the signature verification function. The results of the
24002400+ verification function indicate whether the signature presented is
24012401+ valid.
24022402+24032403+ Note that the output of ECDSA signature algorithms is non-
24042404+ deterministic; therefore, it is not correct to recalculate a new
24052405+ signature on the signature base and compare the results to an
24062406+ existing signature. Instead, the verification algorithm defined here
24072407+ needs to be used. See Section 7.3.5.
24082408+24092409+ The use of this algorithm can be indicated at runtime using the
24102410+ ecdsa-p384-sha384 value for the alg signature parameter.
24112411+24122412+3.3.6. EdDSA Using Curve edwards25519
24132413+24142414+ To sign using this algorithm, the signer applies the Ed25519
24152415+ algorithm defined in Section 5.1.6 of [RFC8032] with the signer's
24162416+ private signing key and the signature base (Section 2.5). The
24172417+ signature base is taken as the input message (M) with no prehash
24182418+ function. The signature is a 64-octet concatenation of R and S as
24192419+ specified in Section 5.1.6 of [RFC8032], and this is taken as a byte
24202420+ array for the HTTP message signature output used in Section 3.1.
24212421+24222422+ To verify using this algorithm, the signer applies the Ed25519
24232423+ algorithm defined in Section 5.1.7 of [RFC8032] using the public key
24242424+ portion of the verification key material (A) and the signature base
24252425+ recreated as described in Section 3.2. The signature base is taken
24262426+ as the input message (M) with no prehash function. The signature to
24272427+ be verified is processed as the 64-octet concatenation of R and S as
24282428+ specified in Section 5.1.7 of [RFC8032]. The results of the
24292429+ verification function indicate whether the signature presented is
24302430+ valid.
24312431+24322432+ The use of this algorithm can be indicated at runtime using the
24332433+ ed25519 value for the alg signature parameter.
24342434+24352435+3.3.7. JSON Web Signature (JWS) Algorithms
24362436+24372437+ If the signing algorithm is a JSON Object Signing and Encryption
24382438+ (JOSE) signing algorithm from the "JSON Web Signature and Encryption
24392439+ Algorithms" registry established by [RFC7518], the JWS algorithm
24402440+ definition determines the signature and hashing algorithms to apply
24412441+ for both signing and verification.
24422442+24432443+ For both signing and verification, the HTTP message's signature base
24442444+ (Section 2.5) is used as the entire "JWS Signing Input". The JOSE
24452445+ Header [JWS] [RFC7517] is not used, and the signature base is not
24462446+ first encoded in Base64 before applying the algorithm. The output of
24472447+ the JWS Signature is taken as a byte array prior to the Base64url
24482448+ encoding used in JOSE.
24492449+24502450+ The JWS algorithm MUST NOT be "none" and MUST NOT be any algorithm
24512451+ with a JOSE Implementation Requirement of "Prohibited".
24522452+24532453+ JSON Web Algorithm (JWA) values from the "JSON Web Signature and
24542454+ Encryption Algorithms" registry are not included as signature
24552455+ parameters. Typically, the JWS algorithm can be signaled using JSON
24562456+ Web Keys (JWKs) or other mechanisms common to JOSE implementations.
24572457+ In fact, JWA values are not registered in the "HTTP Signature
24582458+ Algorithms" registry (Section 6.2), and so the explicit alg signature
24592459+ parameter is not used at all when using JOSE signing algorithms.
24602460+24612461+4. Including a Message Signature in a Message
24622462+24632463+ HTTP message signatures can be included within an HTTP message via
24642464+ the Signature-Input and Signature fields, both defined within this
24652465+ specification.
24662466+24672467+ The Signature-Input field identifies the covered components and
24682468+ parameters that describe how the signature was generated, while the
24692469+ Signature field contains the signature value. Each field MAY contain
24702470+ multiple labeled values.
24712471+24722472+ An HTTP message signature is identified by a label within an HTTP
24732473+ message. This label MUST be unique within a given HTTP message and
24742474+ MUST be used in both the Signature-Input field and the Signature
24752475+ field. The label is chosen by the signer, except where a specific
24762476+ label is dictated by protocol negotiations such as those described in
24772477+ Section 5.
24782478+24792479+ An HTTP message signature MUST use both the Signature-Input field and
24802480+ the Signature field, and each field MUST contain the same labels.
24812481+ The presence of a label in one field but not the other is an error.
24822482+24832483+4.1. The Signature-Input HTTP Field
24842484+24852485+ The Signature-Input field is a Dictionary Structured Field (defined
24862486+ in Section 3.2 of [STRUCTURED-FIELDS]) containing the metadata for
24872487+ one or more message signatures generated from components within the
24882488+ HTTP message. Each member describes a single message signature. The
24892489+ member's key is the label that uniquely identifies the message
24902490+ signature within the HTTP message. The member's value is the covered
24912491+ components ordered set serialized as an Inner List, including all
24922492+ signature metadata parameters identified by the label:
24932493+24942494+ NOTE: '\' line wrapping per RFC 8792
24952495+24962496+ Signature-Input: sig1=("@method" "@target-uri" "@authority" \
24972497+ "content-digest" "cache-control");\
24982498+ created=1618884475;keyid="test-key-rsa-pss"
24992499+25002500+ To facilitate signature validation, the Signature-Input field value
25012501+ MUST contain the same serialized value used in generating the
25022502+ signature base's @signature-params value defined in Section 2.3.
25032503+ Note that in a Structured Field value, list order and parameter order
25042504+ have to be preserved.
25052505+25062506+ The signer MAY include the Signature-Input field as a trailer to
25072507+ facilitate signing a message after its content has been processed by
25082508+ the signer. However, since intermediaries are allowed to drop
25092509+ trailers as per [HTTP], it is RECOMMENDED that the Signature-Input
25102510+ field be included only as a header field to avoid signatures being
25112511+ inadvertently stripped from a message.
25122512+25132513+ Multiple Signature-Input fields MAY be included in a single HTTP
25142514+ message. The signature labels MUST be unique across all field
25152515+ values.
25162516+25172517+4.2. The Signature HTTP Field
25182518+25192519+ The Signature field is a Dictionary Structured Field (defined in
25202520+ Section 3.2 of [STRUCTURED-FIELDS]) containing one or more message
25212521+ signatures generated from the signature context of the target
25222522+ message. The member's key is the label that uniquely identifies the
25232523+ message signature within the HTTP message. The member's value is a
25242524+ Byte Sequence containing the signature value for the message
25252525+ signature identified by the label:
25262526+25272527+ NOTE: '\' line wrapping per RFC 8792
25282528+25292529+ Signature: sig1=:P0wLUszWQjoi54udOtydf9IWTfNhy+r53jGFj9XZuP4uKwxyJo\
25302530+ 1RSHi+oEF1FuX6O29d+lbxwwBao1BAgadijW+7O/PyezlTnqAOVPWx9GlyntiCiHz\
25312531+ C87qmSQjvu1CFyFuWSjdGa3qLYYlNm7pVaJFalQiKWnUaqfT4LyttaXyoyZW84jS8\
25322532+ gyarxAiWI97mPXU+OVM64+HVBHmnEsS+lTeIsEQo36T3NFf2CujWARPQg53r58Rmp\
25332533+ Z+J9eKR2CD6IJQvacn5A4Ix5BUAVGqlyp8JYm+S/CWJi31PNUjRRCusCVRj05NrxA\
25342534+ BNFv3r5S9IXf2fYJK+eyW4AiGVMvMcOg==:
25352535+25362536+ The signer MAY include the Signature field as a trailer to facilitate
25372537+ signing a message after its content has been processed by the signer.
25382538+ However, since intermediaries are allowed to drop trailers as per
25392539+ [HTTP], it is RECOMMENDED that the Signature field be included only
25402540+ as a header field to avoid signatures being inadvertently stripped
25412541+ from a message.
25422542+25432543+ Multiple Signature fields MAY be included in a single HTTP message.
25442544+ The signature labels MUST be unique across all field values.
25452545+25462546+4.3. Multiple Signatures
25472547+25482548+ Multiple distinct signatures MAY be included in a single message.
25492549+ Each distinct signature MUST have a unique label. These multiple
25502550+ signatures could all be added by the same signer, or they could come
25512551+ from several different signers. For example, a signer may include
25522552+ multiple signatures signing the same message components with
25532553+ different keys or algorithms to support verifiers with different
25542554+ capabilities, or a reverse proxy may include information about the
25552555+ client in fields when forwarding the request to a service host,
25562556+ including a signature over the client's original signature values.
25572557+25582558+ The following non-normative example starts with a signed request from
25592559+ the client. A reverse proxy takes this request and validates the
25602560+ client's signature:
25612561+25622562+ NOTE: '\' line wrapping per RFC 8792
25632563+25642564+ POST /foo?param=Value&Pet=dog HTTP/1.1
25652565+ Host: example.com
25662566+ Date: Tue, 20 Apr 2021 02:07:55 GMT
25672567+ Content-Type: application/json
25682568+ Content-Length: 18
25692569+ Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+T\
25702570+ aPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:
25712571+ Signature-Input: sig1=("@method" "@authority" "@path" \
25722572+ "content-digest" "content-type" "content-length")\
25732573+ ;created=1618884475;keyid="test-key-ecc-p256"
25742574+ Signature: sig1=:X5spyd6CFnAG5QnDyHfqoSNICd+BUP4LYMz2Q0JXlb//4Ijpzp\
25752575+ +kve2w4NIyqeAuM7jTDX+sNalzA8ESSaHD3A==:
25762576+25772577+ {"hello": "world"}
25782578+25792579+ The proxy then alters the message before forwarding it on to the
25802580+ origin server, changing the target host and adding the Forwarded
25812581+ header field defined in [RFC7239]:
25822582+25832583+ NOTE: '\' line wrapping per RFC 8792
25842584+25852585+ POST /foo?param=Value&Pet=dog HTTP/1.1
25862586+ Host: origin.host.internal.example
25872587+ Date: Tue, 20 Apr 2021 02:07:56 GMT
25882588+ Content-Type: application/json
25892589+ Content-Length: 18
25902590+ Forwarded: for=192.0.2.123;host=example.com;proto=https
25912591+ Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+T\
25922592+ aPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:
25932593+ Signature-Input: sig1=("@method" "@authority" "@path" \
25942594+ "content-digest" "content-type" "content-length")\
25952595+ ;created=1618884475;keyid="test-key-ecc-p256"
25962596+ Signature: sig1=:X5spyd6CFnAG5QnDyHfqoSNICd+BUP4LYMz2Q0JXlb//4Ijpzp\
25972597+ +kve2w4NIyqeAuM7jTDX+sNalzA8ESSaHD3A==:
25982598+25992599+ {"hello": "world"}
26002600+26012601+ The proxy is in a position to validate the incoming client's
26022602+ signature and make its own statement to the origin server about the
26032603+ nature of the request that it is forwarding by adding its own
26042604+ signature over the new message before passing it along to the origin
26052605+ server. The proxy also includes all the elements from the original
26062606+ message that are relevant to the origin server's processing. In many
26072607+ cases, the proxy will want to cover all the same components that were
26082608+ covered by the client's signature, which is the case in the following
26092609+ example. Note that in this example, the proxy is signing over the
26102610+ new authority value, which it has changed. The proxy also adds the
26112611+ Forwarded header field to its own signature value. The proxy
26122612+ identifies its own key and algorithm and, in this example, includes
26132613+ an expiration for the signature to indicate to downstream systems
26142614+ that the proxy will not vouch for this signed message past this short
26152615+ time window. This results in a signature base of:
26162616+26172617+ NOTE: '\' line wrapping per RFC 8792
26182618+26192619+ "@method": POST
26202620+ "@authority": origin.host.internal.example
26212621+ "@path": /foo
26222622+ "content-digest": sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX\
26232623+ +TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:
26242624+ "content-type": application/json
26252625+ "content-length": 18
26262626+ "forwarded": for=192.0.2.123;host=example.com;proto=https
26272627+ "@signature-params": ("@method" "@authority" "@path" \
26282628+ "content-digest" "content-type" "content-length" "forwarded")\
26292629+ ;created=1618884480;keyid="test-key-rsa";alg="rsa-v1_5-sha256"\
26302630+ ;expires=1618884540
26312631+26322632+ and a signature output value of:
26332633+26342634+ NOTE: '\' line wrapping per RFC 8792
26352635+26362636+ S6ZzPXSdAMOPjN/6KXfXWNO/f7V6cHm7BXYUh3YD/fRad4BCaRZxP+JH+8XY1I6+8Cy\
26372637+ +CM5g92iHgxtRPz+MjniOaYmdkDcnL9cCpXJleXsOckpURl49GwiyUpZ10KHgOEe11s\
26382638+ x3G2gxI8S0jnxQB+Pu68U9vVcasqOWAEObtNKKZd8tSFu7LB5YAv0RAGhB8tmpv7sFn\
26392639+ Im9y+7X5kXQfi8NMaZaA8i2ZHwpBdg7a6CMfwnnrtflzvZdXAsD3LH2TwevU+/PBPv0\
26402640+ B6NMNk93wUs/vfJvye+YuI87HU38lZHowtznbLVdp770I6VHR6WfgS9ddzirrswsE1w\
26412641+ 5o0LV/g==
26422642+26432643+ These values are added to the HTTP request message by the proxy. The
26442644+ original signature is included under the label sig1, and the reverse
26452645+ proxy's signature is included under the label proxy_sig. The proxy
26462646+ uses the key test-key-rsa to create its signature using the rsa-
26472647+ v1_5-sha256 signature algorithm, while the client's original
26482648+ signature was made using the key test-key-rsa-pss and an RSA-PSS
26492649+ signature algorithm:
26502650+26512651+ NOTE: '\' line wrapping per RFC 8792
26522652+26532653+ POST /foo?param=Value&Pet=dog HTTP/1.1
26542654+ Host: origin.host.internal.example
26552655+ Date: Tue, 20 Apr 2021 02:07:56 GMT
26562656+ Content-Type: application/json
26572657+ Content-Length: 18
26582658+ Forwarded: for=192.0.2.123;host=example.com;proto=https
26592659+ Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+T\
26602660+ aPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:
26612661+ Signature-Input: sig1=("@method" "@authority" "@path" \
26622662+ "content-digest" "content-type" "content-length")\
26632663+ ;created=1618884475;keyid="test-key-ecc-p256", \
26642664+ proxy_sig=("@method" "@authority" "@path" "content-digest" \
26652665+ "content-type" "content-length" "forwarded")\
26662666+ ;created=1618884480;keyid="test-key-rsa";alg="rsa-v1_5-sha256"\
26672667+ ;expires=1618884540
26682668+ Signature: sig1=:X5spyd6CFnAG5QnDyHfqoSNICd+BUP4LYMz2Q0JXlb//4Ijpzp\
26692669+ +kve2w4NIyqeAuM7jTDX+sNalzA8ESSaHD3A==:, \
26702670+ proxy_sig=:S6ZzPXSdAMOPjN/6KXfXWNO/f7V6cHm7BXYUh3YD/fRad4BCaRZxP+\
26712671+ JH+8XY1I6+8Cy+CM5g92iHgxtRPz+MjniOaYmdkDcnL9cCpXJleXsOckpURl49G\
26722672+ wiyUpZ10KHgOEe11sx3G2gxI8S0jnxQB+Pu68U9vVcasqOWAEObtNKKZd8tSFu7\
26732673+ LB5YAv0RAGhB8tmpv7sFnIm9y+7X5kXQfi8NMaZaA8i2ZHwpBdg7a6CMfwnnrtf\
26742674+ lzvZdXAsD3LH2TwevU+/PBPv0B6NMNk93wUs/vfJvye+YuI87HU38lZHowtznbL\
26752675+ Vdp770I6VHR6WfgS9ddzirrswsE1w5o0LV/g==:
26762676+26772677+ {"hello": "world"}
26782678+26792679+ While the proxy could additionally include the client's Signature
26802680+ field value and Signature-Input fields from the original message in
26812681+ the new signature's covered components, this practice is NOT
26822682+ RECOMMENDED due to known weaknesses in signing signature values as
26832683+ discussed in Section 7.3.7. The proxy is in a position to validate
26842684+ the client's signature; the changes the proxy makes to the message
26852685+ will invalidate the existing signature when the message is seen by
26862686+ the origin server. In this example, it is possible for the origin
26872687+ server to have additional information in its signature context to
26882688+ account for the change in authority, though this practice requires
26892689+ additional configuration and extra care as discussed in
26902690+ Section 7.4.4. In other applications, the origin server will not be
26912691+ able to verify the original signature itself but will still want to
26922692+ verify that the proxy has done the appropriate validation of the
26932693+ client's signature. An application that needs to signal successful
26942694+ processing or receipt of a signature would need to carefully specify
26952695+ alternative mechanisms for sending such a signal securely.
26962696+26972697+5. Requesting Signatures
26982698+26992699+ While a signer is free to attach a signature to a request or response
27002700+ without prompting, it is often desirable for a potential verifier to
27012701+ signal that it expects a signature from a potential signer using the
27022702+ Accept-Signature field.
27032703+27042704+ When the Accept-Signature field is sent in an HTTP request message,
27052705+ the field indicates that the client desires the server to sign the
27062706+ response using the identified parameters, and the target message is
27072707+ the response to this request. All responses from resources that
27082708+ support such signature negotiation SHOULD either be uncacheable or
27092709+ contain a Vary header field that lists Accept-Signature, in order to
27102710+ prevent a cache from returning a response with a signature intended
27112711+ for a different request.
27122712+27132713+ When the Accept-Signature field is used in an HTTP response message,
27142714+ the field indicates that the server desires the client to sign its
27152715+ next request to the server with the identified parameters, and the
27162716+ target message is the client's next request. The client can choose
27172717+ to also continue signing future requests to the same server in the
27182718+ same way.
27192719+27202720+ The target message of an Accept-Signature field MUST include all
27212721+ labeled signatures indicated in the Accept-Signature field, each
27222722+ covering the same identified components of the Accept-Signature
27232723+ field.
27242724+27252725+ The sender of an Accept-Signature field MUST include only identifiers
27262726+ that are appropriate for the type of the target message. For
27272727+ example, if the target message is a request, the covered components
27282728+ cannot include the @status component identifier.
27292729+27302730+5.1. The Accept-Signature Field
27312731+27322732+ The Accept-Signature field is a Dictionary Structured Field (defined
27332733+ in Section 3.2 of [STRUCTURED-FIELDS]) containing the metadata for
27342734+ one or more requested message signatures to be generated from message
27352735+ components of the target HTTP message. Each member describes a
27362736+ single message signature. The member's key is the label that
27372737+ uniquely identifies the requested message signature within the
27382738+ context of the target HTTP message.
27392739+27402740+ The member's value is the serialization of the desired covered
27412741+ components of the target message, including any allowed component
27422742+ metadata parameters, using the serialization process defined in
27432743+ Section 2.3:
27442744+27452745+ NOTE: '\' line wrapping per RFC 8792
27462746+27472747+ Accept-Signature: sig1=("@method" "@target-uri" "@authority" \
27482748+ "content-digest" "cache-control");\
27492749+ keyid="test-key-rsa-pss";created;tag="app-123"
27502750+27512751+ The list of component identifiers indicates the exact set of
27522752+ component identifiers to be included in the requested signature,
27532753+ including all applicable component parameters.
27542754+27552755+ The signature request MAY include signature metadata parameters that
27562756+ indicate desired behavior for the signer. The following behavior is
27572757+ defined by this specification:
27582758+27592759+ created: The signer is requested to generate and include a creation
27602760+ time. This parameter has no associated value when sent as a
27612761+ signature request.
27622762+27632763+ expires: The signer is requested to generate and include an
27642764+ expiration time. This parameter has no associated value when sent
27652765+ as a signature request.
27662766+27672767+ nonce: The signer is requested to include the value of this
27682768+ parameter as the signature nonce in the target signature.
27692769+27702770+ alg: The signer is requested to use the indicated signature
27712771+ algorithm from the "HTTP Signature Algorithms" registry to create
27722772+ the target signature.
27732773+27742774+ keyid: The signer is requested to use the indicated key material to
27752775+ create the target signature.
27762776+27772777+ tag: The signer is requested to include the value of this parameter
27782778+ as the signature tag in the target signature.
27792779+27802780+5.2. Processing an Accept-Signature
27812781+27822782+ The receiver of an Accept-Signature field fulfills that header as
27832783+ follows:
27842784+27852785+ 1. Parse the field value as a Dictionary.
27862786+27872787+ 2. For each member of the Dictionary:
27882788+27892789+ 2.1. The key is taken as the label of the output signature as
27902790+ specified in Section 4.1.
27912791+27922792+ 2.2. Parse the value of the member to obtain the set of covered
27932793+ component identifiers.
27942794+27952795+ 2.3. Determine that the covered components are applicable to the
27962796+ target message. If not, the process fails and returns an
27972797+ error.
27982798+27992799+ 2.4. Process the requested parameters, such as the signing
28002800+ algorithm and key material. If any requested parameters
28012801+ cannot be fulfilled or if the requested parameters conflict
28022802+ with those deemed appropriate to the target message, the
28032803+ process fails and returns an error.
28042804+28052805+ 2.5. Select and generate any additional parameters necessary for
28062806+ completing the signature.
28072807+28082808+ 2.6. Create the HTTP message signature over the target message.
28092809+28102810+ 2.7. Create the Signature-Input and Signature field values, and
28112811+ associate them with the label.
28122812+28132813+ 3. Optionally create any additional Signature-Input and Signature
28142814+ field values, with unique labels not found in the Accept-
28152815+ Signature field.
28162816+28172817+ 4. Combine all labeled Signature-Input and Signature field values,
28182818+ and attach both fields to the target message.
28192819+28202820+ By this process, a signature applied to a target message MUST have
28212821+ the same label, MUST include the same set of covered components, MUST
28222822+ process all requested parameters, and MAY have additional parameters.
28232823+28242824+ The receiver of an Accept-Signature field MAY ignore any signature
28252825+ request that does not fit application parameters.
28262826+28272827+ The target message MAY include additional signatures not specified by
28282828+ the Accept-Signature field. For example, to cover additional message
28292829+ components, the signer can create a second signature that includes
28302830+ the additional components as well as the signature output of the
28312831+ requested signature.
28322832+28332833+6. IANA Considerations
28342834+28352835+ IANA has updated one registry and created four new registries,
28362836+ according to the following sections.
28372837+28382838+6.1. HTTP Field Name Registration
28392839+28402840+ IANA has updated the entries in the "Hypertext Transfer Protocol
28412841+ (HTTP) Field Name Registry" as follows:
28422842+28432843+ +==================+===========+=========================+
28442844+ | Field Name | Status | Reference |
28452845+ +==================+===========+=========================+
28462846+ | Signature-Input | permanent | Section 4.1 of RFC 9421 |
28472847+ +------------------+-----------+-------------------------+
28482848+ | Signature | permanent | Section 4.2 of RFC 9421 |
28492849+ +------------------+-----------+-------------------------+
28502850+ | Accept-Signature | permanent | Section 5.1 of RFC 9421 |
28512851+ +------------------+-----------+-------------------------+
28522852+28532853+ Table 1: Updates to the Hypertext Transfer Protocol
28542854+ (HTTP) Field Name Registry
28552855+28562856+6.2. HTTP Signature Algorithms Registry
28572857+28582858+ This document defines HTTP signature algorithms, for which IANA has
28592859+ created and now maintains a new registry titled "HTTP Signature
28602860+ Algorithms". Initial values for this registry are given in
28612861+ Section 6.2.2. Future assignments and modifications to existing
28622862+ assignments are to be made through the Specification Required
28632863+ registration policy [RFC8126].
28642864+28652865+ The algorithms listed in this registry identify some possible
28662866+ cryptographic algorithms for applications to use with this
28672867+ specification, but the entries neither represent an exhaustive list
28682868+ of possible algorithms nor indicate fitness for purpose with any
28692869+ particular application of this specification. An application is free
28702870+ to implement any algorithm that suits its needs, provided the signer
28712871+ and verifier can agree to the parameters of that algorithm in a
28722872+ secure and deterministic fashion. When an application needs to
28732873+ signal the use of a particular algorithm at runtime using the alg
28742874+ signature parameter, this registry provides a mapping between the
28752875+ value of that parameter and a particular algorithm. However, the use
28762876+ of the alg parameter needs to be treated with caution to avoid
28772877+ various forms of algorithm confusion and substitution attacks, as
28782878+ discussed in Section 7.
28792879+28802880+ The Status value should reflect standardization status and the broad
28812881+ opinion of relevant interest groups such as the IETF or security-
28822882+ related Standards Development Organizations (SDOs). When an
28832883+ algorithm is first registered, the designated expert (DE) should set
28842884+ the Status field to "Active" if there is consensus for the algorithm
28852885+ to be generally recommended as secure or "Provisional" if the
28862886+ algorithm has not reached that consensus, e.g., for an experimental
28872887+ algorithm. A status of "Provisional" does not mean that the
28882888+ algorithm is known to be insecure but instead indicates that the
28892889+ algorithm has not reached consensus regarding its properties. If at
28902890+ a future time the algorithm as registered is found to have flaws, the
28912891+ registry entry can be updated and the algorithm can be marked as
28922892+ "Deprecated" to indicate that the algorithm has been found to have
28932893+ problems. This status does not preclude an application from using a
28942894+ particular algorithm; rather, it serves to provide a warning
28952895+ regarding possible known issues with an algorithm that need to be
28962896+ considered by the application. The DE can further ensure that the
28972897+ registration includes an explanation and reference for the Status
28982898+ value; this is particularly important for deprecated algorithms.
28992899+29002900+ The DE is expected to do the following:
29012901+29022902+ * Ensure that the algorithms referenced by a registered algorithm
29032903+ identifier are fully defined with all parameters (e.g., salt,
29042904+ hash, required key length) fixed by the defining text.
29052905+29062906+ * Ensure that the algorithm definition fully specifies the HTTP_SIGN
29072907+ and HTTP_VERIFY primitive functions, including how all defined
29082908+ inputs and outputs map to the underlying cryptographic algorithm.
29092909+29102910+ * Reject any registrations that are aliases of existing
29112911+ registrations.
29122912+29132913+ * Ensure that all registrations follow the template presented in
29142914+ Section 6.2.1; this includes ensuring that the length of the name
29152915+ is not excessive while still being unique and recognizable.
29162916+29172917+ This specification creates algorithm identifiers by including major
29182918+ parameters in the identifier String in order to make the algorithm
29192919+ name unique and recognizable by developers. However, algorithm
29202920+ identifiers in this registry are to be interpreted as whole String
29212921+ values and not as a combination of parts. That is to say, it is
29222922+ expected that implementors understand rsa-pss-sha512 as referring to
29232923+ one specific algorithm with its hash, mask, and salt values set as
29242924+ defined in the defining text that establishes the identifier in
29252925+ question. Implementors do not parse out the rsa, pss, and sha512
29262926+ portions of the identifier to determine parameters of the signing
29272927+ algorithm from the String, and the registration of one combination of
29282928+ parameters does not imply the registration of other combinations.
29292929+29302930+6.2.1. Registration Template
29312931+29322932+ Algorithm Name:
29332933+ An identifier for the HTTP signature algorithm. The name MUST be
29342934+ an ASCII string that conforms to the sf-string ABNF rule in
29352935+ Section 3.3.3 of [STRUCTURED-FIELDS] and SHOULD NOT exceed 20
29362936+ characters in length. The identifier MUST be unique within the
29372937+ context of the registry.
29382938+29392939+ Description:
29402940+ A brief description of the algorithm used to sign the signature
29412941+ base.
29422942+29432943+ Status:
29442944+ The status of the algorithm. MUST start with one of the following
29452945+ values and MAY contain additional explanatory text. The options
29462946+ are:
29472947+29482948+ "Active": For algorithms without known problems. The signature
29492949+ algorithm is fully specified, and its security properties are
29502950+ understood.
29512951+29522952+ "Provisional": For unproven algorithms. The signature algorithm
29532953+ is fully specified, but its security properties are not known
29542954+ or proven.
29552955+29562956+ "Deprecated": For algorithms with known security issues. The
29572957+ signature algorithm is no longer recommended for general use
29582958+ and might be insecure or unsafe in some known circumstances.
29592959+29602960+ Reference:
29612961+ Reference to the document or documents that specify the algorithm,
29622962+ preferably including a URI that can be used to retrieve a copy of
29632963+ the document(s). An indication of the relevant sections may also
29642964+ be included but is not required.
29652965+29662966+6.2.2. Initial Contents
29672967+29682968+ The table below contains the initial contents of the "HTTP Signature
29692969+ Algorithms" registry.
29702970+29712971+ +===================+===================+========+===============+
29722972+ | Algorithm Name | Description | Status | Reference |
29732973+ +===================+===================+========+===============+
29742974+ | rsa-pss-sha512 | RSASSA-PSS using | Active | Section 3.3.1 |
29752975+ | | SHA-512 | | of RFC 9421 |
29762976+ +-------------------+-------------------+--------+---------------+
29772977+ | rsa-v1_5-sha256 | RSASSA-PKCS1-v1_5 | Active | Section 3.3.2 |
29782978+ | | using SHA-256 | | of RFC 9421 |
29792979+ +-------------------+-------------------+--------+---------------+
29802980+ | hmac-sha256 | HMAC using | Active | Section 3.3.3 |
29812981+ | | SHA-256 | | of RFC 9421 |
29822982+ +-------------------+-------------------+--------+---------------+
29832983+ | ecdsa-p256-sha256 | ECDSA using curve | Active | Section 3.3.4 |
29842984+ | | P-256 DSS and | | of RFC 9421 |
29852985+ | | SHA-256 | | |
29862986+ +-------------------+-------------------+--------+---------------+
29872987+ | ecdsa-p384-sha384 | ECDSA using curve | Active | Section 3.3.5 |
29882988+ | | P-384 DSS and | | of RFC 9421 |
29892989+ | | SHA-384 | | |
29902990+ +-------------------+-------------------+--------+---------------+
29912991+ | ed25519 | EdDSA using curve | Active | Section 3.3.6 |
29922992+ | | edwards25519 | | of RFC 9421 |
29932993+ +-------------------+-------------------+--------+---------------+
29942994+29952995+ Table 2: Initial Contents of the HTTP Signature Algorithms
29962996+ Registry
29972997+29982998+6.3. HTTP Signature Metadata Parameters Registry
29992999+30003000+ This document defines the signature parameters structure
30013001+ (Section 2.3), which may have parameters containing metadata about a
30023002+ message signature. IANA has created and now maintains a new registry
30033003+ titled "HTTP Signature Metadata Parameters" to record and maintain
30043004+ the set of parameters defined for use with member values in the
30053005+ signature parameters structure. Initial values for this registry are
30063006+ given in Section 6.3.2. Future assignments and modifications to
30073007+ existing assignments are to be made through the Expert Review
30083008+ registration policy [RFC8126].
30093009+30103010+ The DE is expected to do the following:
30113011+30123012+ * Ensure that the name follows the template presented in
30133013+ Section 6.3.1; this includes ensuring that the length of the name
30143014+ is not excessive while still being unique and recognizable for its
30153015+ defined function.
30163016+30173017+ * Ensure that the defined functionality is clear and does not
30183018+ conflict with other registered parameters.
30193019+30203020+ * Ensure that the definition of the metadata parameter includes its
30213021+ behavior when used as part of the normal signature process as well
30223022+ as when used in an Accept-Signature field.
30233023+30243024+6.3.1. Registration Template
30253025+30263026+ Name:
30273027+ An identifier for the HTTP signature metadata parameter. The name
30283028+ MUST be an ASCII string that conforms to the key ABNF rule defined
30293029+ in Section 3.1.2 of [STRUCTURED-FIELDS] and SHOULD NOT exceed 20
30303030+ characters in length. The identifier MUST be unique within the
30313031+ context of the registry.
30323032+30333033+ Description:
30343034+ A brief description of the metadata parameter and what it
30353035+ represents.
30363036+30373037+ Reference:
30383038+ Reference to the document or documents that specify the parameter,
30393039+ preferably including a URI that can be used to retrieve a copy of
30403040+ the document(s). An indication of the relevant sections may also
30413041+ be included but is not required.
30423042+30433043+6.3.2. Initial Contents
30443044+30453045+ The table below contains the initial contents of the "HTTP Signature
30463046+ Metadata Parameters" registry. Each row in the table represents a
30473047+ distinct entry in the registry.
30483048+30493049+ +=========+===============================+=============+
30503050+ | Name | Description | Reference |
30513051+ +=========+===============================+=============+
30523052+ | alg | Explicitly declared signature | Section 2.3 |
30533053+ | | algorithm | of RFC 9421 |
30543054+ +---------+-------------------------------+-------------+
30553055+ | created | Timestamp of signature | Section 2.3 |
30563056+ | | creation | of RFC 9421 |
30573057+ +---------+-------------------------------+-------------+
30583058+ | expires | Timestamp of proposed | Section 2.3 |
30593059+ | | signature expiration | of RFC 9421 |
30603060+ +---------+-------------------------------+-------------+
30613061+ | keyid | Key identifier for the | Section 2.3 |
30623062+ | | signing and verification keys | of RFC 9421 |
30633063+ | | used to create this signature | |
30643064+ +---------+-------------------------------+-------------+
30653065+ | nonce | A single-use nonce value | Section 2.3 |
30663066+ | | | of RFC 9421 |
30673067+ +---------+-------------------------------+-------------+
30683068+ | tag | An application-specific tag | Section 2.3 |
30693069+ | | for a signature | of RFC 9421 |
30703070+ +---------+-------------------------------+-------------+
30713071+30723072+ Table 3: Initial Contents of the HTTP Signature
30733073+ Metadata Parameters Registry
30743074+30753075+6.4. HTTP Signature Derived Component Names Registry
30763076+30773077+ This document defines a method for canonicalizing HTTP message
30783078+ components, including components that can be derived from the context
30793079+ of the target message outside of the HTTP fields. These derived
30803080+ components are identified by a unique String, known as the component
30813081+ name. Component names for derived components always start with the
30823082+ "at" (@) symbol to distinguish them from HTTP field names. IANA has
30833083+ created and now maintains a new registry titled "HTTP Signature
30843084+ Derived Component Names" to record and maintain the set of non-field
30853085+ component names and the methods used to produce their associated
30863086+ component values. Initial values for this registry are given in
30873087+ Section 6.4.2. Future assignments and modifications to existing
30883088+ assignments are to be made through the Expert Review registration
30893089+ policy [RFC8126].
30903090+30913091+ The DE is expected to do the following:
30923092+30933093+ * Ensure that the name follows the template presented in
30943094+ Section 6.4.1; this includes ensuring that the length of the name
30953095+ is not excessive while still being unique and recognizable for its
30963096+ defined function.
30973097+30983098+ * Ensure that the component value represented by the registration
30993099+ request can be deterministically derived from the target HTTP
31003100+ message.
31013101+31023102+ * Ensure that any parameters defined for the registration request
31033103+ are clearly documented, along with their effects on the component
31043104+ value.
31053105+31063106+ The DE should ensure that a registration is sufficiently distinct
31073107+ from existing derived component definitions to warrant its
31083108+ registration.
31093109+31103110+ When setting a registered item's status to "Deprecated", the DE
31113111+ should ensure that a reason for the deprecation is documented, along
31123112+ with instructions for moving away from the deprecated functionality.
31133113+31143114+6.4.1. Registration Template
31153115+31163116+ Name:
31173117+ A name for the HTTP derived component. The name MUST begin with
31183118+ the "at" (@) character followed by an ASCII string consisting only
31193119+ of lowercase characters ("a"-"z"), digits ("0"-"9"), and hyphens
31203120+ ("-"), and SHOULD NOT exceed 20 characters in length. The name
31213121+ MUST be unique within the context of the registry.
31223122+31233123+ Description:
31243124+ A description of the derived component.
31253125+31263126+ Status:
31273127+ A brief text description of the status of the algorithm. The
31283128+ description MUST begin with one of "Active" or "Deprecated" and
31293129+ MAY provide further context or explanation as to the reason for
31303130+ the status. A value of "Deprecated" indicates that the derived
31313131+ component name is no longer recommended for use.
31323132+31333133+ Target:
31343134+ The valid message targets for the derived parameter. MUST be one
31353135+ of the values "Request", "Response", or "Request, Response". The
31363136+ semantics of these entries are defined in Section 2.2.
31373137+31383138+ Reference:
31393139+ Reference to the document or documents that specify the derived
31403140+ component, preferably including a URI that can be used to retrieve
31413141+ a copy of the document(s). An indication of the relevant sections
31423142+ may also be included but is not required.
31433143+31443144+6.4.2. Initial Contents
31453145+31463146+ The table below contains the initial contents of the "HTTP Signature
31473147+ Derived Component Names" registry.
31483148+31493149+ +===================+==============+========+==========+===========+
31503150+ | Name | Description | Status | Target | Reference |
31513151+ +===================+==============+========+==========+===========+
31523152+ | @signature-params | Reserved for | Active | Request, | Section |
31533153+ | | signature | | Response | 2.3 of |
31543154+ | | parameters | | | RFC 9421 |
31553155+ | | line in | | | |
31563156+ | | signature | | | |
31573157+ | | base | | | |
31583158+ +-------------------+--------------+--------+----------+-----------+
31593159+ | @method | The HTTP | Active | Request | Section |
31603160+ | | request | | | 2.2.1 of |
31613161+ | | method | | | RFC 9421 |
31623162+ +-------------------+--------------+--------+----------+-----------+
31633163+ | @authority | The HTTP | Active | Request | Section |
31643164+ | | authority, | | | 2.2.3 of |
31653165+ | | or target | | | RFC 9421 |
31663166+ | | host | | | |
31673167+ +-------------------+--------------+--------+----------+-----------+
31683168+ | @scheme | The URI | Active | Request | Section |
31693169+ | | scheme of | | | 2.2.4 of |
31703170+ | | the request | | | RFC 9421 |
31713171+ | | URI | | | |
31723172+ +-------------------+--------------+--------+----------+-----------+
31733173+ | @target-uri | The full | Active | Request | Section |
31743174+ | | target URI | | | 2.2.2 of |
31753175+ | | of the | | | RFC 9421 |
31763176+ | | request | | | |
31773177+ +-------------------+--------------+--------+----------+-----------+
31783178+ | @request-target | The request | Active | Request | Section |
31793179+ | | target of | | | 2.2.5 of |
31803180+ | | the request | | | RFC 9421 |
31813181+ +-------------------+--------------+--------+----------+-----------+
31823182+ | @path | The full | Active | Request | Section |
31833183+ | | path of the | | | 2.2.6 of |
31843184+ | | request URI | | | RFC 9421 |
31853185+ +-------------------+--------------+--------+----------+-----------+
31863186+ | @query | The full | Active | Request | Section |
31873187+ | | query of the | | | 2.2.7 of |
31883188+ | | request URI | | | RFC 9421 |
31893189+ +-------------------+--------------+--------+----------+-----------+
31903190+ | @query-param | A single | Active | Request | Section |
31913191+ | | named query | | | 2.2.8 of |
31923192+ | | parameter | | | RFC 9421 |
31933193+ +-------------------+--------------+--------+----------+-----------+
31943194+ | @status | The status | Active | Response | Section |
31953195+ | | code of the | | | 2.2.9 of |
31963196+ | | response | | | RFC 9421 |
31973197+ +-------------------+--------------+--------+----------+-----------+
31983198+31993199+ Table 4: Initial Contents of the HTTP Signature Derived
32003200+ Component Names Registry
32013201+32023202+6.5. HTTP Signature Component Parameters Registry
32033203+32043204+ This document defines several kinds of component identifiers, some of
32053205+ which can be parameterized in specific circumstances to provide
32063206+ unique modified behavior. IANA has created and now maintains a new
32073207+ registry titled "HTTP Signature Component Parameters" to record and
32083208+ maintain the set of parameter names, the component identifiers they
32093209+ are associated with, and the modifications these parameters make to
32103210+ the component value. Definitions of parameters MUST define the
32113211+ targets to which they apply (such as specific field types, derived
32123212+ components, or contexts). Initial values for this registry are given
32133213+ in Section 6.5.2. Future assignments and modifications to existing
32143214+ assignments are to be made through the Expert Review registration
32153215+ policy [RFC8126].
32163216+32173217+ The DE is expected to do the following:
32183218+32193219+ * Ensure that the name follows the template presented in
32203220+ Section 6.5.1; this includes ensuring that the length of the name
32213221+ is not excessive while still being unique and recognizable for its
32223222+ defined function.
32233223+32243224+ * Ensure that the definition of the field sufficiently defines any
32253225+ interactions or incompatibilities with other existing parameters
32263226+ known at the time of the registration request.
32273227+32283228+ * Ensure that the component value defined by the component
32293229+ identifier with the parameter applied can be deterministically
32303230+ derived from the target HTTP message in cases where the parameter
32313231+ changes the component value.
32323232+32333233+6.5.1. Registration Template
32343234+32353235+ Name:
32363236+ A name for the parameter. The name MUST be an ASCII string that
32373237+ conforms to the key ABNF rule defined in Section 3.1.2 of
32383238+ [STRUCTURED-FIELDS] and SHOULD NOT exceed 20 characters in length.
32393239+ The name MUST be unique within the context of the registry.
32403240+32413241+ Description:
32423242+ A description of the parameter's function.
32433243+32443244+ Reference:
32453245+ Reference to the document or documents that specify the derived
32463246+ component, preferably including a URI that can be used to retrieve
32473247+ a copy of the document(s). An indication of the relevant sections
32483248+ may also be included but is not required.
32493249+32503250+6.5.2. Initial Contents
32513251+32523252+ The table below contains the initial contents of the "HTTP Signature
32533253+ Component Parameters" registry.
32543254+32553255+ +======+=======================================+===============+
32563256+ | Name | Description | Reference |
32573257+ +======+=======================================+===============+
32583258+ | sf | Strict Structured Field serialization | Section 2.1.1 |
32593259+ | | | of RFC 9421 |
32603260+ +------+---------------------------------------+---------------+
32613261+ | key | Single key value of Dictionary | Section 2.1.2 |
32623262+ | | Structured Fields | of RFC 9421 |
32633263+ +------+---------------------------------------+---------------+
32643264+ | bs | Byte Sequence wrapping indicator | Section 2.1.3 |
32653265+ | | | of RFC 9421 |
32663266+ +------+---------------------------------------+---------------+
32673267+ | tr | Trailer | Section 2.1.4 |
32683268+ | | | of RFC 9421 |
32693269+ +------+---------------------------------------+---------------+
32703270+ | req | Related request indicator | Section 2.4 |
32713271+ | | | of RFC 9421 |
32723272+ +------+---------------------------------------+---------------+
32733273+ | name | Single named query parameter | Section 2.2.8 |
32743274+ | | | of RFC 9421 |
32753275+ +------+---------------------------------------+---------------+
32763276+32773277+ Table 5: Initial Contents of the HTTP Signature Component
32783278+ Parameters Registry
32793279+32803280+7. Security Considerations
32813281+32823282+ In order for an HTTP message to be considered _covered_ by a
32833283+ signature, all of the following conditions have to be true:
32843284+32853285+ * A signature is expected or allowed on the message by the verifier.
32863286+32873287+ * The signature exists on the message.
32883288+32893289+ * The signature is verified against the identified key material and
32903290+ algorithm.
32913291+32923292+ * The key material and algorithm are appropriate for the context of
32933293+ the message.
32943294+32953295+ * The signature is within expected time boundaries.
32963296+32973297+ * The signature covers the expected content, including any critical
32983298+ components.
32993299+33003300+ * The list of covered components is applicable to the context of the
33013301+ message.
33023302+33033303+ In addition to the application requirement definitions listed in
33043304+ Section 1.4, the following security considerations provide discussion
33053305+ and context regarding the requirements of creating and verifying
33063306+ signatures on HTTP messages.
33073307+33083308+7.1. General Considerations
33093309+33103310+7.1.1. Skipping Signature Verification
33113311+33123312+ HTTP message signatures only provide security if the signature is
33133313+ verified by the verifier. Since the message to which the signature
33143314+ is attached remains a valid HTTP message without the Signature or
33153315+ Signature-Input fields, it is possible for a verifier to ignore the
33163316+ output of the verification function and still process the message.
33173317+ Common reasons for this could be relaxed requirements in a
33183318+ development environment or a temporary suspension of enforcing
33193319+ verification while debugging an overall system. Such temporary
33203320+ suspensions are difficult to detect under positive-example testing,
33213321+ since a good signature will always trigger a valid response whether
33223322+ or not it has been checked.
33233323+33243324+ To detect this, verifiers should be tested using both valid and
33253325+ invalid signatures, ensuring that an invalid signature fails as
33263326+ expected.
33273327+33283328+7.1.2. Use of TLS
33293329+33303330+ The use of HTTP message signatures does not negate the need for TLS
33313331+ or its equivalent to protect information in transit. Message
33323332+ signatures provide message integrity over the covered message
33333333+ components but do not provide any confidentiality for communication
33343334+ between parties.
33353335+33363336+ TLS provides such confidentiality between the TLS endpoints. As part
33373337+ of this, TLS also protects the signature data itself from being
33383338+ captured by an attacker. This is an important step in preventing
33393339+ signature replay (Section 7.2.2).
33403340+33413341+ When TLS is used, it needs to be deployed according to the
33423342+ recommendations provided in [BCP195].
33433343+33443344+7.2. Message Processing and Selection
33453345+33463346+7.2.1. Insufficient Coverage
33473347+33483348+ Any portions of the message not covered by the signature are
33493349+ susceptible to modification by an attacker without affecting the
33503350+ signature. An attacker can take advantage of this by introducing or
33513351+ modifying a header field or other message component that will change
33523352+ the processing of the message but will not be covered by the
33533353+ signature. Such an altered message would still pass signature
33543354+ verification, but when the verifier processes the message as a whole,
33553355+ the unsigned content injected by the attacker would subvert the trust
33563356+ conveyed by the valid signature and change the outcome of processing
33573357+ the message.
33583358+33593359+ To combat this, an application of this specification should require
33603360+ as much of the message as possible to be signed, within the limits of
33613361+ the application and deployment. The verifier should only trust
33623362+ message components that have been signed. Verifiers could also strip
33633363+ out any sensitive unsigned portions of the message before processing
33643364+ of the message continues.
33653365+33663366+7.2.2. Signature Replay
33673367+33683368+ Since HTTP message signatures allow sub-portions of the HTTP message
33693369+ to be signed, it is possible for two different HTTP messages to
33703370+ validate against the same signature. The most extreme form of this
33713371+ would be a signature over no message components. If such a signature
33723372+ were intercepted, it could be replayed at will by an attacker,
33733373+ attached to any HTTP message. Even with sufficient component
33743374+ coverage, a given signature could be applied to two similar HTTP
33753375+ messages, allowing a message to be replayed by an attacker with the
33763376+ signature intact.
33773377+33783378+ To counteract these kinds of attacks, it's first important for the
33793379+ signer to cover sufficient portions of the message to differentiate
33803380+ it from other messages. In addition, the signature can use the nonce
33813381+ signature parameter to provide a per-message unique value to allow
33823382+ the verifier to detect replay of the signature itself if a nonce
33833383+ value is repeated. Furthermore, the signer can provide a timestamp
33843384+ for when the signature was created and a time at which the signer
33853385+ considers the signature to be expired, limiting the utility of a
33863386+ captured signature value.
33873387+33883388+ If a verifier wants to trigger a new signature from a signer, it can
33893389+ send the Accept-Signature header field with a new nonce parameter.
33903390+ An attacker that is simply replaying a signature would not be able to
33913391+ generate a new signature with the chosen nonce value.
33923392+33933393+7.2.3. Choosing Message Components
33943394+33953395+ Applications of HTTP message signatures need to decide which message
33963396+ components will be covered by the signature. Depending on the
33973397+ application, some components could be expected to be changed by
33983398+ intermediaries prior to the signature's verification. If these
33993399+ components are covered, such changes would, by design, break the
34003400+ signature.
34013401+34023402+ However, this document allows for flexibility in determining which
34033403+ components are signed precisely so that a given application can
34043404+ choose the appropriate portions of the message that need to be
34053405+ signed, avoiding problematic components. For example, a web
34063406+ application framework that relies on rewriting query parameters might
34073407+ avoid using the @query derived component in favor of sub-indexing the
34083408+ query value using @query-param derived components instead.
34093409+34103410+ Some components are expected to be changed by intermediaries and
34113411+ ought not to be signed under most circumstances. The Via and
34123412+ Forwarded header fields, for example, are expected to be manipulated
34133413+ by proxies and other middleboxes, including replacing or entirely
34143414+ dropping existing values. These fields should not be covered by the
34153415+ signature, except in very limited and tightly coupled scenarios.
34163416+34173417+ Additional considerations for choosing signature aspects are
34183418+ discussed in Section 1.4.
34193419+34203420+7.2.4. Choosing Signature Parameters and Derived Components over HTTP
34213421+ Fields
34223422+34233423+ Some HTTP fields have values and interpretations that are similar to
34243424+ HTTP signature parameters or derived components. In most cases, it
34253425+ is more desirable to sign the non-field alternative. In particular,
34263426+ the following fields should usually not be included in the signature
34273427+ unless the application specifically requires it:
34283428+34293429+ "date" The Date header field value represents the timestamp of the
34303430+ HTTP message. However, the creation time of the signature itself
34313431+ is encoded in the created signature parameter. These two values
34323432+ can be different, depending on how the signature and the HTTP
34333433+ message are created and serialized. Applications processing
34343434+ signatures for valid time windows should use the created signature
34353435+ parameter for such calculations. An application could also put
34363436+ limits on how much skew there is between the Date field and the
34373437+ created signature parameter, in order to limit the application of
34383438+ a generated signature to different HTTP messages. See also
34393439+ Sections 7.2.2 and 7.2.1.
34403440+34413441+ "host" The Host header field is specific to HTTP/1.1, and its
34423442+ functionality is subsumed by the @authority derived component,
34433443+ defined in Section 2.2.3. In order to preserve the value across
34443444+ different HTTP versions, applications should always use the
34453445+ @authority derived component. See also Section 7.5.4.
34463446+34473447+7.2.5. Signature Labels
34483448+34493449+ HTTP message signature values are identified in the Signature and
34503450+ Signature-Input field values by unique labels. These labels are
34513451+ chosen only when attaching the signature values to the message and
34523452+ are not accounted for during the signing process. An intermediary is
34533453+ allowed to relabel an existing signature when processing the message.
34543454+34553455+ Therefore, applications should not rely on specific labels being
34563456+ present, and applications should not put semantic meaning on the
34573457+ labels themselves. Instead, additional signature parameters can be
34583458+ used to convey whatever additional meaning is required to be attached
34593459+ to, and covered by, the signature. In particular, the tag parameter
34603460+ can be used to define an application-specific value as described in
34613461+ Section 7.2.7.
34623462+34633463+7.2.6. Multiple Signature Confusion
34643464+34653465+ Since multiple signatures can be applied to one message
34663466+ (Section 4.3), it is possible for an attacker to attach their own
34673467+ signature to a captured message without modifying existing
34683468+ signatures. This new signature could be completely valid based on
34693469+ the attacker's key, or it could be an invalid signature for any
34703470+ number of reasons. Each of these situations needs to be accounted
34713471+ for.
34723472+34733473+ A verifier processing a set of valid signatures needs to account for
34743474+ all of the signers, identified by the signing keys. Only signatures
34753475+ from expected signers should be accepted, regardless of the
34763476+ cryptographic validity of the signature itself.
34773477+34783478+ A verifier processing a set of signatures on a message also needs to
34793479+ determine what to do when one or more of the signatures are not
34803480+ valid. If a message is accepted when at least one signature is
34813481+ valid, then a verifier could drop all invalid signatures from the
34823482+ request before processing the message further. Alternatively, if the
34833483+ verifier rejects a message for a single invalid signature, an
34843484+ attacker could use this to deny service to otherwise valid messages
34853485+ by injecting invalid signatures alongside the valid signatures.
34863486+34873487+7.2.7. Collision of Application-Specific Signature Tag
34883488+34893489+ Multiple applications and protocols could apply HTTP signatures on
34903490+ the same message simultaneously. In fact, this is a desired feature
34913491+ in many circumstances; see Section 4.3. A naive verifier could
34923492+ become confused while processing multiple signatures, either
34933493+ accepting or rejecting a message based on an unrelated or irrelevant
34943494+ signature. In order to help an application select which signatures
34953495+ apply to its own processing, the application can declare a specific
34963496+ value for the tag signature parameter as defined in Section 2.3. For
34973497+ example, a signature targeting an application gateway could require
34983498+ tag="app-gateway" as part of the signature parameters for that
34993499+ application.
35003500+35013501+ The use of the tag parameter does not prevent an attacker from also
35023502+ using the same value as a target application, since the parameter's
35033503+ value is public and otherwise unrestricted. As a consequence, a
35043504+ verifier should only use a value of the tag parameter to limit which
35053505+ signatures to check. Each signature still needs to be examined by
35063506+ the verifier to ensure that sufficient coverage is provided, as
35073507+ discussed in Section 7.2.1.
35083508+35093509+7.2.8. Message Content
35103510+35113511+ On its own, this specification does not provide coverage for the
35123512+ content of an HTTP message under the signature, in either a request
35133513+ or a response. However, [DIGEST] defines a set of fields that allow
35143514+ a cryptographic digest of the content to be represented in a field.
35153515+ Once this field is created, it can be included just like any other
35163516+ field as defined in Section 2.1.
35173517+35183518+ For example, in the following response message:
35193519+35203520+ HTTP/1.1 200 OK
35213521+ Content-Type: application/json
35223522+35233523+ {"hello": "world"}
35243524+35253525+ The digest of the content can be added to the Content-Digest field as
35263526+ follows:
35273527+35283528+ NOTE: '\' line wrapping per RFC 8792
35293529+35303530+ HTTP/1.1 200 OK
35313531+ Content-Type: application/json
35323532+ Content-Digest: \
35333533+ sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:
35343534+35353535+ {"hello": "world"}
35363536+35373537+ This field can be included in a signature base just like any other
35383538+ field along with the basic signature parameters:
35393539+35403540+ "@status": 200
35413541+ "content-digest": \
35423542+ sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:
35433543+ "@signature-input": ("@status" "content-digest")
35443544+35453545+ From here, the signing process proceeds as usual.
35463546+35473547+ Upon verification, it is important that the verifier validate not
35483548+ only the signature but also the value of the Content-Digest field
35493549+ itself against the actual received content. Unless the verifier
35503550+ performs this step, it would be possible for an attacker to
35513551+ substitute the message content but leave the Content-Digest field
35523552+ value untouched to pass the signature. Since only the field value is
35533553+ covered by the signature directly, checking only the signature is not
35543554+ sufficient protection against such a substitution attack.
35553555+35563556+ As discussed in [DIGEST], the value of the Content-Digest field is
35573557+ dependent on the content encoding of the message. If an intermediary
35583558+ changes the content encoding, the resulting Content-Digest value
35593559+ would change. This would in turn invalidate the signature. Any
35603560+ intermediary performing such an action would need to apply a new
35613561+ signature with the updated Content-Digest field value, similar to the
35623562+ reverse proxy use case discussed in Section 4.3.
35633563+35643564+ Applications that make use of the req parameter (Section 2.4) also
35653565+ need to be aware of the limitations of this functionality.
35663566+ Specifically, if a client does not include something like a Content-
35673567+ Digest header field in the request, the server is unable to include a
35683568+ signature that covers the request's content.
35693569+35703570+7.3. Cryptographic Considerations
35713571+35723572+7.3.1. Cryptography and Signature Collision
35733573+35743574+ This document does not define any of its own cryptographic primitives
35753575+ and instead relies on other specifications to define such elements.
35763576+ If the signature algorithm or key used to process the signature base
35773577+ is vulnerable to any attacks, the resulting signature will also be
35783578+ susceptible to these same attacks.
35793579+35803580+ A common attack against signature systems is to force a signature
35813581+ collision, where the same signature value successfully verifies
35823582+ against multiple different inputs. Since this specification relies
35833583+ on reconstruction of the signature base from an HTTP message and the
35843584+ list of components signed is fixed in the signature, it is difficult
35853585+ but not impossible for an attacker to effect such a collision. An
35863586+ attacker would need to manipulate the HTTP message and its covered
35873587+ message components in order to make the collision effective.
35883588+35893589+ To counter this, only vetted keys and signature algorithms should be
35903590+ used to sign HTTP messages. The "HTTP Signature Algorithms" registry
35913591+ is one source of trusted signature algorithms for applications to
35923592+ apply to their messages.
35933593+35943594+ While it is possible for an attacker to substitute the signature
35953595+ parameters value or the signature value separately, the signature
35963596+ base generation algorithm (Section 2.5) always covers the signature
35973597+ parameters as the final value in the signature base using a
35983598+ deterministic serialization method. This step strongly binds the
35993599+ signature base with the signature value in a way that makes it much
36003600+ more difficult for an attacker to perform a partial substitution on
36013601+ the signature base.
36023602+36033603+7.3.2. Key Theft
36043604+36053605+ A foundational assumption of signature-based cryptographic systems is
36063606+ that the signing key is not compromised by an attacker. If the keys
36073607+ used to sign the message are exfiltrated or stolen, the attacker will
36083608+ be able to generate their own signatures using those keys. As a
36093609+ consequence, signers have to protect any signing key material from
36103610+ exfiltration, capture, and use by an attacker.
36113611+36123612+ To combat this, signers can rotate keys over time to limit the amount
36133613+ of time that stolen keys are useful. Signers can also use key escrow
36143614+ and storage systems to limit the attack surface against keys.
36153615+ Furthermore, the use of asymmetric signing algorithms exposes key
36163616+ material less than the use of symmetric signing algorithms
36173617+ (Section 7.3.3).
36183618+36193619+7.3.3. Symmetric Cryptography
36203620+36213621+ This document allows both asymmetric and symmetric cryptography to be
36223622+ applied to HTTP messages. By their nature, symmetric cryptographic
36233623+ methods require the same key material to be known by both the signer
36243624+ and verifier. This effectively means that a verifier is capable of
36253625+ generating a valid signature, since they have access to the same key
36263626+ material. An attacker that is able to compromise a verifier would be
36273627+ able to then impersonate a signer.
36283628+36293629+ Where possible, asymmetric methods or secure key agreement mechanisms
36303630+ should be used in order to avoid this type of attack. When symmetric
36313631+ methods are used, distribution of the key material needs to be
36323632+ protected by the overall system. One technique for this is the use
36333633+ of separate cryptographic modules that separate the verification
36343634+ process (and therefore the key material) from other code, minimizing
36353635+ the vulnerable attack surface. Another technique is the use of key
36363636+ derivation functions that allow the signer and verifier to agree on
36373637+ unique keys for each message without having to share the key values
36383638+ directly.
36393639+36403640+ Additionally, if symmetric algorithms are allowed within a system,
36413641+ special care must be taken to avoid key downgrade attacks
36423642+ (Section 7.3.6).
36433643+36443644+7.3.4. Key Specification Mixup
36453645+36463646+ The existence of a valid signature on an HTTP message is not
36473647+ sufficient to prove that the message has been signed by the
36483648+ appropriate party. It is up to the verifier to ensure that a given
36493649+ key and algorithm are appropriate for the message in question. If
36503650+ the verifier does not perform such a step, an attacker could
36513651+ substitute their own signature using their own key on a message and
36523652+ force a verifier to accept and process it. To combat this, the
36533653+ verifier needs to ensure not only that the signature can be validated
36543654+ for a message but that the key and algorithm used are appropriate.
36553655+36563656+7.3.5. Non-deterministic Signature Primitives
36573657+36583658+ Some cryptographic primitives, such as RSA-PSS and ECDSA, have non-
36593659+ deterministic outputs, which include some amount of entropy within
36603660+ the algorithm. For such algorithms, multiple signatures generated in
36613661+ succession will not match. A lazy implementation of a verifier could
36623662+ ignore this distinction and simply check for the same value being
36633663+ created by re-signing the signature base. Such an implementation
36643664+ would work for deterministic algorithms such as HMAC and EdDSA but
36653665+ fail to verify valid signatures made using non-deterministic
36663666+ algorithms. It is therefore important that a verifier always use the
36673667+ correctly defined verification function for the algorithm in question
36683668+ and not do a simple comparison.
36693669+36703670+7.3.6. Key and Algorithm Specification Downgrades
36713671+36723672+ Applications of this specification need to protect against key
36733673+ specification downgrade attacks. For example, the same RSA key can
36743674+ be used for both RSA-PSS and RSA v1.5 signatures. If an application
36753675+ expects a key to only be used with RSA-PSS, it needs to reject
36763676+ signatures for any key that uses the weaker RSA 1.5 specification.
36773677+36783678+ Another example of a downgrade attack would be when an asymmetric
36793679+ algorithm is expected, such as RSA-PSS, but an attacker substitutes a
36803680+ signature using a symmetric algorithm, such as HMAC. A naive
36813681+ verifier implementation could use the value of the public RSA key as
36823682+ the input to the HMAC verification function. Since the public key is
36833683+ known to the attacker, this would allow the attacker to create a
36843684+ valid HMAC signature against this known key. To prevent this, the
36853685+ verifier needs to ensure that both the key material and the algorithm
36863686+ are appropriate for the usage in question. Additionally, while this
36873687+ specification does allow runtime specification of the algorithm using
36883688+ the alg signature parameter, applications are encouraged to use other
36893689+ mechanisms such as static configuration or a higher-protocol-level
36903690+ algorithm specification instead, preventing an attacker from
36913691+ substituting the algorithm specified.
36923692+36933693+7.3.7. Signing Signature Values
36943694+36953695+ When applying the req parameter (Section 2.4) or multiple signatures
36963696+ (Section 4.3) to a message, it is possible to sign the value of an
36973697+ existing Signature field, thereby covering the bytes of the existing
36983698+ signature output in the new signature's value. While it would seem
36993699+ that this practice would transitively cover the components under the
37003700+ original signature in a verifiable fashion, the attacks described in
37013701+ [JACKSON2019] can be used to impersonate a signature output value on
37023702+ an unrelated message.
37033703+37043704+ In this example, Alice intends to send a signed request to Bob, and
37053705+ Bob wants to provide a signed response to Alice that includes a
37063706+ cryptographic proof that Bob is responding to Alice's incoming
37073707+ message. Mallory wants to intercept this traffic and replace Alice's
37083708+ message with her own, without Alice being aware that the interception
37093709+ has taken place.
37103710+37113711+ 1. Alice creates a message Req_A and applies a signature Sig_A
37123712+ using her private key Key_A_Sign.
37133713+37143714+ 2. Alice believes she is sending Req_A to Bob.
37153715+37163716+ 3. Mallory intercepts Req_A and reads the value Sig_A from this
37173717+ message.
37183718+37193719+ 4. Mallory generates a different message Req_M to send to Bob
37203720+ instead.
37213721+37223722+ 5. Mallory crafts a signing key Key_M_Sign such that she can create
37233723+ a valid signature Sig_M over her request Req_M using this key,
37243724+ but the byte value of Sig_M exactly equals that of Sig_A.
37253725+37263726+ 6. Mallory sends Req_M with Sig_M to Bob.
37273727+37283728+ 7. Bob validates Sig_M against Mallory's verification key
37293729+ Key_M_Verify. At no time does Bob think that he's responding to
37303730+ Alice.
37313731+37323732+ 8. Bob responds with response message Res_B to Req_M and creates
37333733+ signature Sig_B over this message using his key Key_B_Sign. Bob
37343734+ includes the value of Sig_M under Sig_B's covered components but
37353735+ does not include anything else from the request message.
37363736+37373737+ 9. Mallory receives the response Res_B from Bob, including the
37383738+ signature Sig_B value. Mallory replays this response to Alice.
37393739+37403740+ 10. Alice reads Res_B from Mallory and verifies Sig_B using Bob's
37413741+ verification key Key_B_Verify. Alice includes the bytes of her
37423742+ original signature Sig_A in the signature base, and the
37433743+ signature verifies.
37443744+37453745+ 11. Alice is led to believe that Bob has responded to her message
37463746+ and believes she has cryptographic proof of this happening, but
37473747+ in fact Bob responded to Mallory's malicious request and Alice
37483748+ is none the wiser.
37493749+37503750+ To mitigate this, Bob can sign more portions of the request message
37513751+ than just the Signature field, in order to more fully differentiate
37523752+ Alice's message from Mallory's. Applications using this feature,
37533753+ particularly for non-repudiation purposes, can stipulate that any
37543754+ components required in the original signature also be covered
37553755+ separately in the second signature. For signed messages, requiring
37563756+ coverage of the corresponding Signature-Input field of the first
37573757+ signature ensures that unique items such as nonces and timestamps are
37583758+ also covered sufficiently by the second signature.
37593759+37603760+7.4. Matching Signature Parameters to the Target Message
37613761+37623762+7.4.1. Modification of Required Message Parameters
37633763+37643764+ An attacker could effectively deny a service by modifying an
37653765+ otherwise benign signature parameter or signed message component.
37663766+ While rejecting a modified message is the desired behavior,
37673767+ consistently failing signatures could lead to (1) the verifier
37683768+ turning off signature checking in order to make systems work again
37693769+ (see Section 7.1.1) or (2) the application minimizing the
37703770+ requirements related to the signed component.
37713771+37723772+ If such failures are common within an application, the signer and
37733773+ verifier should compare their generated signature bases with each
37743774+ other to determine which part of the message is being modified. If
37753775+ an expected modification is found, the signer and verifier can agree
37763776+ on an alternative set of requirements that will pass. However, the
37773777+ signer and verifier should not remove the requirement to sign the
37783778+ modified component when it is suspected that an attacker is modifying
37793779+ the component.
37803780+37813781+7.4.2. Matching Values of Covered Components to Values in the Target
37823782+ Message
37833783+37843784+ The verifier needs to make sure that the signed message components
37853785+ match those in the message itself. For example, the @method derived
37863786+ component requires that the value within the signature base be the
37873787+ same as the HTTP method used when presenting this message. This
37883788+ specification encourages this by requiring the verifier to derive the
37893789+ signature base from the message, but lazy caching or conveyance of a
37903790+ raw signature base to a processing subsystem could lead to downstream
37913791+ verifiers accepting a message that does not match the presented
37923792+ signature.
37933793+37943794+ To counter this, the component that generates the signature base
37953795+ needs to be trusted by both the signer and verifier within a system.
37963796+37973797+7.4.3. Message Component Source and Context
37983798+37993799+ The signature context for deriving message component values includes
38003800+ the target HTTP message itself, any associated messages (such as the
38013801+ request that triggered a response), and additional information that
38023802+ the signer or verifier has access to. Both signers and verifiers
38033803+ need to carefully consider the source of all information when
38043804+ creating component values for the signature base and take care not to
38053805+ take information from untrusted sources. Otherwise, an attacker
38063806+ could leverage such a loosely defined message context to inject their
38073807+ own values into the signature base string, overriding or corrupting
38083808+ the intended values.
38093809+38103810+ For example, in most situations, the target URI of the message is as
38113811+ defined in [HTTP], Section 7.1. However, let's say that there is an
38123812+ application that requires signing of the @authority of the incoming
38133813+ request, but the application doing the processing is behind a reverse
38143814+ proxy. Such an application would expect a change in the @authority
38153815+ value, and it could be configured to know the external target URI as
38163816+ seen by the client on the other side of the proxy. This application
38173817+ would use this configured value as its target URI for the purposes of
38183818+ deriving message component values such as @authority instead of using
38193819+ the target URI of the incoming message.
38203820+38213821+ This approach is not without problems, as a misconfigured system
38223822+ could accept signed requests intended for different components in the
38233823+ system. For this scenario, an intermediary could instead add its own
38243824+ signature to be verified by the application directly, as demonstrated
38253825+ in Section 4.3. This alternative approach requires a more active
38263826+ intermediary but relies less on the target application knowing
38273827+ external configuration values.
38283828+38293829+ As another example, Section 2.4 defines a method for signing response
38303830+ messages and also including portions of the request message that
38313831+ triggered the response. In this case, the context for component
38323832+ value calculation is the combination of the response and request
38333833+ messages, not just the single message to which the signature is
38343834+ applied. For this feature, the req flag allows both signers to
38353835+ explicitly signal which part of the context is being sourced for a
38363836+ component identifier's value. Implementations need to ensure that
38373837+ only the intended message is being referred to for each component;
38383838+ otherwise, an attacker could attempt to subvert a signature by
38393839+ manipulating one side or the other.
38403840+38413841+7.4.4. Multiple Message Component Contexts
38423842+38433843+ It is possible that the context for deriving message component values
38443844+ could be distinct for each signature present within a single message.
38453845+ This is particularly the case when proxies mutate messages and
38463846+ include signatures over the mutated values, in addition to any
38473847+ existing signatures. For example, a reverse proxy can replace a
38483848+ public hostname in a request to a service with the hostname for the
38493849+ individual service host to which it is forwarding the request. If
38503850+ both the client and the reverse proxy add signatures covering
38513851+ @authority, the service host will see two signatures on the request,
38523852+ each signing different values for the @authority message component,
38533853+ reflecting the change to that component as the message made its way
38543854+ from the client to the service host.
38553855+38563856+ In such a case, it's common for the internal service to verify only
38573857+ one of the signatures or to use externally configured information, as
38583858+ discussed in Section 7.4.3. However, a verifier processing both
38593859+ signatures has to use a different message component context for each
38603860+ signature, since the component value for the @authority component
38613861+ will be different for each signature. Verifiers like this need to be
38623862+ aware of both the reverse proxy's context for incoming messages and
38633863+ the target service's context for the message coming from the reverse
38643864+ proxy. The verifier needs to take particular care to apply the
38653865+ correct context to the correct signature; otherwise, an attacker
38663866+ could use knowledge of this complex setup to confuse the inputs to
38673867+ the verifier.
38683868+38693869+ Such verifiers also need to ensure that any differences in message
38703870+ component contexts between signatures are expected and permitted.
38713871+ For example, in the above scenario, the reverse proxy could include
38723872+ the original hostname in a Forwarded header field and could sign
38733873+ @authority, forwarded, and the client's entry in the Signature field.
38743874+ The verifier can use the hostname from the Forwarded header field to
38753875+ confirm that the hostname was transformed as expected.
38763876+38773877+7.5. HTTP Processing
38783878+38793879+7.5.1. Processing Invalid HTTP Field Names as Derived Component Names
38803880+38813881+ The definition of HTTP field names does not allow for the use of the
38823882+ @ character anywhere in the name. As such, since all derived
38833883+ component names start with the @ character, these namespaces should
38843884+ be completely separate. However, some HTTP implementations are not
38853885+ sufficiently strict about the characters accepted in HTTP field
38863886+ names. In such implementations, a sender (or attacker) could inject
38873887+ a header field starting with an @ character and have it passed
38883888+ through to the application code. These invalid header fields could
38893889+ be used to override a portion of the derived message content and
38903890+ substitute an arbitrary value, providing a potential place for an
38913891+ attacker to mount a signature collision (Section 7.3.1) attack or
38923892+ other functional substitution attack (such as using the signature
38933893+ from a GET request on a crafted POST request).
38943894+38953895+ To combat this, when selecting values for a message component, if the
38963896+ component name starts with the @ character, it needs to be processed
38973897+ as a derived component and never processed as an HTTP field. Only if
38983898+ the component name does not start with the @ character can it be
38993899+ taken from the fields of the message. The algorithm discussed in
39003900+ Section 2.5 provides a safe order of operations.
39013901+39023902+7.5.2. Semantically Equivalent Field Values
39033903+39043904+ The signature base generation algorithm (Section 2.5) uses the value
39053905+ of an HTTP field as its component value. In the common case, this
39063906+ amounts to taking the actual bytes of the field value as the
39073907+ component value for both the signer and verifier. However, some
39083908+ field values allow for transformation of the values in semantically
39093909+ equivalent ways that alter the bytes used in the value itself. For
39103910+ example, a field definition can declare some or all of its values to
39113911+ be case insensitive or to have special handling of internal
39123912+ whitespace characters. Other fields have expected transformations
39133913+ from intermediaries, such as the removal of comments in the Via
39143914+ header field. In such cases, a verifier could be tripped up by using
39153915+ the equivalent transformed field value, which would differ from the
39163916+ byte value used by the signer. The verifier would have a difficult
39173917+ time finding this class of errors, since the value of the field is
39183918+ still acceptable for the application but the actual bytes required by
39193919+ the signature base would not match.
39203920+39213921+ When processing such fields, the signer and verifier have to agree on
39223922+ how to handle such transformations, if at all. One option is to not
39233923+ sign problematic fields, but care must be taken to ensure that there
39243924+ is still sufficient signature coverage (Section 7.2.1) for the
39253925+ application. Another option is to define an application-specific
39263926+ canonicalization value for the field before it is added to the HTTP
39273927+ message, such as to always remove internal comments before signing or
39283928+ to always transform values to lowercase. Since these transformations
39293929+ are applied prior to the field being used as input to the signature
39303930+ base generation algorithm, the signature base will still simply
39313931+ contain the byte value of the field as it appears within the message.
39323932+ If the transformations were to be applied after the value is
39333933+ extracted from the message but before it is added to the signature
39343934+ base, different attack surfaces such as value substitution attacks
39353935+ could be launched against the application. All application-specific
39363936+ additional rules are outside the scope of this specification, and by
39373937+ their very nature these transformations would harm interoperability
39383938+ of the implementation outside of this specific application. It is
39393939+ recommended that applications avoid the use of such additional rules
39403940+ wherever possible.
39413941+39423942+7.5.3. Parsing Structured Field Values
39433943+39443944+ Several parts of this specification rely on the parsing of Structured
39453945+ Field values [STRUCTURED-FIELDS] -- in particular, strict
39463946+ serialization of HTTP Structured Field values (Section 2.1.1),
39473947+ referencing members of a Dictionary Structured Field (Section 2.1.2),
39483948+ and processing the @signature-input value when verifying a signature
39493949+ (Section 3.2). While Structured Field values are designed to be
39503950+ relatively simple to parse, a naive or broken implementation of such
39513951+ a parser could lead to subtle attack surfaces being exposed in the
39523952+ implementation.
39533953+39543954+ For example, if a buggy parser of the @signature-input value does not
39553955+ enforce proper closing of quotes around string values within the list
39563956+ of component identifiers, an attacker could take advantage of this
39573957+ and inject additional content into the signature base through
39583958+ manipulating the Signature-Input field value on a message.
39593959+39603960+ To counteract this, implementations should use fully compliant and
39613961+ trusted parsers for all Structured Field processing, on both the
39623962+ signer side and the verifier side.
39633963+39643964+7.5.4. HTTP Versions and Component Ambiguity
39653965+39663966+ Some message components are expressed in different ways across HTTP
39673967+ versions. For example, the authority of the request target is sent
39683968+ using the Host header field in HTTP/1.1 but with the :authority
39693969+ pseudo-header in HTTP/2. If a signer sends an HTTP/1.1 message and
39703970+ signs the Host header field but the message is translated to HTTP/2
39713971+ before it reaches the verifier, the signature will not validate, as
39723972+ the Host header field could be dropped.
39733973+39743974+ It is for this reason that HTTP message signatures define a set of
39753975+ derived components that define a single way to get the value in
39763976+ question, such as the @authority derived component (Section 2.2.3) in
39773977+ lieu of the Host header field. Applications should therefore prefer
39783978+ derived components for such options where possible.
39793979+39803980+7.5.5. Canonicalization Attacks
39813981+39823982+ Any ambiguity in the generation of the signature base could provide
39833983+ an attacker with leverage to substitute or break a signature on a
39843984+ message. Some message component values, particularly HTTP field
39853985+ values, are potentially susceptible to broken implementations that
39863986+ could lead to unexpected and insecure behavior. Naive
39873987+ implementations of this specification might implement HTTP field
39883988+ processing by taking the single value of a field and using it as the
39893989+ direct component value without processing it appropriately.
39903990+39913991+ For example, if the handling of obs-fold field values does not remove
39923992+ the internal line folding and whitespace, additional newlines could
39933993+ be introduced into the signature base by the signer, providing a
39943994+ potential place for an attacker to mount a signature collision
39953995+ (Section 7.3.1) attack. Alternatively, if header fields that appear
39963996+ multiple times are not joined into a single string value, as required
39973997+ by this specification, similar attacks can be mounted, as a signed
39983998+ component value would show up in the signature base more than once
39993999+ and could be substituted or otherwise attacked in this way.
40004000+40014001+ To counter this, the entire field value processing algorithm needs to
40024002+ be implemented by all implementations of signers and verifiers.
40034003+40044004+7.5.6. Non-List Field Values
40054005+40064006+ When an HTTP field occurs multiple times in a single message, these
40074007+ values need to be combined into a single one-line string value to be
40084008+ included in the HTTP signature base, as described in Section 2.5.
40094009+ Not all HTTP fields can be combined into a single value in this way
40104010+ and still be a valid value for the field. For the purposes of
40114011+ generating the signature base, the message component value is never
40124012+ meant to be read back out of the signature base string or used in the
40134013+ application. Therefore, it is considered best practice to treat the
40144014+ signature base generation algorithm separately from processing the
40154015+ field values by the application, particularly for fields that are
40164016+ known to have this property. If the field values that are being
40174017+ signed do not validate, the signed message should also be rejected.
40184018+40194019+ If an HTTP field allows for unquoted commas within its values,
40204020+ combining multiple field values can lead to a situation where two
40214021+ semantically different messages produce the same line in a signature
40224022+ base. For example, take the following hypothetical header field with
40234023+ an internal comma in its syntax, here used to define two separate
40244024+ lists of values:
40254025+40264026+ Example-Header: value, with, lots
40274027+ Example-Header: of, commas
40284028+40294029+ For this header field, sending all of these values as a single field
40304030+ value results in a single list of values:
40314031+40324032+ Example-Header: value, with, lots, of, commas
40334033+40344034+ Both of these messages would create the following line in the
40354035+ signature base:
40364036+40374037+ "example-header": value, with, lots, of, commas
40384038+40394039+ Since two semantically distinct inputs can create the same output in
40404040+ the signature base, special care has to be taken when handling such
40414041+ values.
40424042+40434043+ Specifically, the Set-Cookie field [COOKIE] defines an internal
40444044+ syntax that does not conform to the List syntax provided in
40454045+ [STRUCTURED-FIELDS]. In particular, some portions allow unquoted
40464046+ commas, and the field is typically sent as multiple separate field
40474047+ lines with distinct values when sending multiple cookies. When
40484048+ multiple Set-Cookie fields are sent in the same message, it is not
40494049+ generally possible to combine these into a single line and be able to
40504050+ parse and use the results, as discussed in [HTTP], Section 5.3.
40514051+ Therefore, all the cookies need to be processed from their separate
40524052+ field values, without being combined, while the signature base needs
40534053+ to be processed from the special combined value generated solely for
40544054+ this purpose. If the cookie value is invalid, the signed message
40554055+ ought to be rejected, as this is a possible padding attack as
40564056+ described in Section 7.5.7.
40574057+40584058+ To deal with this, an application can choose to limit signing of
40594059+ problematic fields like Set-Cookie, such as including the field in a
40604060+ signature only when a single field value is present and the results
40614061+ would be unambiguous. Similar caution needs to be taken with all
40624062+ fields that could have non-deterministic mappings into the signature
40634063+ base. Signers can also make use of the bs parameter to armor such
40644064+ fields, as described in Section 2.1.3.
40654065+40664066+7.5.7. Padding Attacks with Multiple Field Values
40674067+40684068+ Since HTTP field values need to be combined into a single string
40694069+ value to be included in the HTTP signature base (see Section 2.5), it
40704070+ is possible for an attacker to inject an additional value for a given
40714071+ field and add this to the signature base of the verifier.
40724072+40734073+ In most circumstances, this causes the signature validation to fail
40744074+ as expected, since the new signature base value will not match the
40754075+ one used by the signer to create the signature. However, it is
40764076+ theoretically possible for the attacker to inject both a garbage
40774077+ value into a field and a desired value into another field in order to
40784078+ force a particular input. This is a variation of the collision
40794079+ attack described in Section 7.3.1, where the attacker accomplishes
40804080+ their change in the message by adding to existing field values.
40814081+40824082+ To counter this, an application needs to validate the content of the
40834083+ fields covered in the signature in addition to ensuring that the
40844084+ signature itself validates. With such protections, the attacker's
40854085+ padding attack would be rejected by the field value processor, even
40864086+ in the case where the attacker could force a signature collision.
40874087+40884088+7.5.8. Ambiguous Handling of Query Elements
40894089+40904090+ The HTML form parameters format defined in Section 5 ("application/
40914091+ x-www-form-urlencoded") of [HTMLURL] is widely deployed and supported
40924092+ by many application frameworks. For convenience, some of these
40934093+ frameworks in particular combine query parameters that are found in
40944094+ the HTTP query and those found in the message content, particularly
40954095+ for POST messages with a Content-Type value of "application/x-www-
40964096+ form-urlencoded". The @query-param derived component identifier
40974097+ defined in Section 2.2.8 draws its values only from the query section
40984098+ of the target URI of the request. As such, it would be possible for
40994099+ an attacker to shadow or replace query parameters in a request by
41004100+ overriding a signed query parameter with an unsigned form parameter,
41014101+ or vice versa.
41024102+41034103+ To counter this, an application needs to make sure that values used
41044104+ for the signature base and the application are drawn from a
41054105+ consistent context, in this case the query component of the target
41064106+ URI. Additionally, when the HTTP request has content, an application
41074107+ should sign the message content as well, as discussed in
41084108+ Section 7.2.8.
41094109+41104110+8. Privacy Considerations
41114111+41124112+8.1. Identification through Keys
41134113+41144114+ If a signer uses the same key with multiple verifiers or uses the
41154115+ same key over time with a single verifier, the ongoing use of that
41164116+ key can be used to track the signer throughout the set of verifiers
41174117+ that messages are sent to. Since cryptographic keys are meant to be
41184118+ functionally unique, the use of the same key over time is a strong
41194119+ indicator that it is the same party signing multiple messages.
41204120+41214121+ In many applications, this is a desirable trait, and it allows HTTP
41224122+ message signatures to be used as part of authenticating the signer to
41234123+ the verifier. However, it could also result in unintentional
41244124+ tracking that a signer might not be aware of. To counter this kind
41254125+ of tracking, a signer can use a different key for each verifier that
41264126+ it is in communication with. Sometimes, a signer could also rotate
41274127+ their key when sending messages to a given verifier. These
41284128+ approaches do not negate the need for other anti-tracking techniques
41294129+ to be applied as necessary.
41304130+41314131+8.2. Signatures do not provide confidentiality
41324132+41334133+ HTTP message signatures do not provide confidentiality for any of the
41344134+ information protected by the signature. The content of the HTTP
41354135+ message, including the value of all fields and the value of the
41364136+ signature itself, is presented in plaintext to any party with access
41374137+ to the message.
41384138+41394139+ To provide confidentiality at the transport level, TLS or its
41404140+ equivalent can be used, as discussed in Section 7.1.2.
41414141+41424142+8.3. Oracles
41434143+41444144+ It is important to balance the need for providing useful feedback to
41454145+ developers regarding error conditions without providing additional
41464146+ information to an attacker. For example, a naive but helpful server
41474147+ implementation might try to indicate the required key identifier
41484148+ needed for requesting a resource. If someone knows who controls that
41494149+ key, a correlation can be made between the resource's existence and
41504150+ the party identified by the key. Access to such information could be
41514151+ used by an attacker as a means to target the legitimate owner of the
41524152+ resource for further attacks.
41534153+41544154+8.4. Required Content
41554155+41564156+ A core design tenet of this specification is that all message
41574157+ components covered by the signature need to be available to the
41584158+ verifier in order to recreate the signature base and verify the
41594159+ signature. As a consequence, if an application of this specification
41604160+ requires that a particular field be signed, the verifier will need
41614161+ access to the value of that field.
41624162+41634163+ For example, in some complex systems with intermediary processors,
41644164+ this could cause surprising behavior where, for fear of breaking the
41654165+ signature, an intermediary cannot remove privacy-sensitive
41664166+ information from a message before forwarding it on for processing.
41674167+ One way to mitigate this specific situation would be for the
41684168+ intermediary to verify the signature itself and then modify the
41694169+ message to remove the privacy-sensitive information. The
41704170+ intermediary can add its own signature at this point to signal to the
41714171+ next destination that the incoming signature was validated, as shown
41724172+ in the example in Section 4.3.
41734173+41744174+9. References
41754175+41764176+9.1. Normative References
41774177+41784178+ [ABNF] Crocker, D., Ed. and P. Overell, "Augmented BNF for Syntax
41794179+ Specifications: ABNF", STD 68, RFC 5234,
41804180+ DOI 10.17487/RFC5234, January 2008,
41814181+ <https://www.rfc-editor.org/info/rfc5234>.
41824182+41834183+ [ASCII] Cerf, V., "ASCII format for network interchange", STD 80,
41844184+ RFC 20, DOI 10.17487/RFC0020, October 1969,
41854185+ <https://www.rfc-editor.org/info/rfc20>.
41864186+41874187+ [FIPS186-5]
41884188+ NIST, "Digital Signature Standard (DSS)",
41894189+ DOI 10.6028/NIST.FIPS.186-5, February 2023,
41904190+ <https://doi.org/10.6028/NIST.FIPS.186-5>.
41914191+41924192+ [HTMLURL] WHATWG, "URL (Living Standard)", January 2024,
41934193+ <https://url.spec.whatwg.org/>.
41944194+41954195+ [HTTP] Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke,
41964196+ Ed., "HTTP Semantics", STD 97, RFC 9110,
41974197+ DOI 10.17487/RFC9110, June 2022,
41984198+ <https://www.rfc-editor.org/info/rfc9110>.
41994199+42004200+ [HTTP/1.1] Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke,
42014201+ Ed., "HTTP/1.1", STD 99, RFC 9112, DOI 10.17487/RFC9112,
42024202+ June 2022, <https://www.rfc-editor.org/info/rfc9112>.
42034203+42044204+ [POSIX.1] IEEE, "The Open Group Base Specifications Issue 7, 2018
42054205+ edition", 2018,
42064206+ <https://pubs.opengroup.org/onlinepubs/9699919799/>.
42074207+42084208+ [RFC2104] Krawczyk, H., Bellare, M., and R. Canetti, "HMAC: Keyed-
42094209+ Hashing for Message Authentication", RFC 2104,
42104210+ DOI 10.17487/RFC2104, February 1997,
42114211+ <https://www.rfc-editor.org/info/rfc2104>.
42124212+42134213+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
42144214+ Requirement Levels", BCP 14, RFC 2119,
42154215+ DOI 10.17487/RFC2119, March 1997,
42164216+ <https://www.rfc-editor.org/info/rfc2119>.
42174217+42184218+ [RFC6234] Eastlake 3rd, D. and T. Hansen, "US Secure Hash Algorithms
42194219+ (SHA and SHA-based HMAC and HKDF)", RFC 6234,
42204220+ DOI 10.17487/RFC6234, May 2011,
42214221+ <https://www.rfc-editor.org/info/rfc6234>.
42224222+42234223+ [RFC7517] Jones, M., "JSON Web Key (JWK)", RFC 7517,
42244224+ DOI 10.17487/RFC7517, May 2015,
42254225+ <https://www.rfc-editor.org/info/rfc7517>.
42264226+42274227+ [RFC7518] Jones, M., "JSON Web Algorithms (JWA)", RFC 7518,
42284228+ DOI 10.17487/RFC7518, May 2015,
42294229+ <https://www.rfc-editor.org/info/rfc7518>.
42304230+42314231+ [RFC8017] Moriarty, K., Ed., Kaliski, B., Jonsson, J., and A. Rusch,
42324232+ "PKCS #1: RSA Cryptography Specifications Version 2.2",
42334233+ RFC 8017, DOI 10.17487/RFC8017, November 2016,
42344234+ <https://www.rfc-editor.org/info/rfc8017>.
42354235+42364236+ [RFC8032] Josefsson, S. and I. Liusvaara, "Edwards-Curve Digital
42374237+ Signature Algorithm (EdDSA)", RFC 8032,
42384238+ DOI 10.17487/RFC8032, January 2017,
42394239+ <https://www.rfc-editor.org/info/rfc8032>.
42404240+42414241+ [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
42424242+ 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
42434243+ May 2017, <https://www.rfc-editor.org/info/rfc8174>.
42444244+42454245+ [STRUCTURED-FIELDS]
42464246+ Nottingham, M. and P. Kamp, "Structured Field Values for
42474247+ HTTP", RFC 8941, DOI 10.17487/RFC8941, February 2021,
42484248+ <https://www.rfc-editor.org/info/rfc8941>.
42494249+42504250+ [URI] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform
42514251+ Resource Identifier (URI): Generic Syntax", STD 66,
42524252+ RFC 3986, DOI 10.17487/RFC3986, January 2005,
42534253+ <https://www.rfc-editor.org/info/rfc3986>.
42544254+42554255+9.2. Informative References
42564256+42574257+ [AWS-SIGv4]
42584258+ Amazon Simple Storage Service, "Authenticating Requests
42594259+ (AWS Signature Version 4)", March 2006,
42604260+ <https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-
42614261+ authenticating-requests.html>.
42624262+42634263+ [BCP195] Moriarty, K. and S. Farrell, "Deprecating TLS 1.0 and TLS
42644264+ 1.1", BCP 195, RFC 8996, March 2021.
42654265+42664266+ Sheffer, Y., Saint-Andre, P., and T. Fossati,
42674267+ "Recommendations for Secure Use of Transport Layer
42684268+ Security (TLS) and Datagram Transport Layer Security
42694269+ (DTLS)", BCP 195, RFC 9325, November 2022.
42704270+42714271+ <https://www.rfc-editor.org/info/bcp195>
42724272+42734273+ [CLIENT-CERT]
42744274+ Campbell, B. and M. Bishop, "Client-Cert HTTP Header
42754275+ Field", RFC 9440, DOI 10.17487/RFC9440, July 2023,
42764276+ <https://www.rfc-editor.org/info/rfc9440>.
42774277+42784278+ [COOKIE] Barth, A., "HTTP State Management Mechanism", RFC 6265,
42794279+ DOI 10.17487/RFC6265, April 2011,
42804280+ <https://www.rfc-editor.org/info/rfc6265>.
42814281+42824282+ [DIGEST] Polli, R. and L. Pardue, "Digest Fields", RFC 9530,
42834283+ DOI 10.17487/RFC9530, February 2024,
42844284+ <https://www.rfc-editor.org/info/rfc9530>.
42854285+42864286+ [JACKSON2019]
42874287+ Jackson, D., Cremers, C., Cohn-Gordon, K., and R. Sasse,
42884288+ "Seems Legit: Automated Analysis of Subtle Attacks on
42894289+ Protocols that Use Signatures", CCS '19: Proceedings of
42904290+ the 2019 ACM SIGSAC Conference on Computer and
42914291+ Communications Security, pp. 2165-2180,
42924292+ DOI 10.1145/3319535.3339813, November 2019,
42934293+ <https://dl.acm.org/doi/10.1145/3319535.3339813>.
42944294+42954295+ [JWS] Jones, M., Bradley, J., and N. Sakimura, "JSON Web
42964296+ Signature (JWS)", RFC 7515, DOI 10.17487/RFC7515, May
42974297+ 2015, <https://www.rfc-editor.org/info/rfc7515>.
42984298+42994299+ [RFC7239] Petersson, A. and M. Nilsson, "Forwarded HTTP Extension",
43004300+ RFC 7239, DOI 10.17487/RFC7239, June 2014,
43014301+ <https://www.rfc-editor.org/info/rfc7239>.
43024302+43034303+ [RFC7468] Josefsson, S. and S. Leonard, "Textual Encodings of PKIX,
43044304+ PKCS, and CMS Structures", RFC 7468, DOI 10.17487/RFC7468,
43054305+ April 2015, <https://www.rfc-editor.org/info/rfc7468>.
43064306+43074307+ [RFC7807] Nottingham, M. and E. Wilde, "Problem Details for HTTP
43084308+ APIs", RFC 7807, DOI 10.17487/RFC7807, March 2016,
43094309+ <https://www.rfc-editor.org/info/rfc7807>.
43104310+43114311+ [RFC8126] Cotton, M., Leiba, B., and T. Narten, "Guidelines for
43124312+ Writing an IANA Considerations Section in RFCs", BCP 26,
43134313+ RFC 8126, DOI 10.17487/RFC8126, June 2017,
43144314+ <https://www.rfc-editor.org/info/rfc8126>.
43154315+43164316+ [RFC8792] Watsen, K., Auerswald, E., Farrel, A., and Q. Wu,
43174317+ "Handling Long Lines in Content of Internet-Drafts and
43184318+ RFCs", RFC 8792, DOI 10.17487/RFC8792, June 2020,
43194319+ <https://www.rfc-editor.org/info/rfc8792>.
43204320+43214321+ [RFC9457] Nottingham, M., Wilde, E., and S. Dalal, "Problem Details
43224322+ for HTTP APIs", RFC 9457, DOI 10.17487/RFC9457, July 2023,
43234323+ <https://www.rfc-editor.org/info/rfc9457>.
43244324+43254325+ [SIGNING-HTTP-MESSAGES]
43264326+ Cavage, M. and M. Sporny, "Signing HTTP Messages", Work in
43274327+ Progress, Internet-Draft, draft-cavage-http-signatures-12,
43284328+ 21 October 2019, <https://datatracker.ietf.org/doc/html/
43294329+ draft-cavage-http-signatures-12>.
43304330+43314331+ [SIGNING-HTTP-REQS-OAUTH]
43324332+ Richer, J., Ed., Bradley, J., and H. Tschofenig, "A Method
43334333+ for Signing HTTP Requests for OAuth", Work in Progress,
43344334+ Internet-Draft, draft-ietf-oauth-signed-http-request-03, 8
43354335+ August 2016, <https://datatracker.ietf.org/doc/html/draft-
43364336+ ietf-oauth-signed-http-request-03>.
43374337+43384338+ [TLS] Rescorla, E., "The Transport Layer Security (TLS) Protocol
43394339+ Version 1.3", RFC 8446, DOI 10.17487/RFC8446, August 2018,
43404340+ <https://www.rfc-editor.org/info/rfc8446>.
43414341+43424342+Appendix A. Detecting HTTP Message Signatures
43434343+43444344+ There have been many attempts to create signed HTTP messages in the
43454345+ past, including other non-standardized definitions of the Signature
43464346+ field that is used within this specification. It is recommended that
43474347+ developers wishing to support this specification, other published
43484348+ documents, or other historical drafts do so carefully and
43494349+ deliberately, as incompatibilities between this specification and
43504350+ other documents or various versions of other drafts could lead to
43514351+ unexpected problems.
43524352+43534353+ It is recommended that implementors first detect and validate the
43544354+ Signature-Input field defined in this specification to detect that
43554355+ the mechanism described in this document is in use and not an
43564356+ alternative. If the Signature-Input field is present, all Signature
43574357+ fields can be parsed and interpreted in the context of this
43584358+ specification.
43594359+43604360+Appendix B. Examples
43614361+43624362+ The following non-normative examples are provided as a means of
43634363+ testing implementations of HTTP message signatures. The signed
43644364+ messages given can be used to create the signature base with the
43654365+ stated parameters, creating signatures using the stated algorithms
43664366+ and keys.
43674367+43684368+ The private keys given can be used to generate signatures, though
43694369+ since several of the demonstrated algorithms are non-deterministic,
43704370+ the results of a signature are expected to be different from the
43714371+ exact bytes of the examples. The public keys given can be used to
43724372+ validate all signed examples.
43734373+43744374+B.1. Example Keys
43754375+43764376+ This section provides cryptographic keys that are referenced in
43774377+ example signatures throughout this document. These keys MUST NOT be
43784378+ used for any purpose other than testing.
43794379+43804380+ The key identifiers for each key are used throughout the examples in
43814381+ this specification. It is assumed for these examples that the signer
43824382+ and verifier can unambiguously dereference all key identifiers used
43834383+ here and that the keys and algorithms used are appropriate for the
43844384+ context in which the signature is presented.
43854385+43864386+ The components for each private key, in PEM format [RFC7468], can be
43874387+ displayed by executing the following OpenSSL command:
43884388+43894389+ openssl pkey -text
43904390+43914391+ This command was tested with all the example keys on OpenSSL version
43924392+ 1.1.1m. Note that some systems cannot produce or use all of these
43934393+ keys directly and may require additional processing. All keys are
43944394+ also made available in JWK format.
43954395+43964396+B.1.1. Example RSA Key
43974397+43984398+ The following key is a 2048-bit RSA public and private key pair,
43994399+ referred to in this document as test-key-rsa. This key is encoded in
44004400+ PEM format, with no encryption.
44014401+44024402+ -----BEGIN RSA PUBLIC KEY-----
44034403+ MIIBCgKCAQEAhAKYdtoeoy8zcAcR874L8cnZxKzAGwd7v36APp7Pv6Q2jdsPBRrw
44044404+ WEBnez6d0UDKDwGbc6nxfEXAy5mbhgajzrw3MOEt8uA5txSKobBpKDeBLOsdJKFq
44054405+ MGmXCQvEG7YemcxDTRPxAleIAgYYRjTSd/QBwVW9OwNFhekro3RtlinV0a75jfZg
44064406+ kne/YiktSvLG34lw2zqXBDTC5NHROUqGTlML4PlNZS5Ri2U4aCNx2rUPRcKIlE0P
44074407+ uKxI4T+HIaFpv8+rdV6eUgOrB2xeI1dSFFn/nnv5OoZJEIB+VmuKn3DCUcCZSFlQ
44084408+ PSXSfBDiUGhwOw76WuSSsf1D4b/vLoJ10wIDAQAB
44094409+ -----END RSA PUBLIC KEY-----
44104410+44114411+ -----BEGIN RSA PRIVATE KEY-----
44124412+ MIIEqAIBAAKCAQEAhAKYdtoeoy8zcAcR874L8cnZxKzAGwd7v36APp7Pv6Q2jdsP
44134413+ BRrwWEBnez6d0UDKDwGbc6nxfEXAy5mbhgajzrw3MOEt8uA5txSKobBpKDeBLOsd
44144414+ JKFqMGmXCQvEG7YemcxDTRPxAleIAgYYRjTSd/QBwVW9OwNFhekro3RtlinV0a75
44154415+ jfZgkne/YiktSvLG34lw2zqXBDTC5NHROUqGTlML4PlNZS5Ri2U4aCNx2rUPRcKI
44164416+ lE0PuKxI4T+HIaFpv8+rdV6eUgOrB2xeI1dSFFn/nnv5OoZJEIB+VmuKn3DCUcCZ
44174417+ SFlQPSXSfBDiUGhwOw76WuSSsf1D4b/vLoJ10wIDAQABAoIBAG/JZuSWdoVHbi56
44184418+ vjgCgkjg3lkO1KrO3nrdm6nrgA9P9qaPjxuKoWaKO1cBQlE1pSWp/cKncYgD5WxE
44194419+ CpAnRUXG2pG4zdkzCYzAh1i+c34L6oZoHsirK6oNcEnHveydfzJL5934egm6p8DW
44204420+ +m1RQ70yUt4uRc0YSor+q1LGJvGQHReF0WmJBZHrhz5e63Pq7lE0gIwuBqL8SMaA
44214421+ yRXtK+JGxZpImTq+NHvEWWCu09SCq0r838ceQI55SvzmTkwqtC+8AT2zFviMZkKR
44224422+ Qo6SPsrqItxZWRty2izawTF0Bf5S2VAx7O+6t3wBsQ1sLptoSgX3QblELY5asI0J
44234423+ YFz7LJECgYkAsqeUJmqXE3LP8tYoIjMIAKiTm9o6psPlc8CrLI9CH0UbuaA2JCOM
44244424+ cCNq8SyYbTqgnWlB9ZfcAm/cFpA8tYci9m5vYK8HNxQr+8FS3Qo8N9RJ8d0U5Csw
44254425+ DzMYfRghAfUGwmlWj5hp1pQzAuhwbOXFtxKHVsMPhz1IBtF9Y8jvgqgYHLbmyiu1
44264426+ mwJ5AL0pYF0G7x81prlARURwHo0Yf52kEw1dxpx+JXER7hQRWQki5/NsUEtv+8RT
44274427+ qn2m6qte5DXLyn83b1qRscSdnCCwKtKWUug5q2ZbwVOCJCtmRwmnP131lWRYfj67
44284428+ B/xJ1ZA6X3GEf4sNReNAtaucPEelgR2nsN0gKQKBiGoqHWbK1qYvBxX2X3kbPDkv
44294429+ 9C+celgZd2PW7aGYLCHq7nPbmfDV0yHcWjOhXZ8jRMjmANVR/eLQ2EfsRLdW69bn
44304430+ f3ZD7JS1fwGnO3exGmHO3HZG+6AvberKYVYNHahNFEw5TsAcQWDLRpkGybBcxqZo
44314431+ 81YCqlqidwfeO5YtlO7etx1xLyqa2NsCeG9A86UjG+aeNnXEIDk1PDK+EuiThIUa
44324432+ /2IxKzJKWl1BKr2d4xAfR0ZnEYuRrbeDQYgTImOlfW6/GuYIxKYgEKCFHFqJATAG
44334433+ IxHrq1PDOiSwXd2GmVVYyEmhZnbcp8CxaEMQoevxAta0ssMK3w6UsDtvUvYvF22m
44344434+ qQKBiD5GwESzsFPy3Ga0MvZpn3D6EJQLgsnrtUPZx+z2Ep2x0xc5orneB5fGyF1P
44354435+ WtP+fG5Q6Dpdz3LRfm+KwBCWFKQjg7uTxcjerhBWEYPmEMKYwTJF5PBG9/ddvHLQ
44364436+ EQeNC8fHGg4UXU8mhHnSBt3EA10qQJfRDs15M38eG2cYwB1PZpDHScDnDA0=
44374437+ -----END RSA PRIVATE KEY-----
44384438+44394439+ The same public and private key pair in JWK format:
44404440+44414441+ NOTE: '\' line wrapping per RFC 8792
44424442+44434443+ {
44444444+ "kty": "RSA",
44454445+ "kid": "test-key-rsa",
44464446+ "p": "sqeUJmqXE3LP8tYoIjMIAKiTm9o6psPlc8CrLI9CH0UbuaA2JCOMcCNq8Sy\
44474447+ YbTqgnWlB9ZfcAm_cFpA8tYci9m5vYK8HNxQr-8FS3Qo8N9RJ8d0U5CswDzMYfRgh\
44484448+ AfUGwmlWj5hp1pQzAuhwbOXFtxKHVsMPhz1IBtF9Y8jvgqgYHLbmyiu1mw",
44494449+ "q": "vSlgXQbvHzWmuUBFRHAejRh_naQTDV3GnH4lcRHuFBFZCSLn82xQS2_7xFO\
44504450+ qfabqq17kNcvKfzdvWpGxxJ2cILAq0pZS6DmrZlvBU4IkK2ZHCac_XfWVZFh-PrsH\
44514451+ _EnVkDpfcYR_iw1F40C1q5w8R6WBHaew3SAp",
44524452+ "d": "b8lm5JZ2hUduLnq-OAKCSODeWQ7Uqs7eet2bqeuAD0_2po-PG4qhZoo7VwF\
44534453+ CUTWlJan9wqdxiAPlbEQKkCdFRcbakbjN2TMJjMCHWL5zfgvqhmgeyKsrqg1wSce9\
44544454+ 7J1_Mkvn3fh6CbqnwNb6bVFDvTJS3i5FzRhKiv6rUsYm8ZAdF4XRaYkFkeuHPl7rc\
44554455+ -ruUTSAjC4GovxIxoDJFe0r4kbFmkiZOr40e8RZYK7T1IKrSvzfxx5AjnlK_OZOTC\
44564456+ q0L7wBPbMW-IxmQpFCjpI-yuoi3FlZG3LaLNrBMXQF_lLZUDHs77q3fAGxDWwum2h\
44574457+ KBfdBuUQtjlqwjQlgXPsskQ",
44584458+ "e": "AQAB",
44594459+ "qi": "PkbARLOwU_LcZrQy9mmfcPoQlAuCyeu1Q9nH7PYSnbHTFzmiud4Hl8bIXU\
44604460+ 9a0_58blDoOl3PctF-b4rAEJYUpCODu5PFyN6uEFYRg-YQwpjBMkXk8Eb39128ctA\
44614461+ RB40Lx8caDhRdTyaEedIG3cQDXSpAl9EOzXkzfx4bZxjAHU9mkMdJwOcMDQ",
44624462+ "dp": "aiodZsrWpi8HFfZfeRs8OS_0L5x6WBl3Y9btoZgsIeruc9uZ8NXTIdxaM6\
44634463+ FdnyNEyOYA1VH94tDYR-xEt1br1ud_dkPslLV_Aac7d7EaYc7cdkb7oC9t6sphVg0\
44644464+ dqE0UTDlOwBxBYMtGmQbJsFzGpmjzVgKqWqJ3B947li2U7t63HXEvKprY2w",
44654465+ "dq": "b0DzpSMb5p42dcQgOTU8Mr4S6JOEhRr_YjErMkpaXUEqvZ3jEB9HRmcRi5\
44664466+ Gtt4NBiBMiY6V9br8a5gjEpiAQoIUcWokBMAYjEeurU8M6JLBd3YaZVVjISaFmdty\
44674467+ nwLFoQxCh6_EC1rSywwrfDpSwO29S9i8Xbaap",
44684468+ "n": "hAKYdtoeoy8zcAcR874L8cnZxKzAGwd7v36APp7Pv6Q2jdsPBRrwWEBnez6\
44694469+ d0UDKDwGbc6nxfEXAy5mbhgajzrw3MOEt8uA5txSKobBpKDeBLOsdJKFqMGmXCQvE\
44704470+ G7YemcxDTRPxAleIAgYYRjTSd_QBwVW9OwNFhekro3RtlinV0a75jfZgkne_YiktS\
44714471+ vLG34lw2zqXBDTC5NHROUqGTlML4PlNZS5Ri2U4aCNx2rUPRcKIlE0PuKxI4T-HIa\
44724472+ Fpv8-rdV6eUgOrB2xeI1dSFFn_nnv5OoZJEIB-VmuKn3DCUcCZSFlQPSXSfBDiUGh\
44734473+ wOw76WuSSsf1D4b_vLoJ10w"
44744474+ }
44754475+44764476+B.1.2. Example RSA-PSS Key
44774477+44784478+ The following key is a 2048-bit RSA public and private key pair,
44794479+ referred to in this document as test-key-rsa-pss. This key is PKCS
44804480+ #8 encoded in PEM format, with no encryption.
44814481+44824482+ -----BEGIN PUBLIC KEY-----
44834483+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr4tmm3r20Wd/PbqvP1s2
44844484+ +QEtvpuRaV8Yq40gjUR8y2Rjxa6dpG2GXHbPfvMs8ct+Lh1GH45x28Rw3Ry53mm+
44854485+ oAXjyQ86OnDkZ5N8lYbggD4O3w6M6pAvLkhk95AndTrifbIFPNU8PPMO7OyrFAHq
44864486+ gDsznjPFmTOtCEcN2Z1FpWgchwuYLPL+Wokqltd11nqqzi+bJ9cvSKADYdUAAN5W
44874487+ Utzdpiy6LbTgSxP7ociU4Tn0g5I6aDZJ7A8Lzo0KSyZYoA485mqcO0GVAdVw9lq4
44884488+ aOT9v6d+nb4bnNkQVklLQ3fVAvJm+xdDOp9LCNCN48V2pnDOkFV6+U9nV5oyc6XI
44894489+ 2wIDAQAB
44904490+ -----END PUBLIC KEY-----
44914491+44924492+ -----BEGIN PRIVATE KEY-----
44934493+ MIIEvgIBADALBgkqhkiG9w0BAQoEggSqMIIEpgIBAAKCAQEAr4tmm3r20Wd/Pbqv
44944494+ P1s2+QEtvpuRaV8Yq40gjUR8y2Rjxa6dpG2GXHbPfvMs8ct+Lh1GH45x28Rw3Ry5
44954495+ 3mm+oAXjyQ86OnDkZ5N8lYbggD4O3w6M6pAvLkhk95AndTrifbIFPNU8PPMO7Oyr
44964496+ FAHqgDsznjPFmTOtCEcN2Z1FpWgchwuYLPL+Wokqltd11nqqzi+bJ9cvSKADYdUA
44974497+ AN5WUtzdpiy6LbTgSxP7ociU4Tn0g5I6aDZJ7A8Lzo0KSyZYoA485mqcO0GVAdVw
44984498+ 9lq4aOT9v6d+nb4bnNkQVklLQ3fVAvJm+xdDOp9LCNCN48V2pnDOkFV6+U9nV5oy
44994499+ c6XI2wIDAQABAoIBAQCUB8ip+kJiiZVKF8AqfB/aUP0jTAqOQewK1kKJ/iQCXBCq
45004500+ pbo360gvdt05H5VZ/RDVkEgO2k73VSsbulqezKs8RFs2tEmU+JgTI9MeQJPWcP6X
45014501+ aKy6LIYs0E2cWgp8GADgoBs8llBq0UhX0KffglIeek3n7Z6Gt4YFge2TAcW2WbN4
45024502+ XfK7lupFyo6HHyWRiYHMMARQXLJeOSdTn5aMBP0PO4bQyk5ORxTUSeOciPJUFktQ
45034503+ HkvGbym7KryEfwH8Tks0L7WhzyP60PL3xS9FNOJi9m+zztwYIXGDQuKM2GDsITeD
45044504+ 2mI2oHoPMyAD0wdI7BwSVW18p1h+jgfc4dlexKYRAoGBAOVfuiEiOchGghV5vn5N
45054505+ RDNscAFnpHj1QgMr6/UG05RTgmcLfVsI1I4bSkbrIuVKviGGf7atlkROALOG/xRx
45064506+ DLadgBEeNyHL5lz6ihQaFJLVQ0u3U4SB67J0YtVO3R6lXcIjBDHuY8SjYJ7Ci6Z6
45074507+ vuDcoaEujnlrtUhaMxvSfcUJAoGBAMPsCHXte1uWNAqYad2WdLjPDlKtQJK1diCm
45084508+ rqmB2g8QE99hDOHItjDBEdpyFBKOIP+NpVtM2KLhRajjcL9Ph8jrID6XUqikQuVi
45094509+ 4J9FV2m42jXMuioTT13idAILanYg8D3idvy/3isDVkON0X3UAVKrgMEne0hJpkPL
45104510+ FYqgetvDAoGBAKLQ6JZMbSe0pPIJkSamQhsehgL5Rs51iX4m1z7+sYFAJfhvN3Q/
45114511+ OGIHDRp6HjMUcxHpHw7U+S1TETxePwKLnLKj6hw8jnX2/nZRgWHzgVcY+sPsReRx
45124512+ NJVf+Cfh6yOtznfX00p+JWOXdSY8glSSHJwRAMog+hFGW1AYdt7w80XBAoGBAImR
45134513+ NUugqapgaEA8TrFxkJmngXYaAqpA0iYRA7kv3S4QavPBUGtFJHBNULzitydkNtVZ
45144514+ 3w6hgce0h9YThTo/nKc+OZDZbgfN9s7cQ75x0PQCAO4fx2P91Q+mDzDUVTeG30mE
45154515+ t2m3S0dGe47JiJxifV9P3wNBNrZGSIF3mrORBVNDAoGBAI0QKn2Iv7Sgo4T/XjND
45164516+ dl2kZTXqGAk8dOhpUiw/HdM3OGWbhHj2NdCzBliOmPyQtAr770GITWvbAI+IRYyF
45174517+ S7Fnk6ZVVVHsxjtaHy1uJGFlaZzKR4AGNaUTOJMs6NadzCmGPAxNQQOCqoUjn4XR
45184518+ rOjr9w349JooGXhOxbu8nOxX
45194519+ -----END PRIVATE KEY-----
45204520+45214521+ The same public and private key pair in JWK format:
45224522+45234523+ NOTE: '\' line wrapping per RFC 8792
45244524+45254525+ {
45264526+ "kty": "RSA",
45274527+ "kid": "test-key-rsa-pss",
45284528+ "p": "5V-6ISI5yEaCFXm-fk1EM2xwAWekePVCAyvr9QbTlFOCZwt9WwjUjhtKRus\
45294529+ i5Uq-IYZ_tq2WRE4As4b_FHEMtp2AER43IcvmXPqKFBoUktVDS7dThIHrsnRi1U7d\
45304530+ HqVdwiMEMe5jxKNgnsKLpnq-4NyhoS6OeWu1SFozG9J9xQk",
45314531+ "q": "w-wIde17W5Y0Cphp3ZZ0uM8OUq1AkrV2IKauqYHaDxAT32EM4ci2MMER2nI\
45324532+ UEo4g_42lW0zYouFFqONwv0-HyOsgPpdSqKRC5WLgn0VXabjaNcy6KhNPXeJ0Agtq\
45334533+ diDwPeJ2_L_eKwNWQ43RfdQBUquAwSd7SEmmQ8sViqB628M",
45344534+ "d": "lAfIqfpCYomVShfAKnwf2lD9I0wKjkHsCtZCif4kAlwQqqW6N-tIL3bdOR-\
45354535+ VWf0Q1ZBIDtpO91UrG7pansyrPERbNrRJlPiYEyPTHkCT1nD-l2isuiyGLNBNnFoK\
45364536+ fBgA4KAbPJZQatFIV9Cn34JSHnpN5-2ehreGBYHtkwHFtlmzeF3yu5bqRcqOhx8lk\
45374537+ YmBzDAEUFyyXjknU5-WjAT9DzuG0MpOTkcU1EnjnIjyVBZLUB5Lxm8puyq8hH8B_E\
45384538+ 5LNC-1oc8j-tDy98UvRTTiYvZvs87cGCFxg0LijNhg7CE3g9piNqB6DzMgA9MHSOw\
45394539+ cElVtfKdYfo4H3OHZXsSmEQ",
45404540+ "e": "AQAB",
45414541+ "qi": "jRAqfYi_tKCjhP9eM0N2XaRlNeoYCTx06GlSLD8d0zc4ZZuEePY10LMGWI\
45424542+ 6Y_JC0CvvvQYhNa9sAj4hFjIVLsWeTplVVUezGO1ofLW4kYWVpnMpHgAY1pRM4kyz\
45434543+ o1p3MKYY8DE1BA4KqhSOfhdGs6Ov3Dfj0migZeE7Fu7yc7Fc",
45444544+ "dp": "otDolkxtJ7Sk8gmRJqZCGx6GAvlGznWJfibXPv6xgUAl-G83dD84YgcNGn\
45454545+ oeMxRzEekfDtT5LVMRPF4_AoucsqPqHDyOdfb-dlGBYfOBVxj6w-xF5HE0lV_4J-H\
45464546+ rI63Od9fTSn4lY5d1JjyCVJIcnBEAyiD6EUZbUBh23vDzRcE",
45474547+ "dq": "iZE1S6CpqmBoQDxOsXGQmaeBdhoCqkDSJhEDuS_dLhBq88FQa0UkcE1QvO\
45484548+ K3J2Q21VnfDqGBx7SH1hOFOj-cpz45kNluB832ztxDvnHQ9AIA7h_HY_3VD6YPMNR\
45494549+ VN4bfSYS3abdLR0Z7jsmInGJ9X0_fA0E2tkZIgXeas5EFU0M",
45504550+ "n": "r4tmm3r20Wd_PbqvP1s2-QEtvpuRaV8Yq40gjUR8y2Rjxa6dpG2GXHbPfvM\
45514551+ s8ct-Lh1GH45x28Rw3Ry53mm-oAXjyQ86OnDkZ5N8lYbggD4O3w6M6pAvLkhk95An\
45524552+ dTrifbIFPNU8PPMO7OyrFAHqgDsznjPFmTOtCEcN2Z1FpWgchwuYLPL-Wokqltd11\
45534553+ nqqzi-bJ9cvSKADYdUAAN5WUtzdpiy6LbTgSxP7ociU4Tn0g5I6aDZJ7A8Lzo0KSy\
45544554+ ZYoA485mqcO0GVAdVw9lq4aOT9v6d-nb4bnNkQVklLQ3fVAvJm-xdDOp9LCNCN48V\
45554555+ 2pnDOkFV6-U9nV5oyc6XI2w"
45564556+ }
45574557+45584558+B.1.3. Example ECC P-256 Test Key
45594559+45604560+ The following key is a public and private elliptical curve key pair
45614561+ over the curve P-256, referred to in this document as test-key-ecc-
45624562+ p256. This key is encoded in PEM format, with no encryption.
45634563+45644564+ -----BEGIN PUBLIC KEY-----
45654565+ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqIVYZVLCrPZHGHjP17CTW0/+D9Lf
45664566+ w0EkjqF7xB4FivAxzic30tMM4GF+hR6Dxh71Z50VGGdldkkDXZCnTNnoXQ==
45674567+ -----END PUBLIC KEY-----
45684568+45694569+ -----BEGIN EC PRIVATE KEY-----
45704570+ MHcCAQEEIFKbhfNZfpDsW43+0+JjUr9K+bTeuxopu653+hBaXGA7oAoGCCqGSM49
45714571+ AwEHoUQDQgAEqIVYZVLCrPZHGHjP17CTW0/+D9Lfw0EkjqF7xB4FivAxzic30tMM
45724572+ 4GF+hR6Dxh71Z50VGGdldkkDXZCnTNnoXQ==
45734573+ -----END EC PRIVATE KEY-----
45744574+45754575+ The same public and private key pair in JWK format:
45764576+45774577+ {
45784578+ "kty": "EC",
45794579+ "crv": "P-256",
45804580+ "kid": "test-key-ecc-p256",
45814581+ "d": "UpuF81l-kOxbjf7T4mNSv0r5tN67Gim7rnf6EFpcYDs",
45824582+ "x": "qIVYZVLCrPZHGHjP17CTW0_-D9Lfw0EkjqF7xB4FivA",
45834583+ "y": "Mc4nN9LTDOBhfoUeg8Ye9WedFRhnZXZJA12Qp0zZ6F0"
45844584+ }
45854585+45864586+B.1.4. Example Ed25519 Test Key
45874587+45884588+ The following key is an elliptical curve key over the Edwards curve
45894589+ ed25519, referred to in this document as test-key-ed25519. This key
45904590+ is PKCS #8 encoded in PEM format, with no encryption.
45914591+45924592+ -----BEGIN PUBLIC KEY-----
45934593+ MCowBQYDK2VwAyEAJrQLj5P/89iXES9+vFgrIy29clF9CC/oPPsw3c5D0bs=
45944594+ -----END PUBLIC KEY-----
45954595+45964596+ -----BEGIN PRIVATE KEY-----
45974597+ MC4CAQAwBQYDK2VwBCIEIJ+DYvh6SEqVTm50DFtMDoQikTmiCqirVv9mWG9qfSnF
45984598+ -----END PRIVATE KEY-----
45994599+46004600+ The same public and private key pair in JWK format:
46014601+46024602+ {
46034603+ "kty": "OKP",
46044604+ "crv": "Ed25519",
46054605+ "kid": "test-key-ed25519",
46064606+ "d": "n4Ni-HpISpVObnQMW0wOhCKROaIKqKtW_2ZYb2p9KcU",
46074607+ "x": "JrQLj5P_89iXES9-vFgrIy29clF9CC_oPPsw3c5D0bs"
46084608+ }
46094609+46104610+B.1.5. Example Shared Secret
46114611+46124612+ The following shared secret is 64 randomly generated bytes encoded in
46134613+ Base64, referred to in this document as test-shared-secret:
46144614+46154615+ NOTE: '\' line wrapping per RFC 8792
46164616+46174617+ uzvJfB4u3N0Jy4T7NZ75MDVcr8zSTInedJtkgcu46YW4XByzNJjxBdtjUkdJPBt\
46184618+ bmHhIDi6pcl8jsasjlTMtDQ==
46194619+46204620+B.2. Test Cases
46214621+46224622+ This section provides non-normative examples that may be used as test
46234623+ cases to validate implementation correctness. These examples are
46244624+ based on the following HTTP messages:
46254625+46264626+ For requests, this test-request message is used:
46274627+46284628+ NOTE: '\' line wrapping per RFC 8792
46294629+46304630+ POST /foo?param=Value&Pet=dog HTTP/1.1
46314631+ Host: example.com
46324632+ Date: Tue, 20 Apr 2021 02:07:55 GMT
46334633+ Content-Type: application/json
46344634+ Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+T\
46354635+ aPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:
46364636+ Content-Length: 18
46374637+46384638+ {"hello": "world"}
46394639+46404640+ For responses, this test-response message is used:
46414641+46424642+ NOTE: '\' line wrapping per RFC 8792
46434643+46444644+ HTTP/1.1 200 OK
46454645+ Date: Tue, 20 Apr 2021 02:07:56 GMT
46464646+ Content-Type: application/json
46474647+ Content-Digest: sha-512=:mEWXIS7MaLRuGgxOBdODa3xqM1XdEvxoYhvlCFJ41Q\
46484648+ JgJc4GTsPp29l5oGX69wWdXymyU0rjJuahq4l5aGgfLQ==:
46494649+ Content-Length: 23
46504650+46514651+ {"message": "good dog"}
46524652+46534653+B.2.1. Minimal Signature Using rsa-pss-sha512
46544654+46554655+ This example presents a minimal signature using the rsa-pss-sha512
46564656+ algorithm over test-request, covering none of the components of the
46574657+ HTTP message but providing a timestamped signature proof of
46584658+ possession of the key with a signer-provided nonce.
46594659+46604660+ The corresponding signature base is:
46614661+46624662+ NOTE: '\' line wrapping per RFC 8792
46634663+46644664+ "@signature-params": ();created=1618884473;keyid="test-key-rsa-pss"\
46654665+ ;nonce="b3k2pp5k7z-50gnwp.yemd"
46664666+46674667+ This results in the following Signature-Input and Signature header
46684668+ fields being added to the message under the signature label sig-b21:
46694669+46704670+ NOTE: '\' line wrapping per RFC 8792
46714671+46724672+ Signature-Input: sig-b21=();created=1618884473\
46734673+ ;keyid="test-key-rsa-pss";nonce="b3k2pp5k7z-50gnwp.yemd"
46744674+ Signature: sig-b21=:d2pmTvmbncD3xQm8E9ZV2828BjQWGgiwAaw5bAkgibUopem\
46754675+ LJcWDy/lkbbHAve4cRAtx31Iq786U7it++wgGxbtRxf8Udx7zFZsckzXaJMkA7ChG\
46764676+ 52eSkFxykJeNqsrWH5S+oxNFlD4dzVuwe8DhTSja8xxbR/Z2cOGdCbzR72rgFWhzx\
46774677+ 2VjBqJzsPLMIQKhO4DGezXehhWwE56YCE+O6c0mKZsfxVrogUvA4HELjVKWmAvtl6\
46784678+ UnCh8jYzuVG5WSb/QEVPnP5TmcAnLH1g+s++v6d4s8m0gCw1fV5/SITLq9mhho8K3\
46794679+ +7EPYTU8IU1bLhdxO5Nyt8C8ssinQ98Xw9Q==:
46804680+46814681+ Note that since the covered components list is empty, this signature
46824682+ could be applied by an attacker to an unrelated HTTP message. In
46834683+ this example, the nonce parameter is included to prevent the same
46844684+ signature from being replayed more than once, but if an attacker
46854685+ intercepts the signature and prevents its delivery to the verifier,
46864686+ the attacker could apply this signature to another message.
46874687+ Therefore, the use of an empty covered components set is discouraged.
46884688+ See Section 7.2.1 for more discussion.
46894689+46904690+ Note that the RSA-PSS algorithm in use here is non-deterministic,
46914691+ meaning that a different signature value will be created every time
46924692+ the algorithm is run. The signature value provided here can be
46934693+ validated against the given keys, but newly generated signature
46944694+ values are not expected to match the example. See Section 7.3.5.
46954695+46964696+B.2.2. Selective Covered Components Using rsa-pss-sha512
46974697+46984698+ This example covers additional components (the authority, the
46994699+ Content-Digest header field, and a single named query parameter) in
47004700+ test-request using the rsa-pss-sha512 algorithm. This example also
47014701+ adds a tag parameter with the application-specific value of header-
47024702+ example.
47034703+47044704+ The corresponding signature base is:
47054705+47064706+ NOTE: '\' line wrapping per RFC 8792
47074707+47084708+ "@authority": example.com
47094709+ "content-digest": sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX\
47104710+ +TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:
47114711+ "@query-param";name="Pet": dog
47124712+ "@signature-params": ("@authority" "content-digest" \
47134713+ "@query-param";name="Pet")\
47144714+ ;created=1618884473;keyid="test-key-rsa-pss"\
47154715+ ;tag="header-example"
47164716+47174717+ This results in the following Signature-Input and Signature header
47184718+ fields being added to the message under the label sig-b22:
47194719+47204720+ NOTE: '\' line wrapping per RFC 8792
47214721+47224722+ Signature-Input: sig-b22=("@authority" "content-digest" \
47234723+ "@query-param";name="Pet");created=1618884473\
47244724+ ;keyid="test-key-rsa-pss";tag="header-example"
47254725+ Signature: sig-b22=:LjbtqUbfmvjj5C5kr1Ugj4PmLYvx9wVjZvD9GsTT4F7GrcQ\
47264726+ EdJzgI9qHxICagShLRiLMlAJjtq6N4CDfKtjvuJyE5qH7KT8UCMkSowOB4+ECxCmT\
47274727+ 8rtAmj/0PIXxi0A0nxKyB09RNrCQibbUjsLS/2YyFYXEu4TRJQzRw1rLEuEfY17SA\
47284728+ RYhpTlaqwZVtR8NV7+4UKkjqpcAoFqWFQh62s7Cl+H2fjBSpqfZUJcsIk4N6wiKYd\
47294729+ 4je2U/lankenQ99PZfB4jY3I5rSV2DSBVkSFsURIjYErOs0tFTQosMTAoxk//0RoK\
47304730+ UqiYY8Bh0aaUEb0rQl3/XaVe4bXTugEjHSw==:
47314731+47324732+ Note that the RSA-PSS algorithm in use here is non-deterministic,
47334733+ meaning that a different signature value will be created every time
47344734+ the algorithm is run. The signature value provided here can be
47354735+ validated against the given keys, but newly generated signature
47364736+ values are not expected to match the example. See Section 7.3.5.
47374737+47384738+B.2.3. Full Coverage Using rsa-pss-sha512
47394739+47404740+ This example covers all applicable message components in test-request
47414741+ (including the content type and length) plus many derived components,
47424742+ again using the rsa-pss-sha512 algorithm. Note that the Host header
47434743+ field is not covered because the @authority derived component is
47444744+ included instead.
47454745+47464746+ The corresponding signature base is:
47474747+47484748+ NOTE: '\' line wrapping per RFC 8792
47494749+47504750+ "date": Tue, 20 Apr 2021 02:07:55 GMT
47514751+ "@method": POST
47524752+ "@path": /foo
47534753+ "@query": ?param=Value&Pet=dog
47544754+ "@authority": example.com
47554755+ "content-type": application/json
47564756+ "content-digest": sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX\
47574757+ +TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:
47584758+ "content-length": 18
47594759+ "@signature-params": ("date" "@method" "@path" "@query" \
47604760+ "@authority" "content-type" "content-digest" "content-length")\
47614761+ ;created=1618884473;keyid="test-key-rsa-pss"
47624762+47634763+ This results in the following Signature-Input and Signature header
47644764+ fields being added to the message under the label sig-b23:
47654765+47664766+ NOTE: '\' line wrapping per RFC 8792
47674767+47684768+ Signature-Input: sig-b23=("date" "@method" "@path" "@query" \
47694769+ "@authority" "content-type" "content-digest" "content-length")\
47704770+ ;created=1618884473;keyid="test-key-rsa-pss"
47714771+ Signature: sig-b23=:bbN8oArOxYoyylQQUU6QYwrTuaxLwjAC9fbY2F6SVWvh0yB\
47724772+ iMIRGOnMYwZ/5MR6fb0Kh1rIRASVxFkeGt683+qRpRRU5p2voTp768ZrCUb38K0fU\
47734773+ xN0O0iC59DzYx8DFll5GmydPxSmme9v6ULbMFkl+V5B1TP/yPViV7KsLNmvKiLJH1\
47744774+ pFkh/aYA2HXXZzNBXmIkoQoLd7YfW91kE9o/CCoC1xMy7JA1ipwvKvfrs65ldmlu9\
47754775+ bpG6A9BmzhuzF8Eim5f8ui9eH8LZH896+QIF61ka39VBrohr9iyMUJpvRX2Zbhl5Z\
47764776+ JzSRxpJyoEZAFL2FUo5fTIztsDZKEgM4cUA==:
47774777+47784778+ Note in this example that the value of the Date header field and the
47794779+ value of the created signature parameter need not be the same. This
47804780+ is due to the fact that the Date header field is added when creating
47814781+ the HTTP message and the created parameter is populated when creating
47824782+ the signature over that message, and these two times could vary. If
47834783+ the Date header field is covered by the signature, it is up to the
47844784+ verifier to determine whether its value has to match that of the
47854785+ created parameter or not. See Section 7.2.4 for more discussion.
47864786+47874787+ Note that the RSA-PSS algorithm in use here is non-deterministic,
47884788+ meaning that a different signature value will be created every time
47894789+ the algorithm is run. The signature value provided here can be
47904790+ validated against the given keys, but newly generated signature
47914791+ values are not expected to match the example. See Section 7.3.5.
47924792+47934793+B.2.4. Signing a Response Using ecdsa-p256-sha256
47944794+47954795+ This example covers portions of the test-response message using the
47964796+ ecdsa-p256-sha256 algorithm and the key test-key-ecc-p256.
47974797+47984798+ The corresponding signature base is:
47994799+48004800+ NOTE: '\' line wrapping per RFC 8792
48014801+48024802+ "@status": 200
48034803+ "content-type": application/json
48044804+ "content-digest": sha-512=:mEWXIS7MaLRuGgxOBdODa3xqM1XdEvxoYhvlCFJ4\
48054805+ 1QJgJc4GTsPp29l5oGX69wWdXymyU0rjJuahq4l5aGgfLQ==:
48064806+ "content-length": 23
48074807+ "@signature-params": ("@status" "content-type" "content-digest" \
48084808+ "content-length");created=1618884473;keyid="test-key-ecc-p256"
48094809+48104810+ This results in the following Signature-Input and Signature header
48114811+ fields being added to the message under the label sig-b24:
48124812+48134813+ NOTE: '\' line wrapping per RFC 8792
48144814+48154815+ Signature-Input: sig-b24=("@status" "content-type" \
48164816+ "content-digest" "content-length");created=1618884473\
48174817+ ;keyid="test-key-ecc-p256"
48184818+ Signature: sig-b24=:wNmSUAhwb5LxtOtOpNa6W5xj067m5hFrj0XQ4fvpaCLx0NK\
48194819+ ocgPquLgyahnzDnDAUy5eCdlYUEkLIj+32oiasw==:
48204820+48214821+ Note that the ECDSA signature algorithm in use here is non-
48224822+ deterministic, meaning that a different signature value will be
48234823+ created every time the algorithm is run. The signature value
48244824+ provided here can be validated against the given keys, but newly
48254825+ generated signature values are not expected to match the example.
48264826+ See Section 7.3.5.
48274827+48284828+B.2.5. Signing a Request Using hmac-sha256
48294829+48304830+ This example covers portions of the test-request message using the
48314831+ hmac-sha256 algorithm and the secret test-shared-secret.
48324832+48334833+ The corresponding signature base is:
48344834+48354835+ NOTE: '\' line wrapping per RFC 8792
48364836+48374837+ "date": Tue, 20 Apr 2021 02:07:55 GMT
48384838+ "@authority": example.com
48394839+ "content-type": application/json
48404840+ "@signature-params": ("date" "@authority" "content-type")\
48414841+ ;created=1618884473;keyid="test-shared-secret"
48424842+48434843+ This results in the following Signature-Input and Signature header
48444844+ fields being added to the message under the label sig-b25:
48454845+48464846+ NOTE: '\' line wrapping per RFC 8792
48474847+48484848+ Signature-Input: sig-b25=("date" "@authority" "content-type")\
48494849+ ;created=1618884473;keyid="test-shared-secret"
48504850+ Signature: sig-b25=:pxcQw6G3AjtMBQjwo8XzkZf/bws5LelbaMk5rGIGtE8=:
48514851+48524852+ Before using symmetric signatures in practice, see the discussion
48534853+ regarding security trade-offs in Section 7.3.3.
48544854+48554855+B.2.6. Signing a Request Using ed25519
48564856+48574857+ This example covers portions of the test-request message using the
48584858+ Ed25519 algorithm and the key test-key-ed25519.
48594859+48604860+ The corresponding signature base is:
48614861+48624862+ NOTE: '\' line wrapping per RFC 8792
48634863+48644864+ "date": Tue, 20 Apr 2021 02:07:55 GMT
48654865+ "@method": POST
48664866+ "@path": /foo
48674867+ "@authority": example.com
48684868+ "content-type": application/json
48694869+ "content-length": 18
48704870+ "@signature-params": ("date" "@method" "@path" "@authority" \
48714871+ "content-type" "content-length");created=1618884473\
48724872+ ;keyid="test-key-ed25519"
48734873+48744874+ This results in the following Signature-Input and Signature header
48754875+ fields being added to the message under the label sig-b26:
48764876+48774877+ NOTE: '\' line wrapping per RFC 8792
48784878+48794879+ Signature-Input: sig-b26=("date" "@method" "@path" "@authority" \
48804880+ "content-type" "content-length");created=1618884473\
48814881+ ;keyid="test-key-ed25519"
48824882+ Signature: sig-b26=:wqcAqbmYJ2ji2glfAMaRy4gruYYnx2nEFN2HN6jrnDnQCK1\
48834883+ u02Gb04v9EDgwUPiu4A0w6vuQv5lIp5WPpBKRCw==:
48844884+48854885+B.3. TLS-Terminating Proxies
48864886+48874887+ In this example, there is a TLS-terminating reverse proxy sitting in
48884888+ front of the resource. The client does not sign the request but
48894889+ instead uses mutual TLS to make its call. The terminating proxy
48904890+ validates the TLS stream and injects a Client-Cert header field
48914891+ according to [CLIENT-CERT], and then applies a signature to this
48924892+ field. By signing this header field, a reverse proxy not only can
48934893+ attest to its own validation of the initial request's TLS parameters
48944894+ but can also authenticate itself to the backend system independently
48954895+ of the client's actions.
48964896+48974897+ The client makes the following request to the TLS-terminating proxy
48984898+ using mutual TLS:
48994899+49004900+ POST /foo?param=Value&Pet=dog HTTP/1.1
49014901+ Host: example.com
49024902+ Date: Tue, 20 Apr 2021 02:07:55 GMT
49034903+ Content-Type: application/json
49044904+ Content-Length: 18
49054905+49064906+ {"hello": "world"}
49074907+49084908+ The proxy processes the TLS connection and extracts the client's TLS
49094909+ certificate to a Client-Cert header field and passes it along to the
49104910+ internal service hosted at service.internal.example. This results in
49114911+ the following unsigned request:
49124912+49134913+ NOTE: '\' line wrapping per RFC 8792
49144914+49154915+ POST /foo?param=Value&Pet=dog HTTP/1.1
49164916+ Host: service.internal.example
49174917+ Date: Tue, 20 Apr 2021 02:07:55 GMT
49184918+ Content-Type: application/json
49194919+ Content-Length: 18
49204920+ Client-Cert: :MIIBqDCCAU6gAwIBAgIBBzAKBggqhkjOPQQDAjA6MRswGQYDVQQKD\
49214921+ BJMZXQncyBBdXRoZW50aWNhdGUxGzAZBgNVBAMMEkxBIEludGVybWVkaWF0ZSBDQT\
49224922+ AeFw0yMDAxMTQyMjU1MzNaFw0yMTAxMjMyMjU1MzNaMA0xCzAJBgNVBAMMAkJDMFk\
49234923+ wEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8YnXXfaUgmnMtOXU/IncWalRhebrXmck\
49244924+ C8vdgJ1p5Be5F/3YC8OthxM4+k1M6aEAEFcGzkJiNy6J84y7uzo9M6NyMHAwCQYDV\
49254925+ R0TBAIwADAfBgNVHSMEGDAWgBRm3WjLa38lbEYCuiCPct0ZaSED2DAOBgNVHQ8BAf\
49264926+ 8EBAMCBsAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwHQYDVR0RAQH/BBMwEYEPYmRjQGV\
49274927+ 4YW1wbGUuY29tMAoGCCqGSM49BAMCA0gAMEUCIBHda/r1vaL6G3VliL4/Di6YK0Q6\
49284928+ bMjeSkC3dFCOOB8TAiEAx/kHSB4urmiZ0NX5r5XarmPk0wmuydBVoU4hBVZ1yhk=:
49294929+49304930+ {"hello": "world"}
49314931+49324932+ Without a signature, the internal service would need to trust that
49334933+ the incoming connection has the right information. By signing the
49344934+ Client-Cert header field and other portions of the internal request,
49354935+ the internal service can be assured that the correct party, the
49364936+ trusted proxy, has processed the request and presented it to the
49374937+ correct service. The proxy's signature base consists of the
49384938+ following:
49394939+49404940+ NOTE: '\' line wrapping per RFC 8792
49414941+49424942+ "@path": /foo
49434943+ "@query": ?param=Value&Pet=dog
49444944+ "@method": POST
49454945+ "@authority": service.internal.example
49464946+ "client-cert": :MIIBqDCCAU6gAwIBAgIBBzAKBggqhkjOPQQDAjA6MRswGQYDVQQ\
49474947+ KDBJMZXQncyBBdXRoZW50aWNhdGUxGzAZBgNVBAMMEkxBIEludGVybWVkaWF0ZSBD\
49484948+ QTAeFw0yMDAxMTQyMjU1MzNaFw0yMTAxMjMyMjU1MzNaMA0xCzAJBgNVBAMMAkJDM\
49494949+ FkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8YnXXfaUgmnMtOXU/IncWalRhebrXm\
49504950+ ckC8vdgJ1p5Be5F/3YC8OthxM4+k1M6aEAEFcGzkJiNy6J84y7uzo9M6NyMHAwCQY\
49514951+ DVR0TBAIwADAfBgNVHSMEGDAWgBRm3WjLa38lbEYCuiCPct0ZaSED2DAOBgNVHQ8B\
49524952+ Af8EBAMCBsAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwHQYDVR0RAQH/BBMwEYEPYmRjQ\
49534953+ GV4YW1wbGUuY29tMAoGCCqGSM49BAMCA0gAMEUCIBHda/r1vaL6G3VliL4/Di6YK0\
49544954+ Q6bMjeSkC3dFCOOB8TAiEAx/kHSB4urmiZ0NX5r5XarmPk0wmuydBVoU4hBVZ1yhk=:
49554955+ "@signature-params": ("@path" "@query" "@method" "@authority" \
49564956+ "client-cert");created=1618884473;keyid="test-key-ecc-p256"
49574957+49584958+ This results in the following signature:
49594959+49604960+ NOTE: '\' line wrapping per RFC 8792
49614961+49624962+ xVMHVpawaAC/0SbHrKRs9i8I3eOs5RtTMGCWXm/9nvZzoHsIg6Mce9315T6xoklyy0y\
49634963+ zhD9ah4JHRwMLOgmizw==
49644964+49654965+ which results in the following signed request sent from the proxy to
49664966+ the internal service with the proxy's signature under the label ttrp:
49674967+49684968+ NOTE: '\' line wrapping per RFC 8792
49694969+49704970+ POST /foo?param=Value&Pet=dog HTTP/1.1
49714971+ Host: service.internal.example
49724972+ Date: Tue, 20 Apr 2021 02:07:55 GMT
49734973+ Content-Type: application/json
49744974+ Content-Length: 18
49754975+ Client-Cert: :MIIBqDCCAU6gAwIBAgIBBzAKBggqhkjOPQQDAjA6MRswGQYDVQQKD\
49764976+ BJMZXQncyBBdXRoZW50aWNhdGUxGzAZBgNVBAMMEkxBIEludGVybWVkaWF0ZSBDQT\
49774977+ AeFw0yMDAxMTQyMjU1MzNaFw0yMTAxMjMyMjU1MzNaMA0xCzAJBgNVBAMMAkJDMFk\
49784978+ wEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8YnXXfaUgmnMtOXU/IncWalRhebrXmck\
49794979+ C8vdgJ1p5Be5F/3YC8OthxM4+k1M6aEAEFcGzkJiNy6J84y7uzo9M6NyMHAwCQYDV\
49804980+ R0TBAIwADAfBgNVHSMEGDAWgBRm3WjLa38lbEYCuiCPct0ZaSED2DAOBgNVHQ8BAf\
49814981+ 8EBAMCBsAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwHQYDVR0RAQH/BBMwEYEPYmRjQGV\
49824982+ 4YW1wbGUuY29tMAoGCCqGSM49BAMCA0gAMEUCIBHda/r1vaL6G3VliL4/Di6YK0Q6\
49834983+ bMjeSkC3dFCOOB8TAiEAx/kHSB4urmiZ0NX5r5XarmPk0wmuydBVoU4hBVZ1yhk=:
49844984+ Signature-Input: ttrp=("@path" "@query" "@method" "@authority" \
49854985+ "client-cert");created=1618884473;keyid="test-key-ecc-p256"
49864986+ Signature: ttrp=:xVMHVpawaAC/0SbHrKRs9i8I3eOs5RtTMGCWXm/9nvZzoHsIg6\
49874987+ Mce9315T6xoklyy0yzhD9ah4JHRwMLOgmizw==:
49884988+49894989+ {"hello": "world"}
49904990+49914991+ The internal service can validate the proxy's signature and therefore
49924992+ be able to trust that the client's certificate has been appropriately
49934993+ processed.
49944994+49954995+B.4. HTTP Message Transformations
49964996+49974997+ HTTP allows intermediaries and applications to transform an HTTP
49984998+ message without affecting the semantics of the message itself. HTTP
49994999+ message signatures are designed to be robust against many of these
50005000+ transformations in different circumstances.
50015001+50025002+ For example, the following HTTP request message has been signed using
50035003+ the Ed25519 algorithm and the key test-key-ed25519:
50045004+50055005+ NOTE: '\' line wrapping per RFC 8792
50065006+50075007+ GET /demo?name1=Value1&Name2=value2 HTTP/1.1
50085008+ Host: example.org
50095009+ Date: Fri, 15 Jul 2022 14:24:55 GMT
50105010+ Accept: application/json
50115011+ Accept: */*
50125012+ Signature-Input: transform=("@method" "@path" "@authority" \
50135013+ "accept");created=1618884473;keyid="test-key-ed25519"
50145014+ Signature: transform=:ZT1kooQsEHpZ0I1IjCqtQppOmIqlJPeo7DHR3SoMn0s5J\
50155015+ Z1eRGS0A+vyYP9t/LXlh5QMFFQ6cpLt2m0pmj3NDA==:
50165016+50175017+ The signature base string for this message is:
50185018+50195019+ "@method": GET
50205020+ "@path": /demo
50215021+ "@authority": example.org
50225022+ "accept": application/json, */*
50235023+ "@signature-params": ("@method" "@path" "@authority" "accept")\
50245024+ ;created=1618884473;keyid="test-key-ed25519"
50255025+50265026+ The following message has been altered by adding the Accept-Language
50275027+ header field as well as adding a query parameter. However, since
50285028+ neither the Accept-Language header field nor the query is covered by
50295029+ the signature, the same signature is still valid:
50305030+50315031+ NOTE: '\' line wrapping per RFC 8792
50325032+50335033+ GET /demo?name1=Value1&Name2=value2¶m=added HTTP/1.1
50345034+ Host: example.org
50355035+ Date: Fri, 15 Jul 2022 14:24:55 GMT
50365036+ Accept: application/json
50375037+ Accept: */*
50385038+ Accept-Language: en-US,en;q=0.5
50395039+ Signature-Input: transform=("@method" "@path" "@authority" \
50405040+ "accept");created=1618884473;keyid="test-key-ed25519"
50415041+ Signature: transform=:ZT1kooQsEHpZ0I1IjCqtQppOmIqlJPeo7DHR3SoMn0s5J\
50425042+ Z1eRGS0A+vyYP9t/LXlh5QMFFQ6cpLt2m0pmj3NDA==:
50435043+50445044+ The following message has been altered by removing the Date header
50455045+ field, adding a Referer header field, and collapsing the Accept
50465046+ header field into a single line. The Date and Referer header fields
50475047+ are not covered by the signature, and the collapsing of the Accept
50485048+ header field is an allowed transformation that is already accounted
50495049+ for by the canonicalization algorithm for HTTP field values. The
50505050+ same signature is still valid:
50515051+50525052+ NOTE: '\' line wrapping per RFC 8792
50535053+50545054+ GET /demo?name1=Value1&Name2=value2 HTTP/1.1
50555055+ Host: example.org
50565056+ Referer: https://developer.example.org/demo
50575057+ Accept: application/json, */*
50585058+ Signature-Input: transform=("@method" "@path" "@authority" \
50595059+ "accept");created=1618884473;keyid="test-key-ed25519"
50605060+ Signature: transform=:ZT1kooQsEHpZ0I1IjCqtQppOmIqlJPeo7DHR3SoMn0s5J\
50615061+ Z1eRGS0A+vyYP9t/LXlh5QMFFQ6cpLt2m0pmj3NDA==:
50625062+50635063+ The following message has been altered by reordering the field values
50645064+ of the original message but not reordering the individual Accept
50655065+ header fields. The same signature is still valid:
50665066+50675067+ NOTE: '\' line wrapping per RFC 8792
50685068+50695069+ GET /demo?name1=Value1&Name2=value2 HTTP/1.1
50705070+ Accept: application/json
50715071+ Accept: */*
50725072+ Date: Fri, 15 Jul 2022 14:24:55 GMT
50735073+ Host: example.org
50745074+ Signature-Input: transform=("@method" "@path" "@authority" \
50755075+ "accept");created=1618884473;keyid="test-key-ed25519"
50765076+ Signature: transform=:ZT1kooQsEHpZ0I1IjCqtQppOmIqlJPeo7DHR3SoMn0s5J\
50775077+ Z1eRGS0A+vyYP9t/LXlh5QMFFQ6cpLt2m0pmj3NDA==:
50785078+50795079+ The following message has been altered by changing the method to POST
50805080+ and the authority to "example.com" (inside the Host header field).
50815081+ Since both the method and authority are covered by the signature, the
50825082+ same signature is NOT still valid:
50835083+50845084+ NOTE: '\' line wrapping per RFC 8792
50855085+50865086+ POST /demo?name1=Value1&Name2=value2 HTTP/1.1
50875087+ Host: example.com
50885088+ Date: Fri, 15 Jul 2022 14:24:55 GMT
50895089+ Accept: application/json
50905090+ Accept: */*
50915091+ Signature-Input: transform=("@method" "@path" "@authority" \
50925092+ "accept");created=1618884473;keyid="test-key-ed25519"
50935093+ Signature: transform=:ZT1kooQsEHpZ0I1IjCqtQppOmIqlJPeo7DHR3SoMn0s5J\
50945094+ Z1eRGS0A+vyYP9t/LXlh5QMFFQ6cpLt2m0pmj3NDA==:
50955095+50965096+ The following message has been altered by changing the order of the
50975097+ two instances of the Accept header field. Since the order of fields
50985098+ with the same name is semantically significant in HTTP, this changes
50995099+ the value used in the signature base, and the same signature is NOT
51005100+ still valid:
51015101+51025102+ NOTE: '\' line wrapping per RFC 8792
51035103+51045104+ GET /demo?name1=Value1&Name2=value2 HTTP/1.1
51055105+ Host: example.org
51065106+ Date: Fri, 15 Jul 2022 14:24:55 GMT
51075107+ Accept: */*
51085108+ Accept: application/json
51095109+ Signature-Input: transform=("@method" "@path" "@authority" \
51105110+ "accept");created=1618884473;keyid="test-key-ed25519"
51115111+ Signature: transform=:ZT1kooQsEHpZ0I1IjCqtQppOmIqlJPeo7DHR3SoMn0s5J\
51125112+ Z1eRGS0A+vyYP9t/LXlh5QMFFQ6cpLt2m0pmj3NDA==:
51135113+51145114+Acknowledgements
51155115+51165116+ This specification was initially based on [SIGNING-HTTP-MESSAGES].
51175117+ The editors would like to thank the authors of
51185118+ [SIGNING-HTTP-MESSAGES] -- Mark Cavage and Manu Sporny -- for their
51195119+ work on that Internet-Draft and their continuing contributions. This
51205120+ specification also includes contributions from
51215121+ [SIGNING-HTTP-REQS-OAUTH] and other similar efforts.
51225122+51235123+ The editors would also like to thank the following individuals
51245124+ (listed in alphabetical order) for feedback, insight, and
51255125+ implementation of this document and its predecessors: Mark Adamcin,
51265126+ Mark Allen, Paul Annesley, Karl Böhlmark, Stéphane Bortzmeyer, Sarven
51275127+ Capadisli, Liam Dennehy, Stephen Farrell, Phillip Hallam-Baker, Tyler
51285128+ Ham, Eric Holmes, Andrey Kislyuk, Adam Knight, Dave Lehn, Ilari
51295129+ Liusvaara, Dave Longley, James H. Manger, Kathleen Moriarty, Yoav
51305130+ Nir, Mark Nottingham, Adrian Palmer, Lucas Pardue, Roberto Polli,
51315131+ Julian Reschke, Michael Richardson, Wojciech Rygielski, Rich Salz,
51325132+ Adam Scarr, Cory J. Slep, Dirk Stein, Henry Story, Lukasz Szewc,
51335133+ Chris Webber, and Jeffrey Yasskin.
51345134+51355135+Authors' Addresses
51365136+51375137+ Annabelle Backman (editor)
51385138+ Amazon
51395139+ P.O. Box 81226
51405140+ Seattle, WA 98108-1226
51415141+ United States of America
51425142+ Email: richanna@amazon.com
51435143+ URI: https://www.amazon.com/
51445144+51455145+51465146+ Justin Richer (editor)
51475147+ Bespoke Engineering
51485148+ Email: ietf@justin.richer.org
51495149+ URI: https://bspk.io/
51505150+51515151+51525152+ Manu Sporny
51535153+ Digital Bazaar
51545154+ 203 Roanoke Street W.
51555155+ Blacksburg, VA 24060
51565156+ United States of America
51575157+ Email: msporny@digitalbazaar.com