A batteries included HTTP/1.1 client in OCaml

http signature support

TODO is to improve the SF implementation in the future

+7654 -5
+3
dune-project
··· 27 27 ca-certs 28 28 mirage-crypto 29 29 mirage-crypto-rng 30 + mirage-crypto-ec 31 + mirage-crypto-pk 32 + eqaf 30 33 uri 31 34 jsont 32 35 bytesrw
+46 -3
lib/auth.ml
··· 13 13 | Bearer_form of { token : string } 14 14 (** RFC 6750 Section 2.2: Bearer token in form-encoded body *) 15 15 | Digest of { username : string; password : string } 16 + | Signature of Signature.config 17 + (** RFC 9421: HTTP Message Signatures *) 16 18 | Custom of (Headers.t -> Headers.t) 17 19 18 20 (** Digest authentication challenge parsed from WWW-Authenticate header *) ··· 33 35 let bearer ~token = Bearer { token } 34 36 35 37 let digest ~username ~password = Digest { username; password } 38 + 39 + let signature config = Signature config 36 40 37 41 let custom f = Custom f 38 42 ··· 50 54 | Bearer _ -> "Bearer" 51 55 | Bearer_form _ -> "Bearer (form)" 52 56 | Digest _ -> "Digest" 57 + | Signature _ -> "Signature" 53 58 | Custom _ -> "Custom" 54 59 55 60 (** Check if auth type requires HTTPS (per RFC 7617/6750). 56 - Basic, Bearer, and Digest send credentials that can be intercepted. *) 61 + Basic, Bearer, and Digest send credentials that can be intercepted. 62 + Signature does not strictly require HTTPS as it provides its own integrity. *) 57 63 let requires_https = function 58 64 | Basic _ | Bearer _ | Bearer_form _ | Digest _ -> true 59 - | No_auth | Custom _ -> false 65 + | No_auth | Signature _ | Custom _ -> false 60 66 61 67 (** Validate that sensitive authentication is used over HTTPS. 62 68 Per RFC 7617 Section 4 (Basic) and RFC 6750 Section 5.1 (Bearer): ··· 96 102 | Digest { username; password = _ } -> 97 103 Log.debug (fun m -> m "Digest auth configured for user: %s (requires server challenge)" username); 98 104 (* Digest auth requires server challenge first, handled elsewhere *) 105 + headers 106 + | Signature _ -> 107 + Log.debug (fun m -> m "Signature auth configured (requires request context)"); 108 + (* Signature auth requires request context (method, URI) to compute. 109 + Handled separately in request flow via apply_signature. *) 99 110 headers 100 111 | Custom f -> 101 112 Log.debug (fun m -> m "Applying custom authentication handler"); ··· 378 389 (** Check if stale=true in digest challenge, indicating password is still valid. 379 390 Per RFC 7616: If stale=true, the client should retry with same credentials 380 391 using the new nonce. If stale=false or not present, credentials are wrong. *) 381 - let digest_is_stale challenge = challenge.stale 392 + let digest_is_stale challenge = challenge.stale 393 + 394 + (** {1 HTTP Message Signatures (RFC 9421)} *) 395 + 396 + let is_signature = function 397 + | Signature _ -> true 398 + | _ -> false 399 + 400 + let get_signature_config = function 401 + | Signature config -> Some config 402 + | _ -> None 403 + 404 + (** Apply HTTP Message Signature to headers given request context. 405 + This computes and adds the Signature-Input and Signature headers. 406 + 407 + @param method_ The HTTP method 408 + @param uri The request URI 409 + @param headers The headers to sign (and add signature to) 410 + @param auth The authentication configuration (must be [Signature]) 411 + @return Updated headers with signature, or original headers if not Signature auth *) 412 + let apply_signature ~method_ ~uri ~headers auth = 413 + match auth with 414 + | Signature config -> 415 + let context = Signature.Context.request ~method_ ~uri ~headers in 416 + (match Signature.sign ~config ~context ~headers with 417 + | Ok signed_headers -> 418 + Log.debug (fun m -> m "Applied HTTP message signature"); 419 + signed_headers 420 + | Error e -> 421 + Log.err (fun m -> m "Failed to apply HTTP message signature: %s" 422 + (Signature.sign_error_to_string e)); 423 + headers) 424 + | _ -> headers
+43 -1
lib/auth.mli
··· 48 48 Note: SHA-512-256 is not supported as it requires special initialization 49 49 vectors not available in standard libraries. *) 50 50 51 + val signature : Signature.config -> t 52 + (** HTTP Message Signatures (RFC 9421). 53 + 54 + Creates cryptographic signatures over HTTP message components. 55 + The signature covers selected headers and derived values like 56 + the method, path, and authority. 57 + 58 + Use {!Signature.config} to create the configuration: 59 + {[ 60 + let key = Signature.Key.ed25519 ~priv:... ~pub:... in 61 + let config = Signature.config ~key ~keyid:"my-key" () in 62 + let auth = Auth.signature config 63 + ]} 64 + 65 + The signature is computed and added when the request is made, 66 + as it requires the full request context (method, URI, headers). *) 67 + 51 68 val bearer_form : token:string -> t 52 69 (** Bearer token in form-encoded body (RFC 6750 Section 2.2). 53 70 ··· 170 187 Per RFC 7616 Section 3.2.2: If stale=true, the nonce is expired but the 171 188 credentials are still valid. The client should retry with the same 172 189 credentials using the new nonce. If stale=false or not present, the 173 - credentials themselves are wrong. *) 190 + credentials themselves are wrong. *) 191 + 192 + (** {1 HTTP Message Signatures (RFC 9421)} *) 193 + 194 + val is_signature : t -> bool 195 + (** [is_signature auth] returns [true] if [auth] is HTTP Message Signature authentication. *) 196 + 197 + val get_signature_config : t -> Signature.config option 198 + (** [get_signature_config auth] returns [Some config] if [auth] is HTTP Message 199 + Signature authentication, [None] otherwise. *) 200 + 201 + val apply_signature : 202 + method_:Method.t -> 203 + uri:Uri.t -> 204 + headers:Headers.t -> 205 + t -> 206 + Headers.t 207 + (** [apply_signature ~method_ ~uri ~headers auth] applies HTTP Message Signature 208 + to [headers] if [auth] is Signature authentication. Returns the headers with 209 + [Signature-Input] and [Signature] headers added. 210 + 211 + This function computes the signature based on the request context and adds 212 + the appropriate headers per RFC 9421. 213 + 214 + If [auth] is not Signature authentication, returns [headers] unchanged. 215 + If signature computation fails, logs an error and returns [headers] unchanged. *)
+4
lib/dune
··· 13 13 xdge 14 14 logs 15 15 ptime 16 + ptime.clock.os 16 17 cmdliner 17 18 mirage-crypto 18 19 mirage-crypto-rng 19 20 mirage-crypto-rng.unix 21 + mirage-crypto-ec 22 + mirage-crypto-pk 23 + eqaf 20 24 tls 21 25 tls-eio 22 26 ca-certs
+10 -1
lib/requests.ml
··· 32 32 module Header_name = Header_name 33 33 module Header_parsing = Header_parsing 34 34 module Websocket = Websocket 35 + module Signature = Signature 35 36 36 37 (** Minimum TLS version configuration - re-exported from Tls_config. *) 37 38 type tls_version = Tls_config.tls_version = ··· 658 659 in 659 660 660 661 let max_redir = Option.value max_redirects ~default:t.max_redirects in 662 + 663 + (* Apply HTTP Message Signature if configured (RFC 9421) *) 664 + let signed_headers = match auth with 665 + | Some a when Auth.is_signature a -> 666 + Auth.apply_signature ~method_ ~uri:original_uri ~headers:base_headers a 667 + | _ -> base_headers 668 + in 669 + 661 670 let final_status, final_headers, final_body_str, final_url = 662 - make_with_redirects ~headers_for_request:base_headers 671 + make_with_redirects ~headers_for_request:signed_headers 663 672 ~method_ ~body:request_body url max_redir 664 673 in 665 674
+3
lib/requests.mli
··· 928 928 @see <https://www.rfc-editor.org/rfc/rfc6455> *) 929 929 module Websocket = Websocket 930 930 931 + (** HTTP Message Signatures (RFC 9421) *) 932 + module Signature = Signature 933 + 931 934 (** {2 Logging} *) 932 935 933 936 (** Log source for the requests library.
+299
lib/sf.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** RFC 8941 Structured Fields (minimal subset for HTTP Signatures) *) 7 + 8 + (* Result monad syntax for cleaner parsing *) 9 + let ( let* ) = Result.bind 10 + 11 + type item = 12 + | String of string 13 + | Token of string 14 + | Integer of int64 15 + | Decimal of float 16 + | Boolean of bool 17 + | Byte_seq of string 18 + 19 + type parameters = (string * item) list 20 + 21 + type inner_list = (item * parameters) list * parameters 22 + 23 + type list_member = 24 + | Item of item * parameters 25 + | Inner_list of inner_list 26 + 27 + type dictionary = (string * list_member) list 28 + 29 + (* Convenience constructors *) 30 + let string_item s = String s 31 + let token_item s = Token s 32 + let integer_item i = Integer i 33 + let byte_seq_item s = Byte_seq s 34 + let bool_item b = Boolean b 35 + 36 + (* Serialization *) 37 + 38 + let item_to_string = function 39 + | String s -> 40 + (* Escape backslashes and quotes *) 41 + let escaped = String.to_seq s 42 + |> Seq.flat_map (fun c -> 43 + if c = '\\' || c = '"' then List.to_seq ['\\'; c] 44 + else Seq.return c) 45 + |> String.of_seq 46 + in 47 + "\"" ^ escaped ^ "\"" 48 + | Token s -> s 49 + | Integer i -> Int64.to_string i 50 + | Decimal f -> Printf.sprintf "%g" f 51 + | Boolean true -> "?1" 52 + | Boolean false -> "?0" 53 + | Byte_seq s -> 54 + ":" ^ Base64.encode_string s ^ ":" 55 + 56 + let parameters_to_string params = 57 + String.concat "" (List.map (fun (k, v) -> 58 + match v with 59 + | Boolean true -> ";" ^ k 60 + | _ -> ";" ^ k ^ "=" ^ item_to_string v 61 + ) params) 62 + 63 + let inner_list_to_string (items, params) = 64 + let item_strs = List.map (fun (item, item_params) -> 65 + item_to_string item ^ parameters_to_string item_params 66 + ) items in 67 + "(" ^ String.concat " " item_strs ^ ")" ^ parameters_to_string params 68 + 69 + let dictionary_to_string dict = 70 + String.concat ", " (List.map (fun (k, member) -> 71 + match member with 72 + | Item (Boolean true, params) -> 73 + k ^ parameters_to_string params 74 + | Item (item, params) -> 75 + k ^ "=" ^ item_to_string item ^ parameters_to_string params 76 + | Inner_list il -> 77 + k ^ "=" ^ inner_list_to_string il 78 + ) dict) 79 + 80 + (* Parsing helpers *) 81 + 82 + let is_alpha c = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') 83 + let is_digit c = c >= '0' && c <= '9' 84 + let is_lcalpha c = c >= 'a' && c <= 'z' 85 + let is_tchar c = 86 + is_alpha c || is_digit c || 87 + c = '!' || c = '#' || c = '$' || c = '%' || c = '&' || c = '\'' || 88 + c = '*' || c = '+' || c = '-' || c = '.' || c = '^' || c = '_' || 89 + c = '`' || c = '|' || c = '~' 90 + 91 + type parser_state = { 92 + input : string; 93 + mutable pos : int; 94 + } 95 + 96 + let peek s = if s.pos < String.length s.input then Some s.input.[s.pos] else None 97 + let advance s = s.pos <- s.pos + 1 98 + let _consume s = let c = s.input.[s.pos] in advance s; c 99 + let skip_sp s = while peek s = Some ' ' do advance s done 100 + let skip_ows s = while peek s = Some ' ' || peek s = Some '\t' do advance s done 101 + 102 + let parse_token s = 103 + let start = s.pos in 104 + match peek s with 105 + | Some c when is_alpha c || c = '*' -> 106 + advance s; 107 + while match peek s with 108 + | Some c -> is_tchar c || c = ':' || c = '/' 109 + | None -> false 110 + do advance s done; 111 + Ok (String.sub s.input start (s.pos - start)) 112 + | _ -> Error "Expected token" 113 + 114 + let parse_key s = 115 + let start = s.pos in 116 + match peek s with 117 + | Some c when is_lcalpha c || c = '*' -> 118 + advance s; 119 + while match peek s with 120 + | Some c -> is_lcalpha c || is_digit c || c = '_' || c = '-' || c = '.' || c = '*' 121 + | None -> false 122 + do advance s done; 123 + Ok (String.sub s.input start (s.pos - start)) 124 + | _ -> Error "Expected key" 125 + 126 + let parse_integer s = 127 + let start = s.pos in 128 + let neg = if peek s = Some '-' then (advance s; true) else false in 129 + if not (match peek s with Some c -> is_digit c | None -> false) then 130 + Error "Expected integer" 131 + else begin 132 + while match peek s with Some c -> is_digit c | None -> false do advance s done; 133 + let str = String.sub s.input start (s.pos - start) in 134 + match Int64.of_string_opt str with 135 + | Some i -> Ok (if neg then Int64.neg i else i) 136 + | None -> Error "Integer overflow" 137 + end 138 + 139 + let parse_decimal s = 140 + let start = s.pos in 141 + let _ = if peek s = Some '-' then advance s in 142 + while match peek s with Some c -> is_digit c | None -> false do advance s done; 143 + if peek s = Some '.' then begin 144 + advance s; 145 + while match peek s with Some c -> is_digit c | None -> false do advance s done 146 + end; 147 + let str = String.sub s.input start (s.pos - start) in 148 + match float_of_string_opt str with 149 + | Some f -> Ok f 150 + | None -> Error "Invalid decimal" 151 + 152 + let parse_string s = 153 + if peek s <> Some '"' then Error "Expected string" 154 + else begin 155 + advance s; 156 + let buf = Buffer.create 32 in 157 + let rec loop () = 158 + match peek s with 159 + | None -> Error "Unterminated string" 160 + | Some '"' -> advance s; Ok (Buffer.contents buf) 161 + | Some '\\' -> 162 + advance s; 163 + (match peek s with 164 + | Some c when c = '\\' || c = '"' -> 165 + Buffer.add_char buf c; advance s; loop () 166 + | _ -> Error "Invalid escape") 167 + | Some c -> 168 + if Char.code c >= 0x20 && Char.code c <= 0x7e then begin 169 + Buffer.add_char buf c; advance s; loop () 170 + end else Error "Invalid character in string" 171 + in 172 + loop () 173 + end 174 + 175 + let parse_byte_seq s = 176 + if peek s <> Some ':' then Error "Expected byte sequence" 177 + else begin 178 + advance s; 179 + let start = s.pos in 180 + while match peek s with 181 + | Some c -> c <> ':' 182 + | None -> false 183 + do advance s done; 184 + if peek s <> Some ':' then Error "Unterminated byte sequence" 185 + else begin 186 + let b64 = String.sub s.input start (s.pos - start) in 187 + advance s; 188 + match Base64.decode b64 with 189 + | Ok decoded -> Ok decoded 190 + | Error (`Msg msg) -> Error msg 191 + end 192 + end 193 + 194 + let parse_boolean s = 195 + if peek s <> Some '?' then Error "Expected boolean" 196 + else begin 197 + advance s; 198 + match peek s with 199 + | Some '1' -> advance s; Ok true 200 + | Some '0' -> advance s; Ok false 201 + | _ -> Error "Expected ?0 or ?1" 202 + end 203 + 204 + let rec parse_bare_item s = 205 + match peek s with 206 + | Some '"' -> Result.map (fun x -> String x) (parse_string s) 207 + | Some ':' -> Result.map (fun x -> Byte_seq x) (parse_byte_seq s) 208 + | Some '?' -> Result.map (fun x -> Boolean x) (parse_boolean s) 209 + | Some c when c = '-' || is_digit c -> 210 + (* Could be integer or decimal, peek ahead *) 211 + let start = s.pos in 212 + let _ = if peek s = Some '-' then advance s in 213 + while match peek s with Some c -> is_digit c | None -> false do advance s done; 214 + if peek s = Some '.' then begin 215 + s.pos <- start; 216 + Result.map (fun x -> Decimal x) (parse_decimal s) 217 + end else begin 218 + s.pos <- start; 219 + Result.map (fun x -> Integer x) (parse_integer s) 220 + end 221 + | Some c when is_alpha c || c = '*' -> 222 + Result.map (fun x -> Token x) (parse_token s) 223 + | _ -> Error "Expected bare item" 224 + 225 + and parse_parameters s = 226 + let rec loop acc = 227 + if peek s = Some ';' then begin 228 + advance s; 229 + skip_sp s; 230 + match parse_key s with 231 + | Error _ -> Ok (List.rev acc) 232 + | Ok key -> 233 + if peek s = Some '=' then begin 234 + advance s; 235 + match parse_bare_item s with 236 + | Ok v -> loop ((key, v) :: acc) 237 + | Error e -> Error e 238 + end else 239 + loop ((key, Boolean true) :: acc) 240 + end else Ok (List.rev acc) 241 + in 242 + loop [] 243 + 244 + let parse_item s = 245 + let* item = parse_bare_item s in 246 + let* params = parse_parameters s in 247 + Ok (item, params) 248 + 249 + let parse_inner_list_items s = 250 + let rec loop acc = 251 + skip_sp s; 252 + match peek s with 253 + | Some ')' -> advance s; Ok (List.rev acc) 254 + | Some _ -> 255 + let* item = parse_item s in 256 + loop (item :: acc) 257 + | None -> Error "Unterminated inner list" 258 + in 259 + loop [] 260 + 261 + let parse_inner_list str = 262 + let s = { input = str; pos = 0 } in 263 + skip_sp s; 264 + if peek s <> Some '(' then Error "Expected '('" 265 + else begin 266 + advance s; 267 + let* items = parse_inner_list_items s in 268 + let* params = parse_parameters s in 269 + Ok (items, params) 270 + end 271 + 272 + let parse_list_member s = 273 + if peek s = Some '(' then begin 274 + advance s; 275 + let* items = parse_inner_list_items s in 276 + let* params = parse_parameters s in 277 + Ok (Inner_list (items, params)) 278 + end else 279 + let* (item, params) = parse_item s in 280 + Ok (Item (item, params)) 281 + 282 + let parse_dictionary str = 283 + let s = { input = str; pos = 0 } in 284 + let rec loop acc = 285 + skip_ows s; 286 + if s.pos >= String.length s.input then Ok (List.rev acc) 287 + else 288 + let* key = parse_key s in 289 + let* member = 290 + if peek s = Some '=' then (advance s; parse_list_member s) 291 + else 292 + let* params = parse_parameters s in 293 + Ok (Item (Boolean true, params)) 294 + in 295 + skip_ows s; 296 + if peek s = Some ',' then (advance s; skip_ows s); 297 + loop ((key, member) :: acc) 298 + in 299 + loop []
+80
lib/sf.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** RFC 8941 Structured Fields (minimal subset for HTTP Signatures) 7 + 8 + This module implements the subset of 9 + {{:https://datatracker.ietf.org/doc/html/rfc8941}RFC 8941} Structured Fields 10 + required for HTTP Message Signatures (RFC 9421). 11 + 12 + Only Dictionary and Inner List types are fully supported, as these are 13 + used by the Signature-Input and Signature headers. *) 14 + 15 + (** {1 Bare Items} *) 16 + 17 + type item = 18 + | String of string (** Quoted string: "value" *) 19 + | Token of string (** Unquoted token: value *) 20 + | Integer of int64 (** Integer: 123 *) 21 + | Decimal of float (** Decimal: 1.23 *) 22 + | Boolean of bool (** Boolean: ?1 or ?0 *) 23 + | Byte_seq of string (** Byte sequence: :base64: *) 24 + 25 + val item_to_string : item -> string 26 + (** Serialize an item to its Structured Field representation. *) 27 + 28 + (** {1 Parameters} *) 29 + 30 + type parameters = (string * item) list 31 + (** Parameters are key-value pairs attached to items. *) 32 + 33 + val parameters_to_string : parameters -> string 34 + (** Serialize parameters (e.g., [;key=value;key2]). *) 35 + 36 + (** {1 Inner Lists} *) 37 + 38 + type inner_list = (item * parameters) list * parameters 39 + (** An inner list is a list of parameterized items with list-level parameters. 40 + Format: [(item1;param item2);listparam=value] *) 41 + 42 + val inner_list_to_string : inner_list -> string 43 + (** Serialize an inner list. *) 44 + 45 + val parse_inner_list : string -> (inner_list, string) result 46 + (** Parse an inner list from its string representation. *) 47 + 48 + (** {1 List Members} *) 49 + 50 + type list_member = 51 + | Item of item * parameters 52 + | Inner_list of inner_list 53 + 54 + (** {1 Dictionaries} *) 55 + 56 + type dictionary = (string * list_member) list 57 + (** A dictionary is an ordered map of string keys to list members. *) 58 + 59 + val dictionary_to_string : dictionary -> string 60 + (** Serialize a dictionary to its Structured Field representation. *) 61 + 62 + val parse_dictionary : string -> (dictionary, string) result 63 + (** Parse a dictionary from its string representation. *) 64 + 65 + (** {1 Convenience Functions} *) 66 + 67 + val string_item : string -> item 68 + (** Create a string item. *) 69 + 70 + val token_item : string -> item 71 + (** Create a token item. *) 72 + 73 + val integer_item : int64 -> item 74 + (** Create an integer item. *) 75 + 76 + val byte_seq_item : string -> item 77 + (** Create a byte sequence item (raw bytes, will be base64 encoded). *) 78 + 79 + val bool_item : bool -> item 80 + (** Create a boolean item. *)
+969
lib/signature.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** RFC 9421 HTTP Message Signatures *) 7 + 8 + let src = Logs.Src.create "requests.signature" ~doc:"HTTP Message Signatures" 9 + module Log = (val Logs.src_log src : Logs.LOG) 10 + 11 + (* Result monad syntax for cleaner error handling *) 12 + let ( let* ) = Result.bind 13 + 14 + (* ========================================================================= *) 15 + (* Algorithms *) 16 + (* ========================================================================= *) 17 + 18 + module Algorithm = struct 19 + type t = [ 20 + | `Rsa_pss_sha512 21 + | `Rsa_v1_5_sha256 22 + | `Hmac_sha256 23 + | `Ecdsa_p256_sha256 24 + | `Ecdsa_p384_sha384 25 + | `Ed25519 26 + ] 27 + 28 + let to_string : t -> string = function 29 + | `Rsa_pss_sha512 -> "rsa-pss-sha512" 30 + | `Rsa_v1_5_sha256 -> "rsa-v1_5-sha256" 31 + | `Hmac_sha256 -> "hmac-sha256" 32 + | `Ecdsa_p256_sha256 -> "ecdsa-p256-sha256" 33 + | `Ecdsa_p384_sha384 -> "ecdsa-p384-sha384" 34 + | `Ed25519 -> "ed25519" 35 + 36 + let of_string : string -> t option = function 37 + | "rsa-pss-sha512" -> Some `Rsa_pss_sha512 38 + | "rsa-v1_5-sha256" -> Some `Rsa_v1_5_sha256 39 + | "hmac-sha256" -> Some `Hmac_sha256 40 + | "ecdsa-p256-sha256" -> Some `Ecdsa_p256_sha256 41 + | "ecdsa-p384-sha384" -> Some `Ecdsa_p384_sha384 42 + | "ed25519" -> Some `Ed25519 43 + | _ -> None 44 + 45 + let of_string_exn s = 46 + match of_string s with 47 + | Some a -> a 48 + | None -> invalid_arg ("Unknown algorithm: " ^ s) 49 + end 50 + 51 + (* ========================================================================= *) 52 + (* Components *) 53 + (* ========================================================================= *) 54 + 55 + module Component = struct 56 + type derived = [ 57 + | `Method 58 + | `Authority 59 + | `Path 60 + | `Query 61 + | `Query_param of string 62 + | `Target_uri 63 + | `Status 64 + | `Request_target 65 + ] 66 + 67 + type param = [ 68 + | `Sf 69 + | `Key of string 70 + | `Bs 71 + | `Tr 72 + | `Req 73 + ] 74 + 75 + type t = [ 76 + | `Derived of derived * param list 77 + | `Field of string * param list 78 + ] 79 + 80 + (* Constructors *) 81 + let method_ : t = `Derived (`Method, []) 82 + let authority : t = `Derived (`Authority, []) 83 + let path : t = `Derived (`Path, []) 84 + let query : t = `Derived (`Query, []) 85 + let query_param name : t = `Derived (`Query_param name, []) 86 + let target_uri : t = `Derived (`Target_uri, []) 87 + let status : t = `Derived (`Status, []) 88 + let request_target : t = `Derived (`Request_target, []) 89 + 90 + let field name : t = `Field (String.lowercase_ascii name, []) 91 + let field_sf name : t = `Field (String.lowercase_ascii name, [`Sf]) 92 + let field_bs name : t = `Field (String.lowercase_ascii name, [`Bs]) 93 + let field_key name ~key : t = `Field (String.lowercase_ascii name, [`Key key]) 94 + let field_req name : t = `Field (String.lowercase_ascii name, [`Req]) 95 + 96 + let content_type = field "content-type" 97 + let content_length = field "content-length" 98 + let content_digest = field "content-digest" 99 + let date = field "date" 100 + let host = field "host" 101 + 102 + let derived_to_string : derived -> string = function 103 + | `Method -> "@method" 104 + | `Authority -> "@authority" 105 + | `Path -> "@path" 106 + | `Query -> "@query" 107 + | `Query_param _ -> "@query-param" 108 + | `Target_uri -> "@target-uri" 109 + | `Status -> "@status" 110 + | `Request_target -> "@request-target" 111 + 112 + let param_to_sf : param -> string * Sf.item = function 113 + | `Sf -> ("sf", Sf.Boolean true) 114 + | `Key k -> ("key", Sf.String k) 115 + | `Bs -> ("bs", Sf.Boolean true) 116 + | `Tr -> ("tr", Sf.Boolean true) 117 + | `Req -> ("req", Sf.Boolean true) 118 + 119 + let param_to_string : param -> string = function 120 + | `Sf -> ";sf" 121 + | `Key k -> ";key=\"" ^ k ^ "\"" 122 + | `Bs -> ";bs" 123 + | `Tr -> ";tr" 124 + | `Req -> ";req" 125 + 126 + let to_identifier : t -> string = function 127 + | `Derived (d, params) -> 128 + let base = derived_to_string d in 129 + let name_param = match d with 130 + | `Query_param name -> ";name=\"" ^ name ^ "\"" 131 + | _ -> "" 132 + in 133 + "\"" ^ base ^ "\"" ^ name_param ^ String.concat "" (List.map param_to_string params) 134 + | `Field (name, params) -> 135 + "\"" ^ name ^ "\"" ^ String.concat "" (List.map param_to_string params) 136 + 137 + let to_sf_item : t -> Sf.item * Sf.parameters = function 138 + | `Derived (d, params) -> 139 + let base = derived_to_string d in 140 + let sf_params = List.map param_to_sf params in 141 + let sf_params = match d with 142 + | `Query_param name -> ("name", Sf.String name) :: sf_params 143 + | _ -> sf_params 144 + in 145 + (Sf.String base, sf_params) 146 + | `Field (name, params) -> 147 + let sf_params = List.map param_to_sf params in 148 + (Sf.String name, sf_params) 149 + 150 + let of_identifier str = 151 + (* Parse component identifier like "@method" or "content-type;sf" *) 152 + let parse_params rest = 153 + let rec loop acc s = 154 + if String.length s = 0 then Ok (List.rev acc) 155 + else if s.[0] = ';' then 156 + let s = String.sub s 1 (String.length s - 1) in 157 + if String.length s >= 2 && String.sub s 0 2 = "sf" then 158 + loop (`Sf :: acc) (String.sub s 2 (String.length s - 2)) 159 + else if String.length s >= 2 && String.sub s 0 2 = "bs" then 160 + loop (`Bs :: acc) (String.sub s 2 (String.length s - 2)) 161 + else if String.length s >= 2 && String.sub s 0 2 = "tr" then 162 + loop (`Tr :: acc) (String.sub s 2 (String.length s - 2)) 163 + else if String.length s >= 3 && String.sub s 0 3 = "req" then 164 + loop (`Req :: acc) (String.sub s 3 (String.length s - 3)) 165 + else if String.length s >= 4 && String.sub s 0 4 = "key=" then 166 + let s = String.sub s 4 (String.length s - 4) in 167 + if String.length s > 0 && s.[0] = '"' then 168 + match String.index_from_opt s 1 '"' with 169 + | Some idx -> 170 + let key = String.sub s 1 (idx - 1) in 171 + loop (`Key key :: acc) (String.sub s (idx + 1) (String.length s - idx - 1)) 172 + | None -> Error "Unterminated key parameter" 173 + else Error "Expected quoted key value" 174 + else if String.length s >= 5 && String.sub s 0 5 = "name=" then 175 + (* Skip name parameter, handled separately for query-param *) 176 + let s = String.sub s 5 (String.length s - 5) in 177 + if String.length s > 0 && s.[0] = '"' then 178 + match String.index_from_opt s 1 '"' with 179 + | Some idx -> 180 + loop acc (String.sub s (idx + 1) (String.length s - idx - 1)) 181 + | None -> Error "Unterminated name parameter" 182 + else Error "Expected quoted name value" 183 + else Error ("Unknown parameter: " ^ s) 184 + else Error ("Unexpected character: " ^ s) 185 + in 186 + loop [] rest 187 + in 188 + (* Extract name parameter for query-param *) 189 + let extract_name s = 190 + match String.index_opt s ';' with 191 + | None -> (s, None) 192 + | Some idx -> 193 + let rest = String.sub s idx (String.length s - idx) in 194 + if String.length rest >= 6 && 195 + String.sub rest 0 6 = ";name=" then 196 + let after = String.sub rest 6 (String.length rest - 6) in 197 + if String.length after > 0 && after.[0] = '"' then 198 + match String.index_from_opt after 1 '"' with 199 + | Some end_idx -> 200 + let name = String.sub after 1 (end_idx - 1) in 201 + let remaining = String.sub s 0 idx ^ 202 + String.sub after (end_idx + 1) (String.length after - end_idx - 1) in 203 + (remaining, Some name) 204 + | None -> (s, None) 205 + else (s, None) 206 + else (s, None) 207 + in 208 + let (ident, query_name) = extract_name str in 209 + match String.index_opt ident ';' with 210 + | None -> 211 + if String.length ident > 0 && ident.[0] = '@' then 212 + match ident with 213 + | "@method" -> Ok method_ 214 + | "@authority" -> Ok authority 215 + | "@path" -> Ok path 216 + | "@query" -> Ok query 217 + | "@query-param" -> 218 + (match query_name with 219 + | Some n -> Ok (query_param n) 220 + | None -> Error "@query-param requires name parameter") 221 + | "@target-uri" -> Ok target_uri 222 + | "@status" -> Ok status 223 + | "@request-target" -> Ok request_target 224 + | _ -> Error ("Unknown derived component: " ^ ident) 225 + else Ok (field ident) 226 + | Some idx -> 227 + let name = String.sub ident 0 idx in 228 + let params_str = String.sub ident idx (String.length ident - idx) in 229 + match parse_params params_str with 230 + | Error e -> Error e 231 + | Ok params -> 232 + if String.length name > 0 && name.[0] = '@' then 233 + match name with 234 + | "@method" -> Ok (`Derived (`Method, params)) 235 + | "@authority" -> Ok (`Derived (`Authority, params)) 236 + | "@path" -> Ok (`Derived (`Path, params)) 237 + | "@query" -> Ok (`Derived (`Query, params)) 238 + | "@query-param" -> 239 + (match query_name with 240 + | Some n -> Ok (`Derived (`Query_param n, params)) 241 + | None -> Error "@query-param requires name parameter") 242 + | "@target-uri" -> Ok (`Derived (`Target_uri, params)) 243 + | "@status" -> Ok (`Derived (`Status, params)) 244 + | "@request-target" -> Ok (`Derived (`Request_target, params)) 245 + | _ -> Error ("Unknown derived component: " ^ name) 246 + else Ok (`Field (name, params)) 247 + end 248 + 249 + (* ========================================================================= *) 250 + (* Parameters *) 251 + (* ========================================================================= *) 252 + 253 + module Params = struct 254 + type t = { 255 + created : Ptime.t option; 256 + expires : Ptime.t option; 257 + nonce : string option; 258 + alg : Algorithm.t option; 259 + keyid : string option; 260 + tag : string option; 261 + } 262 + 263 + let empty = { 264 + created = None; 265 + expires = None; 266 + nonce = None; 267 + alg = None; 268 + keyid = None; 269 + tag = None; 270 + } 271 + 272 + let created time t = { t with created = Some time } 273 + let expires time t = { t with expires = Some time } 274 + let nonce value t = { t with nonce = Some value } 275 + let alg algorithm t = { t with alg = Some algorithm } 276 + let keyid id t = { t with keyid = Some id } 277 + let tag value t = { t with tag = Some value } 278 + 279 + let get_created t = t.created 280 + let get_expires t = t.expires 281 + let get_nonce t = t.nonce 282 + let get_alg t = t.alg 283 + let get_keyid t = t.keyid 284 + let get_tag t = t.tag 285 + 286 + let ptime_to_unix t = 287 + Int64.of_float (Ptime.to_float_s t) 288 + 289 + let to_sf_params t = 290 + List.filter_map Fun.id [ 291 + Option.map (fun c -> ("created", Sf.Integer (ptime_to_unix c))) t.created; 292 + Option.map (fun e -> ("expires", Sf.Integer (ptime_to_unix e))) t.expires; 293 + Option.map (fun n -> ("nonce", Sf.String n)) t.nonce; 294 + Option.map (fun a -> ("alg", Sf.String (Algorithm.to_string a))) t.alg; 295 + Option.map (fun k -> ("keyid", Sf.String k)) t.keyid; 296 + Option.map (fun tag -> ("tag", Sf.String tag)) t.tag; 297 + ] 298 + 299 + let of_sf_params params = 300 + let get_int k = 301 + match List.assoc_opt k params with 302 + | Some (Sf.Integer i) -> Some (Ptime.of_float_s (Int64.to_float i)) 303 + | _ -> None 304 + in 305 + let get_string k = 306 + match List.assoc_opt k params with 307 + | Some (Sf.String s) -> Some s 308 + | Some (Sf.Token s) -> Some s 309 + | _ -> None 310 + in 311 + { 312 + created = Option.join (get_int "created"); 313 + expires = Option.join (get_int "expires"); 314 + nonce = get_string "nonce"; 315 + alg = Option.bind (get_string "alg") Algorithm.of_string; 316 + keyid = get_string "keyid"; 317 + tag = get_string "tag"; 318 + } 319 + end 320 + 321 + (* ========================================================================= *) 322 + (* Key Material *) 323 + (* ========================================================================= *) 324 + 325 + module Key = struct 326 + type private_key = 327 + | Symmetric_priv of string 328 + | Ed25519_priv of string 329 + | P256_priv of Mirage_crypto_ec.P256.Dsa.priv 330 + | P384_priv of Mirage_crypto_ec.P384.Dsa.priv 331 + | Rsa_priv of Mirage_crypto_pk.Rsa.priv 332 + 333 + type public_key = 334 + | Symmetric_pub of string 335 + | Ed25519_pub of string 336 + | P256_pub of Mirage_crypto_ec.P256.Dsa.pub 337 + | P384_pub of Mirage_crypto_ec.P384.Dsa.pub 338 + | Rsa_pub of Mirage_crypto_pk.Rsa.pub 339 + 340 + type t = { 341 + priv : private_key option; 342 + pub : public_key option; 343 + } 344 + 345 + let symmetric secret = { 346 + priv = Some (Symmetric_priv secret); 347 + pub = Some (Symmetric_pub secret); 348 + } 349 + 350 + let ed25519 ~priv ~pub = { 351 + priv = Some (Ed25519_priv priv); 352 + pub = Some (Ed25519_pub pub); 353 + } 354 + 355 + let ed25519_priv priv = { 356 + priv = Some (Ed25519_priv priv); 357 + pub = None; 358 + } 359 + 360 + let ed25519_pub pub = { 361 + priv = None; 362 + pub = Some (Ed25519_pub pub); 363 + } 364 + 365 + let p256 ~priv = { 366 + priv = Some (P256_priv priv); 367 + pub = Some (P256_pub (Mirage_crypto_ec.P256.Dsa.pub_of_priv priv)); 368 + } 369 + 370 + let p256_pub pub = { 371 + priv = None; 372 + pub = Some (P256_pub pub); 373 + } 374 + 375 + let p384 ~priv = { 376 + priv = Some (P384_priv priv); 377 + pub = Some (P384_pub (Mirage_crypto_ec.P384.Dsa.pub_of_priv priv)); 378 + } 379 + 380 + let p384_pub pub = { 381 + priv = None; 382 + pub = Some (P384_pub pub); 383 + } 384 + 385 + let rsa ~priv = { 386 + priv = Some (Rsa_priv priv); 387 + pub = Some (Rsa_pub (Mirage_crypto_pk.Rsa.pub_of_priv priv)); 388 + } 389 + 390 + let rsa_pub pub = { 391 + priv = None; 392 + pub = Some (Rsa_pub pub); 393 + } 394 + 395 + let can_sign t = Option.is_some t.priv 396 + let can_verify t = Option.is_some t.pub 397 + 398 + let algorithm t : Algorithm.t option = 399 + match t.priv, t.pub with 400 + | Some (Symmetric_priv _), _ | _, Some (Symmetric_pub _) -> Some `Hmac_sha256 401 + | Some (Ed25519_priv _), _ | _, Some (Ed25519_pub _) -> Some `Ed25519 402 + | Some (P256_priv _), _ | _, Some (P256_pub _) -> Some `Ecdsa_p256_sha256 403 + | Some (P384_priv _), _ | _, Some (P384_pub _) -> Some `Ecdsa_p384_sha384 404 + | Some (Rsa_priv _), _ | _, Some (Rsa_pub _) -> Some `Rsa_pss_sha512 405 + | None, None -> None 406 + end 407 + 408 + (* ========================================================================= *) 409 + (* Context *) 410 + (* ========================================================================= *) 411 + 412 + module Context = struct 413 + type request_ctx = { 414 + method_ : Method.t; 415 + uri : Uri.t; 416 + headers : Headers.t; 417 + } 418 + 419 + type response_ctx = { 420 + status : int; 421 + headers : Headers.t; 422 + request : request_ctx option; 423 + } 424 + 425 + type t = [ 426 + | `Request of request_ctx 427 + | `Response of response_ctx 428 + ] 429 + 430 + let request ~method_ ~uri ~headers : t = 431 + `Request { method_; uri; headers } 432 + 433 + let response ~status ~headers ?request () : t = 434 + let req_ctx = match request with 435 + | Some (`Request r) -> Some r 436 + | Some (`Response _) -> None 437 + | None -> None 438 + in 439 + `Response { status; headers; request = req_ctx } 440 + end 441 + 442 + (* ========================================================================= *) 443 + (* Content-Digest *) 444 + (* ========================================================================= *) 445 + 446 + module Content_digest = struct 447 + type algorithm = [ `Sha256 | `Sha512 ] 448 + 449 + let compute ~algorithm ~body = 450 + match algorithm with 451 + | `Sha256 -> 452 + let hash = Digestif.SHA256.digest_string body in 453 + let b64 = Base64.encode_string (Digestif.SHA256.to_raw_string hash) in 454 + "sha-256=:" ^ b64 ^ ":" 455 + | `Sha512 -> 456 + let hash = Digestif.SHA512.digest_string body in 457 + let b64 = Base64.encode_string (Digestif.SHA512.to_raw_string hash) in 458 + "sha-512=:" ^ b64 ^ ":" 459 + 460 + let add ~algorithm ~body headers = 461 + let value = compute ~algorithm ~body in 462 + Headers.set `Content_digest value headers 463 + 464 + let verify ~header ~body = 465 + (* Extract base64 value from :base64: format *) 466 + let extract_b64 rest = 467 + if String.length rest >= 2 && rest.[0] = ':' then 468 + let end_idx = String.rindex rest ':' in 469 + if end_idx > 0 then Some (String.sub rest 1 (end_idx - 1)) 470 + else None 471 + else None 472 + in 473 + (* Verify a single digest entry *) 474 + let verify_one part = 475 + let part = String.trim part in 476 + let try_algorithm ~prefix ~hash_fn ~name = 477 + if String.length part > String.length prefix && 478 + String.sub part 0 (String.length prefix) = prefix then 479 + let rest = String.sub part (String.length prefix) 480 + (String.length part - String.length prefix) in 481 + match extract_b64 rest with 482 + | None -> Some (Error "Invalid Content-Digest format") 483 + | Some b64 -> 484 + match Base64.decode b64 with 485 + | Error _ -> Some (Error "Invalid base64 in Content-Digest") 486 + | Ok expected -> 487 + let actual = hash_fn body in 488 + if Eqaf.equal expected actual then Some (Ok ()) 489 + else Some (Error ("Content-Digest mismatch (" ^ name ^ ")")) 490 + else None 491 + in 492 + match try_algorithm ~prefix:"sha-256=" 493 + ~hash_fn:Digestif.SHA256.(fun s -> to_raw_string (digest_string s)) 494 + ~name:"sha-256" with 495 + | Some r -> r 496 + | None -> 497 + match try_algorithm ~prefix:"sha-512=" 498 + ~hash_fn:Digestif.SHA512.(fun s -> to_raw_string (digest_string s)) 499 + ~name:"sha-512" with 500 + | Some r -> r 501 + | None -> Error ("Unknown Content-Digest algorithm: " ^ part) 502 + in 503 + let parts = String.split_on_char ',' header in 504 + let results = List.map verify_one parts in 505 + if List.exists Result.is_ok results then Ok () 506 + else match results with 507 + | [] -> Error "Empty Content-Digest header" 508 + | err :: _ -> err 509 + end 510 + 511 + (* ========================================================================= *) 512 + (* Signature Base Construction *) 513 + (* ========================================================================= *) 514 + 515 + let resolve_component (ctx : Context.t) (component : Component.t) = 516 + let get_headers : Context.t -> Headers.t = function 517 + | `Request r -> r.Context.headers 518 + | `Response r -> r.Context.headers 519 + in 520 + let get_request_headers : Context.t -> Headers.t option = function 521 + | `Request r -> Some r.Context.headers 522 + | `Response (r : Context.response_ctx) -> Option.map (fun (req : Context.request_ctx) -> req.headers) r.request 523 + in 524 + match component with 525 + | `Derived (d, params) -> 526 + let has_req = List.mem `Req params in 527 + let target_ctx : Context.t = if has_req then 528 + match ctx with 529 + | `Response { request = Some req; _ } -> `Request req 530 + | _ -> ctx 531 + else ctx 532 + in 533 + (match d with 534 + | `Method -> 535 + (match target_ctx with 536 + | `Request r -> Ok (Method.to_string r.method_) 537 + | `Response _ -> Error "Cannot resolve @method on response") 538 + | `Authority -> 539 + (match target_ctx with 540 + | `Request r -> 541 + let host = Uri.host r.uri |> Option.value ~default:"" in 542 + let port = Uri.port r.uri in 543 + let authority = match port with 544 + | Some p when p <> 80 && p <> 443 -> host ^ ":" ^ string_of_int p 545 + | _ -> host 546 + in 547 + Ok (String.lowercase_ascii authority) 548 + | `Response _ -> Error "Cannot resolve @authority on response") 549 + | `Path -> 550 + (match target_ctx with 551 + | `Request r -> 552 + let path = Uri.path r.uri in 553 + Ok (if path = "" then "/" else path) 554 + | `Response _ -> Error "Cannot resolve @path on response") 555 + | `Query -> 556 + (match target_ctx with 557 + | `Request r -> 558 + let query = Uri.query r.uri in 559 + let encoded = Uri.encoded_of_query query in 560 + Ok ("?" ^ encoded) 561 + | `Response _ -> Error "Cannot resolve @query on response") 562 + | `Query_param name -> 563 + (match target_ctx with 564 + | `Request r -> 565 + (match Uri.get_query_param r.uri name with 566 + | Some v -> Ok v 567 + | None -> Error ("Missing query parameter: " ^ name)) 568 + | `Response _ -> Error "Cannot resolve @query-param on response") 569 + | `Target_uri -> 570 + (match target_ctx with 571 + | `Request r -> Ok (Uri.to_string r.uri) 572 + | `Response _ -> Error "Cannot resolve @target-uri on response") 573 + | `Status -> 574 + (match ctx with 575 + | `Response r -> Ok (string_of_int r.status) 576 + | `Request _ -> Error "Cannot resolve @status on request") 577 + | `Request_target -> 578 + (match target_ctx with 579 + | `Request r -> 580 + let path = Uri.path r.uri in 581 + let path = if path = "" then "/" else path in 582 + let query = Uri.query r.uri in 583 + if query = [] then Ok path 584 + else Ok (path ^ "?" ^ Uri.encoded_of_query query) 585 + | `Response _ -> Error "Cannot resolve @request-target on response")) 586 + | `Field (name, params) -> 587 + let has_req = List.mem `Req params in 588 + let headers = if has_req then get_request_headers ctx else Some (get_headers ctx) in 589 + match headers with 590 + | None -> Error ("Cannot resolve request-bound field: " ^ name) 591 + | Some hdrs -> 592 + let name_typed = Header_name.of_string name in 593 + match Headers.get name_typed hdrs with 594 + | None -> Error ("Missing header: " ^ name) 595 + | Some value -> 596 + (* Apply bs (byte sequence) parameter if present *) 597 + if List.mem `Bs params then 598 + Ok (":" ^ Base64.encode_string value ^ ":") 599 + else 600 + Ok value 601 + 602 + let build_signature_base ~(components : Component.t list) ~(params : Params.t) (ctx : Context.t) = 603 + let component_lines = List.map (fun c -> 604 + match resolve_component ctx c with 605 + | Error e -> Error e 606 + | Ok value -> 607 + let id = Component.to_identifier c in 608 + Ok (id ^ ": " ^ value) 609 + ) components in 610 + (* Check for errors *) 611 + let errors = List.filter_map (function Error e -> Some e | Ok _ -> None) component_lines in 612 + if errors <> [] then Error (List.hd errors) 613 + else 614 + let lines = List.filter_map (function Ok l -> Some l | Error _ -> None) component_lines in 615 + (* Build @signature-params line *) 616 + let component_items = List.map Component.to_sf_item components in 617 + let sf_params = Params.to_sf_params params in 618 + let sig_params_il : Sf.inner_list = (component_items, sf_params) in 619 + let sig_params_str = Sf.inner_list_to_string sig_params_il in 620 + let sig_params_line = "\"@signature-params\": " ^ sig_params_str in 621 + let base = String.concat "\n" (lines @ [sig_params_line]) in 622 + Log.debug (fun m -> m "Signature base:\n%s" base); 623 + Ok base 624 + 625 + (* ========================================================================= *) 626 + (* Cryptographic Operations *) 627 + (* ========================================================================= *) 628 + 629 + (* RSA-PSS requires a functor instantiation *) 630 + module Rsa_pss_sha512 = Mirage_crypto_pk.Rsa.PSS(Digestif.SHA512) 631 + 632 + let sign_bytes ~(alg : Algorithm.t) ~(key : Key.t) (data : string) = 633 + match alg, key.priv with 634 + | `Hmac_sha256, Some (Key.Symmetric_priv secret) -> 635 + let mac = Digestif.SHA256.hmac_string ~key:secret data in 636 + Ok (Digestif.SHA256.to_raw_string mac) 637 + 638 + | `Ed25519, Some (Key.Ed25519_priv priv) -> 639 + (match Mirage_crypto_ec.Ed25519.priv_of_octets priv with 640 + | Error _ -> Error "Invalid Ed25519 private key" 641 + | Ok priv_key -> 642 + let sig_ = Mirage_crypto_ec.Ed25519.sign ~key:priv_key data in 643 + Ok sig_) 644 + 645 + | `Ecdsa_p256_sha256, Some (Key.P256_priv priv) -> 646 + let hash = Digestif.SHA256.(to_raw_string (digest_string data)) in 647 + let (r, s) = Mirage_crypto_ec.P256.Dsa.sign ~key:priv hash in 648 + (* Concatenate r and s for raw signature format *) 649 + Ok (r ^ s) 650 + 651 + | `Ecdsa_p384_sha384, Some (Key.P384_priv priv) -> 652 + let hash = Digestif.SHA384.(to_raw_string (digest_string data)) in 653 + let (r, s) = Mirage_crypto_ec.P384.Dsa.sign ~key:priv hash in 654 + Ok (r ^ s) 655 + 656 + | `Rsa_pss_sha512, Some (Key.Rsa_priv priv) -> 657 + let hash = Digestif.SHA512.(to_raw_string (digest_string data)) in 658 + let sig_ = Rsa_pss_sha512.sign ~key:priv (`Digest hash) in 659 + Ok sig_ 660 + 661 + | `Rsa_v1_5_sha256, Some (Key.Rsa_priv priv) -> 662 + let hash = Digestif.SHA256.(to_raw_string (digest_string data)) in 663 + let sig_ = Mirage_crypto_pk.Rsa.PKCS1.sign ~hash:`SHA256 ~key:priv (`Digest hash) in 664 + Ok sig_ 665 + 666 + | alg, None -> Error ("Missing private key for " ^ Algorithm.to_string alg) 667 + | _, _ -> Error "Key type mismatch for algorithm" 668 + 669 + let verify_bytes ~(alg : Algorithm.t) ~(key : Key.t) ~(signature : string) (data : string) = 670 + match alg, key.pub with 671 + | `Hmac_sha256, Some (Key.Symmetric_pub secret) -> 672 + let expected = Digestif.SHA256.hmac_string ~key:secret data in 673 + let expected_str = Digestif.SHA256.to_raw_string expected in 674 + if Eqaf.equal signature expected_str then Ok () 675 + else Error "HMAC signature mismatch" 676 + 677 + | `Ed25519, Some (Key.Ed25519_pub pub) -> 678 + (match Mirage_crypto_ec.Ed25519.pub_of_octets pub with 679 + | Error _ -> Error "Invalid Ed25519 public key" 680 + | Ok pub_key -> 681 + let valid = Mirage_crypto_ec.Ed25519.verify ~key:pub_key signature ~msg:data in 682 + if valid then Ok () else Error "Ed25519 signature verification failed") 683 + 684 + | `Ecdsa_p256_sha256, Some (Key.P256_pub pub) -> 685 + let hash = Digestif.SHA256.(to_raw_string (digest_string data)) in 686 + (* Split signature into r and s (each 32 bytes for P-256) *) 687 + if String.length signature <> 64 then Error "Invalid P-256 signature length" 688 + else 689 + let r = String.sub signature 0 32 in 690 + let s = String.sub signature 32 32 in 691 + let valid = Mirage_crypto_ec.P256.Dsa.verify ~key:pub (r, s) hash in 692 + if valid then Ok () else Error "ECDSA P-256 signature verification failed" 693 + 694 + | `Ecdsa_p384_sha384, Some (Key.P384_pub pub) -> 695 + let hash = Digestif.SHA384.(to_raw_string (digest_string data)) in 696 + (* Split signature into r and s (each 48 bytes for P-384) *) 697 + if String.length signature <> 96 then Error "Invalid P-384 signature length" 698 + else 699 + let r = String.sub signature 0 48 in 700 + let s = String.sub signature 48 48 in 701 + let valid = Mirage_crypto_ec.P384.Dsa.verify ~key:pub (r, s) hash in 702 + if valid then Ok () else Error "ECDSA P-384 signature verification failed" 703 + 704 + | `Rsa_pss_sha512, Some (Key.Rsa_pub pub) -> 705 + let hash = Digestif.SHA512.(to_raw_string (digest_string data)) in 706 + let valid = Rsa_pss_sha512.verify ~key:pub ~signature (`Digest hash) in 707 + if valid then Ok () else Error "RSA-PSS signature verification failed" 708 + 709 + | `Rsa_v1_5_sha256, Some (Key.Rsa_pub pub) -> 710 + let hash = Digestif.SHA256.(to_raw_string (digest_string data)) in 711 + let hashp = function `SHA256 -> true | _ -> false in 712 + let valid = Mirage_crypto_pk.Rsa.PKCS1.verify ~hashp ~key:pub 713 + ~signature (`Digest hash) in 714 + if valid then Ok () else Error "RSA-PKCS1 signature verification failed" 715 + 716 + | alg, None -> Error ("Missing public key for " ^ Algorithm.to_string alg) 717 + | _, _ -> Error "Key type mismatch for algorithm" 718 + 719 + (* ========================================================================= *) 720 + (* Configuration *) 721 + (* ========================================================================= *) 722 + 723 + type config = { 724 + key : Key.t; 725 + keyid : string option; 726 + components : Component.t list; 727 + tag : string option; 728 + include_created : bool; 729 + label : string; 730 + } 731 + 732 + let default_components = [ 733 + Component.method_; 734 + Component.authority; 735 + Component.path; 736 + ] 737 + 738 + let config ~key ?keyid ?(components = default_components) ?tag 739 + ?(include_created = true) ?(label = "sig1") () = 740 + { key; keyid; components; tag; include_created; label } 741 + 742 + (* ========================================================================= *) 743 + (* Signing *) 744 + (* ========================================================================= *) 745 + 746 + type sign_error = [ 747 + | `Key_algorithm_mismatch of string 748 + | `Missing_private_key 749 + | `Component_resolution_error of string 750 + | `Crypto_error of string 751 + ] 752 + 753 + let sign_error_to_string : sign_error -> string = function 754 + | `Key_algorithm_mismatch s -> "Key/algorithm mismatch: " ^ s 755 + | `Missing_private_key -> "Missing private key" 756 + | `Component_resolution_error s -> "Component resolution error: " ^ s 757 + | `Crypto_error s -> "Cryptographic error: " ^ s 758 + 759 + let sign ~(config : config) ~(context : Context.t) ~(headers : Headers.t) = 760 + if not (Key.can_sign config.key) then Error `Missing_private_key 761 + else 762 + let alg = Key.algorithm config.key |> Option.value ~default:`Ed25519 in 763 + let params = 764 + Params.empty 765 + |> (fun p -> if config.include_created then Params.created (Ptime_clock.now ()) p else p) 766 + |> Option.fold ~none:Fun.id ~some:Params.keyid config.keyid 767 + |> Option.fold ~none:Fun.id ~some:Params.tag config.tag 768 + |> Params.alg alg 769 + in 770 + let* base = Result.map_error (fun e -> `Component_resolution_error e) 771 + (build_signature_base ~components:config.components ~params context) in 772 + let* sig_bytes = Result.map_error (fun e -> `Crypto_error e) 773 + (sign_bytes ~alg ~key:config.key base) in 774 + (* Build headers *) 775 + let component_items = List.map Component.to_sf_item config.components in 776 + let sf_params = Params.to_sf_params params in 777 + let sig_input_il : Sf.inner_list = (component_items, sf_params) in 778 + let sig_input_header = Sf.dictionary_to_string [(config.label, Sf.Inner_list sig_input_il)] in 779 + let sig_header = Sf.dictionary_to_string [(config.label, Sf.Item (Sf.Byte_seq sig_bytes, []))] in 780 + Log.debug (fun m -> m "Signature-Input: %s" sig_input_header); 781 + Log.debug (fun m -> m "Signature: %s" sig_header); 782 + Ok (headers 783 + |> Headers.set `Signature_input sig_input_header 784 + |> Headers.set `Signature sig_header) 785 + 786 + let sign_with_digest ~(config : config) ~(context : Context.t) ~(headers : Headers.t) 787 + ~(body : string) ~(digest_algorithm : Content_digest.algorithm) = 788 + (* Add Content-Digest header *) 789 + let headers = Content_digest.add ~algorithm:digest_algorithm ~body headers in 790 + (* Add content-digest to components if not already present *) 791 + let has_content_digest = List.exists (function 792 + | `Field ("content-digest", _) -> true 793 + | _ -> false 794 + ) config.components in 795 + let components = if has_content_digest then config.components 796 + else config.components @ [Component.content_digest] 797 + in 798 + let config = { config with components } in 799 + sign ~config ~context ~headers 800 + 801 + (* ========================================================================= *) 802 + (* Verification *) 803 + (* ========================================================================= *) 804 + 805 + type verify_error = [ 806 + | `Missing_signature_header 807 + | `Missing_signature_input_header 808 + | `Invalid_signature_input of string 809 + | `Signature_label_not_found of string 810 + | `Key_algorithm_mismatch of string 811 + | `Missing_public_key 812 + | `Component_resolution_error of string 813 + | `Signature_mismatch 814 + | `Signature_expired 815 + | `Required_component_missing of string 816 + | `Crypto_error of string 817 + ] 818 + 819 + let verify_error_to_string : verify_error -> string = function 820 + | `Missing_signature_header -> "Missing Signature header" 821 + | `Missing_signature_input_header -> "Missing Signature-Input header" 822 + | `Invalid_signature_input s -> "Invalid Signature-Input: " ^ s 823 + | `Signature_label_not_found s -> "Signature label not found: " ^ s 824 + | `Key_algorithm_mismatch s -> "Key/algorithm mismatch: " ^ s 825 + | `Missing_public_key -> "Missing public key" 826 + | `Component_resolution_error s -> "Component resolution error: " ^ s 827 + | `Signature_mismatch -> "Signature verification failed" 828 + | `Signature_expired -> "Signature has expired" 829 + | `Required_component_missing s -> "Required component missing from signature: " ^ s 830 + | `Crypto_error s -> "Cryptographic error: " ^ s 831 + 832 + type verify_result = { 833 + label : string; 834 + keyid : string option; 835 + created : Ptime.t option; 836 + expires : Ptime.t option; 837 + verified_components : Component.t list; 838 + } 839 + 840 + let verify ~(key : Key.t) ?label ?max_age ?required_components 841 + ~(context : Context.t) ~(headers : Headers.t) () = 842 + (* Helper to require a value or return an error *) 843 + let require opt err = match opt with Some x -> Ok x | None -> Error err in 844 + (* Parse components from SF items *) 845 + let parse_components items = 846 + List.filter_map (fun (item, item_params) -> 847 + match item with 848 + | Sf.String s -> 849 + let params_str = String.concat "" (List.map (fun (k, v) -> 850 + match v with 851 + | Sf.Boolean true -> ";" ^ k 852 + | Sf.String s -> ";" ^ k ^ "=\"" ^ s ^ "\"" 853 + | _ -> ";" ^ k ^ "=" ^ Sf.item_to_string v 854 + ) item_params) in 855 + Result.to_option (Component.of_identifier (s ^ params_str)) 856 + | _ -> None 857 + ) items 858 + in 859 + (* Check required components are present *) 860 + let check_required components = 861 + match required_components with 862 + | None -> Ok () 863 + | Some req -> 864 + let missing = List.find_opt (fun rc -> 865 + not (List.exists (fun c -> 866 + Component.to_identifier c = Component.to_identifier rc 867 + ) components) 868 + ) req in 869 + match missing with 870 + | Some m -> Error (`Required_component_missing (Component.to_identifier m)) 871 + | None -> Ok () 872 + in 873 + (* Check signature hasn't expired *) 874 + let check_expiration sig_params = 875 + match sig_params.Params.expires with 876 + | Some exp when Ptime.is_earlier exp ~than:(Ptime_clock.now ()) -> 877 + Error `Signature_expired 878 + | _ -> Ok () 879 + in 880 + (* Check signature isn't too old *) 881 + let check_max_age sig_params = 882 + match max_age, sig_params.Params.created with 883 + | Some age, Some created -> 884 + let now = Ptime_clock.now () in 885 + (match Ptime.add_span created age with 886 + | Some limit when Ptime.is_earlier limit ~than:now -> Error `Signature_expired 887 + | _ -> Ok ()) 888 + | _ -> Ok () 889 + in 890 + (* Main verification flow using let* *) 891 + if not (Key.can_verify key) then Error `Missing_public_key 892 + else 893 + let* sig_header = require (Headers.get `Signature headers) `Missing_signature_header in 894 + let* sig_input_header = require (Headers.get `Signature_input headers) `Missing_signature_input_header in 895 + let* sig_dict = Result.map_error (fun e -> `Invalid_signature_input ("Signature: " ^ e)) 896 + (Sf.parse_dictionary sig_header) in 897 + let* sig_input_dict = Result.map_error (fun e -> `Invalid_signature_input ("Signature-Input: " ^ e)) 898 + (Sf.parse_dictionary sig_input_header) in 899 + let target_label = label |> Option.value ~default:( 900 + match sig_dict with (l, _) :: _ -> l | [] -> "sig1" 901 + ) in 902 + let* sig_member = require (List.assoc_opt target_label sig_dict) 903 + (`Signature_label_not_found target_label) in 904 + let* sig_bytes = match sig_member with 905 + | Sf.Item (Sf.Byte_seq s, _) -> Ok s 906 + | _ -> Error (`Invalid_signature_input "Expected byte sequence") 907 + in 908 + let* input_member = require (List.assoc_opt target_label sig_input_dict) 909 + (`Signature_label_not_found target_label) in 910 + let* (items, params) = match input_member with 911 + | Sf.Inner_list il -> Ok il 912 + | _ -> Error (`Invalid_signature_input "Expected inner list") 913 + in 914 + let components = parse_components items in 915 + let sig_params = Params.of_sf_params params in 916 + let alg = match sig_params.Params.alg, Key.algorithm key with 917 + | Some a, _ -> a 918 + | None, Some a -> a 919 + | None, None -> `Ed25519 920 + in 921 + let* () = check_required components in 922 + let* () = check_expiration sig_params in 923 + let* () = check_max_age sig_params in 924 + let* base = Result.map_error (fun e -> `Component_resolution_error e) 925 + (build_signature_base ~components ~params:sig_params context) in 926 + let* () = Result.map_error (fun e -> `Crypto_error e) 927 + (verify_bytes ~alg ~key ~signature:sig_bytes base) in 928 + Ok { 929 + label = target_label; 930 + keyid = sig_params.Params.keyid; 931 + created = sig_params.Params.created; 932 + expires = sig_params.Params.expires; 933 + verified_components = components; 934 + } 935 + 936 + let verify_all ~key_resolver ?max_age ~(context : Context.t) ~(headers : Headers.t) () = 937 + match Headers.get `Signature headers, Headers.get `Signature_input headers with 938 + | None, _ -> Error `Missing_signature_header 939 + | _, None -> Error `Missing_signature_input_header 940 + | Some sig_header, Some sig_input_header -> 941 + match Sf.parse_dictionary sig_header, Sf.parse_dictionary sig_input_header with 942 + | Error e, _ -> Error (`Invalid_signature_input e) 943 + | _, Error e -> Error (`Invalid_signature_input e) 944 + | Ok sig_dict, Ok sig_input_dict -> 945 + let results = List.filter_map (fun (label, _) -> 946 + (* Get keyid from signature-input *) 947 + let keyid = match List.assoc_opt label sig_input_dict with 948 + | Some (Sf.Inner_list (_, params)) -> 949 + (match List.assoc_opt "keyid" params with 950 + | Some (Sf.String k) -> Some k 951 + | _ -> None) 952 + | _ -> None 953 + in 954 + match keyid with 955 + | None -> None 956 + | Some kid -> 957 + match key_resolver kid with 958 + | None -> None 959 + | Some key -> 960 + match verify ~key ~label ?max_age ~context ~headers () with 961 + | Ok r -> Some (Ok r) 962 + | Error e -> Some (Error e) 963 + ) sig_dict in 964 + let successes = List.filter_map (function Ok r -> Some r | _ -> None) results in 965 + let errors = List.filter_map (function Error e -> Some e | _ -> None) results in 966 + if successes <> [] then Ok successes 967 + else match errors with 968 + | e :: _ -> Error e 969 + | [] -> Error (`Signature_label_not_found "no signatures found")
+453
lib/signature.mli
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** RFC 9421 HTTP Message Signatures 7 + 8 + This module implements {{:https://datatracker.ietf.org/doc/html/rfc9421}RFC 9421} 9 + HTTP Message Signatures, enabling cryptographic signing and verification of 10 + HTTP request and response messages. 11 + 12 + {2 Overview} 13 + 14 + HTTP Message Signatures provide a mechanism to create and verify cryptographic 15 + signatures on HTTP messages. The signature covers specific message components 16 + (headers, derived values) and metadata (timestamps, key identifiers). 17 + 18 + {2 Example Usage} 19 + 20 + {[ 21 + (* Create a signing configuration *) 22 + let key = Signature.Key.ed25519 23 + ~priv:(Base64.decode_exn "private-key-bytes") 24 + ~pub:(Base64.decode_exn "public-key-bytes") in 25 + let config = Signature.config 26 + ~key 27 + ~keyid:"my-key-id" 28 + ~components:[Signature.Component.method_; Signature.Component.authority] 29 + () in 30 + 31 + (* Sign headers *) 32 + let ctx = Signature.Context.request ~method_:`GET ~uri:(Uri.of_string "https://example.com/") ~headers in 33 + let signed_headers = Signature.sign ~config ~context:ctx ~headers |> Result.get_ok in 34 + ]} 35 + 36 + {2 References} 37 + {ul 38 + {- {{:https://datatracker.ietf.org/doc/html/rfc9421}RFC 9421} - HTTP Message Signatures} 39 + {- {{:https://datatracker.ietf.org/doc/html/rfc9530}RFC 9530} - Digest Fields}} *) 40 + 41 + (** {1 Algorithms} *) 42 + 43 + module Algorithm : sig 44 + (** Signature algorithms per 45 + {{:https://datatracker.ietf.org/doc/html/rfc9421#section-3.3}RFC 9421 Section 3.3}. *) 46 + 47 + type t = [ 48 + | `Rsa_pss_sha512 (** RSASSA-PSS using SHA-512 *) 49 + | `Rsa_v1_5_sha256 (** RSASSA-PKCS1-v1_5 using SHA-256 *) 50 + | `Hmac_sha256 (** HMAC using SHA-256 *) 51 + | `Ecdsa_p256_sha256 (** ECDSA using P-256 curve with SHA-256 *) 52 + | `Ecdsa_p384_sha384 (** ECDSA using P-384 curve with SHA-384 *) 53 + | `Ed25519 (** EdDSA using curve25519 *) 54 + ] 55 + 56 + val to_string : t -> string 57 + (** [to_string alg] returns the algorithm identifier string. *) 58 + 59 + val of_string : string -> t option 60 + (** [of_string s] parses an algorithm identifier string. *) 61 + 62 + val of_string_exn : string -> t 63 + (** [of_string_exn s] parses an algorithm identifier or raises [Invalid_argument]. *) 64 + end 65 + 66 + (** {1 Message Components} *) 67 + 68 + module Component : sig 69 + (** Message components that can be included in signatures per 70 + {{:https://datatracker.ietf.org/doc/html/rfc9421#section-2}RFC 9421 Section 2}. *) 71 + 72 + (** {2 Derived Components} 73 + 74 + Derived components are computed from message context, not raw headers. 75 + Per {{:https://datatracker.ietf.org/doc/html/rfc9421#section-2.2}Section 2.2}. *) 76 + 77 + type derived = [ 78 + | `Method (** [@method] - HTTP request method *) 79 + | `Authority (** [@authority] - Target host (host:port) *) 80 + | `Path (** [@path] - Request target path *) 81 + | `Query (** [@query] - Query string with leading [?] *) 82 + | `Query_param of string (** [@query-param;name="..."] - Individual query parameter *) 83 + | `Target_uri (** [@target-uri] - Full target URI *) 84 + | `Status (** [@status] - Response status code (responses only) *) 85 + | `Request_target (** [@request-target] - Deprecated form *) 86 + ] 87 + 88 + (** {2 Component Parameters} 89 + 90 + Parameters that modify component behavior per 91 + {{:https://datatracker.ietf.org/doc/html/rfc9421#section-2.1}Section 2.1}. *) 92 + 93 + type param = [ 94 + | `Sf (** Strict structured field serialization *) 95 + | `Key of string (** Dictionary member selection *) 96 + | `Bs (** Byte sequence wrapping *) 97 + | `Tr (** Trailer field designation *) 98 + | `Req (** Request-bound component (for response signatures) *) 99 + ] 100 + 101 + (** A component identifier, either derived or a header field. *) 102 + type t = [ 103 + | `Derived of derived * param list 104 + | `Field of string * param list 105 + ] 106 + 107 + (** {2 Constructors} *) 108 + 109 + val method_ : t 110 + (** The [@method] derived component. *) 111 + 112 + val authority : t 113 + (** The [@authority] derived component. *) 114 + 115 + val path : t 116 + (** The [@path] derived component. *) 117 + 118 + val query : t 119 + (** The [@query] derived component. *) 120 + 121 + val query_param : string -> t 122 + (** [query_param name] creates a [@query-param;name="..."] component. *) 123 + 124 + val target_uri : t 125 + (** The [@target-uri] derived component. *) 126 + 127 + val status : t 128 + (** The [@status] derived component (for responses). *) 129 + 130 + val request_target : t 131 + (** The [@request-target] derived component (deprecated). *) 132 + 133 + val field : string -> t 134 + (** [field name] creates a header field component (lowercased). *) 135 + 136 + val field_sf : string -> t 137 + (** [field_sf name] creates a header field with strict structured field serialization. *) 138 + 139 + val field_bs : string -> t 140 + (** [field_bs name] creates a header field with byte sequence wrapping. *) 141 + 142 + val field_key : string -> key:string -> t 143 + (** [field_key name ~key] creates a header field selecting a dictionary member. *) 144 + 145 + val field_req : string -> t 146 + (** [field_req name] creates a request-bound header field (for responses). *) 147 + 148 + (** {2 Common Fields} *) 149 + 150 + val content_type : t 151 + (** The [content-type] header field. *) 152 + 153 + val content_length : t 154 + (** The [content-length] header field. *) 155 + 156 + val content_digest : t 157 + (** The [content-digest] header field (RFC 9530). *) 158 + 159 + val date : t 160 + (** The [date] header field. *) 161 + 162 + val host : t 163 + (** The [host] header field. *) 164 + 165 + (** {2 Serialization} *) 166 + 167 + val to_identifier : t -> string 168 + (** [to_identifier c] returns the component identifier string. *) 169 + 170 + val of_identifier : string -> (t, string) result 171 + (** [of_identifier s] parses a component identifier. *) 172 + end 173 + 174 + (** {1 Signature Parameters} *) 175 + 176 + module Params : sig 177 + (** Signature parameters per 178 + {{:https://datatracker.ietf.org/doc/html/rfc9421#section-2.3}RFC 9421 Section 2.3}. *) 179 + 180 + type t 181 + (** Signature parameters. *) 182 + 183 + val empty : t 184 + (** Empty parameters. *) 185 + 186 + val created : Ptime.t -> t -> t 187 + (** [created time params] sets the creation timestamp. *) 188 + 189 + val expires : Ptime.t -> t -> t 190 + (** [expires time params] sets the expiration timestamp. *) 191 + 192 + val nonce : string -> t -> t 193 + (** [nonce value params] sets a unique nonce. *) 194 + 195 + val alg : Algorithm.t -> t -> t 196 + (** [alg algorithm params] sets the algorithm identifier. *) 197 + 198 + val keyid : string -> t -> t 199 + (** [keyid id params] sets the key identifier. *) 200 + 201 + val tag : string -> t -> t 202 + (** [tag value params] sets an application-specific tag. *) 203 + 204 + val get_created : t -> Ptime.t option 205 + val get_expires : t -> Ptime.t option 206 + val get_nonce : t -> string option 207 + val get_alg : t -> Algorithm.t option 208 + val get_keyid : t -> string option 209 + val get_tag : t -> string option 210 + end 211 + 212 + (** {1 Key Material} *) 213 + 214 + module Key : sig 215 + (** Cryptographic key material for signing and verification. *) 216 + 217 + type t 218 + (** A key (may contain private key, public key, or both). *) 219 + 220 + (** {2 Symmetric Keys} *) 221 + 222 + val symmetric : string -> t 223 + (** [symmetric secret] creates a symmetric key for HMAC algorithms. *) 224 + 225 + (** {2 Ed25519 Keys} *) 226 + 227 + val ed25519 : priv:string -> pub:string -> t 228 + (** [ed25519 ~priv ~pub] creates an Ed25519 key pair. 229 + Both [priv] and [pub] should be raw 32-byte keys. *) 230 + 231 + val ed25519_priv : string -> t 232 + (** [ed25519_priv priv] creates an Ed25519 private key (for signing only). *) 233 + 234 + val ed25519_pub : string -> t 235 + (** [ed25519_pub pub] creates an Ed25519 public key (for verification only). *) 236 + 237 + (** {2 ECDSA P-256 Keys} *) 238 + 239 + val p256 : priv:Mirage_crypto_ec.P256.Dsa.priv -> t 240 + (** [p256 ~priv] creates a P-256 key from the private key 241 + (public key derived automatically). *) 242 + 243 + val p256_pub : Mirage_crypto_ec.P256.Dsa.pub -> t 244 + (** [p256_pub pub] creates a P-256 public key (for verification only). *) 245 + 246 + (** {2 ECDSA P-384 Keys} *) 247 + 248 + val p384 : priv:Mirage_crypto_ec.P384.Dsa.priv -> t 249 + (** [p384 ~priv] creates a P-384 key from the private key. *) 250 + 251 + val p384_pub : Mirage_crypto_ec.P384.Dsa.pub -> t 252 + (** [p384_pub pub] creates a P-384 public key (for verification only). *) 253 + 254 + (** {2 RSA Keys} *) 255 + 256 + val rsa : priv:Mirage_crypto_pk.Rsa.priv -> t 257 + (** [rsa ~priv] creates an RSA key from the private key. *) 258 + 259 + val rsa_pub : Mirage_crypto_pk.Rsa.pub -> t 260 + (** [rsa_pub pub] creates an RSA public key (for verification only). *) 261 + 262 + (** {2 Key Properties} *) 263 + 264 + val can_sign : t -> bool 265 + (** [can_sign key] returns [true] if the key can be used for signing. *) 266 + 267 + val can_verify : t -> bool 268 + (** [can_verify key] returns [true] if the key can be used for verification. *) 269 + 270 + val algorithm : t -> Algorithm.t option 271 + (** [algorithm key] returns the algorithm associated with the key, if known. *) 272 + end 273 + 274 + (** {1 Signing Context} *) 275 + 276 + module Context : sig 277 + (** Context for resolving message components. *) 278 + 279 + type request_ctx = { 280 + method_ : Method.t; 281 + uri : Uri.t; 282 + headers : Headers.t; 283 + } 284 + 285 + type response_ctx = { 286 + status : int; 287 + headers : Headers.t; 288 + request : request_ctx option; 289 + } 290 + 291 + type t = [ 292 + | `Request of request_ctx 293 + | `Response of response_ctx 294 + ] 295 + (** Message context (request or response). *) 296 + 297 + val request : 298 + method_:Method.t -> 299 + uri:Uri.t -> 300 + headers:Headers.t -> 301 + t 302 + (** [request ~method_ ~uri ~headers] creates a request context. *) 303 + 304 + val response : 305 + status:int -> 306 + headers:Headers.t -> 307 + ?request:t -> 308 + unit -> 309 + t 310 + (** [response ~status ~headers ?request ()] creates a response context. 311 + The optional [request] context is used for request-bound components. *) 312 + end 313 + 314 + (** {1 Content-Digest} *) 315 + 316 + module Content_digest : sig 317 + (** RFC 9530 Content-Digest support. 318 + 319 + {{:https://datatracker.ietf.org/doc/html/rfc9530}RFC 9530} defines the 320 + Content-Digest header for message body integrity. *) 321 + 322 + type algorithm = [ `Sha256 | `Sha512 ] 323 + 324 + val compute : algorithm:algorithm -> body:string -> string 325 + (** [compute ~algorithm ~body] returns the Content-Digest header value. *) 326 + 327 + val add : algorithm:algorithm -> body:string -> Headers.t -> Headers.t 328 + (** [add ~algorithm ~body headers] adds a Content-Digest header. *) 329 + 330 + val verify : header:string -> body:string -> (unit, string) result 331 + (** [verify ~header ~body] verifies a Content-Digest header value. *) 332 + end 333 + 334 + (** {1 Signing Configuration} *) 335 + 336 + type config 337 + (** Configuration for signing requests. *) 338 + 339 + val config : 340 + key:Key.t -> 341 + ?keyid:string -> 342 + ?components:Component.t list -> 343 + ?tag:string -> 344 + ?include_created:bool -> 345 + ?label:string -> 346 + unit -> 347 + config 348 + (** [config ~key ?keyid ?components ?tag ?include_created ?label ()] 349 + creates a signing configuration. 350 + 351 + @param key The signing key. 352 + @param keyid Key identifier (included in signature parameters). 353 + @param components Components to sign. Default: [[\@method; \@authority; \@path]]. 354 + @param tag Application-specific tag. 355 + @param include_created Include creation timestamp. Default: [true]. 356 + @param label Signature label for dictionary key. Default: ["sig1"]. *) 357 + 358 + val default_components : Component.t list 359 + (** Default components to sign: [[\@method; \@authority; \@path]]. *) 360 + 361 + (** {1 Signing} *) 362 + 363 + type sign_error = [ 364 + | `Key_algorithm_mismatch of string 365 + | `Missing_private_key 366 + | `Component_resolution_error of string 367 + | `Crypto_error of string 368 + ] 369 + 370 + val sign_error_to_string : sign_error -> string 371 + (** [sign_error_to_string err] returns a human-readable error message. *) 372 + 373 + val sign : 374 + config:config -> 375 + context:Context.t -> 376 + headers:Headers.t -> 377 + (Headers.t, sign_error) result 378 + (** [sign ~config ~context ~headers] signs the message and returns 379 + headers with [Signature-Input] and [Signature] headers added. *) 380 + 381 + val sign_with_digest : 382 + config:config -> 383 + context:Context.t -> 384 + headers:Headers.t -> 385 + body:string -> 386 + digest_algorithm:Content_digest.algorithm -> 387 + (Headers.t, sign_error) result 388 + (** [sign_with_digest ~config ~context ~headers ~body ~digest_algorithm] 389 + computes Content-Digest, adds it to headers, and signs. 390 + The [content-digest] component is automatically added to signed components. *) 391 + 392 + (** {1 Verification} *) 393 + 394 + type verify_error = [ 395 + | `Missing_signature_header 396 + | `Missing_signature_input_header 397 + | `Invalid_signature_input of string 398 + | `Signature_label_not_found of string 399 + | `Key_algorithm_mismatch of string 400 + | `Missing_public_key 401 + | `Component_resolution_error of string 402 + | `Signature_mismatch 403 + | `Signature_expired 404 + | `Required_component_missing of string 405 + | `Crypto_error of string 406 + ] 407 + 408 + val verify_error_to_string : verify_error -> string 409 + (** [verify_error_to_string err] returns a human-readable error message. *) 410 + 411 + type verify_result = { 412 + label : string; 413 + keyid : string option; 414 + created : Ptime.t option; 415 + expires : Ptime.t option; 416 + verified_components : Component.t list; 417 + } 418 + (** Successful verification result. *) 419 + 420 + val verify : 421 + key:Key.t -> 422 + ?label:string -> 423 + ?max_age:Ptime.Span.t -> 424 + ?required_components:Component.t list -> 425 + context:Context.t -> 426 + headers:Headers.t -> 427 + unit -> 428 + (verify_result, verify_error) result 429 + (** [verify ~key ?label ?max_age ?required_components ~context ~headers] 430 + verifies a signature on the message. 431 + 432 + @param key The verification key. 433 + @param label Signature label to verify. Default: first signature found. 434 + @param max_age Maximum age of signature. If [created] is present and older 435 + than [max_age], verification fails. 436 + @param required_components Components that must be signed. Verification 437 + fails if any are missing from the signature. 438 + @param context Message context for component resolution. 439 + @param headers Headers containing [Signature] and [Signature-Input]. *) 440 + 441 + val verify_all : 442 + key_resolver:(string -> Key.t option) -> 443 + ?max_age:Ptime.Span.t -> 444 + context:Context.t -> 445 + headers:Headers.t -> 446 + unit -> 447 + (verify_result list, verify_error) result 448 + (** [verify_all ~key_resolver ?max_age ~context ~headers] verifies all 449 + signatures on a message. 450 + 451 + @param key_resolver Function to resolve keys by [keyid]. Called for 452 + each signature with its [keyid] parameter. 453 + @param max_age Maximum age for all signatures. *)
+3
requests.opam
··· 17 17 "ca-certs" 18 18 "mirage-crypto" 19 19 "mirage-crypto-rng" 20 + "mirage-crypto-ec" 21 + "mirage-crypto-pk" 22 + "eqaf" 20 23 "uri" 21 24 "jsont" 22 25 "bytesrw"
+5157
spec/rfc9421.txt
··· 1 +  2 + 3 + 4 + 5 + Internet Engineering Task Force (IETF) A. Backman, Ed. 6 + Request for Comments: 9421 Amazon 7 + Category: Standards Track J. Richer, Ed. 8 + ISSN: 2070-1721 Bespoke Engineering 9 + M. Sporny 10 + Digital Bazaar 11 + February 2024 12 + 13 + 14 + HTTP Message Signatures 15 + 16 + Abstract 17 + 18 + This document describes a mechanism for creating, encoding, and 19 + verifying digital signatures or message authentication codes over 20 + components of an HTTP message. This mechanism supports use cases 21 + where the full HTTP message may not be known to the signer and where 22 + the message may be transformed (e.g., by intermediaries) before 23 + reaching the verifier. This document also describes a means for 24 + requesting that a signature be applied to a subsequent HTTP message 25 + in an ongoing HTTP exchange. 26 + 27 + Status of This Memo 28 + 29 + This is an Internet Standards Track document. 30 + 31 + This document is a product of the Internet Engineering Task Force 32 + (IETF). It represents the consensus of the IETF community. It has 33 + received public review and has been approved for publication by the 34 + Internet Engineering Steering Group (IESG). Further information on 35 + Internet Standards is available in Section 2 of RFC 7841. 36 + 37 + Information about the current status of this document, any errata, 38 + and how to provide feedback on it may be obtained at 39 + https://www.rfc-editor.org/info/rfc9421. 40 + 41 + Copyright Notice 42 + 43 + Copyright (c) 2024 IETF Trust and the persons identified as the 44 + document authors. All rights reserved. 45 + 46 + This document is subject to BCP 78 and the IETF Trust's Legal 47 + Provisions Relating to IETF Documents 48 + (https://trustee.ietf.org/license-info) in effect on the date of 49 + publication of this document. Please review these documents 50 + carefully, as they describe your rights and restrictions with respect 51 + to this document. Code Components extracted from this document must 52 + include Revised BSD License text as described in Section 4.e of the 53 + Trust Legal Provisions and are provided without warranty as described 54 + in the Revised BSD License. 55 + 56 + Table of Contents 57 + 58 + 1. Introduction 59 + 1.1. Conventions and Terminology 60 + 1.2. Requirements 61 + 1.3. HTTP Message Transformations 62 + 1.4. Application of HTTP Message Signatures 63 + 2. HTTP Message Components 64 + 2.1. HTTP Fields 65 + 2.1.1. Strict Serialization of HTTP Structured Fields 66 + 2.1.2. Dictionary Structured Field Members 67 + 2.1.3. Binary-Wrapped HTTP Fields 68 + 2.1.4. Trailer Fields 69 + 2.2. Derived Components 70 + 2.2.1. Method 71 + 2.2.2. Target URI 72 + 2.2.3. Authority 73 + 2.2.4. Scheme 74 + 2.2.5. Request Target 75 + 2.2.6. Path 76 + 2.2.7. Query 77 + 2.2.8. Query Parameters 78 + 2.2.9. Status Code 79 + 2.3. Signature Parameters 80 + 2.4. Signing Request Components in a Response Message 81 + 2.5. Creating the Signature Base 82 + 3. HTTP Message Signatures 83 + 3.1. Creating a Signature 84 + 3.2. Verifying a Signature 85 + 3.2.1. Enforcing Application Requirements 86 + 3.3. Signature Algorithms 87 + 3.3.1. RSASSA-PSS Using SHA-512 88 + 3.3.2. RSASSA-PKCS1-v1_5 Using SHA-256 89 + 3.3.3. HMAC Using SHA-256 90 + 3.3.4. ECDSA Using Curve P-256 DSS and SHA-256 91 + 3.3.5. ECDSA Using Curve P-384 DSS and SHA-384 92 + 3.3.6. EdDSA Using Curve edwards25519 93 + 3.3.7. JSON Web Signature (JWS) Algorithms 94 + 4. Including a Message Signature in a Message 95 + 4.1. The Signature-Input HTTP Field 96 + 4.2. The Signature HTTP Field 97 + 4.3. Multiple Signatures 98 + 5. Requesting Signatures 99 + 5.1. The Accept-Signature Field 100 + 5.2. Processing an Accept-Signature 101 + 6. IANA Considerations 102 + 6.1. HTTP Field Name Registration 103 + 6.2. HTTP Signature Algorithms Registry 104 + 6.2.1. Registration Template 105 + 6.2.2. Initial Contents 106 + 6.3. HTTP Signature Metadata Parameters Registry 107 + 6.3.1. Registration Template 108 + 6.3.2. Initial Contents 109 + 6.4. HTTP Signature Derived Component Names Registry 110 + 6.4.1. Registration Template 111 + 6.4.2. Initial Contents 112 + 6.5. HTTP Signature Component Parameters Registry 113 + 6.5.1. Registration Template 114 + 6.5.2. Initial Contents 115 + 7. Security Considerations 116 + 7.1. General Considerations 117 + 7.1.1. Skipping Signature Verification 118 + 7.1.2. Use of TLS 119 + 7.2. Message Processing and Selection 120 + 7.2.1. Insufficient Coverage 121 + 7.2.2. Signature Replay 122 + 7.2.3. Choosing Message Components 123 + 7.2.4. Choosing Signature Parameters and Derived Components 124 + over HTTP Fields 125 + 7.2.5. Signature Labels 126 + 7.2.6. Multiple Signature Confusion 127 + 7.2.7. Collision of Application-Specific Signature Tag 128 + 7.2.8. Message Content 129 + 7.3. Cryptographic Considerations 130 + 7.3.1. Cryptography and Signature Collision 131 + 7.3.2. Key Theft 132 + 7.3.3. Symmetric Cryptography 133 + 7.3.4. Key Specification Mixup 134 + 7.3.5. Non-deterministic Signature Primitives 135 + 7.3.6. Key and Algorithm Specification Downgrades 136 + 7.3.7. Signing Signature Values 137 + 7.4. Matching Signature Parameters to the Target Message 138 + 7.4.1. Modification of Required Message Parameters 139 + 7.4.2. Matching Values of Covered Components to Values in the 140 + Target Message 141 + 7.4.3. Message Component Source and Context 142 + 7.4.4. Multiple Message Component Contexts 143 + 7.5. HTTP Processing 144 + 7.5.1. Processing Invalid HTTP Field Names as Derived 145 + Component Names 146 + 7.5.2. Semantically Equivalent Field Values 147 + 7.5.3. Parsing Structured Field Values 148 + 7.5.4. HTTP Versions and Component Ambiguity 149 + 7.5.5. Canonicalization Attacks 150 + 7.5.6. Non-List Field Values 151 + 7.5.7. Padding Attacks with Multiple Field Values 152 + 7.5.8. Ambiguous Handling of Query Elements 153 + 8. Privacy Considerations 154 + 8.1. Identification through Keys 155 + 8.2. Signatures do not provide confidentiality 156 + 8.3. Oracles 157 + 8.4. Required Content 158 + 9. References 159 + 9.1. Normative References 160 + 9.2. Informative References 161 + Appendix A. Detecting HTTP Message Signatures 162 + Appendix B. Examples 163 + B.1. Example Keys 164 + B.1.1. Example RSA Key 165 + B.1.2. Example RSA-PSS Key 166 + B.1.3. Example ECC P-256 Test Key 167 + B.1.4. Example Ed25519 Test Key 168 + B.1.5. Example Shared Secret 169 + B.2. Test Cases 170 + B.2.1. Minimal Signature Using rsa-pss-sha512 171 + B.2.2. Selective Covered Components Using rsa-pss-sha512 172 + B.2.3. Full Coverage Using rsa-pss-sha512 173 + B.2.4. Signing a Response Using ecdsa-p256-sha256 174 + B.2.5. Signing a Request Using hmac-sha256 175 + B.2.6. Signing a Request Using ed25519 176 + B.3. TLS-Terminating Proxies 177 + B.4. HTTP Message Transformations 178 + Acknowledgements 179 + Authors' Addresses 180 + 181 + 1. Introduction 182 + 183 + Message integrity and authenticity are security properties that are 184 + critical to the secure operation of many HTTP applications. 185 + Application developers typically rely on the transport layer to 186 + provide these properties, by operating their application over TLS 187 + [TLS]. However, TLS only guarantees these properties over a single 188 + TLS connection, and the path between the client and application may 189 + be composed of multiple independent TLS connections (for example, if 190 + the application is hosted behind a TLS-terminating gateway or if the 191 + client is behind a TLS Inspection appliance). In such cases, TLS 192 + cannot guarantee end-to-end message integrity or authenticity between 193 + the client and application. Additionally, some operating 194 + environments present obstacles that make it impractical to use TLS 195 + (such as the presentation of client certificates from a browser) or 196 + to use features necessary to provide message authenticity. 197 + Furthermore, some applications require the binding of a higher-level 198 + application-specific key to the HTTP message, separate from any TLS 199 + certificates in use. Consequently, while TLS can meet message 200 + integrity and authenticity needs for many HTTP-based applications, it 201 + is not a universal solution. 202 + 203 + Additionally, many applications need to be able to generate and 204 + verify signatures despite incomplete knowledge of the HTTP message as 205 + seen on the wire, due to the use of libraries, proxies, or 206 + application frameworks that alter or hide portions of the message 207 + from the application at the time of signing or verification. These 208 + applications need a means to protect the parts of the message that 209 + are most relevant to the application without having to violate 210 + layering and abstraction. 211 + 212 + Finally, object-based signature mechanisms such as JSON Web Signature 213 + [JWS] require the intact conveyance of the exact information that was 214 + signed. When applying such technologies to an HTTP message, elements 215 + of the HTTP message need to be duplicated in the object payload 216 + either directly or through the inclusion of a hash. This practice 217 + introduces complexity, since the repeated information needs to be 218 + carefully checked for consistency when the signature is verified. 219 + 220 + This document defines a mechanism for providing end-to-end integrity 221 + and authenticity for components of an HTTP message by using a 222 + detached signature on HTTP messages. The mechanism allows 223 + applications to create digital signatures or message authentication 224 + codes (MACs) over only the components of the message that are 225 + meaningful and appropriate for the application. Strict 226 + canonicalization rules ensure that the verifier can verify the 227 + signature even if the message has been transformed in many of the 228 + ways permitted by HTTP. 229 + 230 + The signing mechanism described in this document consists of three 231 + parts: 232 + 233 + * A common nomenclature and canonicalization rule set for the 234 + different protocol elements and other components of HTTP messages, 235 + used to create the signature base (Section 2). 236 + 237 + * Algorithms for generating and verifying signatures over HTTP 238 + message components using this signature base through the 239 + application of cryptographic primitives (Section 3). 240 + 241 + * A mechanism for attaching a signature and related metadata to an 242 + HTTP message and for parsing attached signatures and metadata from 243 + HTTP messages. To facilitate this, this document defines the 244 + "Signature-Input" and "Signature" fields (Section 4). 245 + 246 + This document also provides a mechanism for negotiating the use of 247 + signatures in one or more subsequent messages via the "Accept- 248 + Signature" field (Section 5). This optional negotiation mechanism 249 + can be used along with opportunistic or application-driven message 250 + signatures by either party. 251 + 252 + The mechanisms defined in this document are important tools that can 253 + be used to build an overall security mechanism for an application. 254 + This toolkit provides some powerful capabilities but is not 255 + sufficient in creating an overall security story. In particular, the 256 + requirements listed in Section 1.4 and the security considerations 257 + discussed in Section 7 are of high importance to all implementors of 258 + this specification. For example, this specification does not define 259 + a means to directly cover HTTP message content (defined in 260 + Section 6.4 of [HTTP]); rather, it relies on the Digest specification 261 + [DIGEST] to provide a hash of the message content, as discussed in 262 + Section 7.2.8. 263 + 264 + 1.1. Conventions and Terminology 265 + 266 + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 267 + "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and 268 + "OPTIONAL" in this document are to be interpreted as described in 269 + BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all 270 + capitals, as shown here. 271 + 272 + The terms "HTTP message", "HTTP request", "HTTP response", "target 273 + URI", "gateway", "header field", "intermediary", "request target", 274 + "trailer field", "sender", "method", and "recipient" are used as 275 + defined in [HTTP]. 276 + 277 + For brevity, the term "signature" on its own is used in this document 278 + to refer to both digital signatures (which use asymmetric 279 + cryptography) and keyed MACs (which use symmetric cryptography). 280 + Similarly, the verb "sign" refers to the generation of either a 281 + digital signature or keyed MAC over a given signature base. The 282 + qualified term "digital signature" refers specifically to the output 283 + of an asymmetric cryptographic signing operation. 284 + 285 + This document uses the following terminology from Section 3 of 286 + [STRUCTURED-FIELDS] to specify data types: List, Inner List, 287 + Dictionary, Item, String, Integer, Byte Sequence, and Boolean. 288 + 289 + This document defines several string constructions using ABNF [ABNF] 290 + and uses the following ABNF rules: VCHAR, SP, DQUOTE, and LF. This 291 + document uses the following ABNF rules from [STRUCTURED-FIELDS]: sf- 292 + string, inner-list, and parameters. This document uses the following 293 + ABNF rules from [HTTP] and [HTTP/1.1]: field-content, obs-fold, and 294 + obs-text. 295 + 296 + In addition to those listed above, this document uses the following 297 + terms: 298 + 299 + HTTP Message Signature: 300 + A digital signature or keyed MAC that covers one or more portions 301 + of an HTTP message. Note that a given HTTP message can contain 302 + multiple HTTP message signatures. 303 + 304 + Signer: 305 + The entity that is generating or has generated an HTTP message 306 + signature. Note that multiple entities can act as signers and 307 + apply separate HTTP message signatures to a given HTTP message. 308 + 309 + Verifier: 310 + An entity that is verifying or has verified an HTTP message 311 + signature against an HTTP message. Note that an HTTP message 312 + signature may be verified multiple times, potentially by different 313 + entities. 314 + 315 + HTTP Message Component: 316 + A portion of an HTTP message that is capable of being covered by 317 + an HTTP message signature. 318 + 319 + Derived Component: 320 + An HTTP message component derived from the HTTP message through 321 + the use of a specified algorithm or process. See Section 2.2. 322 + 323 + HTTP Message Component Name: 324 + A String that identifies an HTTP message component's source, such 325 + as a field name or derived component name. 326 + 327 + HTTP Message Component Identifier: 328 + The combination of an HTTP message component name and any 329 + parameters. This combination uniquely identifies a specific HTTP 330 + message component with respect to a particular HTTP message 331 + signature and the HTTP message it applies to. 332 + 333 + HTTP Message Component Value: 334 + The value associated with a given component identifier within the 335 + context of a particular HTTP message. Component values are 336 + derived from the HTTP message and are usually subject to a 337 + canonicalization process. 338 + 339 + Covered Components: 340 + An ordered set of HTTP message component identifiers for fields 341 + (Section 2.1) and derived components (Section 2.2) that indicates 342 + the set of message components covered by the signature, never 343 + including the @signature-params identifier itself. The order of 344 + this set is preserved and communicated between the signer and 345 + verifier to facilitate reconstruction of the signature base. 346 + 347 + Signature Base: 348 + The sequence of bytes generated by the signer and verifier using 349 + the covered components set and the HTTP message. The signature 350 + base is processed by the cryptographic algorithm to produce or 351 + verify the HTTP message signature. 352 + 353 + HTTP Message Signature Algorithm: 354 + A cryptographic algorithm that describes the signing and 355 + verification process for the signature, defined in terms of the 356 + HTTP_SIGN and HTTP_VERIFY primitives described in Section 3.3. 357 + 358 + Key Material: 359 + The key material required to create or verify the signature. The 360 + key material is often identified with an explicit key identifier, 361 + allowing the signer to indicate to the verifier which key was 362 + used. 363 + 364 + Creation Time: 365 + A timestamp representing the point in time that the signature was 366 + generated, as asserted by the signer. 367 + 368 + Expiration Time: 369 + A timestamp representing the point in time after which the 370 + signature should no longer be accepted by the verifier, as 371 + asserted by the signer. 372 + 373 + Target Message: 374 + The HTTP message to which an HTTP message signature is applied. 375 + 376 + Signature Context: 377 + The data source from which the HTTP message component values are 378 + drawn. The context includes the target message and any additional 379 + information the signer or verifier might have, such as the full 380 + target URI of a request or the related request message for a 381 + response. 382 + 383 + The term "UNIX timestamp" refers to what Section 4.16 of [POSIX.1] 384 + calls "seconds since the Epoch". 385 + 386 + This document contains non-normative examples of partial and complete 387 + HTTP messages. Some examples use a single trailing backslash (\) to 388 + indicate line wrapping for long values, as per [RFC8792]. The \ 389 + character and leading spaces on wrapped lines are not part of the 390 + value. 391 + 392 + 1.2. Requirements 393 + 394 + HTTP permits, and sometimes requires, intermediaries to transform 395 + messages in a variety of ways. This can result in a recipient 396 + receiving a message that is not bitwise-equivalent to the message 397 + that was originally sent. In such a case, the recipient will be 398 + unable to verify integrity protections over the raw bytes of the 399 + sender's HTTP message, as verifying digital signatures or MACs 400 + requires both signer and verifier to have the exact same signature 401 + base. Since the exact raw bytes of the message cannot be relied upon 402 + as a reliable source for a signature base, the signer and verifier 403 + have to independently create the signature base from their respective 404 + versions of the message, via a mechanism that is resilient to safe 405 + changes that do not alter the meaning of the message. 406 + 407 + For a variety of reasons, it is impractical to strictly define what 408 + constitutes a safe change versus an unsafe one. Applications use 409 + HTTP in a wide variety of ways and may disagree on whether a 410 + particular piece of information in a message (e.g., the message 411 + content, the method, or a particular header field) is relevant. 412 + Thus, a general-purpose solution needs to provide signers with some 413 + degree of control over which message components are signed. 414 + 415 + HTTP applications may be running in environments that do not provide 416 + complete access to or control over HTTP messages (such as a web 417 + browser's JavaScript environment) or may be using libraries that 418 + abstract away the details of the protocol (such as the Java HTTP 419 + Client (HttpClient) library 420 + (https://openjdk.java.net/groups/net/httpclient/intro.html)). These 421 + applications need to be able to generate and verify signatures 422 + despite incomplete knowledge of the HTTP message. 423 + 424 + 1.3. HTTP Message Transformations 425 + 426 + As mentioned earlier, HTTP explicitly permits, and in some cases 427 + requires, implementations to transform messages in a variety of ways. 428 + Implementations are required to tolerate many of these 429 + transformations. What follows is a non-normative and non-exhaustive 430 + list of transformations that could occur under HTTP, provided as 431 + context: 432 + 433 + * Reordering of fields with different field names (Section 5.3 of 434 + [HTTP]). 435 + 436 + * Combination of fields with the same field name (Section 5.2 of 437 + [HTTP]). 438 + 439 + * Removal of fields listed in the Connection header field 440 + (Section 7.6.1 of [HTTP]). 441 + 442 + * Addition of fields that indicate control options (Section 7.6.1 of 443 + [HTTP]). 444 + 445 + * Addition or removal of a transfer coding (Section 7.7 of [HTTP]). 446 + 447 + * Addition of fields such as Via (Section 7.6.3 of [HTTP]) and 448 + Forwarded (Section 4 of [RFC7239]). 449 + 450 + * Conversion between different versions of HTTP (e.g., HTTP/1.x to 451 + HTTP/2, or vice versa). 452 + 453 + * Changes in case (e.g., "Origin" to "origin") of any case- 454 + insensitive components such as field names, request URI scheme, or 455 + host. 456 + 457 + * Changes to the request target and authority that, when applied 458 + together, do not result in a change to the message's target URI, 459 + as defined in Section 7.1 of [HTTP]. 460 + 461 + Additionally, there are some transformations that are either 462 + deprecated or otherwise not allowed but that could still occur in the 463 + wild. These transformations can still be handled without breaking 464 + the signature; they include such actions as: 465 + 466 + * Use, addition, or removal of leading or trailing whitespace in a 467 + field value. 468 + 469 + * Use, addition, or removal of obs-fold in field values (Section 5.2 470 + of [HTTP/1.1]). 471 + 472 + We can identify these types of transformations as transformations 473 + that should not prevent signature verification, even when performed 474 + on message components covered by the signature. Additionally, all 475 + changes to components not covered by the signature should not prevent 476 + signature verification. 477 + 478 + Some examples of these kinds of transformations, and the effect they 479 + have on the message signature, are found in Appendix B.4. 480 + 481 + Other transformations, such as parsing and reserializing the field 482 + values of a covered component or changing the value of a derived 483 + component, can cause a signature to no longer validate against a 484 + target message. Applications of this specification need to take care 485 + to ensure that the transformations expected by the application are 486 + adequately handled by the choice of covered components. 487 + 488 + 1.4. Application of HTTP Message Signatures 489 + 490 + HTTP message signatures are designed to be a general-purpose tool 491 + applicable in a wide variety of circumstances and applications. In 492 + order to properly and safely apply HTTP message signatures, an 493 + application or profile of this specification MUST specify, at a 494 + minimum, all of the following items: 495 + 496 + * The set of component identifiers (Section 2) and signature 497 + parameters (Section 2.3) that are expected and required to be 498 + included in the covered components list. For example, an 499 + authorization protocol could mandate that the Authorization field 500 + be covered to protect the authorization credentials and mandate 501 + that the signature parameters contain a created parameter 502 + (Section 2.3), while an API expecting semantically relevant HTTP 503 + message content could require the Content-Digest field defined in 504 + [DIGEST] to be present and covered as well as mandate a value for 505 + the tag parameter (Section 2.3) that is specific to the API being 506 + protected. 507 + 508 + * The expected Structured Field types [STRUCTURED-FIELDS] of any 509 + required or expected covered component fields or parameters. 510 + 511 + * A means of retrieving the key material used to verify the 512 + signature. An application will usually use the keyid parameter of 513 + the signature parameters (Section 2.3) and define rules for 514 + resolving a key from there, though the appropriate key could be 515 + known from other means such as preregistration of a signer's key. 516 + 517 + * The set of allowable signature algorithms to be used by signers 518 + and accepted by verifiers. 519 + 520 + * A means of determining that the signature algorithm used to verify 521 + the signature is appropriate for the key material and context of 522 + the message. For example, the process could use the alg parameter 523 + of the signature parameters (Section 2.3) to state the algorithm 524 + explicitly, derive the algorithm from the key material, or use 525 + some preconfigured algorithm agreed upon by the signer and 526 + verifier. 527 + 528 + * A means of determining that a given key and algorithm used for a 529 + signature are appropriate for the context of the message. For 530 + example, a server expecting only ECDSA signatures should know to 531 + reject any RSA signatures, or a server expecting asymmetric 532 + cryptography should know to reject any symmetric cryptography. 533 + 534 + * A means of determining the context for derivation of message 535 + components from an HTTP message and its application context. 536 + While this is normally the target HTTP message itself, the context 537 + could include additional information known to the application 538 + through configuration, such as an external hostname. 539 + 540 + * If binding between a request and response is needed using the 541 + mechanism provided in Section 2.4, all elements of the request 542 + message and the response message that would be required to provide 543 + properties of such a binding. 544 + 545 + * The error messages and codes that are returned from the verifier 546 + to the signer when the signature is invalid, the key material is 547 + inappropriate, the validity time window is out of specification, a 548 + component value cannot be calculated, or any other errors occur 549 + during the signature verification process. For example, if a 550 + signature is being used as an authentication mechanism, an HTTP 551 + status code of 401 (Unauthorized) or 403 (Forbidden) could be 552 + appropriate. If the response is from an HTTP API, a response with 553 + an HTTP status code such as 400 (Bad Request) could include more 554 + details [RFC7807] [RFC9457], such as an indicator that the wrong 555 + key material was used. 556 + 557 + When choosing these parameters, an application of HTTP message 558 + signatures has to ensure that the verifier will have access to all 559 + required information needed to recreate the signature base. For 560 + example, a server behind a reverse proxy would need to know the 561 + original request URI to make use of the derived component @target- 562 + uri, even though the apparent target URI would be changed by the 563 + reverse proxy (see also Section 7.4.3). Additionally, an application 564 + using signatures in responses would need to ensure that clients 565 + receiving signed responses have access to all the signed portions of 566 + the message, including any portions of the request that were signed 567 + by the server using the req ("request-response") parameter 568 + (Section 2.4). 569 + 570 + Details regarding this kind of profiling are within the purview of 571 + the application and outside the scope of this specification; however, 572 + some additional considerations are discussed in Section 7. In 573 + particular, when choosing the required set of component identifiers, 574 + care has to be taken to make sure that the coverage is sufficient for 575 + the application, as discussed in Sections 7.2.1 and 7.2.8. This 576 + specification defines only part of a full security system for an 577 + application. When building a complete security system based on this 578 + tool, it is important to perform a security analysis of the entire 579 + system, of which HTTP message signatures is a part. Historical 580 + systems, such as AWS Signature Version 4 [AWS-SIGv4], can provide 581 + inspiration and examples of how to apply similar mechanisms to an 582 + application, though review of such historical systems does not negate 583 + the need for a security analysis of an application of HTTP message 584 + signatures. 585 + 586 + 2. HTTP Message Components 587 + 588 + In order to allow signers and verifiers to establish which components 589 + are covered by a signature, this document defines component 590 + identifiers for components covered by an HTTP message signature, a 591 + set of rules for deriving and canonicalizing the values associated 592 + with these component identifiers from the HTTP message, and the means 593 + for combining these canonicalized values into a signature base. 594 + 595 + The signature context for deriving these values MUST be accessible to 596 + both the signer and the verifier of the message. The context MUST be 597 + the same across all components in a given signature. For example, it 598 + would be an error to use the raw query string for the @query derived 599 + component but combined query and form parameters for the @query-param 600 + derived component. For more considerations regarding the message 601 + component context, see Section 7.4.3. 602 + 603 + A component identifier is composed of a component name and any 604 + parameters associated with that name. Each component name is either 605 + an HTTP field name (Section 2.1) or a registered derived component 606 + name (Section 2.2). The possible parameters for a component 607 + identifier are dependent on the component identifier. The "HTTP 608 + Signature Component Parameters" registry, which catalogs all possible 609 + parameters, is defined in Section 6.5. 610 + 611 + Within a single list of covered components, each component identifier 612 + MUST occur only once. One component identifier is distinct from 613 + another if the component name differs or if any of the parameters 614 + differ for the same component name. Multiple component identifiers 615 + having the same component name MAY be included if they have 616 + parameters that make them distinct, such as "foo";bar and "foo";baz. 617 + The order of parameters MUST be preserved when processing a component 618 + identifier (such as when parsing during verification), but the order 619 + of parameters is not significant when comparing two component 620 + identifiers for equality checks. That is to say, "foo";bar;baz 621 + cannot be in the same message as "foo";baz;bar, since these two 622 + component identifiers are equivalent, but a system processing one 623 + form is not allowed to transform it into the other form. 624 + 625 + The component value associated with a component identifier is defined 626 + by the identifier itself. Component values MUST NOT contain newline 627 + (\n) characters. Some HTTP message components can undergo 628 + transformations that change the bitwise value without altering the 629 + meaning of the component's value (for example, when combining field 630 + values). Message component values therefore need to be canonicalized 631 + before they are signed, to ensure that a signature can be verified 632 + despite such intermediary transformations. This document defines 633 + rules for each component identifier that transform the identifier's 634 + associated component value into such a canonical form. 635 + 636 + The following sections define component identifier names, their 637 + parameters, their associated values, and the canonicalization rules 638 + for their values. The method for combining message components into 639 + the signature base is defined in Section 2.5. 640 + 641 + 2.1. HTTP Fields 642 + 643 + The component name for an HTTP field is the lowercased form of its 644 + field name as defined in Section 5.1 of [HTTP]. While HTTP field 645 + names are case insensitive, implementations MUST use lowercased field 646 + names (e.g., content-type, date, etag) when using them as component 647 + names. 648 + 649 + The component value for an HTTP field is the field value for the 650 + named field as defined in Section 5.5 of [HTTP]. The field value 651 + MUST be taken from the named header field of the target message 652 + unless this behavior is overridden by additional parameters and 653 + rules, such as the req and tr flags, below. For most fields, the 654 + field value is an ASCII string as recommended by [HTTP], and the 655 + component value is exactly that string. Other encodings could exist 656 + in some implementations, and all non-ASCII field values MUST be 657 + encoded to ASCII before being added to the signature base. The bs 658 + parameter, as described in Section 2.1.3, provides a method for 659 + wrapping such problematic field values. 660 + 661 + Unless overridden by additional parameters and rules, HTTP field 662 + values MUST be combined into a single value as defined in Section 5.2 663 + of [HTTP] to create the component value. Specifically, HTTP fields 664 + sent as multiple fields MUST be combined by concatenating the values 665 + using a single comma and a single space as a separator ("," + " "). 666 + Note that intermediaries are allowed to combine values of HTTP fields 667 + with any amount of whitespace between the commas, and if this 668 + behavior is not accounted for by the verifier, the signature can 669 + fail, since the signer and verifier will see a different component 670 + value in their respective signature bases. For robustness, it is 671 + RECOMMENDED that signed messages include only a single instance of 672 + any field covered under the signature, particularly with the value 673 + for any list-based fields serialized using the algorithm below. This 674 + approach increases the chances of the field value remaining untouched 675 + through intermediaries. Where that approach is not possible and 676 + multiple instances of a field need to be sent separately, it is 677 + RECOMMENDED that signers and verifiers process any list-based fields 678 + taking all individual field values and combining them based on the 679 + strict algorithm below, to counter possible intermediary behavior. 680 + When the field in question is a Structured Field of type List or 681 + Dictionary, this effect can be accomplished more directly by 682 + requiring the strict Structured Field serialization of the field 683 + value, as described in Section 2.1.1. 684 + 685 + Note that some HTTP fields, such as Set-Cookie [COOKIE], do not 686 + follow a syntax that allows for the combination of field values in 687 + this manner (such that the combined output is unambiguous from 688 + multiple inputs). Even though the component value is never parsed by 689 + the message signature process and is used only as part of the 690 + signature base (Section 2.5), caution needs to be taken when 691 + including such fields in signatures, since the combined value could 692 + be ambiguous. The bs parameter, as described in Section 2.1.3, 693 + provides a method for wrapping such problematic fields. See 694 + Section 7.5.6 for more discussion regarding this issue. 695 + 696 + If the correctly combined value is not directly available for a given 697 + field by an implementation, the following algorithm will produce 698 + canonicalized results for list-based fields: 699 + 700 + 1. Create an ordered list of the field values of each instance of 701 + the field in the message, in the order they occur (or will occur) 702 + in the message. 703 + 704 + 2. Strip leading and trailing whitespace from each item in the list. 705 + Note that since HTTP field values are not allowed to contain 706 + leading and trailing whitespace, this would be a no-op in a 707 + compliant implementation. 708 + 709 + 3. Remove any obsolete line folding within the line, and replace it 710 + with a single space (" "), as discussed in Section 5.2 of 711 + [HTTP/1.1]. Note that this behavior is specific to HTTP/1.1 and 712 + does not apply to other versions of the HTTP specification, which 713 + do not allow internal line folding. 714 + 715 + 4. Concatenate the list of values with a single comma (",") and a 716 + single space (" ") between each item. 717 + 718 + The resulting string is the component value for the field. 719 + 720 + Note that some HTTP fields have values with multiple valid 721 + serializations that have equivalent semantics, such as allowing case- 722 + insensitive values that intermediaries could change. Applications 723 + signing and processing such fields MUST consider how to handle the 724 + values of such fields to ensure that the signer and verifier can 725 + derive the same value, as discussed in Section 7.5.2. 726 + 727 + The following are non-normative examples of component values for 728 + header fields, given the following example HTTP message fragment: 729 + 730 + Host: www.example.com 731 + Date: Tue, 20 Apr 2021 02:07:56 GMT 732 + X-OWS-Header: Leading and trailing whitespace. 733 + X-Obs-Fold-Header: Obsolete 734 + line folding. 735 + Cache-Control: max-age=60 736 + Cache-Control: must-revalidate 737 + Example-Dict: a=1, b=2;x=1;y=2, c=(a b c) 738 + 739 + The following example shows the component values for these example 740 + header fields, presented using the signature base format defined in 741 + Section 2.5: 742 + 743 + "host": www.example.com 744 + "date": Tue, 20 Apr 2021 02:07:56 GMT 745 + "x-ows-header": Leading and trailing whitespace. 746 + "x-obs-fold-header": Obsolete line folding. 747 + "cache-control": max-age=60, must-revalidate 748 + "example-dict": a=1, b=2;x=1;y=2, c=(a b c) 749 + 750 + Empty HTTP fields can also be signed when present in a message. The 751 + canonicalized value is the empty string. This means that the 752 + following empty header field, with (SP) indicating a single trailing 753 + space character before the empty field value: 754 + 755 + X-Empty-Header:(SP) 756 + 757 + is serialized by the signature base generation algorithm 758 + (Section 2.5) with an empty string value following the colon and 759 + space added after the component identifier. 760 + 761 + "x-empty-header":(SP) 762 + 763 + Any HTTP field component identifiers MAY have the following 764 + parameters in specific circumstances, each described in detail in 765 + their own sections: 766 + 767 + sf A Boolean flag indicating that the component value is serialized 768 + using strict encoding of the Structured Field value 769 + (Section 2.1.1). 770 + 771 + key A String parameter used to select a single member value from a 772 + Dictionary Structured Field (Section 2.1.2). 773 + 774 + bs A Boolean flag indicating that individual field values are 775 + encoded using Byte Sequence data structures before being combined 776 + into the component value (Section 2.1.3). 777 + 778 + req A Boolean flag for signed responses indicating that the 779 + component value is derived from the request that triggered this 780 + response message and not from the response message directly. Note 781 + that this parameter can also be applied to any derived component 782 + identifiers that target the request (Section 2.4). 783 + 784 + tr A Boolean flag indicating that the field value is taken from the 785 + trailers of the message as defined in Section 6.5 of [HTTP]. If 786 + this flag is absent, the field value is taken from the header 787 + fields of the message as defined in Section 6.3 of [HTTP] 788 + (Section 2.1.4). 789 + 790 + Multiple parameters MAY be specified together, though some 791 + combinations are redundant or incompatible. For example, the sf 792 + parameter's functionality is already covered when the key parameter 793 + is used on a Dictionary item, since key requires strict serialization 794 + of the value. The bs parameter, which requires the raw bytes of the 795 + field values from the message, is not compatible with the use of the 796 + sf or key parameters, which require the parsed data structures of the 797 + field values after combination. 798 + 799 + Additional parameters can be defined in the "HTTP Signature Component 800 + Parameters" registry established in Section 6.5. 801 + 802 + 2.1.1. Strict Serialization of HTTP Structured Fields 803 + 804 + If the value of an HTTP field is known by the application to be a 805 + Structured Field type (as defined in [STRUCTURED-FIELDS] or its 806 + extensions or updates) and the expected type of the Structured Field 807 + is known, the signer MAY include the sf parameter in the component 808 + identifier. If this parameter is included with a component 809 + identifier, the HTTP field value MUST be serialized using the formal 810 + serialization rules specified in Section 4 of [STRUCTURED-FIELDS] (or 811 + the applicable formal serialization section of its extensions or 812 + updates) applicable to the type of the HTTP field. Note that this 813 + process will replace any optional internal whitespace with a single 814 + space character, among other potential transformations of the value. 815 + 816 + If multiple field values occur within a message, these values MUST be 817 + combined into a single List or Dictionary structure before 818 + serialization. 819 + 820 + If the application does not know the type of the field or does not 821 + know how to serialize the type of the field, the use of this flag 822 + will produce an error. As a consequence, the signer can only 823 + reliably sign fields using this flag when the verifier's system knows 824 + the type as well. 825 + 826 + For example, the following Dictionary field is a valid serialization: 827 + 828 + Example-Dict: a=1, b=2;x=1;y=2, c=(a b c) 829 + 830 + If included in the signature base without parameters, its value would 831 + be: 832 + 833 + "example-dict": a=1, b=2;x=1;y=2, c=(a b c) 834 + 835 + However, if the sf parameter is added, the value is reserialized as 836 + follows: 837 + 838 + "example-dict";sf: a=1, b=2;x=1;y=2, c=(a b c) 839 + 840 + The resulting string is used as the component value; see Section 2.1. 841 + 842 + 2.1.2. Dictionary Structured Field Members 843 + 844 + If a given field is known by the application to be a Dictionary 845 + Structured Field, an individual member in the value of that 846 + Dictionary is identified by using the parameter key and the 847 + Dictionary member key as a String value. 848 + 849 + If multiple field values occur within a message, these values MUST be 850 + combined into a single Dictionary structure before serialization. 851 + 852 + An individual member value of a Dictionary Structured Field is 853 + canonicalized by applying the serialization algorithm described in 854 + Section 4.1.2 of [STRUCTURED-FIELDS] on the member_value and its 855 + parameters, not including the Dictionary key itself. Specifically, 856 + the value is serialized as an Item or Inner List (the two possible 857 + values of a Dictionary member), with all parameters and possible 858 + subfields serialized using the strict serialization rules defined in 859 + Section 4 of [STRUCTURED-FIELDS] (or the applicable section of its 860 + extensions or updates). 861 + 862 + Each parameterized key for a given field MUST NOT appear more than 863 + once in the signature base. Parameterized keys MAY appear in any 864 + order in the signature base, regardless of the order they occur in 865 + the source Dictionary. 866 + 867 + If a Dictionary key is named as a covered component but it does not 868 + occur in the Dictionary, this MUST cause an error in the signature 869 + base generation. 870 + 871 + The following are non-normative examples of canonicalized values for 872 + Dictionary Structured Field members, given the following example 873 + header field, whose value is known by the application to be a 874 + Dictionary: 875 + 876 + Example-Dict: a=1, b=2;x=1;y=2, c=(a b c), d 877 + 878 + The following example shows canonicalized values for different 879 + component identifiers of this field, presented using the signature 880 + base format discussed in Section 2.5: 881 + 882 + "example-dict";key="a": 1 883 + "example-dict";key="d": ?1 884 + "example-dict";key="b": 2;x=1;y=2 885 + "example-dict";key="c": (a b c) 886 + 887 + Note that the value for key="c" has been reserialized according to 888 + the strict member_value algorithm, and the value for key="d" has been 889 + serialized as a Boolean value. 890 + 891 + 2.1.3. Binary-Wrapped HTTP Fields 892 + 893 + If the value of the HTTP field in question is known by the 894 + application to cause problems with serialization, particularly with 895 + the combination of multiple values into a single line as discussed in 896 + Section 7.5.6, the signer SHOULD include the bs parameter in a 897 + component identifier to indicate that the values of the field need to 898 + be wrapped as binary structures before being combined. 899 + 900 + If this parameter is included with a component identifier, the 901 + component value MUST be calculated using the following algorithm: 902 + 903 + 1. Let the input be the ordered set of values for a field, in the 904 + order they appear in the message. 905 + 906 + 2. Create an empty List for accumulating processed field values. 907 + 908 + 3. For each field value in the set: 909 + 910 + 3.1. Strip leading and trailing whitespace from the field value. 911 + Note that since HTTP field values are not allowed to 912 + contain leading and trailing whitespace, this would be a 913 + no-op in a compliant implementation. 914 + 915 + 3.2. Remove any obsolete line folding within the line, and 916 + replace it with a single space (" "), as discussed in 917 + Section 5.2 of [HTTP/1.1]. Note that this behavior is 918 + specific to [HTTP/1.1] and does not apply to other versions 919 + of the HTTP specification. 920 + 921 + 3.3. Encode the bytes of the resulting field value as a Byte 922 + Sequence. Note that most fields are restricted to ASCII 923 + characters, but other octets could be included in the value 924 + in some implementations. 925 + 926 + 3.4. Add the Byte Sequence to the List accumulator. 927 + 928 + 4. The intermediate result is a List of Byte Sequence values. 929 + 930 + 5. Follow the strict serialization of a List as described in 931 + Section 4.1.1 of [STRUCTURED-FIELDS], and return this output. 932 + 933 + For example, the following field with internal commas prevents the 934 + distinct field values from being safely combined: 935 + 936 + Example-Header: value, with, lots 937 + Example-Header: of, commas 938 + 939 + In our example, the same field can be sent with a semantically 940 + different single value: 941 + 942 + Example-Header: value, with, lots, of, commas 943 + 944 + Both of these versions are treated differently by the application. 945 + However, if included in the signature base without parameters, the 946 + component value would be the same in both cases: 947 + 948 + "example-header": value, with, lots, of, commas 949 + 950 + However, if the bs parameter is added, the two separate instances are 951 + encoded and serialized as follows: 952 + 953 + "example-header";bs: :dmFsdWUsIHdpdGgsIGxvdHM=:, :b2YsIGNvbW1hcw==: 954 + 955 + For the single-instance field above, the encoding with the bs 956 + parameter is: 957 + 958 + "example-header";bs: :dmFsdWUsIHdpdGgsIGxvdHMsIG9mLCBjb21tYXM=: 959 + 960 + This component value is distinct from the multiple-instance field 961 + above, preventing a collision that could potentially be exploited. 962 + 963 + 2.1.4. Trailer Fields 964 + 965 + If the signer wants to include a trailer field in the signature, the 966 + signer MUST include the tr Boolean parameter to indicate that the 967 + value MUST be taken from the trailer fields and not from the header 968 + fields. 969 + 970 + For example, given the following message: 971 + 972 + HTTP/1.1 200 OK 973 + Content-Type: text/plain 974 + Transfer-Encoding: chunked 975 + Trailer: Expires 976 + 977 + 4 978 + HTTP 979 + 7 980 + Message 981 + a 982 + Signatures 983 + 0 984 + Expires: Wed, 9 Nov 2022 07:28:00 GMT 985 + 986 + The signer decides to add both the Trailer header field and the 987 + Expires trailer field to the signature base, along with the status 988 + code derived component: 989 + 990 + "@status": 200 991 + "trailer": Expires 992 + "expires";tr: Wed, 9 Nov 2022 07:28:00 GMT 993 + 994 + If a field is available as both a header and a trailer in a message, 995 + both values MAY be signed, but the values MUST be signed separately. 996 + The values of header fields and trailer fields of the same name MUST 997 + NOT be combined for purposes of the signature. 998 + 999 + Since trailer fields could be merged into the header fields or 1000 + dropped entirely by intermediaries as per Section 6.5.1 of [HTTP], it 1001 + is NOT RECOMMENDED to include trailers in the signature unless the 1002 + signer knows that the verifier will have access to the values of the 1003 + trailers as sent. 1004 + 1005 + 2.2. Derived Components 1006 + 1007 + In addition to HTTP fields, there are a number of different 1008 + components that can be derived from the control data, signature 1009 + context, or other aspects of the HTTP message being signed. Such 1010 + derived components can be included in the signature base by defining 1011 + a component name, possible parameters, message targets, and the 1012 + derivation method for its component value. 1013 + 1014 + Derived component names MUST start with the "at" (@) character. This 1015 + differentiates derived component names from HTTP field names, which 1016 + cannot contain the @ character as per Section 5.1 of [HTTP]. 1017 + Processors of HTTP message signatures MUST treat derived component 1018 + names separately from field names, as discussed in Section 7.5.1. 1019 + 1020 + This specification defines the following derived components: 1021 + 1022 + @method The method used for a request (Section 2.2.1). 1023 + 1024 + @target-uri The full target URI for a request (Section 2.2.2). 1025 + 1026 + @authority The authority of the target URI for a request 1027 + (Section 2.2.3). 1028 + 1029 + @scheme The scheme of the target URI for a request (Section 2.2.4). 1030 + 1031 + @request-target The request target (Section 2.2.5). 1032 + 1033 + @path The absolute path portion of the target URI for a request 1034 + (Section 2.2.6). 1035 + 1036 + @query The query portion of the target URI for a request 1037 + (Section 2.2.7). 1038 + 1039 + @query-param A parsed and encoded query parameter of the target URI 1040 + for a request (Section 2.2.8). 1041 + 1042 + @status The status code for a response (Section 2.2.9). 1043 + 1044 + Additional derived component names are defined in the "HTTP Signature 1045 + Derived Component Names" registry (Section 6.4). 1046 + 1047 + Derived component values are taken from the context of the target 1048 + message for the signature. This context includes information about 1049 + the message itself, such as its control data, as well as any 1050 + additional state and context held by the signer or verifier. In 1051 + particular, when signing a response, the signer can include any 1052 + derived components from the originating request by using the req 1053 + parameter (Section 2.4). 1054 + 1055 + request: Values derived from, and results applied to, an HTTP 1056 + request message as described in Section 3.4 of [HTTP]. If the 1057 + target message of the signature is a response, derived components 1058 + that target request messages can be included by using the req 1059 + parameter as defined in Section 2.4. 1060 + 1061 + response: Values derived from, and results applied to, an HTTP 1062 + response message as described in Section 3.4 of [HTTP]. 1063 + 1064 + request, response: Values derived from, and results applied to, 1065 + either a request message or a response message. 1066 + 1067 + A derived component definition MUST define all target message types 1068 + to which it can be applied. 1069 + 1070 + Derived component values MUST be limited to printable characters and 1071 + spaces and MUST NOT contain any newline characters. Derived 1072 + component values MUST NOT start or end with whitespace characters. 1073 + 1074 + 2.2.1. Method 1075 + 1076 + The @method derived component refers to the HTTP method of a request 1077 + message. The component value is canonicalized by taking the value of 1078 + the method as a string. Note that the method name is case sensitive 1079 + as per [HTTP], Section 9.1. While conventionally standardized method 1080 + names are uppercase [ASCII], no transformation to the input method 1081 + value's case is performed. 1082 + 1083 + For example, the following request message: 1084 + 1085 + POST /path?param=value HTTP/1.1 1086 + Host: www.example.com 1087 + 1088 + would result in the following @method component value: 1089 + 1090 + POST 1091 + 1092 + and the following signature base line: 1093 + 1094 + "@method": POST 1095 + 1096 + 2.2.2. Target URI 1097 + 1098 + The @target-uri derived component refers to the target URI of a 1099 + request message. The component value is the target URI of the 1100 + request ([HTTP], Section 7.1), assembled from all available URI 1101 + components, including the authority. 1102 + 1103 + For example, the following message sent over HTTPS: 1104 + 1105 + POST /path?param=value HTTP/1.1 1106 + Host: www.example.com 1107 + 1108 + would result in the following @target-uri component value: 1109 + 1110 + https://www.example.com/path?param=value 1111 + 1112 + and the following signature base line: 1113 + 1114 + "@target-uri": https://www.example.com/path?param=value 1115 + 1116 + 2.2.3. Authority 1117 + 1118 + The @authority derived component refers to the authority component of 1119 + the target URI of the HTTP request message, as defined in [HTTP], 1120 + Section 7.2. In HTTP/1.1, this is usually conveyed using the Host 1121 + header field, while in HTTP/2 and HTTP/3 it is conveyed using the 1122 + :authority pseudo-header. The value is the fully qualified authority 1123 + component of the request, comprised of the host and, optionally, port 1124 + of the request target, as a string. The component value MUST be 1125 + normalized according to the rules provided in [HTTP], Section 4.2.3. 1126 + Namely, the hostname is normalized to lowercase, and the default port 1127 + is omitted. 1128 + 1129 + For example, the following request message: 1130 + 1131 + POST /path?param=value HTTP/1.1 1132 + Host: www.example.com 1133 + 1134 + would result in the following @authority component value: 1135 + 1136 + www.example.com 1137 + 1138 + and the following signature base line: 1139 + 1140 + "@authority": www.example.com 1141 + 1142 + The @authority derived component SHOULD be used instead of signing 1143 + the Host header field directly. See Section 7.2.4. 1144 + 1145 + 2.2.4. Scheme 1146 + 1147 + The @scheme derived component refers to the scheme of the target URL 1148 + of the HTTP request message. The component value is the scheme as a 1149 + lowercase string as defined in [HTTP], Section 4.2. While the scheme 1150 + itself is case insensitive, it MUST be normalized to lowercase for 1151 + inclusion in the signature base. 1152 + 1153 + For example, the following request message sent over plain HTTP: 1154 + 1155 + POST /path?param=value HTTP/1.1 1156 + Host: www.example.com 1157 + 1158 + would result in the following @scheme component value: 1159 + 1160 + http 1161 + 1162 + and the following signature base line: 1163 + 1164 + "@scheme": http 1165 + 1166 + 2.2.5. Request Target 1167 + 1168 + The @request-target derived component refers to the full request 1169 + target of the HTTP request message, as defined in [HTTP], 1170 + Section 7.1. The component value of the request target can take 1171 + different forms, depending on the type of request, as described 1172 + below. 1173 + 1174 + For HTTP/1.1, the component value is equivalent to the request target 1175 + portion of the request line. However, this value is more difficult 1176 + to reliably construct in other versions of HTTP. Therefore, it is 1177 + NOT RECOMMENDED that this component be used when versions of HTTP 1178 + other than 1.1 might be in use. 1179 + 1180 + The origin form value is a combination of the absolute path and query 1181 + components of the request URL. 1182 + 1183 + For example, the following request message: 1184 + 1185 + POST /path?param=value HTTP/1.1 1186 + Host: www.example.com 1187 + 1188 + would result in the following @request-target component value: 1189 + 1190 + /path?param=value 1191 + 1192 + and the following signature base line: 1193 + 1194 + "@request-target": /path?param=value 1195 + 1196 + The following request to an HTTP proxy with the absolute-form value, 1197 + containing the fully qualified target URI: 1198 + 1199 + GET https://www.example.com/path?param=value HTTP/1.1 1200 + 1201 + would result in the following @request-target component value: 1202 + 1203 + https://www.example.com/path?param=value 1204 + 1205 + and the following signature base line: 1206 + 1207 + "@request-target": https://www.example.com/path?param=value 1208 + 1209 + The following CONNECT request with an authority-form value, 1210 + containing the host and port of the target: 1211 + 1212 + CONNECT www.example.com:80 HTTP/1.1 1213 + Host: www.example.com 1214 + 1215 + would result in the following @request-target component value: 1216 + 1217 + www.example.com:80 1218 + 1219 + and the following signature base line: 1220 + 1221 + "@request-target": www.example.com:80 1222 + 1223 + The following OPTIONS request message with the asterisk-form value, 1224 + containing a single asterisk (*) character: 1225 + 1226 + OPTIONS * HTTP/1.1 1227 + Host: www.example.com 1228 + 1229 + would result in the following @request-target component value: 1230 + 1231 + * 1232 + 1233 + and the following signature base line: 1234 + 1235 + "@request-target": * 1236 + 1237 + 2.2.6. Path 1238 + 1239 + The @path derived component refers to the target path of the HTTP 1240 + request message. The component value is the absolute path of the 1241 + request target defined by [URI], with no query component and no 1242 + trailing question mark (?) character. The value is normalized 1243 + according to the rules provided in [HTTP], Section 4.2.3. Namely, an 1244 + empty path string is normalized as a single slash (/) character. 1245 + Path components are represented by their values before decoding any 1246 + percent-encoded octets, as described in the simple string comparison 1247 + rules provided in Section 6.2.1 of [URI]. 1248 + 1249 + For example, the following request message: 1250 + 1251 + GET /path?param=value HTTP/1.1 1252 + Host: www.example.com 1253 + 1254 + would result in the following @path component value: 1255 + 1256 + /path 1257 + 1258 + and the following signature base line: 1259 + 1260 + "@path": /path 1261 + 1262 + 2.2.7. Query 1263 + 1264 + The @query derived component refers to the query component of the 1265 + HTTP request message. The component value is the entire normalized 1266 + query string defined by [URI], including the leading ? character. 1267 + The value is read using the simple string comparison rules provided 1268 + in Section 6.2.1 of [URI]. Namely, percent-encoded octets are not 1269 + decoded. 1270 + 1271 + For example, the following request message: 1272 + 1273 + GET /path?param=value&foo=bar&baz=bat%2Dman HTTP/1.1 1274 + Host: www.example.com 1275 + 1276 + would result in the following @query component value: 1277 + 1278 + ?param=value&foo=bar&baz=bat%2Dman 1279 + 1280 + and the following signature base line: 1281 + 1282 + "@query": ?param=value&foo=bar&baz=bat%2Dman 1283 + 1284 + The following request message: 1285 + 1286 + POST /path?queryString HTTP/1.1 1287 + Host: www.example.com 1288 + 1289 + would result in the following @query component value: 1290 + 1291 + ?queryString 1292 + 1293 + and the following signature base line: 1294 + 1295 + "@query": ?queryString 1296 + 1297 + Just like including an empty path component, the signer can include 1298 + an empty query component to indicate that this component is not used 1299 + in the message. If the query string is absent from the request 1300 + message, the component value is the leading ? character alone: 1301 + 1302 + ? 1303 + 1304 + resulting in the following signature base line: 1305 + 1306 + "@query": ? 1307 + 1308 + 2.2.8. Query Parameters 1309 + 1310 + If the query portion of a request target URI uses HTML form 1311 + parameters in the format defined in Section 5 ("application/ 1312 + x-www-form-urlencoded") of [HTMLURL], the @query-param derived 1313 + component allows addressing of these individual query parameters. 1314 + The query parameters MUST be parsed according to Section 5.1 1315 + ("application/x-www-form-urlencoded parsing") of [HTMLURL], resulting 1316 + in a list of (nameString, valueString) tuples. The REQUIRED name 1317 + parameter of each component identifier contains the encoded 1318 + nameString of a single query parameter as a String value. The 1319 + component value of a single named parameter is the encoded 1320 + valueString of that single query parameter. Several different named 1321 + query parameters MAY be included in the covered components. Single 1322 + named parameters MAY occur in any order in the covered components, 1323 + regardless of the order they occur in the query string. 1324 + 1325 + The value of the name parameter and the component value of a single 1326 + named parameter are calculated via the following process: 1327 + 1328 + 1. Parse the nameString or valueString of the named query parameter 1329 + defined by Section 5.1 ("application/x-www-form-urlencoded 1330 + parsing") of [HTMLURL]; this is the value after percent-encoded 1331 + octets are decoded. 1332 + 1333 + 2. Encode the nameString or valueString using the "percent-encode 1334 + after encoding" process defined by Section 5.2 ("application/ 1335 + x-www-form-urlencoded serializing") of [HTMLURL]; this results in 1336 + an ASCII string [ASCII]. 1337 + 1338 + 3. Output the ASCII string. 1339 + 1340 + Note that the component value does not include any leading question 1341 + mark (?) characters, equals sign (=) characters, or separating 1342 + ampersand (&) characters. Named query parameters with an empty 1343 + valueString have an empty string as the component value. Note that 1344 + due to inconsistencies in implementations, some query parameter 1345 + parsing libraries drop such empty values. 1346 + 1347 + If a query parameter is named as a covered component but it does not 1348 + occur in the query parameters, this MUST cause an error in the 1349 + signature base generation. 1350 + 1351 + For example, for the following request: 1352 + 1353 + GET /path?param=value&foo=bar&baz=batman&qux= HTTP/1.1 1354 + Host: www.example.com 1355 + 1356 + Indicating the baz, qux, and param named query parameters would 1357 + result in the following @query-param component values: 1358 + 1359 + _baz_: batman 1360 + 1361 + _qux_: an empty string 1362 + 1363 + _param_: value 1364 + 1365 + and the following signature base lines, with (SP) indicating a single 1366 + trailing space character before the empty component value: 1367 + 1368 + "@query-param";name="baz": batman 1369 + "@query-param";name="qux":(SP) 1370 + "@query-param";name="param": value 1371 + 1372 + This derived component has some limitations. Specifically, the 1373 + algorithms provided in Section 5 ("application/ 1374 + x-www-form-urlencoded") of [HTMLURL] only support query parameters 1375 + using percent-escaped UTF-8 encoding. Other encodings are not 1376 + supported. Additionally, multiple instances of a named parameter are 1377 + not reliably supported in the wild. If a parameter name occurs 1378 + multiple times in a request, the named query parameter MUST NOT be 1379 + included. If multiple parameters are common within an application, 1380 + it is RECOMMENDED to sign the entire query string using the @query 1381 + component identifier defined in Section 2.2.7. 1382 + 1383 + The encoding process allows query parameters that include newlines or 1384 + other problematic characters in their values, or with alternative 1385 + encodings such as using the plus (+) character to represent spaces. 1386 + For the query parameters in this message: 1387 + 1388 + NOTE: '\' line wrapping per RFC 8792 1389 + 1390 + GET /parameters?var=this%20is%20a%20big%0Amultiline%20value&\ 1391 + bar=with+plus+whitespace&fa%C3%A7ade%22%3A%20=something HTTP/1.1 1392 + Host: www.example.com 1393 + Date: Tue, 20 Apr 2021 02:07:56 GMT 1394 + 1395 + The resulting values are encoded as follows: 1396 + 1397 + "@query-param";name="var": this%20is%20a%20big%0Amultiline%20value 1398 + "@query-param";name="bar": with%20plus%20whitespace 1399 + "@query-param";name="fa%C3%A7ade%22%3A%20": something 1400 + 1401 + If the encoding were not applied, the resultant values would be: 1402 + 1403 + "@query-param";name="var": this is a big 1404 + multiline value 1405 + "@query-param";name="bar": with plus whitespace 1406 + "@query-param";name="façade\": ": something 1407 + 1408 + This base string contains characters that violate the constraints on 1409 + component names and values and is therefore invalid. 1410 + 1411 + 2.2.9. Status Code 1412 + 1413 + The @status derived component refers to the three-digit numeric HTTP 1414 + status code of a response message as defined in [HTTP], Section 15. 1415 + The component value is the serialized three-digit integer of the HTTP 1416 + status code, with no descriptive text. 1417 + 1418 + For example, the following response message: 1419 + 1420 + HTTP/1.1 200 OK 1421 + Date: Fri, 26 Mar 2010 00:05:00 GMT 1422 + 1423 + would result in the following @status component value: 1424 + 1425 + 200 1426 + 1427 + and the following signature base line: 1428 + 1429 + "@status": 200 1430 + 1431 + The @status component identifier MUST NOT be used in a request 1432 + message. 1433 + 1434 + 2.3. Signature Parameters 1435 + 1436 + HTTP message signatures have metadata properties that provide 1437 + information regarding the signature's generation and verification, 1438 + consisting of the ordered set of covered components and the ordered 1439 + set of parameters, where the parameters include a timestamp of 1440 + signature creation, identifiers for verification key material, and 1441 + other utilities. This metadata is represented by a special message 1442 + component in the signature base for signature parameters; this 1443 + special message component is treated slightly differently from other 1444 + message components. Specifically, the signature parameters message 1445 + component is REQUIRED as the last line of the signature base 1446 + (Section 2.5), and the component identifier MUST NOT be enumerated 1447 + within the set of covered components for any signature, including 1448 + itself. 1449 + 1450 + The signature parameters component name is @signature-params. 1451 + 1452 + The signature parameters component value is the serialization of the 1453 + signature parameters for this signature, including the covered 1454 + components ordered set with all associated parameters. These 1455 + parameters include any of the following: 1456 + 1457 + created: Creation time as a UNIX timestamp value of type Integer. 1458 + Sub-second precision is not supported. The inclusion of this 1459 + parameter is RECOMMENDED. 1460 + 1461 + expires: Expiration time as a UNIX timestamp value of type Integer. 1462 + Sub-second precision is not supported. 1463 + 1464 + nonce: A random unique value generated for this signature as a 1465 + String value. 1466 + 1467 + alg: The HTTP message signature algorithm from the "HTTP Signature 1468 + Algorithms" registry, as a String value. 1469 + 1470 + keyid: The identifier for the key material as a String value. 1471 + 1472 + tag: An application-specific tag for the signature as a String 1473 + value. This value is used by applications to help identify 1474 + signatures relevant for specific applications or protocols. 1475 + 1476 + Additional parameters can be defined in the "HTTP Signature Metadata 1477 + Parameters" registry (Section 6.3). Note that the parameters are not 1478 + in any general order, but once an ordering is chosen for a given set 1479 + of parameters, it cannot be changed without altering the signature 1480 + parameters value. 1481 + 1482 + The signature parameters component value is serialized as a 1483 + parameterized Inner List using the rules provided in Section 4 of 1484 + [STRUCTURED-FIELDS] as follows: 1485 + 1486 + 1. Let the output be an empty string. 1487 + 1488 + 2. Determine an order for the component identifiers of the covered 1489 + components, not including the @signature-params component 1490 + identifier itself. Once this order is chosen, it cannot be 1491 + changed. This order MUST be the same order as that used in 1492 + creating the signature base (Section 2.5). 1493 + 1494 + 3. Serialize the component identifiers of the covered components, 1495 + including all parameters, as an ordered Inner List of String 1496 + values according to Section 4.1.1.1 of [STRUCTURED-FIELDS]; then, 1497 + append this to the output. Note that the component identifiers 1498 + can include their own parameters, and these parameters are 1499 + ordered sets. Once an order is chosen for a component's 1500 + parameters, the order cannot be changed. 1501 + 1502 + 4. Determine an order for any signature parameters. Once this order 1503 + is chosen, it cannot be changed. 1504 + 1505 + 5. Append the parameters to the Inner List in order according to 1506 + Section 4.1.1.2 of [STRUCTURED-FIELDS], skipping parameters that 1507 + are not available or not used for this message signature. 1508 + 1509 + 6. The output contains the signature parameters component value. 1510 + 1511 + Note that the Inner List serialization from Section 4.1.1.1 of 1512 + [STRUCTURED-FIELDS] is used for the covered component value instead 1513 + of the List serialization from Section 4.1.1 of [STRUCTURED-FIELDS] 1514 + in order to facilitate parallelism with this value's inclusion in the 1515 + Signature-Input field, as discussed in Section 4.1. 1516 + 1517 + This example shows the serialized component value for the parameters 1518 + of an example message signature: 1519 + 1520 + NOTE: '\' line wrapping per RFC 8792 1521 + 1522 + ("@target-uri" "@authority" "date" "cache-control")\ 1523 + ;keyid="test-key-rsa-pss";alg="rsa-pss-sha512";\ 1524 + created=1618884475;expires=1618884775 1525 + 1526 + Note that an HTTP message could contain multiple signatures 1527 + (Section 4.3), but only the signature parameters used for a single 1528 + signature are included in a given signature parameters entry. 1529 + 1530 + 2.4. Signing Request Components in a Response Message 1531 + 1532 + When a request message results in a signed response message, the 1533 + signer can include portions of the request message in the signature 1534 + base by adding the req parameter to the component identifier. 1535 + 1536 + req A Boolean flag indicating that the component value is derived 1537 + from the request that triggered this response message and not from 1538 + the response message directly. 1539 + 1540 + This parameter can be applied to both HTTP fields and derived 1541 + components that target the request, with the same semantics. The 1542 + component value for a message component using this parameter is 1543 + calculated in the same manner as it is normally, but data is pulled 1544 + from the request message instead of the target response message to 1545 + which the signature is applied. 1546 + 1547 + Note that the same component name MAY be included with and without 1548 + the req parameter in a single signature base, indicating the same 1549 + named component from both the request message and the response 1550 + message. 1551 + 1552 + The req parameter MAY be combined with other parameters as 1553 + appropriate for the component identifier, such as the key parameter 1554 + for a Dictionary field. 1555 + 1556 + For example, when serving a response for this request: 1557 + 1558 + NOTE: '\' line wrapping per RFC 8792 1559 + 1560 + POST /foo?param=Value&Pet=dog HTTP/1.1 1561 + Host: example.com 1562 + Date: Tue, 20 Apr 2021 02:07:55 GMT 1563 + Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+T\ 1564 + aPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: 1565 + Content-Type: application/json 1566 + Content-Length: 18 1567 + 1568 + {"hello": "world"} 1569 + 1570 + This would result in the following unsigned response message: 1571 + 1572 + NOTE: '\' line wrapping per RFC 8792 1573 + 1574 + HTTP/1.1 503 Service Unavailable 1575 + Date: Tue, 20 Apr 2021 02:07:56 GMT 1576 + Content-Type: application/json 1577 + Content-Length: 62 1578 + Content-Digest: sha-512=:0Y6iCBzGg5rZtoXS95Ijz03mslf6KAMCloESHObfwn\ 1579 + HJDbkkWWQz6PhhU9kxsTbARtY2PTBOzq24uJFpHsMuAg==: 1580 + 1581 + {"busy": true, "message": "Your call is very important to us"} 1582 + 1583 + The server signs the response with its own key, including the @status 1584 + code and several header fields in the covered components. While this 1585 + covers a reasonable amount of the response for this application, the 1586 + server additionally includes several components derived from the 1587 + original request message that triggered this response. In this 1588 + example, the server includes the method, authority, path, and content 1589 + digest from the request in the covered components of the response. 1590 + The Content-Digest for both the request and the response is included 1591 + under the response signature. For the application in this example, 1592 + the query is deemed not to be relevant to the response and is 1593 + therefore not covered. Other applications would make different 1594 + decisions based on application needs, as discussed in Section 1.4. 1595 + 1596 + The signature base for this example is: 1597 + 1598 + NOTE: '\' line wrapping per RFC 8792 1599 + 1600 + "@status": 503 1601 + "content-digest": sha-512=:0Y6iCBzGg5rZtoXS95Ijz03mslf6KAMCloESHObf\ 1602 + wnHJDbkkWWQz6PhhU9kxsTbARtY2PTBOzq24uJFpHsMuAg==: 1603 + "content-type": application/json 1604 + "@authority";req: example.com 1605 + "@method";req: POST 1606 + "@path";req: /foo 1607 + "content-digest";req: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A\ 1608 + 2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: 1609 + "@signature-params": ("@status" "content-digest" "content-type" \ 1610 + "@authority";req "@method";req "@path";req "content-digest";req)\ 1611 + ;created=1618884479;keyid="test-key-ecc-p256" 1612 + 1613 + The signed response message is: 1614 + 1615 + NOTE: '\' line wrapping per RFC 8792 1616 + 1617 + HTTP/1.1 503 Service Unavailable 1618 + Date: Tue, 20 Apr 2021 02:07:56 GMT 1619 + Content-Type: application/json 1620 + Content-Length: 62 1621 + Content-Digest: sha-512=:0Y6iCBzGg5rZtoXS95Ijz03mslf6KAMCloESHObfwn\ 1622 + HJDbkkWWQz6PhhU9kxsTbARtY2PTBOzq24uJFpHsMuAg==: 1623 + Signature-Input: reqres=("@status" "content-digest" "content-type" \ 1624 + "@authority";req "@method";req "@path";req "content-digest";req)\ 1625 + ;created=1618884479;keyid="test-key-ecc-p256" 1626 + Signature: reqres=:dMT/A/76ehrdBTD/2Xx8QuKV6FoyzEP/I9hdzKN8LQJLNgzU\ 1627 + 4W767HK05rx1i8meNQQgQPgQp8wq2ive3tV5Ag==: 1628 + 1629 + {"busy": true, "message": "Your call is very important to us"} 1630 + 1631 + Note that the ECDSA signature algorithm in use here is non- 1632 + deterministic, meaning that a different signature value will be 1633 + created every time the algorithm is run. The signature value 1634 + provided here can be validated against the given keys, but newly 1635 + generated signature values are not expected to match the example. 1636 + See Section 7.3.5. 1637 + 1638 + Since the component values from the request are not repeated in the 1639 + response message, the requester MUST keep the original message 1640 + component values around long enough to validate the signature of the 1641 + response that uses this component identifier parameter. In most 1642 + cases, this means the requester needs to keep the original request 1643 + message around, since the signer could choose to include any portions 1644 + of the request in its response, according to the needs of the 1645 + application. Since it is possible for an intermediary to alter a 1646 + request message before it is processed by the server, applications 1647 + need to take care not to sign such altered values, as the client 1648 + would not be able to validate the resulting signature. 1649 + 1650 + It is also possible for a server to create a signed response in 1651 + response to a signed request. For this example of a signed request: 1652 + 1653 + NOTE: '\' line wrapping per RFC 8792 1654 + 1655 + POST /foo?param=Value&Pet=dog HTTP/1.1 1656 + Host: example.com 1657 + Date: Tue, 20 Apr 2021 02:07:55 GMT 1658 + Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+T\ 1659 + aPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: 1660 + Content-Type: application/json 1661 + Content-Length: 18 1662 + Signature-Input: sig1=("@method" "@authority" "@path" "@query" \ 1663 + "content-digest" "content-type" "content-length")\ 1664 + ;created=1618884475;keyid="test-key-rsa-pss" 1665 + Signature: sig1=:e8UJ5wMiRaonlth5ERtE8GIiEH7Akcr493nQ07VPNo6y3qvjdK\ 1666 + t0fo8VHO8xXDjmtYoatGYBGJVlMfIp06eVMEyNW2I4vN7XDAz7m5v1108vGzaDljr\ 1667 + d0H8+SJ28g7bzn6h2xeL/8q+qUwahWA/JmC8aOC9iVnwbOKCc0WSrLgWQwTY6VLp4\ 1668 + 2Qt7jjhYT5W7/wCvfK9A1VmHH1lJXsV873Z6hpxesd50PSmO+xaNeYvDLvVdZlhtw\ 1669 + 5PCtUYzKjHqwmaQ6DEuM8udRjYsoNqp2xZKcuCO1nKc0V3RjpqMZLuuyVbHDAbCzr\ 1670 + 0pg2d2VM/OC33JAU7meEjjaNz+d7LWPg==: 1671 + 1672 + {"hello": "world"} 1673 + 1674 + The server could choose to sign portions of this response, including 1675 + several portions of the request, resulting in this signature base: 1676 + 1677 + NOTE: '\' line wrapping per RFC 8792 1678 + 1679 + "@status": 503 1680 + "content-digest": sha-512=:0Y6iCBzGg5rZtoXS95Ijz03mslf6KAMCloESHObf\ 1681 + wnHJDbkkWWQz6PhhU9kxsTbARtY2PTBOzq24uJFpHsMuAg==: 1682 + "content-type": application/json 1683 + "@authority";req: example.com 1684 + "@method";req: POST 1685 + "@path";req: /foo 1686 + "@query";req: ?param=Value&Pet=dog 1687 + "content-digest";req: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A\ 1688 + 2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: 1689 + "content-type";req: application/json 1690 + "content-length";req: 18 1691 + "@signature-params": ("@status" "content-digest" "content-type" \ 1692 + "@authority";req "@method";req "@path";req "@query";req \ 1693 + "content-digest";req "content-type";req "content-length";req)\ 1694 + ;created=1618884479;keyid="test-key-ecc-p256" 1695 + 1696 + and the following signed response: 1697 + 1698 + NOTE: '\' line wrapping per RFC 8792 1699 + 1700 + HTTP/1.1 503 Service Unavailable 1701 + Date: Tue, 20 Apr 2021 02:07:56 GMT 1702 + Content-Type: application/json 1703 + Content-Length: 62 1704 + Content-Digest: sha-512=:0Y6iCBzGg5rZtoXS95Ijz03mslf6KAMCloESHObfwn\ 1705 + HJDbkkWWQz6PhhU9kxsTbARtY2PTBOzq24uJFpHsMuAg==: 1706 + Signature-Input: reqres=("@status" "content-digest" "content-type" \ 1707 + "@authority";req "@method";req "@path";req "@query";req \ 1708 + "content-digest";req "content-type";req "content-length";req)\ 1709 + ;created=1618884479;keyid="test-key-ecc-p256" 1710 + Signature: reqres=:C73J41GVKc+TYXbSobvZf0CmNcptRiWN+NY1Or0A36ISg6ym\ 1711 + dRN6ZgR2QfrtopFNzqAyv+CeWrMsNbcV2Ojsgg==: 1712 + 1713 + {"busy": true, "message": "Your call is very important to us"} 1714 + 1715 + Note that the ECDSA signature algorithm in use here is non- 1716 + deterministic, meaning that a different signature value will be 1717 + created every time the algorithm is run. The signature value 1718 + provided here can be validated against the given keys, but newly 1719 + generated signature values are not expected to match the example. 1720 + See Section 7.3.5. 1721 + 1722 + Applications signing a response to a signed request SHOULD sign all 1723 + of the components of the request signature value to provide 1724 + sufficient coverage and protection against a class of collision 1725 + attacks, as discussed in Section 7.3.7. The server in this example 1726 + has included all components listed in the Signature-Input field of 1727 + the client's signature on the request in the response signature, in 1728 + addition to components of the response. 1729 + 1730 + While it is syntactically possible to include the Signature and 1731 + Signature-Input fields of the request message in the signature 1732 + components of a response to a message using this mechanism, this 1733 + practice is NOT RECOMMENDED. This is because signatures of 1734 + signatures do not provide transitive coverage of covered components 1735 + as one might expect, and the practice is susceptible to several 1736 + attacks as discussed in Section 7.3.7. An application that needs to 1737 + signal successful processing or receipt of a signature would need to 1738 + carefully specify alternative mechanisms for sending such a signal 1739 + securely. 1740 + 1741 + The response signature can only ever cover what is included in the 1742 + request message when using this flag. Consequently, if an 1743 + application needs to include the message content of the request under 1744 + the signature of its response, the client needs to include a means 1745 + for covering that content, such as a Content-Digest field. See the 1746 + discussion in Section 7.2.8 for more information. 1747 + 1748 + The req parameter MUST NOT be used for any component in a signature 1749 + that targets a request message. 1750 + 1751 + 2.5. Creating the Signature Base 1752 + 1753 + The signature base is an ASCII string [ASCII] containing the 1754 + canonicalized HTTP message components covered by the signature. The 1755 + input to the signature base creation algorithm is the ordered set of 1756 + covered component identifiers and their associated values, along with 1757 + any additional signature parameters discussed in Section 2.3. 1758 + 1759 + Component identifiers are serialized using the strict serialization 1760 + rules defined by [STRUCTURED-FIELDS], Section 4. The component 1761 + identifier has a component name, which is a String Item value 1762 + serialized using the sf-string ABNF rule. The component identifier 1763 + MAY also include defined parameters that are serialized using the 1764 + parameters ABNF rule. The signature parameters line defined in 1765 + Section 2.3 follows this same pattern, but the component identifier 1766 + is a String Item with a fixed value and no parameters, and the 1767 + component value is always an Inner List with optional parameters. 1768 + 1769 + Note that this means the serialization of the component name itself 1770 + is encased in double quotes, with parameters following as a 1771 + semicolon-separated list, such as "cache-control", "@authority", 1772 + "@signature-params", or "example-dictionary";key="foo". 1773 + 1774 + The output is the ordered set of bytes that form the signature base, 1775 + which conforms to the following ABNF: 1776 + 1777 + signature-base = *( signature-base-line LF ) signature-params-line 1778 + signature-base-line = component-identifier ":" SP 1779 + ( derived-component-value / *field-content ) 1780 + ; no obs-fold nor obs-text 1781 + component-identifier = component-name parameters 1782 + component-name = sf-string 1783 + derived-component-value = *( VCHAR / SP ) 1784 + signature-params-line = DQUOTE "@signature-params" DQUOTE 1785 + ":" SP inner-list 1786 + 1787 + To create the signature base, the signer or verifier concatenates 1788 + entries for each component identifier in the signature's covered 1789 + components (including their parameters) using the following 1790 + algorithm. All errors produced as described MUST fail the algorithm 1791 + immediately, without outputting a signature base. 1792 + 1793 + 1. Let the output be an empty string. 1794 + 1795 + 2. For each message component item in the covered components set (in 1796 + order): 1797 + 1798 + 2.1. If the component identifier (including its parameters) has 1799 + already been added to the signature base, produce an error. 1800 + 1801 + 2.2. Append the component identifier for the covered component 1802 + serialized according to the component-identifier ABNF rule. 1803 + Note that this serialization places the component name in 1804 + double quotes and appends any parameters outside of the 1805 + quotes. 1806 + 1807 + 2.3. Append a single colon (:). 1808 + 1809 + 2.4. Append a single space (" "). 1810 + 1811 + 2.5. Determine the component value for the component identifier. 1812 + 1813 + * If the component identifier has a parameter that is not 1814 + understood, produce an error. 1815 + 1816 + * If the component identifier has parameters that are 1817 + mutually incompatible with one another, such as bs and 1818 + sf, produce an error. 1819 + 1820 + * If the component identifier contains the req parameter 1821 + and the target message is a request, produce an error. 1822 + 1823 + * If the component identifier contains the req parameter 1824 + and the target message is a response, the context for 1825 + the component value is the related request message of 1826 + the target response message. Otherwise, the context for 1827 + the component value is the target message. 1828 + 1829 + * If the component name starts with an "at" (@) character, 1830 + derive the component's value from the message according 1831 + to the specific rules defined for the derived component, 1832 + as provided in Section 2.2, including processing of any 1833 + known valid parameters. If the derived component name 1834 + is unknown or the value cannot be derived, produce an 1835 + error. 1836 + 1837 + * If the component name does not start with an "at" (@) 1838 + character, canonicalize the HTTP field value as 1839 + described in Section 2.1, including processing of any 1840 + known valid parameters. If the field cannot be found in 1841 + the message or the value cannot be obtained in the 1842 + context, produce an error. 1843 + 1844 + 2.6. Append the covered component's canonicalized component 1845 + value. 1846 + 1847 + 2.7. Append a single newline (\n). 1848 + 1849 + 3. Append the signature parameters component (Section 2.3) according 1850 + to the signature-params-line rule as follows: 1851 + 1852 + 3.1. Append the component identifier for the signature 1853 + parameters serialized according to the component-identifier 1854 + rule, i.e., the exact value "@signature-params" (including 1855 + double quotes). 1856 + 1857 + 3.2. Append a single colon (:). 1858 + 1859 + 3.3. Append a single space (" "). 1860 + 1861 + 3.4. Append the signature parameters' canonicalized component 1862 + values as defined in Section 2.3, i.e., Inner List 1863 + Structured Field values with parameters. 1864 + 1865 + 4. Produce an error if the output string contains any non-ASCII 1866 + characters [ASCII]. 1867 + 1868 + 5. Return the output string. 1869 + 1870 + If covered components reference a component identifier that cannot be 1871 + resolved to a component value in the message, the implementation MUST 1872 + produce an error and not create a signature base. Such situations 1873 + include, but are not limited to, the following: 1874 + 1875 + * The signer or verifier does not understand the derived component 1876 + name. 1877 + 1878 + * The component name identifies a field that is not present in the 1879 + message or whose value is malformed. 1880 + 1881 + * The component identifier includes a parameter that is unknown or 1882 + does not apply to the component identifier to which it is 1883 + attached. 1884 + 1885 + * The component identifier indicates that a Structured Field 1886 + serialization is used (via the sf parameter), but the field in 1887 + question is known to not be a Structured Field or the type of 1888 + Structured Field is not known to the implementation. 1889 + 1890 + * The component identifier is a Dictionary member identifier that 1891 + references a field that is not present in the message, that is not 1892 + a Dictionary Structured Field, or whose value is malformed. 1893 + 1894 + * The component identifier is a Dictionary member identifier or a 1895 + named query parameter identifier that references a member that is 1896 + not present in the component value or whose value is malformed. 1897 + For example, the identifier is "example-dict";key="c", and the 1898 + value of the Example-Dict header field is a=1, b=2, which does not 1899 + have the c value. 1900 + 1901 + In the following non-normative example, the HTTP message being signed 1902 + is the following request: 1903 + 1904 + NOTE: '\' line wrapping per RFC 8792 1905 + 1906 + POST /foo?param=Value&Pet=dog HTTP/1.1 1907 + Host: example.com 1908 + Date: Tue, 20 Apr 2021 02:07:55 GMT 1909 + Content-Type: application/json 1910 + Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+T\ 1911 + aPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: 1912 + Content-Length: 18 1913 + 1914 + {"hello": "world"} 1915 + 1916 + The covered components consist of the @method, @authority, and @path 1917 + derived components followed by the Content-Digest, Content-Length, 1918 + and Content-Type HTTP header fields, in order. The signature 1919 + parameters consist of a creation timestamp of 1618884473 and a key 1920 + identifier of test-key-rsa-pss. Note that no explicit alg parameter 1921 + is given here, since the verifier is known by the application to use 1922 + the RSA-PSS algorithm based on the identified key. The signature 1923 + base for this message with these parameters is: 1924 + 1925 + NOTE: '\' line wrapping per RFC 8792 1926 + 1927 + "@method": POST 1928 + "@authority": example.com 1929 + "@path": /foo 1930 + "content-digest": sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX\ 1931 + +TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: 1932 + "content-length": 18 1933 + "content-type": application/json 1934 + "@signature-params": ("@method" "@authority" "@path" \ 1935 + "content-digest" "content-length" "content-type")\ 1936 + ;created=1618884473;keyid="test-key-rsa-pss" 1937 + 1938 + Figure 1: Non-normative Example Signature Base 1939 + 1940 + Note that the example signature base above does not include the final 1941 + newline that ends the displayed example, nor do other example 1942 + signature bases displayed elsewhere in this specification. 1943 + 1944 + 3. HTTP Message Signatures 1945 + 1946 + An HTTP message signature is a signature over a string generated from 1947 + a subset of the components of an HTTP message in addition to metadata 1948 + about the signature itself. When successfully verified against an 1949 + HTTP message, an HTTP message signature provides cryptographic proof 1950 + that the message is semantically equivalent to the message for which 1951 + the signature was generated, with respect to the subset of message 1952 + components that was signed. 1953 + 1954 + 3.1. Creating a Signature 1955 + 1956 + Creation of an HTTP message signature is a process that takes as its 1957 + input the signature context (including the target message) and the 1958 + requirements for the application. The output is a signature value 1959 + and set of signature parameters that can be communicated to the 1960 + verifier by adding them to the message. 1961 + 1962 + In order to create a signature, a signer MUST apply the following 1963 + algorithm: 1964 + 1965 + 1. The signer chooses an HTTP signature algorithm and key material 1966 + for signing from the set of potential signing algorithms. The 1967 + set of potential algorithms is determined by the application and 1968 + is out of scope for this document. The signer MUST choose key 1969 + material that is appropriate for the signature's algorithm and 1970 + that conforms to any requirements defined by the algorithm, such 1971 + as key size or format. The mechanism by which the signer chooses 1972 + the algorithm and key material is out of scope for this document. 1973 + 1974 + 2. The signer sets the signature's creation time to the current 1975 + time. 1976 + 1977 + 3. If applicable, the signer sets the signature's expiration time 1978 + property to the time at which the signature is to expire. The 1979 + expiration is a hint to the verifier, expressing the time at 1980 + which the signer is no longer willing to vouch for the signature. 1981 + An appropriate expiration length, and the processing requirements 1982 + of this parameter, are application specific. 1983 + 1984 + 4. The signer creates an ordered set of component identifiers 1985 + representing the message components to be covered by the 1986 + signature and attaches signature metadata parameters to this set. 1987 + The serialized value of this set is later used as the value of 1988 + the Signature-Input field as described in Section 4.1. 1989 + 1990 + * Once an order of covered components is chosen, the order MUST 1991 + NOT change for the life of the signature. 1992 + 1993 + * Each covered component identifier MUST be either (1) an HTTP 1994 + field (Section 2.1) in the signature context or (2) a derived 1995 + component listed in Section 2.2 or in the "HTTP Signature 1996 + Derived Component Names" registry. 1997 + 1998 + * Signers of a request SHOULD include some or all of the message 1999 + control data in the covered components, such as the @method, 2000 + @authority, @target-uri, or some combination thereof. 2001 + 2002 + * Signers SHOULD include the created signature metadata 2003 + parameter to indicate when the signature was created. 2004 + 2005 + * The @signature-params derived component identifier MUST NOT be 2006 + present in the list of covered component identifiers. The 2007 + derived component is required to always be the last line in 2008 + the signature base, ensuring that a signature always covers 2009 + its own metadata and the metadata cannot be substituted. 2010 + 2011 + * Further guidance on what to include in this set and in what 2012 + order is out of scope for this document. 2013 + 2014 + 5. The signer creates the signature base using these parameters and 2015 + the signature base creation algorithm (Section 2.5). 2016 + 2017 + 6. The signer uses the HTTP_SIGN primitive function to sign the 2018 + signature base with the chosen signing algorithm using the key 2019 + material chosen by the signer. The HTTP_SIGN primitive and 2020 + several concrete applications of signing algorithms are defined 2021 + in Section 3.3. 2022 + 2023 + 7. The byte array output of the signature function is the HTTP 2024 + message signature output value to be included in the Signature 2025 + field as defined in Section 4.2. 2026 + 2027 + For example, given the HTTP message and signature parameters in the 2028 + example in Section 2.5, the example signature base is signed with the 2029 + test-key-rsa-pss key (see Appendix B.1.2) and the RSASSA-PSS 2030 + algorithm described in Section 3.3.1, giving the following message 2031 + signature output value, encoded in Base64: 2032 + 2033 + NOTE: '\' line wrapping per RFC 8792 2034 + 2035 + HIbjHC5rS0BYaa9v4QfD4193TORw7u9edguPh0AW3dMq9WImrlFrCGUDih47vAxi4L2\ 2036 + YRZ3XMJc1uOKk/J0ZmZ+wcta4nKIgBkKq0rM9hs3CQyxXGxHLMCy8uqK488o+9jrptQ\ 2037 + +xFPHK7a9sRL1IXNaagCNN3ZxJsYapFj+JXbmaI5rtAdSfSvzPuBCh+ARHBmWuNo1Uz\ 2038 + VVdHXrl8ePL4cccqlazIJdC4QEjrF+Sn4IxBQzTZsL9y9TP5FsZYzHvDqbInkTNigBc\ 2039 + E9cKOYNFCn4D/WM7F6TNuZO9EgtzepLWcjTymlHzK7aXq6Am6sfOrpIC49yXjj3ae6H\ 2040 + RalVc/g== 2041 + 2042 + Figure 2: Non-normative Example Signature Value 2043 + 2044 + Note that the RSA-PSS algorithm in use here is non-deterministic, 2045 + meaning that a different signature value will be created every time 2046 + the algorithm is run. The signature value provided here can be 2047 + validated against the given keys, but newly generated signature 2048 + values are not expected to match the example. See Section 7.3.5. 2049 + 2050 + 3.2. Verifying a Signature 2051 + 2052 + Verification of an HTTP message signature is a process that takes as 2053 + its input the signature context (including the target message, 2054 + particularly its Signature and Signature-Input fields) and the 2055 + requirements for the application. The output of the verification is 2056 + either a positive verification or an error. 2057 + 2058 + In order to verify a signature, a verifier MUST apply the following 2059 + algorithm: 2060 + 2061 + 1. Parse the Signature and Signature-Input fields as described in 2062 + Sections 4.1 and 4.2, and extract the signatures to be verified 2063 + and their labels. 2064 + 2065 + 1.1. If there is more than one signature value present, 2066 + determine which signature should be processed for this 2067 + message based on the policy and configuration of the 2068 + verifier. If an applicable signature is not found, produce 2069 + an error. 2070 + 2071 + 1.2. If the chosen Signature field value does not have a 2072 + corresponding Signature-Input field value (i.e., one with 2073 + the same label), produce an error. 2074 + 2075 + 2. Parse the values of the chosen Signature-Input field as a 2076 + parameterized Inner List to get the ordered list of covered 2077 + components and the signature parameters for the signature to be 2078 + verified. 2079 + 2080 + 3. Parse the value of the corresponding Signature field to get the 2081 + byte array value of the signature to be verified. 2082 + 2083 + 4. Examine the signature parameters to confirm that the signature 2084 + meets the requirements described in this document, as well as any 2085 + additional requirements defined by the application such as which 2086 + message components are required to be covered by the signature 2087 + (Section 3.2.1). 2088 + 2089 + 5. Determine the verification key material for this signature. If 2090 + the key material is known through external means such as static 2091 + configuration or external protocol negotiation, the verifier will 2092 + use the applicable technique to obtain the key material from this 2093 + external knowledge. If the key is identified in the signature 2094 + parameters, the verifier will dereference the key identifier to 2095 + appropriate key material to use with the signature. The verifier 2096 + has to determine the trustworthiness of the key material for the 2097 + context in which the signature is presented. If a key is 2098 + identified that the verifier does not know or trust for this 2099 + request or that does not match something preconfigured, the 2100 + verification MUST fail. 2101 + 2102 + 6. Determine the algorithm to apply for verification: 2103 + 2104 + 6.1. Start with the set of allowable algorithms known to the 2105 + application. If any of the following steps select an 2106 + algorithm that is not in this set, the signature validation 2107 + fails. 2108 + 2109 + 6.2. If the algorithm is known through external means such as 2110 + static configuration or external protocol negotiation, the 2111 + verifier will use that algorithm. 2112 + 2113 + 6.3. If the algorithm can be determined from the keying 2114 + material, such as through an algorithm field on the key 2115 + value itself, the verifier will use that algorithm. 2116 + 2117 + 6.4. If the algorithm is explicitly stated in the signature 2118 + parameters using a value from the "HTTP Signature 2119 + Algorithms" registry, the verifier will use the referenced 2120 + algorithm. 2121 + 2122 + 6.5. If the algorithm is specified in more than one location 2123 + (e.g., a combination of static configuration, the algorithm 2124 + signature parameter, and the key material itself), the 2125 + resolved algorithms MUST be the same. If the algorithms 2126 + are not the same, the verifier MUST fail the verification. 2127 + 2128 + 7. Use the received HTTP message and the parsed signature parameters 2129 + to recreate the signature base, using the algorithm defined in 2130 + Section 2.5. The value of the @signature-params input is the 2131 + value of the Signature-Input field for this signature serialized 2132 + according to the rules described in Section 2.3. Note that this 2133 + does not include the signature's label from the Signature-Input 2134 + field. 2135 + 2136 + 8. If the key material is appropriate for the algorithm, apply the 2137 + appropriate HTTP_VERIFY cryptographic verification algorithm to 2138 + the signature, recalculated signature base, key material, and 2139 + signature value. The HTTP_VERIFY primitive and several concrete 2140 + algorithms are defined in Section 3.3. 2141 + 2142 + 9. The results of the verification algorithm function are the final 2143 + results of the cryptographic verification function. 2144 + 2145 + If any of the above steps fail or produce an error, the signature 2146 + validation fails. 2147 + 2148 + For example, verifying the signature with the label sig1 of the 2149 + following message with the test-key-rsa-pss key (see Appendix B.1.2) 2150 + and the RSASSA-PSS algorithm described in Section 3.3.1: 2151 + 2152 + NOTE: '\' line wrapping per RFC 8792 2153 + 2154 + POST /foo?param=Value&Pet=dog HTTP/1.1 2155 + Host: example.com 2156 + Date: Tue, 20 Apr 2021 02:07:55 GMT 2157 + Content-Type: application/json 2158 + Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+T\ 2159 + aPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: 2160 + Content-Length: 18 2161 + Signature-Input: sig1=("@method" "@authority" "@path" \ 2162 + "content-digest" "content-length" "content-type")\ 2163 + ;created=1618884473;keyid="test-key-rsa-pss" 2164 + Signature: sig1=:HIbjHC5rS0BYaa9v4QfD4193TORw7u9edguPh0AW3dMq9WImrl\ 2165 + FrCGUDih47vAxi4L2YRZ3XMJc1uOKk/J0ZmZ+wcta4nKIgBkKq0rM9hs3CQyxXGxH\ 2166 + LMCy8uqK488o+9jrptQ+xFPHK7a9sRL1IXNaagCNN3ZxJsYapFj+JXbmaI5rtAdSf\ 2167 + SvzPuBCh+ARHBmWuNo1UzVVdHXrl8ePL4cccqlazIJdC4QEjrF+Sn4IxBQzTZsL9y\ 2168 + 9TP5FsZYzHvDqbInkTNigBcE9cKOYNFCn4D/WM7F6TNuZO9EgtzepLWcjTymlHzK7\ 2169 + aXq6Am6sfOrpIC49yXjj3ae6HRalVc/g==: 2170 + 2171 + {"hello": "world"} 2172 + 2173 + With the additional requirements that at least the method, authority, 2174 + path, content-digest, content-length, and content-type entries be 2175 + signed, and that the signature creation timestamp be recent enough at 2176 + the time of verification, the verification passes. 2177 + 2178 + 3.2.1. Enforcing Application Requirements 2179 + 2180 + The verification requirements specified in this document are intended 2181 + as a baseline set of restrictions that are generally applicable to 2182 + all use cases. Applications using HTTP message signatures MAY impose 2183 + requirements above and beyond those specified by this document, as 2184 + appropriate for their use case. 2185 + 2186 + Some non-normative examples of additional requirements an application 2187 + might define are: 2188 + 2189 + * Requiring a specific set of header fields to be signed (e.g., 2190 + Authorization, Content-Digest). 2191 + 2192 + * Enforcing a maximum signature age from the time of the created 2193 + timestamp. 2194 + 2195 + * Rejecting signatures past the expiration time in the expires 2196 + timestamp. Note that the expiration time is a hint from the 2197 + signer and that a verifier can always reject a signature ahead of 2198 + its expiration time. 2199 + 2200 + * Prohibiting certain signature metadata parameters, such as runtime 2201 + algorithm signaling with the alg parameter when the algorithm is 2202 + determined from the key information. 2203 + 2204 + * Ensuring successful dereferencing of the keyid parameter to valid 2205 + and appropriate key material. 2206 + 2207 + * Prohibiting the use of certain algorithms or mandating the use of 2208 + a specific algorithm. 2209 + 2210 + * Requiring keys to be of a certain size (e.g., 2048 bits vs. 1024 2211 + bits). 2212 + 2213 + * Enforcing uniqueness of the nonce parameter. 2214 + 2215 + * Requiring an application-specific value for the tag parameter. 2216 + 2217 + Application-specific requirements are expected and encouraged. When 2218 + an application defines additional requirements, it MUST enforce them 2219 + during the signature verification process, and signature verification 2220 + MUST fail if the signature does not conform to the application's 2221 + requirements. 2222 + 2223 + Applications MUST enforce the requirements defined in this document. 2224 + Regardless of use case, applications MUST NOT accept signatures that 2225 + do not conform to these requirements. 2226 + 2227 + 3.3. Signature Algorithms 2228 + 2229 + An HTTP message signature MUST use a cryptographic digital signature 2230 + or MAC method that is appropriate for the key material, environment, 2231 + and needs of the signer and verifier. This specification does not 2232 + strictly limit the available signature algorithms, and any signature 2233 + algorithm that meets these basic requirements MAY be used by an 2234 + application of HTTP message signatures. 2235 + 2236 + For each signing method, HTTP_SIGN takes as its input the signature 2237 + base defined in Section 2.5 as a byte array (M) and the signing key 2238 + material (Ks), and outputs the resultant signature as a byte array 2239 + (S): 2240 + 2241 + HTTP_SIGN (M, Ks) -> S 2242 + 2243 + For each verification method, HTTP_VERIFY takes as its input the 2244 + regenerated signature base defined in Section 2.5 as a byte array 2245 + (M), the verification key material (Kv), and the presented signature 2246 + to be verified as a byte array (S), and outputs the verification 2247 + result (V) as a Boolean: 2248 + 2249 + HTTP_VERIFY (M, Kv, S) -> V 2250 + 2251 + The following sections contain several common signature algorithms 2252 + and demonstrate how these cryptographic primitives map to the 2253 + HTTP_SIGN and HTTP_VERIFY definitions above. Which method to use can 2254 + be communicated through the explicit algorithm (alg) signature 2255 + parameter (Section 2.3), by reference to the key material, or through 2256 + mutual agreement between the signer and verifier. Signature 2257 + algorithms selected using the alg parameter MUST use values from the 2258 + "HTTP Signature Algorithms" registry (Section 6.2). 2259 + 2260 + 3.3.1. RSASSA-PSS Using SHA-512 2261 + 2262 + To sign using this algorithm, the signer applies the RSASSA-PSS-SIGN 2263 + (K, M) function defined in [RFC8017] with the signer's private 2264 + signing key (K) and the signature base (M) (Section 2.5). The mask 2265 + generation function is MGF1 as specified in [RFC8017] with a hash 2266 + function of SHA-512 [RFC6234]. The salt length (sLen) is 64 bytes. 2267 + The hash function (Hash) SHA-512 [RFC6234] is applied to the 2268 + signature base to create the digest content to which the digital 2269 + signature is applied. The resulting signed content byte array (S) is 2270 + the HTTP message signature output used in Section 3.1. 2271 + 2272 + To verify using this algorithm, the verifier applies the RSASSA-PSS- 2273 + VERIFY ((n, e), M, S) function [RFC8017] using the public key portion 2274 + of the verification key material (n, e) and the signature base (M) 2275 + recreated as described in Section 3.2. The mask generation function 2276 + is MGF1 as specified in [RFC8017] with a hash function of SHA-512 2277 + [RFC6234]. The salt length (sLen) is 64 bytes. The hash function 2278 + (Hash) SHA-512 [RFC6234] is applied to the signature base to create 2279 + the digest content to which the verification function is applied. 2280 + The verifier extracts the HTTP message signature to be verified (S) 2281 + as described in Section 3.2. The results of the verification 2282 + function indicate whether the signature presented is valid. 2283 + 2284 + Note that the output of the RSASSA-PSS algorithm is non- 2285 + deterministic; therefore, it is not correct to recalculate a new 2286 + signature on the signature base and compare the results to an 2287 + existing signature. Instead, the verification algorithm defined here 2288 + needs to be used. See Section 7.3.5. 2289 + 2290 + The use of this algorithm can be indicated at runtime using the rsa- 2291 + pss-sha512 value for the alg signature parameter. 2292 + 2293 + 3.3.2. RSASSA-PKCS1-v1_5 Using SHA-256 2294 + 2295 + To sign using this algorithm, the signer applies the RSASSA- 2296 + PKCS1-V1_5-SIGN (K, M) function defined in [RFC8017] with the 2297 + signer's private signing key (K) and the signature base (M) 2298 + (Section 2.5). The hash SHA-256 [RFC6234] is applied to the 2299 + signature base to create the digest content to which the digital 2300 + signature is applied. The resulting signed content byte array (S) is 2301 + the HTTP message signature output used in Section 3.1. 2302 + 2303 + To verify using this algorithm, the verifier applies the RSASSA- 2304 + PKCS1-V1_5-VERIFY ((n, e), M, S) function [RFC8017] using the public 2305 + key portion of the verification key material (n, e) and the signature 2306 + base (M) recreated as described in Section 3.2. The hash function 2307 + SHA-256 [RFC6234] is applied to the signature base to create the 2308 + digest content to which the verification function is applied. The 2309 + verifier extracts the HTTP message signature to be verified (S) as 2310 + described in Section 3.2. The results of the verification function 2311 + indicate whether the signature presented is valid. 2312 + 2313 + The use of this algorithm can be indicated at runtime using the rsa- 2314 + v1_5-sha256 value for the alg signature parameter. 2315 + 2316 + 3.3.3. HMAC Using SHA-256 2317 + 2318 + To sign and verify using this algorithm, the signer applies the HMAC 2319 + function [RFC2104] with the shared signing key (K) and the signature 2320 + base (text) (Section 2.5). The hash function SHA-256 [RFC6234] is 2321 + applied to the signature base to create the digest content to which 2322 + the HMAC is applied, giving the signature result. 2323 + 2324 + For signing, the resulting value is the HTTP message signature output 2325 + used in Section 3.1. 2326 + 2327 + For verification, the verifier extracts the HTTP message signature to 2328 + be verified (S) as described in Section 3.2. The output of the HMAC 2329 + function is compared bytewise to the value of the HTTP message 2330 + signature, and the results of the comparison determine the validity 2331 + of the signature presented. 2332 + 2333 + The use of this algorithm can be indicated at runtime using the hmac- 2334 + sha256 value for the alg signature parameter. 2335 + 2336 + 3.3.4. ECDSA Using Curve P-256 DSS and SHA-256 2337 + 2338 + To sign using this algorithm, the signer applies the ECDSA signature 2339 + algorithm defined in [FIPS186-5] using curve P-256 with the signer's 2340 + private signing key and the signature base (Section 2.5). The hash 2341 + SHA-256 [RFC6234] is applied to the signature base to create the 2342 + digest content to which the digital signature is applied (M). The 2343 + signature algorithm returns two integer values: r and s. These are 2344 + both encoded as big-endian unsigned integers, zero-padded to 32 2345 + octets each. These encoded values are concatenated into a single 2346 + 64-octet array consisting of the encoded value of r followed by the 2347 + encoded value of s. The resulting concatenation of (r, s) is a byte 2348 + array of the HTTP message signature output used in Section 3.1. 2349 + 2350 + To verify using this algorithm, the verifier applies the ECDSA 2351 + signature algorithm defined in [FIPS186-5] using the public key 2352 + portion of the verification key material and the signature base 2353 + recreated as described in Section 3.2. The hash function SHA-256 2354 + [RFC6234] is applied to the signature base to create the digest 2355 + content to which the signature verification function is applied (M). 2356 + The verifier extracts the HTTP message signature to be verified (S) 2357 + as described in Section 3.2. This value is a 64-octet array 2358 + consisting of the encoded values of r and s concatenated in order. 2359 + These are both encoded as big-endian unsigned integers, zero-padded 2360 + to 32 octets each. The resulting signature value (r, s) is used as 2361 + input to the signature verification function. The results of the 2362 + verification function indicate whether the signature presented is 2363 + valid. 2364 + 2365 + Note that the output of ECDSA signature algorithms is non- 2366 + deterministic; therefore, it is not correct to recalculate a new 2367 + signature on the signature base and compare the results to an 2368 + existing signature. Instead, the verification algorithm defined here 2369 + needs to be used. See Section 7.3.5. 2370 + 2371 + The use of this algorithm can be indicated at runtime using the 2372 + ecdsa-p256-sha256 value for the alg signature parameter. 2373 + 2374 + 3.3.5. ECDSA Using Curve P-384 DSS and SHA-384 2375 + 2376 + To sign using this algorithm, the signer applies the ECDSA signature 2377 + algorithm defined in [FIPS186-5] using curve P-384 with the signer's 2378 + private signing key and the signature base (Section 2.5). The hash 2379 + SHA-384 [RFC6234] is applied to the signature base to create the 2380 + digest content to which the digital signature is applied (M). The 2381 + signature algorithm returns two integer values: r and s. These are 2382 + both encoded as big-endian unsigned integers, zero-padded to 48 2383 + octets each. These encoded values are concatenated into a single 2384 + 96-octet array consisting of the encoded value of r followed by the 2385 + encoded value of s. The resulting concatenation of (r, s) is a byte 2386 + array of the HTTP message signature output used in Section 3.1. 2387 + 2388 + To verify using this algorithm, the verifier applies the ECDSA 2389 + signature algorithm defined in [FIPS186-5] using the public key 2390 + portion of the verification key material and the signature base 2391 + recreated as described in Section 3.2. The hash function SHA-384 2392 + [RFC6234] is applied to the signature base to create the digest 2393 + content to which the signature verification function is applied (M). 2394 + The verifier extracts the HTTP message signature to be verified (S) 2395 + as described in Section 3.2. This value is a 96-octet array 2396 + consisting of the encoded values of r and s concatenated in order. 2397 + These are both encoded as big-endian unsigned integers, zero-padded 2398 + to 48 octets each. The resulting signature value (r, s) is used as 2399 + input to the signature verification function. The results of the 2400 + verification function indicate whether the signature presented is 2401 + valid. 2402 + 2403 + Note that the output of ECDSA signature algorithms is non- 2404 + deterministic; therefore, it is not correct to recalculate a new 2405 + signature on the signature base and compare the results to an 2406 + existing signature. Instead, the verification algorithm defined here 2407 + needs to be used. See Section 7.3.5. 2408 + 2409 + The use of this algorithm can be indicated at runtime using the 2410 + ecdsa-p384-sha384 value for the alg signature parameter. 2411 + 2412 + 3.3.6. EdDSA Using Curve edwards25519 2413 + 2414 + To sign using this algorithm, the signer applies the Ed25519 2415 + algorithm defined in Section 5.1.6 of [RFC8032] with the signer's 2416 + private signing key and the signature base (Section 2.5). The 2417 + signature base is taken as the input message (M) with no prehash 2418 + function. The signature is a 64-octet concatenation of R and S as 2419 + specified in Section 5.1.6 of [RFC8032], and this is taken as a byte 2420 + array for the HTTP message signature output used in Section 3.1. 2421 + 2422 + To verify using this algorithm, the signer applies the Ed25519 2423 + algorithm defined in Section 5.1.7 of [RFC8032] using the public key 2424 + portion of the verification key material (A) and the signature base 2425 + recreated as described in Section 3.2. The signature base is taken 2426 + as the input message (M) with no prehash function. The signature to 2427 + be verified is processed as the 64-octet concatenation of R and S as 2428 + specified in Section 5.1.7 of [RFC8032]. The results of the 2429 + verification function indicate whether the signature presented is 2430 + valid. 2431 + 2432 + The use of this algorithm can be indicated at runtime using the 2433 + ed25519 value for the alg signature parameter. 2434 + 2435 + 3.3.7. JSON Web Signature (JWS) Algorithms 2436 + 2437 + If the signing algorithm is a JSON Object Signing and Encryption 2438 + (JOSE) signing algorithm from the "JSON Web Signature and Encryption 2439 + Algorithms" registry established by [RFC7518], the JWS algorithm 2440 + definition determines the signature and hashing algorithms to apply 2441 + for both signing and verification. 2442 + 2443 + For both signing and verification, the HTTP message's signature base 2444 + (Section 2.5) is used as the entire "JWS Signing Input". The JOSE 2445 + Header [JWS] [RFC7517] is not used, and the signature base is not 2446 + first encoded in Base64 before applying the algorithm. The output of 2447 + the JWS Signature is taken as a byte array prior to the Base64url 2448 + encoding used in JOSE. 2449 + 2450 + The JWS algorithm MUST NOT be "none" and MUST NOT be any algorithm 2451 + with a JOSE Implementation Requirement of "Prohibited". 2452 + 2453 + JSON Web Algorithm (JWA) values from the "JSON Web Signature and 2454 + Encryption Algorithms" registry are not included as signature 2455 + parameters. Typically, the JWS algorithm can be signaled using JSON 2456 + Web Keys (JWKs) or other mechanisms common to JOSE implementations. 2457 + In fact, JWA values are not registered in the "HTTP Signature 2458 + Algorithms" registry (Section 6.2), and so the explicit alg signature 2459 + parameter is not used at all when using JOSE signing algorithms. 2460 + 2461 + 4. Including a Message Signature in a Message 2462 + 2463 + HTTP message signatures can be included within an HTTP message via 2464 + the Signature-Input and Signature fields, both defined within this 2465 + specification. 2466 + 2467 + The Signature-Input field identifies the covered components and 2468 + parameters that describe how the signature was generated, while the 2469 + Signature field contains the signature value. Each field MAY contain 2470 + multiple labeled values. 2471 + 2472 + An HTTP message signature is identified by a label within an HTTP 2473 + message. This label MUST be unique within a given HTTP message and 2474 + MUST be used in both the Signature-Input field and the Signature 2475 + field. The label is chosen by the signer, except where a specific 2476 + label is dictated by protocol negotiations such as those described in 2477 + Section 5. 2478 + 2479 + An HTTP message signature MUST use both the Signature-Input field and 2480 + the Signature field, and each field MUST contain the same labels. 2481 + The presence of a label in one field but not the other is an error. 2482 + 2483 + 4.1. The Signature-Input HTTP Field 2484 + 2485 + The Signature-Input field is a Dictionary Structured Field (defined 2486 + in Section 3.2 of [STRUCTURED-FIELDS]) containing the metadata for 2487 + one or more message signatures generated from components within the 2488 + HTTP message. Each member describes a single message signature. The 2489 + member's key is the label that uniquely identifies the message 2490 + signature within the HTTP message. The member's value is the covered 2491 + components ordered set serialized as an Inner List, including all 2492 + signature metadata parameters identified by the label: 2493 + 2494 + NOTE: '\' line wrapping per RFC 8792 2495 + 2496 + Signature-Input: sig1=("@method" "@target-uri" "@authority" \ 2497 + "content-digest" "cache-control");\ 2498 + created=1618884475;keyid="test-key-rsa-pss" 2499 + 2500 + To facilitate signature validation, the Signature-Input field value 2501 + MUST contain the same serialized value used in generating the 2502 + signature base's @signature-params value defined in Section 2.3. 2503 + Note that in a Structured Field value, list order and parameter order 2504 + have to be preserved. 2505 + 2506 + The signer MAY include the Signature-Input field as a trailer to 2507 + facilitate signing a message after its content has been processed by 2508 + the signer. However, since intermediaries are allowed to drop 2509 + trailers as per [HTTP], it is RECOMMENDED that the Signature-Input 2510 + field be included only as a header field to avoid signatures being 2511 + inadvertently stripped from a message. 2512 + 2513 + Multiple Signature-Input fields MAY be included in a single HTTP 2514 + message. The signature labels MUST be unique across all field 2515 + values. 2516 + 2517 + 4.2. The Signature HTTP Field 2518 + 2519 + The Signature field is a Dictionary Structured Field (defined in 2520 + Section 3.2 of [STRUCTURED-FIELDS]) containing one or more message 2521 + signatures generated from the signature context of the target 2522 + message. The member's key is the label that uniquely identifies the 2523 + message signature within the HTTP message. The member's value is a 2524 + Byte Sequence containing the signature value for the message 2525 + signature identified by the label: 2526 + 2527 + NOTE: '\' line wrapping per RFC 8792 2528 + 2529 + Signature: sig1=:P0wLUszWQjoi54udOtydf9IWTfNhy+r53jGFj9XZuP4uKwxyJo\ 2530 + 1RSHi+oEF1FuX6O29d+lbxwwBao1BAgadijW+7O/PyezlTnqAOVPWx9GlyntiCiHz\ 2531 + C87qmSQjvu1CFyFuWSjdGa3qLYYlNm7pVaJFalQiKWnUaqfT4LyttaXyoyZW84jS8\ 2532 + gyarxAiWI97mPXU+OVM64+HVBHmnEsS+lTeIsEQo36T3NFf2CujWARPQg53r58Rmp\ 2533 + Z+J9eKR2CD6IJQvacn5A4Ix5BUAVGqlyp8JYm+S/CWJi31PNUjRRCusCVRj05NrxA\ 2534 + BNFv3r5S9IXf2fYJK+eyW4AiGVMvMcOg==: 2535 + 2536 + The signer MAY include the Signature field as a trailer to facilitate 2537 + signing a message after its content has been processed by the signer. 2538 + However, since intermediaries are allowed to drop trailers as per 2539 + [HTTP], it is RECOMMENDED that the Signature field be included only 2540 + as a header field to avoid signatures being inadvertently stripped 2541 + from a message. 2542 + 2543 + Multiple Signature fields MAY be included in a single HTTP message. 2544 + The signature labels MUST be unique across all field values. 2545 + 2546 + 4.3. Multiple Signatures 2547 + 2548 + Multiple distinct signatures MAY be included in a single message. 2549 + Each distinct signature MUST have a unique label. These multiple 2550 + signatures could all be added by the same signer, or they could come 2551 + from several different signers. For example, a signer may include 2552 + multiple signatures signing the same message components with 2553 + different keys or algorithms to support verifiers with different 2554 + capabilities, or a reverse proxy may include information about the 2555 + client in fields when forwarding the request to a service host, 2556 + including a signature over the client's original signature values. 2557 + 2558 + The following non-normative example starts with a signed request from 2559 + the client. A reverse proxy takes this request and validates the 2560 + client's signature: 2561 + 2562 + NOTE: '\' line wrapping per RFC 8792 2563 + 2564 + POST /foo?param=Value&Pet=dog HTTP/1.1 2565 + Host: example.com 2566 + Date: Tue, 20 Apr 2021 02:07:55 GMT 2567 + Content-Type: application/json 2568 + Content-Length: 18 2569 + Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+T\ 2570 + aPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: 2571 + Signature-Input: sig1=("@method" "@authority" "@path" \ 2572 + "content-digest" "content-type" "content-length")\ 2573 + ;created=1618884475;keyid="test-key-ecc-p256" 2574 + Signature: sig1=:X5spyd6CFnAG5QnDyHfqoSNICd+BUP4LYMz2Q0JXlb//4Ijpzp\ 2575 + +kve2w4NIyqeAuM7jTDX+sNalzA8ESSaHD3A==: 2576 + 2577 + {"hello": "world"} 2578 + 2579 + The proxy then alters the message before forwarding it on to the 2580 + origin server, changing the target host and adding the Forwarded 2581 + header field defined in [RFC7239]: 2582 + 2583 + NOTE: '\' line wrapping per RFC 8792 2584 + 2585 + POST /foo?param=Value&Pet=dog HTTP/1.1 2586 + Host: origin.host.internal.example 2587 + Date: Tue, 20 Apr 2021 02:07:56 GMT 2588 + Content-Type: application/json 2589 + Content-Length: 18 2590 + Forwarded: for=192.0.2.123;host=example.com;proto=https 2591 + Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+T\ 2592 + aPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: 2593 + Signature-Input: sig1=("@method" "@authority" "@path" \ 2594 + "content-digest" "content-type" "content-length")\ 2595 + ;created=1618884475;keyid="test-key-ecc-p256" 2596 + Signature: sig1=:X5spyd6CFnAG5QnDyHfqoSNICd+BUP4LYMz2Q0JXlb//4Ijpzp\ 2597 + +kve2w4NIyqeAuM7jTDX+sNalzA8ESSaHD3A==: 2598 + 2599 + {"hello": "world"} 2600 + 2601 + The proxy is in a position to validate the incoming client's 2602 + signature and make its own statement to the origin server about the 2603 + nature of the request that it is forwarding by adding its own 2604 + signature over the new message before passing it along to the origin 2605 + server. The proxy also includes all the elements from the original 2606 + message that are relevant to the origin server's processing. In many 2607 + cases, the proxy will want to cover all the same components that were 2608 + covered by the client's signature, which is the case in the following 2609 + example. Note that in this example, the proxy is signing over the 2610 + new authority value, which it has changed. The proxy also adds the 2611 + Forwarded header field to its own signature value. The proxy 2612 + identifies its own key and algorithm and, in this example, includes 2613 + an expiration for the signature to indicate to downstream systems 2614 + that the proxy will not vouch for this signed message past this short 2615 + time window. This results in a signature base of: 2616 + 2617 + NOTE: '\' line wrapping per RFC 8792 2618 + 2619 + "@method": POST 2620 + "@authority": origin.host.internal.example 2621 + "@path": /foo 2622 + "content-digest": sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX\ 2623 + +TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: 2624 + "content-type": application/json 2625 + "content-length": 18 2626 + "forwarded": for=192.0.2.123;host=example.com;proto=https 2627 + "@signature-params": ("@method" "@authority" "@path" \ 2628 + "content-digest" "content-type" "content-length" "forwarded")\ 2629 + ;created=1618884480;keyid="test-key-rsa";alg="rsa-v1_5-sha256"\ 2630 + ;expires=1618884540 2631 + 2632 + and a signature output value of: 2633 + 2634 + NOTE: '\' line wrapping per RFC 8792 2635 + 2636 + S6ZzPXSdAMOPjN/6KXfXWNO/f7V6cHm7BXYUh3YD/fRad4BCaRZxP+JH+8XY1I6+8Cy\ 2637 + +CM5g92iHgxtRPz+MjniOaYmdkDcnL9cCpXJleXsOckpURl49GwiyUpZ10KHgOEe11s\ 2638 + x3G2gxI8S0jnxQB+Pu68U9vVcasqOWAEObtNKKZd8tSFu7LB5YAv0RAGhB8tmpv7sFn\ 2639 + Im9y+7X5kXQfi8NMaZaA8i2ZHwpBdg7a6CMfwnnrtflzvZdXAsD3LH2TwevU+/PBPv0\ 2640 + B6NMNk93wUs/vfJvye+YuI87HU38lZHowtznbLVdp770I6VHR6WfgS9ddzirrswsE1w\ 2641 + 5o0LV/g== 2642 + 2643 + These values are added to the HTTP request message by the proxy. The 2644 + original signature is included under the label sig1, and the reverse 2645 + proxy's signature is included under the label proxy_sig. The proxy 2646 + uses the key test-key-rsa to create its signature using the rsa- 2647 + v1_5-sha256 signature algorithm, while the client's original 2648 + signature was made using the key test-key-rsa-pss and an RSA-PSS 2649 + signature algorithm: 2650 + 2651 + NOTE: '\' line wrapping per RFC 8792 2652 + 2653 + POST /foo?param=Value&Pet=dog HTTP/1.1 2654 + Host: origin.host.internal.example 2655 + Date: Tue, 20 Apr 2021 02:07:56 GMT 2656 + Content-Type: application/json 2657 + Content-Length: 18 2658 + Forwarded: for=192.0.2.123;host=example.com;proto=https 2659 + Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+T\ 2660 + aPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: 2661 + Signature-Input: sig1=("@method" "@authority" "@path" \ 2662 + "content-digest" "content-type" "content-length")\ 2663 + ;created=1618884475;keyid="test-key-ecc-p256", \ 2664 + proxy_sig=("@method" "@authority" "@path" "content-digest" \ 2665 + "content-type" "content-length" "forwarded")\ 2666 + ;created=1618884480;keyid="test-key-rsa";alg="rsa-v1_5-sha256"\ 2667 + ;expires=1618884540 2668 + Signature: sig1=:X5spyd6CFnAG5QnDyHfqoSNICd+BUP4LYMz2Q0JXlb//4Ijpzp\ 2669 + +kve2w4NIyqeAuM7jTDX+sNalzA8ESSaHD3A==:, \ 2670 + proxy_sig=:S6ZzPXSdAMOPjN/6KXfXWNO/f7V6cHm7BXYUh3YD/fRad4BCaRZxP+\ 2671 + JH+8XY1I6+8Cy+CM5g92iHgxtRPz+MjniOaYmdkDcnL9cCpXJleXsOckpURl49G\ 2672 + wiyUpZ10KHgOEe11sx3G2gxI8S0jnxQB+Pu68U9vVcasqOWAEObtNKKZd8tSFu7\ 2673 + LB5YAv0RAGhB8tmpv7sFnIm9y+7X5kXQfi8NMaZaA8i2ZHwpBdg7a6CMfwnnrtf\ 2674 + lzvZdXAsD3LH2TwevU+/PBPv0B6NMNk93wUs/vfJvye+YuI87HU38lZHowtznbL\ 2675 + Vdp770I6VHR6WfgS9ddzirrswsE1w5o0LV/g==: 2676 + 2677 + {"hello": "world"} 2678 + 2679 + While the proxy could additionally include the client's Signature 2680 + field value and Signature-Input fields from the original message in 2681 + the new signature's covered components, this practice is NOT 2682 + RECOMMENDED due to known weaknesses in signing signature values as 2683 + discussed in Section 7.3.7. The proxy is in a position to validate 2684 + the client's signature; the changes the proxy makes to the message 2685 + will invalidate the existing signature when the message is seen by 2686 + the origin server. In this example, it is possible for the origin 2687 + server to have additional information in its signature context to 2688 + account for the change in authority, though this practice requires 2689 + additional configuration and extra care as discussed in 2690 + Section 7.4.4. In other applications, the origin server will not be 2691 + able to verify the original signature itself but will still want to 2692 + verify that the proxy has done the appropriate validation of the 2693 + client's signature. An application that needs to signal successful 2694 + processing or receipt of a signature would need to carefully specify 2695 + alternative mechanisms for sending such a signal securely. 2696 + 2697 + 5. Requesting Signatures 2698 + 2699 + While a signer is free to attach a signature to a request or response 2700 + without prompting, it is often desirable for a potential verifier to 2701 + signal that it expects a signature from a potential signer using the 2702 + Accept-Signature field. 2703 + 2704 + When the Accept-Signature field is sent in an HTTP request message, 2705 + the field indicates that the client desires the server to sign the 2706 + response using the identified parameters, and the target message is 2707 + the response to this request. All responses from resources that 2708 + support such signature negotiation SHOULD either be uncacheable or 2709 + contain a Vary header field that lists Accept-Signature, in order to 2710 + prevent a cache from returning a response with a signature intended 2711 + for a different request. 2712 + 2713 + When the Accept-Signature field is used in an HTTP response message, 2714 + the field indicates that the server desires the client to sign its 2715 + next request to the server with the identified parameters, and the 2716 + target message is the client's next request. The client can choose 2717 + to also continue signing future requests to the same server in the 2718 + same way. 2719 + 2720 + The target message of an Accept-Signature field MUST include all 2721 + labeled signatures indicated in the Accept-Signature field, each 2722 + covering the same identified components of the Accept-Signature 2723 + field. 2724 + 2725 + The sender of an Accept-Signature field MUST include only identifiers 2726 + that are appropriate for the type of the target message. For 2727 + example, if the target message is a request, the covered components 2728 + cannot include the @status component identifier. 2729 + 2730 + 5.1. The Accept-Signature Field 2731 + 2732 + The Accept-Signature field is a Dictionary Structured Field (defined 2733 + in Section 3.2 of [STRUCTURED-FIELDS]) containing the metadata for 2734 + one or more requested message signatures to be generated from message 2735 + components of the target HTTP message. Each member describes a 2736 + single message signature. The member's key is the label that 2737 + uniquely identifies the requested message signature within the 2738 + context of the target HTTP message. 2739 + 2740 + The member's value is the serialization of the desired covered 2741 + components of the target message, including any allowed component 2742 + metadata parameters, using the serialization process defined in 2743 + Section 2.3: 2744 + 2745 + NOTE: '\' line wrapping per RFC 8792 2746 + 2747 + Accept-Signature: sig1=("@method" "@target-uri" "@authority" \ 2748 + "content-digest" "cache-control");\ 2749 + keyid="test-key-rsa-pss";created;tag="app-123" 2750 + 2751 + The list of component identifiers indicates the exact set of 2752 + component identifiers to be included in the requested signature, 2753 + including all applicable component parameters. 2754 + 2755 + The signature request MAY include signature metadata parameters that 2756 + indicate desired behavior for the signer. The following behavior is 2757 + defined by this specification: 2758 + 2759 + created: The signer is requested to generate and include a creation 2760 + time. This parameter has no associated value when sent as a 2761 + signature request. 2762 + 2763 + expires: The signer is requested to generate and include an 2764 + expiration time. This parameter has no associated value when sent 2765 + as a signature request. 2766 + 2767 + nonce: The signer is requested to include the value of this 2768 + parameter as the signature nonce in the target signature. 2769 + 2770 + alg: The signer is requested to use the indicated signature 2771 + algorithm from the "HTTP Signature Algorithms" registry to create 2772 + the target signature. 2773 + 2774 + keyid: The signer is requested to use the indicated key material to 2775 + create the target signature. 2776 + 2777 + tag: The signer is requested to include the value of this parameter 2778 + as the signature tag in the target signature. 2779 + 2780 + 5.2. Processing an Accept-Signature 2781 + 2782 + The receiver of an Accept-Signature field fulfills that header as 2783 + follows: 2784 + 2785 + 1. Parse the field value as a Dictionary. 2786 + 2787 + 2. For each member of the Dictionary: 2788 + 2789 + 2.1. The key is taken as the label of the output signature as 2790 + specified in Section 4.1. 2791 + 2792 + 2.2. Parse the value of the member to obtain the set of covered 2793 + component identifiers. 2794 + 2795 + 2.3. Determine that the covered components are applicable to the 2796 + target message. If not, the process fails and returns an 2797 + error. 2798 + 2799 + 2.4. Process the requested parameters, such as the signing 2800 + algorithm and key material. If any requested parameters 2801 + cannot be fulfilled or if the requested parameters conflict 2802 + with those deemed appropriate to the target message, the 2803 + process fails and returns an error. 2804 + 2805 + 2.5. Select and generate any additional parameters necessary for 2806 + completing the signature. 2807 + 2808 + 2.6. Create the HTTP message signature over the target message. 2809 + 2810 + 2.7. Create the Signature-Input and Signature field values, and 2811 + associate them with the label. 2812 + 2813 + 3. Optionally create any additional Signature-Input and Signature 2814 + field values, with unique labels not found in the Accept- 2815 + Signature field. 2816 + 2817 + 4. Combine all labeled Signature-Input and Signature field values, 2818 + and attach both fields to the target message. 2819 + 2820 + By this process, a signature applied to a target message MUST have 2821 + the same label, MUST include the same set of covered components, MUST 2822 + process all requested parameters, and MAY have additional parameters. 2823 + 2824 + The receiver of an Accept-Signature field MAY ignore any signature 2825 + request that does not fit application parameters. 2826 + 2827 + The target message MAY include additional signatures not specified by 2828 + the Accept-Signature field. For example, to cover additional message 2829 + components, the signer can create a second signature that includes 2830 + the additional components as well as the signature output of the 2831 + requested signature. 2832 + 2833 + 6. IANA Considerations 2834 + 2835 + IANA has updated one registry and created four new registries, 2836 + according to the following sections. 2837 + 2838 + 6.1. HTTP Field Name Registration 2839 + 2840 + IANA has updated the entries in the "Hypertext Transfer Protocol 2841 + (HTTP) Field Name Registry" as follows: 2842 + 2843 + +==================+===========+=========================+ 2844 + | Field Name | Status | Reference | 2845 + +==================+===========+=========================+ 2846 + | Signature-Input | permanent | Section 4.1 of RFC 9421 | 2847 + +------------------+-----------+-------------------------+ 2848 + | Signature | permanent | Section 4.2 of RFC 9421 | 2849 + +------------------+-----------+-------------------------+ 2850 + | Accept-Signature | permanent | Section 5.1 of RFC 9421 | 2851 + +------------------+-----------+-------------------------+ 2852 + 2853 + Table 1: Updates to the Hypertext Transfer Protocol 2854 + (HTTP) Field Name Registry 2855 + 2856 + 6.2. HTTP Signature Algorithms Registry 2857 + 2858 + This document defines HTTP signature algorithms, for which IANA has 2859 + created and now maintains a new registry titled "HTTP Signature 2860 + Algorithms". Initial values for this registry are given in 2861 + Section 6.2.2. Future assignments and modifications to existing 2862 + assignments are to be made through the Specification Required 2863 + registration policy [RFC8126]. 2864 + 2865 + The algorithms listed in this registry identify some possible 2866 + cryptographic algorithms for applications to use with this 2867 + specification, but the entries neither represent an exhaustive list 2868 + of possible algorithms nor indicate fitness for purpose with any 2869 + particular application of this specification. An application is free 2870 + to implement any algorithm that suits its needs, provided the signer 2871 + and verifier can agree to the parameters of that algorithm in a 2872 + secure and deterministic fashion. When an application needs to 2873 + signal the use of a particular algorithm at runtime using the alg 2874 + signature parameter, this registry provides a mapping between the 2875 + value of that parameter and a particular algorithm. However, the use 2876 + of the alg parameter needs to be treated with caution to avoid 2877 + various forms of algorithm confusion and substitution attacks, as 2878 + discussed in Section 7. 2879 + 2880 + The Status value should reflect standardization status and the broad 2881 + opinion of relevant interest groups such as the IETF or security- 2882 + related Standards Development Organizations (SDOs). When an 2883 + algorithm is first registered, the designated expert (DE) should set 2884 + the Status field to "Active" if there is consensus for the algorithm 2885 + to be generally recommended as secure or "Provisional" if the 2886 + algorithm has not reached that consensus, e.g., for an experimental 2887 + algorithm. A status of "Provisional" does not mean that the 2888 + algorithm is known to be insecure but instead indicates that the 2889 + algorithm has not reached consensus regarding its properties. If at 2890 + a future time the algorithm as registered is found to have flaws, the 2891 + registry entry can be updated and the algorithm can be marked as 2892 + "Deprecated" to indicate that the algorithm has been found to have 2893 + problems. This status does not preclude an application from using a 2894 + particular algorithm; rather, it serves to provide a warning 2895 + regarding possible known issues with an algorithm that need to be 2896 + considered by the application. The DE can further ensure that the 2897 + registration includes an explanation and reference for the Status 2898 + value; this is particularly important for deprecated algorithms. 2899 + 2900 + The DE is expected to do the following: 2901 + 2902 + * Ensure that the algorithms referenced by a registered algorithm 2903 + identifier are fully defined with all parameters (e.g., salt, 2904 + hash, required key length) fixed by the defining text. 2905 + 2906 + * Ensure that the algorithm definition fully specifies the HTTP_SIGN 2907 + and HTTP_VERIFY primitive functions, including how all defined 2908 + inputs and outputs map to the underlying cryptographic algorithm. 2909 + 2910 + * Reject any registrations that are aliases of existing 2911 + registrations. 2912 + 2913 + * Ensure that all registrations follow the template presented in 2914 + Section 6.2.1; this includes ensuring that the length of the name 2915 + is not excessive while still being unique and recognizable. 2916 + 2917 + This specification creates algorithm identifiers by including major 2918 + parameters in the identifier String in order to make the algorithm 2919 + name unique and recognizable by developers. However, algorithm 2920 + identifiers in this registry are to be interpreted as whole String 2921 + values and not as a combination of parts. That is to say, it is 2922 + expected that implementors understand rsa-pss-sha512 as referring to 2923 + one specific algorithm with its hash, mask, and salt values set as 2924 + defined in the defining text that establishes the identifier in 2925 + question. Implementors do not parse out the rsa, pss, and sha512 2926 + portions of the identifier to determine parameters of the signing 2927 + algorithm from the String, and the registration of one combination of 2928 + parameters does not imply the registration of other combinations. 2929 + 2930 + 6.2.1. Registration Template 2931 + 2932 + Algorithm Name: 2933 + An identifier for the HTTP signature algorithm. The name MUST be 2934 + an ASCII string that conforms to the sf-string ABNF rule in 2935 + Section 3.3.3 of [STRUCTURED-FIELDS] and SHOULD NOT exceed 20 2936 + characters in length. The identifier MUST be unique within the 2937 + context of the registry. 2938 + 2939 + Description: 2940 + A brief description of the algorithm used to sign the signature 2941 + base. 2942 + 2943 + Status: 2944 + The status of the algorithm. MUST start with one of the following 2945 + values and MAY contain additional explanatory text. The options 2946 + are: 2947 + 2948 + "Active": For algorithms without known problems. The signature 2949 + algorithm is fully specified, and its security properties are 2950 + understood. 2951 + 2952 + "Provisional": For unproven algorithms. The signature algorithm 2953 + is fully specified, but its security properties are not known 2954 + or proven. 2955 + 2956 + "Deprecated": For algorithms with known security issues. The 2957 + signature algorithm is no longer recommended for general use 2958 + and might be insecure or unsafe in some known circumstances. 2959 + 2960 + Reference: 2961 + Reference to the document or documents that specify the algorithm, 2962 + preferably including a URI that can be used to retrieve a copy of 2963 + the document(s). An indication of the relevant sections may also 2964 + be included but is not required. 2965 + 2966 + 6.2.2. Initial Contents 2967 + 2968 + The table below contains the initial contents of the "HTTP Signature 2969 + Algorithms" registry. 2970 + 2971 + +===================+===================+========+===============+ 2972 + | Algorithm Name | Description | Status | Reference | 2973 + +===================+===================+========+===============+ 2974 + | rsa-pss-sha512 | RSASSA-PSS using | Active | Section 3.3.1 | 2975 + | | SHA-512 | | of RFC 9421 | 2976 + +-------------------+-------------------+--------+---------------+ 2977 + | rsa-v1_5-sha256 | RSASSA-PKCS1-v1_5 | Active | Section 3.3.2 | 2978 + | | using SHA-256 | | of RFC 9421 | 2979 + +-------------------+-------------------+--------+---------------+ 2980 + | hmac-sha256 | HMAC using | Active | Section 3.3.3 | 2981 + | | SHA-256 | | of RFC 9421 | 2982 + +-------------------+-------------------+--------+---------------+ 2983 + | ecdsa-p256-sha256 | ECDSA using curve | Active | Section 3.3.4 | 2984 + | | P-256 DSS and | | of RFC 9421 | 2985 + | | SHA-256 | | | 2986 + +-------------------+-------------------+--------+---------------+ 2987 + | ecdsa-p384-sha384 | ECDSA using curve | Active | Section 3.3.5 | 2988 + | | P-384 DSS and | | of RFC 9421 | 2989 + | | SHA-384 | | | 2990 + +-------------------+-------------------+--------+---------------+ 2991 + | ed25519 | EdDSA using curve | Active | Section 3.3.6 | 2992 + | | edwards25519 | | of RFC 9421 | 2993 + +-------------------+-------------------+--------+---------------+ 2994 + 2995 + Table 2: Initial Contents of the HTTP Signature Algorithms 2996 + Registry 2997 + 2998 + 6.3. HTTP Signature Metadata Parameters Registry 2999 + 3000 + This document defines the signature parameters structure 3001 + (Section 2.3), which may have parameters containing metadata about a 3002 + message signature. IANA has created and now maintains a new registry 3003 + titled "HTTP Signature Metadata Parameters" to record and maintain 3004 + the set of parameters defined for use with member values in the 3005 + signature parameters structure. Initial values for this registry are 3006 + given in Section 6.3.2. Future assignments and modifications to 3007 + existing assignments are to be made through the Expert Review 3008 + registration policy [RFC8126]. 3009 + 3010 + The DE is expected to do the following: 3011 + 3012 + * Ensure that the name follows the template presented in 3013 + Section 6.3.1; this includes ensuring that the length of the name 3014 + is not excessive while still being unique and recognizable for its 3015 + defined function. 3016 + 3017 + * Ensure that the defined functionality is clear and does not 3018 + conflict with other registered parameters. 3019 + 3020 + * Ensure that the definition of the metadata parameter includes its 3021 + behavior when used as part of the normal signature process as well 3022 + as when used in an Accept-Signature field. 3023 + 3024 + 6.3.1. Registration Template 3025 + 3026 + Name: 3027 + An identifier for the HTTP signature metadata parameter. The name 3028 + MUST be an ASCII string that conforms to the key ABNF rule defined 3029 + in Section 3.1.2 of [STRUCTURED-FIELDS] and SHOULD NOT exceed 20 3030 + characters in length. The identifier MUST be unique within the 3031 + context of the registry. 3032 + 3033 + Description: 3034 + A brief description of the metadata parameter and what it 3035 + represents. 3036 + 3037 + Reference: 3038 + Reference to the document or documents that specify the parameter, 3039 + preferably including a URI that can be used to retrieve a copy of 3040 + the document(s). An indication of the relevant sections may also 3041 + be included but is not required. 3042 + 3043 + 6.3.2. Initial Contents 3044 + 3045 + The table below contains the initial contents of the "HTTP Signature 3046 + Metadata Parameters" registry. Each row in the table represents a 3047 + distinct entry in the registry. 3048 + 3049 + +=========+===============================+=============+ 3050 + | Name | Description | Reference | 3051 + +=========+===============================+=============+ 3052 + | alg | Explicitly declared signature | Section 2.3 | 3053 + | | algorithm | of RFC 9421 | 3054 + +---------+-------------------------------+-------------+ 3055 + | created | Timestamp of signature | Section 2.3 | 3056 + | | creation | of RFC 9421 | 3057 + +---------+-------------------------------+-------------+ 3058 + | expires | Timestamp of proposed | Section 2.3 | 3059 + | | signature expiration | of RFC 9421 | 3060 + +---------+-------------------------------+-------------+ 3061 + | keyid | Key identifier for the | Section 2.3 | 3062 + | | signing and verification keys | of RFC 9421 | 3063 + | | used to create this signature | | 3064 + +---------+-------------------------------+-------------+ 3065 + | nonce | A single-use nonce value | Section 2.3 | 3066 + | | | of RFC 9421 | 3067 + +---------+-------------------------------+-------------+ 3068 + | tag | An application-specific tag | Section 2.3 | 3069 + | | for a signature | of RFC 9421 | 3070 + +---------+-------------------------------+-------------+ 3071 + 3072 + Table 3: Initial Contents of the HTTP Signature 3073 + Metadata Parameters Registry 3074 + 3075 + 6.4. HTTP Signature Derived Component Names Registry 3076 + 3077 + This document defines a method for canonicalizing HTTP message 3078 + components, including components that can be derived from the context 3079 + of the target message outside of the HTTP fields. These derived 3080 + components are identified by a unique String, known as the component 3081 + name. Component names for derived components always start with the 3082 + "at" (@) symbol to distinguish them from HTTP field names. IANA has 3083 + created and now maintains a new registry titled "HTTP Signature 3084 + Derived Component Names" to record and maintain the set of non-field 3085 + component names and the methods used to produce their associated 3086 + component values. Initial values for this registry are given in 3087 + Section 6.4.2. Future assignments and modifications to existing 3088 + assignments are to be made through the Expert Review registration 3089 + policy [RFC8126]. 3090 + 3091 + The DE is expected to do the following: 3092 + 3093 + * Ensure that the name follows the template presented in 3094 + Section 6.4.1; this includes ensuring that the length of the name 3095 + is not excessive while still being unique and recognizable for its 3096 + defined function. 3097 + 3098 + * Ensure that the component value represented by the registration 3099 + request can be deterministically derived from the target HTTP 3100 + message. 3101 + 3102 + * Ensure that any parameters defined for the registration request 3103 + are clearly documented, along with their effects on the component 3104 + value. 3105 + 3106 + The DE should ensure that a registration is sufficiently distinct 3107 + from existing derived component definitions to warrant its 3108 + registration. 3109 + 3110 + When setting a registered item's status to "Deprecated", the DE 3111 + should ensure that a reason for the deprecation is documented, along 3112 + with instructions for moving away from the deprecated functionality. 3113 + 3114 + 6.4.1. Registration Template 3115 + 3116 + Name: 3117 + A name for the HTTP derived component. The name MUST begin with 3118 + the "at" (@) character followed by an ASCII string consisting only 3119 + of lowercase characters ("a"-"z"), digits ("0"-"9"), and hyphens 3120 + ("-"), and SHOULD NOT exceed 20 characters in length. The name 3121 + MUST be unique within the context of the registry. 3122 + 3123 + Description: 3124 + A description of the derived component. 3125 + 3126 + Status: 3127 + A brief text description of the status of the algorithm. The 3128 + description MUST begin with one of "Active" or "Deprecated" and 3129 + MAY provide further context or explanation as to the reason for 3130 + the status. A value of "Deprecated" indicates that the derived 3131 + component name is no longer recommended for use. 3132 + 3133 + Target: 3134 + The valid message targets for the derived parameter. MUST be one 3135 + of the values "Request", "Response", or "Request, Response". The 3136 + semantics of these entries are defined in Section 2.2. 3137 + 3138 + Reference: 3139 + Reference to the document or documents that specify the derived 3140 + component, preferably including a URI that can be used to retrieve 3141 + a copy of the document(s). An indication of the relevant sections 3142 + may also be included but is not required. 3143 + 3144 + 6.4.2. Initial Contents 3145 + 3146 + The table below contains the initial contents of the "HTTP Signature 3147 + Derived Component Names" registry. 3148 + 3149 + +===================+==============+========+==========+===========+ 3150 + | Name | Description | Status | Target | Reference | 3151 + +===================+==============+========+==========+===========+ 3152 + | @signature-params | Reserved for | Active | Request, | Section | 3153 + | | signature | | Response | 2.3 of | 3154 + | | parameters | | | RFC 9421 | 3155 + | | line in | | | | 3156 + | | signature | | | | 3157 + | | base | | | | 3158 + +-------------------+--------------+--------+----------+-----------+ 3159 + | @method | The HTTP | Active | Request | Section | 3160 + | | request | | | 2.2.1 of | 3161 + | | method | | | RFC 9421 | 3162 + +-------------------+--------------+--------+----------+-----------+ 3163 + | @authority | The HTTP | Active | Request | Section | 3164 + | | authority, | | | 2.2.3 of | 3165 + | | or target | | | RFC 9421 | 3166 + | | host | | | | 3167 + +-------------------+--------------+--------+----------+-----------+ 3168 + | @scheme | The URI | Active | Request | Section | 3169 + | | scheme of | | | 2.2.4 of | 3170 + | | the request | | | RFC 9421 | 3171 + | | URI | | | | 3172 + +-------------------+--------------+--------+----------+-----------+ 3173 + | @target-uri | The full | Active | Request | Section | 3174 + | | target URI | | | 2.2.2 of | 3175 + | | of the | | | RFC 9421 | 3176 + | | request | | | | 3177 + +-------------------+--------------+--------+----------+-----------+ 3178 + | @request-target | The request | Active | Request | Section | 3179 + | | target of | | | 2.2.5 of | 3180 + | | the request | | | RFC 9421 | 3181 + +-------------------+--------------+--------+----------+-----------+ 3182 + | @path | The full | Active | Request | Section | 3183 + | | path of the | | | 2.2.6 of | 3184 + | | request URI | | | RFC 9421 | 3185 + +-------------------+--------------+--------+----------+-----------+ 3186 + | @query | The full | Active | Request | Section | 3187 + | | query of the | | | 2.2.7 of | 3188 + | | request URI | | | RFC 9421 | 3189 + +-------------------+--------------+--------+----------+-----------+ 3190 + | @query-param | A single | Active | Request | Section | 3191 + | | named query | | | 2.2.8 of | 3192 + | | parameter | | | RFC 9421 | 3193 + +-------------------+--------------+--------+----------+-----------+ 3194 + | @status | The status | Active | Response | Section | 3195 + | | code of the | | | 2.2.9 of | 3196 + | | response | | | RFC 9421 | 3197 + +-------------------+--------------+--------+----------+-----------+ 3198 + 3199 + Table 4: Initial Contents of the HTTP Signature Derived 3200 + Component Names Registry 3201 + 3202 + 6.5. HTTP Signature Component Parameters Registry 3203 + 3204 + This document defines several kinds of component identifiers, some of 3205 + which can be parameterized in specific circumstances to provide 3206 + unique modified behavior. IANA has created and now maintains a new 3207 + registry titled "HTTP Signature Component Parameters" to record and 3208 + maintain the set of parameter names, the component identifiers they 3209 + are associated with, and the modifications these parameters make to 3210 + the component value. Definitions of parameters MUST define the 3211 + targets to which they apply (such as specific field types, derived 3212 + components, or contexts). Initial values for this registry are given 3213 + in Section 6.5.2. Future assignments and modifications to existing 3214 + assignments are to be made through the Expert Review registration 3215 + policy [RFC8126]. 3216 + 3217 + The DE is expected to do the following: 3218 + 3219 + * Ensure that the name follows the template presented in 3220 + Section 6.5.1; this includes ensuring that the length of the name 3221 + is not excessive while still being unique and recognizable for its 3222 + defined function. 3223 + 3224 + * Ensure that the definition of the field sufficiently defines any 3225 + interactions or incompatibilities with other existing parameters 3226 + known at the time of the registration request. 3227 + 3228 + * Ensure that the component value defined by the component 3229 + identifier with the parameter applied can be deterministically 3230 + derived from the target HTTP message in cases where the parameter 3231 + changes the component value. 3232 + 3233 + 6.5.1. Registration Template 3234 + 3235 + Name: 3236 + A name for the parameter. The name MUST be an ASCII string that 3237 + conforms to the key ABNF rule defined in Section 3.1.2 of 3238 + [STRUCTURED-FIELDS] and SHOULD NOT exceed 20 characters in length. 3239 + The name MUST be unique within the context of the registry. 3240 + 3241 + Description: 3242 + A description of the parameter's function. 3243 + 3244 + Reference: 3245 + Reference to the document or documents that specify the derived 3246 + component, preferably including a URI that can be used to retrieve 3247 + a copy of the document(s). An indication of the relevant sections 3248 + may also be included but is not required. 3249 + 3250 + 6.5.2. Initial Contents 3251 + 3252 + The table below contains the initial contents of the "HTTP Signature 3253 + Component Parameters" registry. 3254 + 3255 + +======+=======================================+===============+ 3256 + | Name | Description | Reference | 3257 + +======+=======================================+===============+ 3258 + | sf | Strict Structured Field serialization | Section 2.1.1 | 3259 + | | | of RFC 9421 | 3260 + +------+---------------------------------------+---------------+ 3261 + | key | Single key value of Dictionary | Section 2.1.2 | 3262 + | | Structured Fields | of RFC 9421 | 3263 + +------+---------------------------------------+---------------+ 3264 + | bs | Byte Sequence wrapping indicator | Section 2.1.3 | 3265 + | | | of RFC 9421 | 3266 + +------+---------------------------------------+---------------+ 3267 + | tr | Trailer | Section 2.1.4 | 3268 + | | | of RFC 9421 | 3269 + +------+---------------------------------------+---------------+ 3270 + | req | Related request indicator | Section 2.4 | 3271 + | | | of RFC 9421 | 3272 + +------+---------------------------------------+---------------+ 3273 + | name | Single named query parameter | Section 2.2.8 | 3274 + | | | of RFC 9421 | 3275 + +------+---------------------------------------+---------------+ 3276 + 3277 + Table 5: Initial Contents of the HTTP Signature Component 3278 + Parameters Registry 3279 + 3280 + 7. Security Considerations 3281 + 3282 + In order for an HTTP message to be considered _covered_ by a 3283 + signature, all of the following conditions have to be true: 3284 + 3285 + * A signature is expected or allowed on the message by the verifier. 3286 + 3287 + * The signature exists on the message. 3288 + 3289 + * The signature is verified against the identified key material and 3290 + algorithm. 3291 + 3292 + * The key material and algorithm are appropriate for the context of 3293 + the message. 3294 + 3295 + * The signature is within expected time boundaries. 3296 + 3297 + * The signature covers the expected content, including any critical 3298 + components. 3299 + 3300 + * The list of covered components is applicable to the context of the 3301 + message. 3302 + 3303 + In addition to the application requirement definitions listed in 3304 + Section 1.4, the following security considerations provide discussion 3305 + and context regarding the requirements of creating and verifying 3306 + signatures on HTTP messages. 3307 + 3308 + 7.1. General Considerations 3309 + 3310 + 7.1.1. Skipping Signature Verification 3311 + 3312 + HTTP message signatures only provide security if the signature is 3313 + verified by the verifier. Since the message to which the signature 3314 + is attached remains a valid HTTP message without the Signature or 3315 + Signature-Input fields, it is possible for a verifier to ignore the 3316 + output of the verification function and still process the message. 3317 + Common reasons for this could be relaxed requirements in a 3318 + development environment or a temporary suspension of enforcing 3319 + verification while debugging an overall system. Such temporary 3320 + suspensions are difficult to detect under positive-example testing, 3321 + since a good signature will always trigger a valid response whether 3322 + or not it has been checked. 3323 + 3324 + To detect this, verifiers should be tested using both valid and 3325 + invalid signatures, ensuring that an invalid signature fails as 3326 + expected. 3327 + 3328 + 7.1.2. Use of TLS 3329 + 3330 + The use of HTTP message signatures does not negate the need for TLS 3331 + or its equivalent to protect information in transit. Message 3332 + signatures provide message integrity over the covered message 3333 + components but do not provide any confidentiality for communication 3334 + between parties. 3335 + 3336 + TLS provides such confidentiality between the TLS endpoints. As part 3337 + of this, TLS also protects the signature data itself from being 3338 + captured by an attacker. This is an important step in preventing 3339 + signature replay (Section 7.2.2). 3340 + 3341 + When TLS is used, it needs to be deployed according to the 3342 + recommendations provided in [BCP195]. 3343 + 3344 + 7.2. Message Processing and Selection 3345 + 3346 + 7.2.1. Insufficient Coverage 3347 + 3348 + Any portions of the message not covered by the signature are 3349 + susceptible to modification by an attacker without affecting the 3350 + signature. An attacker can take advantage of this by introducing or 3351 + modifying a header field or other message component that will change 3352 + the processing of the message but will not be covered by the 3353 + signature. Such an altered message would still pass signature 3354 + verification, but when the verifier processes the message as a whole, 3355 + the unsigned content injected by the attacker would subvert the trust 3356 + conveyed by the valid signature and change the outcome of processing 3357 + the message. 3358 + 3359 + To combat this, an application of this specification should require 3360 + as much of the message as possible to be signed, within the limits of 3361 + the application and deployment. The verifier should only trust 3362 + message components that have been signed. Verifiers could also strip 3363 + out any sensitive unsigned portions of the message before processing 3364 + of the message continues. 3365 + 3366 + 7.2.2. Signature Replay 3367 + 3368 + Since HTTP message signatures allow sub-portions of the HTTP message 3369 + to be signed, it is possible for two different HTTP messages to 3370 + validate against the same signature. The most extreme form of this 3371 + would be a signature over no message components. If such a signature 3372 + were intercepted, it could be replayed at will by an attacker, 3373 + attached to any HTTP message. Even with sufficient component 3374 + coverage, a given signature could be applied to two similar HTTP 3375 + messages, allowing a message to be replayed by an attacker with the 3376 + signature intact. 3377 + 3378 + To counteract these kinds of attacks, it's first important for the 3379 + signer to cover sufficient portions of the message to differentiate 3380 + it from other messages. In addition, the signature can use the nonce 3381 + signature parameter to provide a per-message unique value to allow 3382 + the verifier to detect replay of the signature itself if a nonce 3383 + value is repeated. Furthermore, the signer can provide a timestamp 3384 + for when the signature was created and a time at which the signer 3385 + considers the signature to be expired, limiting the utility of a 3386 + captured signature value. 3387 + 3388 + If a verifier wants to trigger a new signature from a signer, it can 3389 + send the Accept-Signature header field with a new nonce parameter. 3390 + An attacker that is simply replaying a signature would not be able to 3391 + generate a new signature with the chosen nonce value. 3392 + 3393 + 7.2.3. Choosing Message Components 3394 + 3395 + Applications of HTTP message signatures need to decide which message 3396 + components will be covered by the signature. Depending on the 3397 + application, some components could be expected to be changed by 3398 + intermediaries prior to the signature's verification. If these 3399 + components are covered, such changes would, by design, break the 3400 + signature. 3401 + 3402 + However, this document allows for flexibility in determining which 3403 + components are signed precisely so that a given application can 3404 + choose the appropriate portions of the message that need to be 3405 + signed, avoiding problematic components. For example, a web 3406 + application framework that relies on rewriting query parameters might 3407 + avoid using the @query derived component in favor of sub-indexing the 3408 + query value using @query-param derived components instead. 3409 + 3410 + Some components are expected to be changed by intermediaries and 3411 + ought not to be signed under most circumstances. The Via and 3412 + Forwarded header fields, for example, are expected to be manipulated 3413 + by proxies and other middleboxes, including replacing or entirely 3414 + dropping existing values. These fields should not be covered by the 3415 + signature, except in very limited and tightly coupled scenarios. 3416 + 3417 + Additional considerations for choosing signature aspects are 3418 + discussed in Section 1.4. 3419 + 3420 + 7.2.4. Choosing Signature Parameters and Derived Components over HTTP 3421 + Fields 3422 + 3423 + Some HTTP fields have values and interpretations that are similar to 3424 + HTTP signature parameters or derived components. In most cases, it 3425 + is more desirable to sign the non-field alternative. In particular, 3426 + the following fields should usually not be included in the signature 3427 + unless the application specifically requires it: 3428 + 3429 + "date" The Date header field value represents the timestamp of the 3430 + HTTP message. However, the creation time of the signature itself 3431 + is encoded in the created signature parameter. These two values 3432 + can be different, depending on how the signature and the HTTP 3433 + message are created and serialized. Applications processing 3434 + signatures for valid time windows should use the created signature 3435 + parameter for such calculations. An application could also put 3436 + limits on how much skew there is between the Date field and the 3437 + created signature parameter, in order to limit the application of 3438 + a generated signature to different HTTP messages. See also 3439 + Sections 7.2.2 and 7.2.1. 3440 + 3441 + "host" The Host header field is specific to HTTP/1.1, and its 3442 + functionality is subsumed by the @authority derived component, 3443 + defined in Section 2.2.3. In order to preserve the value across 3444 + different HTTP versions, applications should always use the 3445 + @authority derived component. See also Section 7.5.4. 3446 + 3447 + 7.2.5. Signature Labels 3448 + 3449 + HTTP message signature values are identified in the Signature and 3450 + Signature-Input field values by unique labels. These labels are 3451 + chosen only when attaching the signature values to the message and 3452 + are not accounted for during the signing process. An intermediary is 3453 + allowed to relabel an existing signature when processing the message. 3454 + 3455 + Therefore, applications should not rely on specific labels being 3456 + present, and applications should not put semantic meaning on the 3457 + labels themselves. Instead, additional signature parameters can be 3458 + used to convey whatever additional meaning is required to be attached 3459 + to, and covered by, the signature. In particular, the tag parameter 3460 + can be used to define an application-specific value as described in 3461 + Section 7.2.7. 3462 + 3463 + 7.2.6. Multiple Signature Confusion 3464 + 3465 + Since multiple signatures can be applied to one message 3466 + (Section 4.3), it is possible for an attacker to attach their own 3467 + signature to a captured message without modifying existing 3468 + signatures. This new signature could be completely valid based on 3469 + the attacker's key, or it could be an invalid signature for any 3470 + number of reasons. Each of these situations needs to be accounted 3471 + for. 3472 + 3473 + A verifier processing a set of valid signatures needs to account for 3474 + all of the signers, identified by the signing keys. Only signatures 3475 + from expected signers should be accepted, regardless of the 3476 + cryptographic validity of the signature itself. 3477 + 3478 + A verifier processing a set of signatures on a message also needs to 3479 + determine what to do when one or more of the signatures are not 3480 + valid. If a message is accepted when at least one signature is 3481 + valid, then a verifier could drop all invalid signatures from the 3482 + request before processing the message further. Alternatively, if the 3483 + verifier rejects a message for a single invalid signature, an 3484 + attacker could use this to deny service to otherwise valid messages 3485 + by injecting invalid signatures alongside the valid signatures. 3486 + 3487 + 7.2.7. Collision of Application-Specific Signature Tag 3488 + 3489 + Multiple applications and protocols could apply HTTP signatures on 3490 + the same message simultaneously. In fact, this is a desired feature 3491 + in many circumstances; see Section 4.3. A naive verifier could 3492 + become confused while processing multiple signatures, either 3493 + accepting or rejecting a message based on an unrelated or irrelevant 3494 + signature. In order to help an application select which signatures 3495 + apply to its own processing, the application can declare a specific 3496 + value for the tag signature parameter as defined in Section 2.3. For 3497 + example, a signature targeting an application gateway could require 3498 + tag="app-gateway" as part of the signature parameters for that 3499 + application. 3500 + 3501 + The use of the tag parameter does not prevent an attacker from also 3502 + using the same value as a target application, since the parameter's 3503 + value is public and otherwise unrestricted. As a consequence, a 3504 + verifier should only use a value of the tag parameter to limit which 3505 + signatures to check. Each signature still needs to be examined by 3506 + the verifier to ensure that sufficient coverage is provided, as 3507 + discussed in Section 7.2.1. 3508 + 3509 + 7.2.8. Message Content 3510 + 3511 + On its own, this specification does not provide coverage for the 3512 + content of an HTTP message under the signature, in either a request 3513 + or a response. However, [DIGEST] defines a set of fields that allow 3514 + a cryptographic digest of the content to be represented in a field. 3515 + Once this field is created, it can be included just like any other 3516 + field as defined in Section 2.1. 3517 + 3518 + For example, in the following response message: 3519 + 3520 + HTTP/1.1 200 OK 3521 + Content-Type: application/json 3522 + 3523 + {"hello": "world"} 3524 + 3525 + The digest of the content can be added to the Content-Digest field as 3526 + follows: 3527 + 3528 + NOTE: '\' line wrapping per RFC 8792 3529 + 3530 + HTTP/1.1 200 OK 3531 + Content-Type: application/json 3532 + Content-Digest: \ 3533 + sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=: 3534 + 3535 + {"hello": "world"} 3536 + 3537 + This field can be included in a signature base just like any other 3538 + field along with the basic signature parameters: 3539 + 3540 + "@status": 200 3541 + "content-digest": \ 3542 + sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=: 3543 + "@signature-input": ("@status" "content-digest") 3544 + 3545 + From here, the signing process proceeds as usual. 3546 + 3547 + Upon verification, it is important that the verifier validate not 3548 + only the signature but also the value of the Content-Digest field 3549 + itself against the actual received content. Unless the verifier 3550 + performs this step, it would be possible for an attacker to 3551 + substitute the message content but leave the Content-Digest field 3552 + value untouched to pass the signature. Since only the field value is 3553 + covered by the signature directly, checking only the signature is not 3554 + sufficient protection against such a substitution attack. 3555 + 3556 + As discussed in [DIGEST], the value of the Content-Digest field is 3557 + dependent on the content encoding of the message. If an intermediary 3558 + changes the content encoding, the resulting Content-Digest value 3559 + would change. This would in turn invalidate the signature. Any 3560 + intermediary performing such an action would need to apply a new 3561 + signature with the updated Content-Digest field value, similar to the 3562 + reverse proxy use case discussed in Section 4.3. 3563 + 3564 + Applications that make use of the req parameter (Section 2.4) also 3565 + need to be aware of the limitations of this functionality. 3566 + Specifically, if a client does not include something like a Content- 3567 + Digest header field in the request, the server is unable to include a 3568 + signature that covers the request's content. 3569 + 3570 + 7.3. Cryptographic Considerations 3571 + 3572 + 7.3.1. Cryptography and Signature Collision 3573 + 3574 + This document does not define any of its own cryptographic primitives 3575 + and instead relies on other specifications to define such elements. 3576 + If the signature algorithm or key used to process the signature base 3577 + is vulnerable to any attacks, the resulting signature will also be 3578 + susceptible to these same attacks. 3579 + 3580 + A common attack against signature systems is to force a signature 3581 + collision, where the same signature value successfully verifies 3582 + against multiple different inputs. Since this specification relies 3583 + on reconstruction of the signature base from an HTTP message and the 3584 + list of components signed is fixed in the signature, it is difficult 3585 + but not impossible for an attacker to effect such a collision. An 3586 + attacker would need to manipulate the HTTP message and its covered 3587 + message components in order to make the collision effective. 3588 + 3589 + To counter this, only vetted keys and signature algorithms should be 3590 + used to sign HTTP messages. The "HTTP Signature Algorithms" registry 3591 + is one source of trusted signature algorithms for applications to 3592 + apply to their messages. 3593 + 3594 + While it is possible for an attacker to substitute the signature 3595 + parameters value or the signature value separately, the signature 3596 + base generation algorithm (Section 2.5) always covers the signature 3597 + parameters as the final value in the signature base using a 3598 + deterministic serialization method. This step strongly binds the 3599 + signature base with the signature value in a way that makes it much 3600 + more difficult for an attacker to perform a partial substitution on 3601 + the signature base. 3602 + 3603 + 7.3.2. Key Theft 3604 + 3605 + A foundational assumption of signature-based cryptographic systems is 3606 + that the signing key is not compromised by an attacker. If the keys 3607 + used to sign the message are exfiltrated or stolen, the attacker will 3608 + be able to generate their own signatures using those keys. As a 3609 + consequence, signers have to protect any signing key material from 3610 + exfiltration, capture, and use by an attacker. 3611 + 3612 + To combat this, signers can rotate keys over time to limit the amount 3613 + of time that stolen keys are useful. Signers can also use key escrow 3614 + and storage systems to limit the attack surface against keys. 3615 + Furthermore, the use of asymmetric signing algorithms exposes key 3616 + material less than the use of symmetric signing algorithms 3617 + (Section 7.3.3). 3618 + 3619 + 7.3.3. Symmetric Cryptography 3620 + 3621 + This document allows both asymmetric and symmetric cryptography to be 3622 + applied to HTTP messages. By their nature, symmetric cryptographic 3623 + methods require the same key material to be known by both the signer 3624 + and verifier. This effectively means that a verifier is capable of 3625 + generating a valid signature, since they have access to the same key 3626 + material. An attacker that is able to compromise a verifier would be 3627 + able to then impersonate a signer. 3628 + 3629 + Where possible, asymmetric methods or secure key agreement mechanisms 3630 + should be used in order to avoid this type of attack. When symmetric 3631 + methods are used, distribution of the key material needs to be 3632 + protected by the overall system. One technique for this is the use 3633 + of separate cryptographic modules that separate the verification 3634 + process (and therefore the key material) from other code, minimizing 3635 + the vulnerable attack surface. Another technique is the use of key 3636 + derivation functions that allow the signer and verifier to agree on 3637 + unique keys for each message without having to share the key values 3638 + directly. 3639 + 3640 + Additionally, if symmetric algorithms are allowed within a system, 3641 + special care must be taken to avoid key downgrade attacks 3642 + (Section 7.3.6). 3643 + 3644 + 7.3.4. Key Specification Mixup 3645 + 3646 + The existence of a valid signature on an HTTP message is not 3647 + sufficient to prove that the message has been signed by the 3648 + appropriate party. It is up to the verifier to ensure that a given 3649 + key and algorithm are appropriate for the message in question. If 3650 + the verifier does not perform such a step, an attacker could 3651 + substitute their own signature using their own key on a message and 3652 + force a verifier to accept and process it. To combat this, the 3653 + verifier needs to ensure not only that the signature can be validated 3654 + for a message but that the key and algorithm used are appropriate. 3655 + 3656 + 7.3.5. Non-deterministic Signature Primitives 3657 + 3658 + Some cryptographic primitives, such as RSA-PSS and ECDSA, have non- 3659 + deterministic outputs, which include some amount of entropy within 3660 + the algorithm. For such algorithms, multiple signatures generated in 3661 + succession will not match. A lazy implementation of a verifier could 3662 + ignore this distinction and simply check for the same value being 3663 + created by re-signing the signature base. Such an implementation 3664 + would work for deterministic algorithms such as HMAC and EdDSA but 3665 + fail to verify valid signatures made using non-deterministic 3666 + algorithms. It is therefore important that a verifier always use the 3667 + correctly defined verification function for the algorithm in question 3668 + and not do a simple comparison. 3669 + 3670 + 7.3.6. Key and Algorithm Specification Downgrades 3671 + 3672 + Applications of this specification need to protect against key 3673 + specification downgrade attacks. For example, the same RSA key can 3674 + be used for both RSA-PSS and RSA v1.5 signatures. If an application 3675 + expects a key to only be used with RSA-PSS, it needs to reject 3676 + signatures for any key that uses the weaker RSA 1.5 specification. 3677 + 3678 + Another example of a downgrade attack would be when an asymmetric 3679 + algorithm is expected, such as RSA-PSS, but an attacker substitutes a 3680 + signature using a symmetric algorithm, such as HMAC. A naive 3681 + verifier implementation could use the value of the public RSA key as 3682 + the input to the HMAC verification function. Since the public key is 3683 + known to the attacker, this would allow the attacker to create a 3684 + valid HMAC signature against this known key. To prevent this, the 3685 + verifier needs to ensure that both the key material and the algorithm 3686 + are appropriate for the usage in question. Additionally, while this 3687 + specification does allow runtime specification of the algorithm using 3688 + the alg signature parameter, applications are encouraged to use other 3689 + mechanisms such as static configuration or a higher-protocol-level 3690 + algorithm specification instead, preventing an attacker from 3691 + substituting the algorithm specified. 3692 + 3693 + 7.3.7. Signing Signature Values 3694 + 3695 + When applying the req parameter (Section 2.4) or multiple signatures 3696 + (Section 4.3) to a message, it is possible to sign the value of an 3697 + existing Signature field, thereby covering the bytes of the existing 3698 + signature output in the new signature's value. While it would seem 3699 + that this practice would transitively cover the components under the 3700 + original signature in a verifiable fashion, the attacks described in 3701 + [JACKSON2019] can be used to impersonate a signature output value on 3702 + an unrelated message. 3703 + 3704 + In this example, Alice intends to send a signed request to Bob, and 3705 + Bob wants to provide a signed response to Alice that includes a 3706 + cryptographic proof that Bob is responding to Alice's incoming 3707 + message. Mallory wants to intercept this traffic and replace Alice's 3708 + message with her own, without Alice being aware that the interception 3709 + has taken place. 3710 + 3711 + 1. Alice creates a message Req_A and applies a signature Sig_A 3712 + using her private key Key_A_Sign. 3713 + 3714 + 2. Alice believes she is sending Req_A to Bob. 3715 + 3716 + 3. Mallory intercepts Req_A and reads the value Sig_A from this 3717 + message. 3718 + 3719 + 4. Mallory generates a different message Req_M to send to Bob 3720 + instead. 3721 + 3722 + 5. Mallory crafts a signing key Key_M_Sign such that she can create 3723 + a valid signature Sig_M over her request Req_M using this key, 3724 + but the byte value of Sig_M exactly equals that of Sig_A. 3725 + 3726 + 6. Mallory sends Req_M with Sig_M to Bob. 3727 + 3728 + 7. Bob validates Sig_M against Mallory's verification key 3729 + Key_M_Verify. At no time does Bob think that he's responding to 3730 + Alice. 3731 + 3732 + 8. Bob responds with response message Res_B to Req_M and creates 3733 + signature Sig_B over this message using his key Key_B_Sign. Bob 3734 + includes the value of Sig_M under Sig_B's covered components but 3735 + does not include anything else from the request message. 3736 + 3737 + 9. Mallory receives the response Res_B from Bob, including the 3738 + signature Sig_B value. Mallory replays this response to Alice. 3739 + 3740 + 10. Alice reads Res_B from Mallory and verifies Sig_B using Bob's 3741 + verification key Key_B_Verify. Alice includes the bytes of her 3742 + original signature Sig_A in the signature base, and the 3743 + signature verifies. 3744 + 3745 + 11. Alice is led to believe that Bob has responded to her message 3746 + and believes she has cryptographic proof of this happening, but 3747 + in fact Bob responded to Mallory's malicious request and Alice 3748 + is none the wiser. 3749 + 3750 + To mitigate this, Bob can sign more portions of the request message 3751 + than just the Signature field, in order to more fully differentiate 3752 + Alice's message from Mallory's. Applications using this feature, 3753 + particularly for non-repudiation purposes, can stipulate that any 3754 + components required in the original signature also be covered 3755 + separately in the second signature. For signed messages, requiring 3756 + coverage of the corresponding Signature-Input field of the first 3757 + signature ensures that unique items such as nonces and timestamps are 3758 + also covered sufficiently by the second signature. 3759 + 3760 + 7.4. Matching Signature Parameters to the Target Message 3761 + 3762 + 7.4.1. Modification of Required Message Parameters 3763 + 3764 + An attacker could effectively deny a service by modifying an 3765 + otherwise benign signature parameter or signed message component. 3766 + While rejecting a modified message is the desired behavior, 3767 + consistently failing signatures could lead to (1) the verifier 3768 + turning off signature checking in order to make systems work again 3769 + (see Section 7.1.1) or (2) the application minimizing the 3770 + requirements related to the signed component. 3771 + 3772 + If such failures are common within an application, the signer and 3773 + verifier should compare their generated signature bases with each 3774 + other to determine which part of the message is being modified. If 3775 + an expected modification is found, the signer and verifier can agree 3776 + on an alternative set of requirements that will pass. However, the 3777 + signer and verifier should not remove the requirement to sign the 3778 + modified component when it is suspected that an attacker is modifying 3779 + the component. 3780 + 3781 + 7.4.2. Matching Values of Covered Components to Values in the Target 3782 + Message 3783 + 3784 + The verifier needs to make sure that the signed message components 3785 + match those in the message itself. For example, the @method derived 3786 + component requires that the value within the signature base be the 3787 + same as the HTTP method used when presenting this message. This 3788 + specification encourages this by requiring the verifier to derive the 3789 + signature base from the message, but lazy caching or conveyance of a 3790 + raw signature base to a processing subsystem could lead to downstream 3791 + verifiers accepting a message that does not match the presented 3792 + signature. 3793 + 3794 + To counter this, the component that generates the signature base 3795 + needs to be trusted by both the signer and verifier within a system. 3796 + 3797 + 7.4.3. Message Component Source and Context 3798 + 3799 + The signature context for deriving message component values includes 3800 + the target HTTP message itself, any associated messages (such as the 3801 + request that triggered a response), and additional information that 3802 + the signer or verifier has access to. Both signers and verifiers 3803 + need to carefully consider the source of all information when 3804 + creating component values for the signature base and take care not to 3805 + take information from untrusted sources. Otherwise, an attacker 3806 + could leverage such a loosely defined message context to inject their 3807 + own values into the signature base string, overriding or corrupting 3808 + the intended values. 3809 + 3810 + For example, in most situations, the target URI of the message is as 3811 + defined in [HTTP], Section 7.1. However, let's say that there is an 3812 + application that requires signing of the @authority of the incoming 3813 + request, but the application doing the processing is behind a reverse 3814 + proxy. Such an application would expect a change in the @authority 3815 + value, and it could be configured to know the external target URI as 3816 + seen by the client on the other side of the proxy. This application 3817 + would use this configured value as its target URI for the purposes of 3818 + deriving message component values such as @authority instead of using 3819 + the target URI of the incoming message. 3820 + 3821 + This approach is not without problems, as a misconfigured system 3822 + could accept signed requests intended for different components in the 3823 + system. For this scenario, an intermediary could instead add its own 3824 + signature to be verified by the application directly, as demonstrated 3825 + in Section 4.3. This alternative approach requires a more active 3826 + intermediary but relies less on the target application knowing 3827 + external configuration values. 3828 + 3829 + As another example, Section 2.4 defines a method for signing response 3830 + messages and also including portions of the request message that 3831 + triggered the response. In this case, the context for component 3832 + value calculation is the combination of the response and request 3833 + messages, not just the single message to which the signature is 3834 + applied. For this feature, the req flag allows both signers to 3835 + explicitly signal which part of the context is being sourced for a 3836 + component identifier's value. Implementations need to ensure that 3837 + only the intended message is being referred to for each component; 3838 + otherwise, an attacker could attempt to subvert a signature by 3839 + manipulating one side or the other. 3840 + 3841 + 7.4.4. Multiple Message Component Contexts 3842 + 3843 + It is possible that the context for deriving message component values 3844 + could be distinct for each signature present within a single message. 3845 + This is particularly the case when proxies mutate messages and 3846 + include signatures over the mutated values, in addition to any 3847 + existing signatures. For example, a reverse proxy can replace a 3848 + public hostname in a request to a service with the hostname for the 3849 + individual service host to which it is forwarding the request. If 3850 + both the client and the reverse proxy add signatures covering 3851 + @authority, the service host will see two signatures on the request, 3852 + each signing different values for the @authority message component, 3853 + reflecting the change to that component as the message made its way 3854 + from the client to the service host. 3855 + 3856 + In such a case, it's common for the internal service to verify only 3857 + one of the signatures or to use externally configured information, as 3858 + discussed in Section 7.4.3. However, a verifier processing both 3859 + signatures has to use a different message component context for each 3860 + signature, since the component value for the @authority component 3861 + will be different for each signature. Verifiers like this need to be 3862 + aware of both the reverse proxy's context for incoming messages and 3863 + the target service's context for the message coming from the reverse 3864 + proxy. The verifier needs to take particular care to apply the 3865 + correct context to the correct signature; otherwise, an attacker 3866 + could use knowledge of this complex setup to confuse the inputs to 3867 + the verifier. 3868 + 3869 + Such verifiers also need to ensure that any differences in message 3870 + component contexts between signatures are expected and permitted. 3871 + For example, in the above scenario, the reverse proxy could include 3872 + the original hostname in a Forwarded header field and could sign 3873 + @authority, forwarded, and the client's entry in the Signature field. 3874 + The verifier can use the hostname from the Forwarded header field to 3875 + confirm that the hostname was transformed as expected. 3876 + 3877 + 7.5. HTTP Processing 3878 + 3879 + 7.5.1. Processing Invalid HTTP Field Names as Derived Component Names 3880 + 3881 + The definition of HTTP field names does not allow for the use of the 3882 + @ character anywhere in the name. As such, since all derived 3883 + component names start with the @ character, these namespaces should 3884 + be completely separate. However, some HTTP implementations are not 3885 + sufficiently strict about the characters accepted in HTTP field 3886 + names. In such implementations, a sender (or attacker) could inject 3887 + a header field starting with an @ character and have it passed 3888 + through to the application code. These invalid header fields could 3889 + be used to override a portion of the derived message content and 3890 + substitute an arbitrary value, providing a potential place for an 3891 + attacker to mount a signature collision (Section 7.3.1) attack or 3892 + other functional substitution attack (such as using the signature 3893 + from a GET request on a crafted POST request). 3894 + 3895 + To combat this, when selecting values for a message component, if the 3896 + component name starts with the @ character, it needs to be processed 3897 + as a derived component and never processed as an HTTP field. Only if 3898 + the component name does not start with the @ character can it be 3899 + taken from the fields of the message. The algorithm discussed in 3900 + Section 2.5 provides a safe order of operations. 3901 + 3902 + 7.5.2. Semantically Equivalent Field Values 3903 + 3904 + The signature base generation algorithm (Section 2.5) uses the value 3905 + of an HTTP field as its component value. In the common case, this 3906 + amounts to taking the actual bytes of the field value as the 3907 + component value for both the signer and verifier. However, some 3908 + field values allow for transformation of the values in semantically 3909 + equivalent ways that alter the bytes used in the value itself. For 3910 + example, a field definition can declare some or all of its values to 3911 + be case insensitive or to have special handling of internal 3912 + whitespace characters. Other fields have expected transformations 3913 + from intermediaries, such as the removal of comments in the Via 3914 + header field. In such cases, a verifier could be tripped up by using 3915 + the equivalent transformed field value, which would differ from the 3916 + byte value used by the signer. The verifier would have a difficult 3917 + time finding this class of errors, since the value of the field is 3918 + still acceptable for the application but the actual bytes required by 3919 + the signature base would not match. 3920 + 3921 + When processing such fields, the signer and verifier have to agree on 3922 + how to handle such transformations, if at all. One option is to not 3923 + sign problematic fields, but care must be taken to ensure that there 3924 + is still sufficient signature coverage (Section 7.2.1) for the 3925 + application. Another option is to define an application-specific 3926 + canonicalization value for the field before it is added to the HTTP 3927 + message, such as to always remove internal comments before signing or 3928 + to always transform values to lowercase. Since these transformations 3929 + are applied prior to the field being used as input to the signature 3930 + base generation algorithm, the signature base will still simply 3931 + contain the byte value of the field as it appears within the message. 3932 + If the transformations were to be applied after the value is 3933 + extracted from the message but before it is added to the signature 3934 + base, different attack surfaces such as value substitution attacks 3935 + could be launched against the application. All application-specific 3936 + additional rules are outside the scope of this specification, and by 3937 + their very nature these transformations would harm interoperability 3938 + of the implementation outside of this specific application. It is 3939 + recommended that applications avoid the use of such additional rules 3940 + wherever possible. 3941 + 3942 + 7.5.3. Parsing Structured Field Values 3943 + 3944 + Several parts of this specification rely on the parsing of Structured 3945 + Field values [STRUCTURED-FIELDS] -- in particular, strict 3946 + serialization of HTTP Structured Field values (Section 2.1.1), 3947 + referencing members of a Dictionary Structured Field (Section 2.1.2), 3948 + and processing the @signature-input value when verifying a signature 3949 + (Section 3.2). While Structured Field values are designed to be 3950 + relatively simple to parse, a naive or broken implementation of such 3951 + a parser could lead to subtle attack surfaces being exposed in the 3952 + implementation. 3953 + 3954 + For example, if a buggy parser of the @signature-input value does not 3955 + enforce proper closing of quotes around string values within the list 3956 + of component identifiers, an attacker could take advantage of this 3957 + and inject additional content into the signature base through 3958 + manipulating the Signature-Input field value on a message. 3959 + 3960 + To counteract this, implementations should use fully compliant and 3961 + trusted parsers for all Structured Field processing, on both the 3962 + signer side and the verifier side. 3963 + 3964 + 7.5.4. HTTP Versions and Component Ambiguity 3965 + 3966 + Some message components are expressed in different ways across HTTP 3967 + versions. For example, the authority of the request target is sent 3968 + using the Host header field in HTTP/1.1 but with the :authority 3969 + pseudo-header in HTTP/2. If a signer sends an HTTP/1.1 message and 3970 + signs the Host header field but the message is translated to HTTP/2 3971 + before it reaches the verifier, the signature will not validate, as 3972 + the Host header field could be dropped. 3973 + 3974 + It is for this reason that HTTP message signatures define a set of 3975 + derived components that define a single way to get the value in 3976 + question, such as the @authority derived component (Section 2.2.3) in 3977 + lieu of the Host header field. Applications should therefore prefer 3978 + derived components for such options where possible. 3979 + 3980 + 7.5.5. Canonicalization Attacks 3981 + 3982 + Any ambiguity in the generation of the signature base could provide 3983 + an attacker with leverage to substitute or break a signature on a 3984 + message. Some message component values, particularly HTTP field 3985 + values, are potentially susceptible to broken implementations that 3986 + could lead to unexpected and insecure behavior. Naive 3987 + implementations of this specification might implement HTTP field 3988 + processing by taking the single value of a field and using it as the 3989 + direct component value without processing it appropriately. 3990 + 3991 + For example, if the handling of obs-fold field values does not remove 3992 + the internal line folding and whitespace, additional newlines could 3993 + be introduced into the signature base by the signer, providing a 3994 + potential place for an attacker to mount a signature collision 3995 + (Section 7.3.1) attack. Alternatively, if header fields that appear 3996 + multiple times are not joined into a single string value, as required 3997 + by this specification, similar attacks can be mounted, as a signed 3998 + component value would show up in the signature base more than once 3999 + and could be substituted or otherwise attacked in this way. 4000 + 4001 + To counter this, the entire field value processing algorithm needs to 4002 + be implemented by all implementations of signers and verifiers. 4003 + 4004 + 7.5.6. Non-List Field Values 4005 + 4006 + When an HTTP field occurs multiple times in a single message, these 4007 + values need to be combined into a single one-line string value to be 4008 + included in the HTTP signature base, as described in Section 2.5. 4009 + Not all HTTP fields can be combined into a single value in this way 4010 + and still be a valid value for the field. For the purposes of 4011 + generating the signature base, the message component value is never 4012 + meant to be read back out of the signature base string or used in the 4013 + application. Therefore, it is considered best practice to treat the 4014 + signature base generation algorithm separately from processing the 4015 + field values by the application, particularly for fields that are 4016 + known to have this property. If the field values that are being 4017 + signed do not validate, the signed message should also be rejected. 4018 + 4019 + If an HTTP field allows for unquoted commas within its values, 4020 + combining multiple field values can lead to a situation where two 4021 + semantically different messages produce the same line in a signature 4022 + base. For example, take the following hypothetical header field with 4023 + an internal comma in its syntax, here used to define two separate 4024 + lists of values: 4025 + 4026 + Example-Header: value, with, lots 4027 + Example-Header: of, commas 4028 + 4029 + For this header field, sending all of these values as a single field 4030 + value results in a single list of values: 4031 + 4032 + Example-Header: value, with, lots, of, commas 4033 + 4034 + Both of these messages would create the following line in the 4035 + signature base: 4036 + 4037 + "example-header": value, with, lots, of, commas 4038 + 4039 + Since two semantically distinct inputs can create the same output in 4040 + the signature base, special care has to be taken when handling such 4041 + values. 4042 + 4043 + Specifically, the Set-Cookie field [COOKIE] defines an internal 4044 + syntax that does not conform to the List syntax provided in 4045 + [STRUCTURED-FIELDS]. In particular, some portions allow unquoted 4046 + commas, and the field is typically sent as multiple separate field 4047 + lines with distinct values when sending multiple cookies. When 4048 + multiple Set-Cookie fields are sent in the same message, it is not 4049 + generally possible to combine these into a single line and be able to 4050 + parse and use the results, as discussed in [HTTP], Section 5.3. 4051 + Therefore, all the cookies need to be processed from their separate 4052 + field values, without being combined, while the signature base needs 4053 + to be processed from the special combined value generated solely for 4054 + this purpose. If the cookie value is invalid, the signed message 4055 + ought to be rejected, as this is a possible padding attack as 4056 + described in Section 7.5.7. 4057 + 4058 + To deal with this, an application can choose to limit signing of 4059 + problematic fields like Set-Cookie, such as including the field in a 4060 + signature only when a single field value is present and the results 4061 + would be unambiguous. Similar caution needs to be taken with all 4062 + fields that could have non-deterministic mappings into the signature 4063 + base. Signers can also make use of the bs parameter to armor such 4064 + fields, as described in Section 2.1.3. 4065 + 4066 + 7.5.7. Padding Attacks with Multiple Field Values 4067 + 4068 + Since HTTP field values need to be combined into a single string 4069 + value to be included in the HTTP signature base (see Section 2.5), it 4070 + is possible for an attacker to inject an additional value for a given 4071 + field and add this to the signature base of the verifier. 4072 + 4073 + In most circumstances, this causes the signature validation to fail 4074 + as expected, since the new signature base value will not match the 4075 + one used by the signer to create the signature. However, it is 4076 + theoretically possible for the attacker to inject both a garbage 4077 + value into a field and a desired value into another field in order to 4078 + force a particular input. This is a variation of the collision 4079 + attack described in Section 7.3.1, where the attacker accomplishes 4080 + their change in the message by adding to existing field values. 4081 + 4082 + To counter this, an application needs to validate the content of the 4083 + fields covered in the signature in addition to ensuring that the 4084 + signature itself validates. With such protections, the attacker's 4085 + padding attack would be rejected by the field value processor, even 4086 + in the case where the attacker could force a signature collision. 4087 + 4088 + 7.5.8. Ambiguous Handling of Query Elements 4089 + 4090 + The HTML form parameters format defined in Section 5 ("application/ 4091 + x-www-form-urlencoded") of [HTMLURL] is widely deployed and supported 4092 + by many application frameworks. For convenience, some of these 4093 + frameworks in particular combine query parameters that are found in 4094 + the HTTP query and those found in the message content, particularly 4095 + for POST messages with a Content-Type value of "application/x-www- 4096 + form-urlencoded". The @query-param derived component identifier 4097 + defined in Section 2.2.8 draws its values only from the query section 4098 + of the target URI of the request. As such, it would be possible for 4099 + an attacker to shadow or replace query parameters in a request by 4100 + overriding a signed query parameter with an unsigned form parameter, 4101 + or vice versa. 4102 + 4103 + To counter this, an application needs to make sure that values used 4104 + for the signature base and the application are drawn from a 4105 + consistent context, in this case the query component of the target 4106 + URI. Additionally, when the HTTP request has content, an application 4107 + should sign the message content as well, as discussed in 4108 + Section 7.2.8. 4109 + 4110 + 8. Privacy Considerations 4111 + 4112 + 8.1. Identification through Keys 4113 + 4114 + If a signer uses the same key with multiple verifiers or uses the 4115 + same key over time with a single verifier, the ongoing use of that 4116 + key can be used to track the signer throughout the set of verifiers 4117 + that messages are sent to. Since cryptographic keys are meant to be 4118 + functionally unique, the use of the same key over time is a strong 4119 + indicator that it is the same party signing multiple messages. 4120 + 4121 + In many applications, this is a desirable trait, and it allows HTTP 4122 + message signatures to be used as part of authenticating the signer to 4123 + the verifier. However, it could also result in unintentional 4124 + tracking that a signer might not be aware of. To counter this kind 4125 + of tracking, a signer can use a different key for each verifier that 4126 + it is in communication with. Sometimes, a signer could also rotate 4127 + their key when sending messages to a given verifier. These 4128 + approaches do not negate the need for other anti-tracking techniques 4129 + to be applied as necessary. 4130 + 4131 + 8.2. Signatures do not provide confidentiality 4132 + 4133 + HTTP message signatures do not provide confidentiality for any of the 4134 + information protected by the signature. The content of the HTTP 4135 + message, including the value of all fields and the value of the 4136 + signature itself, is presented in plaintext to any party with access 4137 + to the message. 4138 + 4139 + To provide confidentiality at the transport level, TLS or its 4140 + equivalent can be used, as discussed in Section 7.1.2. 4141 + 4142 + 8.3. Oracles 4143 + 4144 + It is important to balance the need for providing useful feedback to 4145 + developers regarding error conditions without providing additional 4146 + information to an attacker. For example, a naive but helpful server 4147 + implementation might try to indicate the required key identifier 4148 + needed for requesting a resource. If someone knows who controls that 4149 + key, a correlation can be made between the resource's existence and 4150 + the party identified by the key. Access to such information could be 4151 + used by an attacker as a means to target the legitimate owner of the 4152 + resource for further attacks. 4153 + 4154 + 8.4. Required Content 4155 + 4156 + A core design tenet of this specification is that all message 4157 + components covered by the signature need to be available to the 4158 + verifier in order to recreate the signature base and verify the 4159 + signature. As a consequence, if an application of this specification 4160 + requires that a particular field be signed, the verifier will need 4161 + access to the value of that field. 4162 + 4163 + For example, in some complex systems with intermediary processors, 4164 + this could cause surprising behavior where, for fear of breaking the 4165 + signature, an intermediary cannot remove privacy-sensitive 4166 + information from a message before forwarding it on for processing. 4167 + One way to mitigate this specific situation would be for the 4168 + intermediary to verify the signature itself and then modify the 4169 + message to remove the privacy-sensitive information. The 4170 + intermediary can add its own signature at this point to signal to the 4171 + next destination that the incoming signature was validated, as shown 4172 + in the example in Section 4.3. 4173 + 4174 + 9. References 4175 + 4176 + 9.1. Normative References 4177 + 4178 + [ABNF] Crocker, D., Ed. and P. Overell, "Augmented BNF for Syntax 4179 + Specifications: ABNF", STD 68, RFC 5234, 4180 + DOI 10.17487/RFC5234, January 2008, 4181 + <https://www.rfc-editor.org/info/rfc5234>. 4182 + 4183 + [ASCII] Cerf, V., "ASCII format for network interchange", STD 80, 4184 + RFC 20, DOI 10.17487/RFC0020, October 1969, 4185 + <https://www.rfc-editor.org/info/rfc20>. 4186 + 4187 + [FIPS186-5] 4188 + NIST, "Digital Signature Standard (DSS)", 4189 + DOI 10.6028/NIST.FIPS.186-5, February 2023, 4190 + <https://doi.org/10.6028/NIST.FIPS.186-5>. 4191 + 4192 + [HTMLURL] WHATWG, "URL (Living Standard)", January 2024, 4193 + <https://url.spec.whatwg.org/>. 4194 + 4195 + [HTTP] Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, 4196 + Ed., "HTTP Semantics", STD 97, RFC 9110, 4197 + DOI 10.17487/RFC9110, June 2022, 4198 + <https://www.rfc-editor.org/info/rfc9110>. 4199 + 4200 + [HTTP/1.1] Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, 4201 + Ed., "HTTP/1.1", STD 99, RFC 9112, DOI 10.17487/RFC9112, 4202 + June 2022, <https://www.rfc-editor.org/info/rfc9112>. 4203 + 4204 + [POSIX.1] IEEE, "The Open Group Base Specifications Issue 7, 2018 4205 + edition", 2018, 4206 + <https://pubs.opengroup.org/onlinepubs/9699919799/>. 4207 + 4208 + [RFC2104] Krawczyk, H., Bellare, M., and R. Canetti, "HMAC: Keyed- 4209 + Hashing for Message Authentication", RFC 2104, 4210 + DOI 10.17487/RFC2104, February 1997, 4211 + <https://www.rfc-editor.org/info/rfc2104>. 4212 + 4213 + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate 4214 + Requirement Levels", BCP 14, RFC 2119, 4215 + DOI 10.17487/RFC2119, March 1997, 4216 + <https://www.rfc-editor.org/info/rfc2119>. 4217 + 4218 + [RFC6234] Eastlake 3rd, D. and T. Hansen, "US Secure Hash Algorithms 4219 + (SHA and SHA-based HMAC and HKDF)", RFC 6234, 4220 + DOI 10.17487/RFC6234, May 2011, 4221 + <https://www.rfc-editor.org/info/rfc6234>. 4222 + 4223 + [RFC7517] Jones, M., "JSON Web Key (JWK)", RFC 7517, 4224 + DOI 10.17487/RFC7517, May 2015, 4225 + <https://www.rfc-editor.org/info/rfc7517>. 4226 + 4227 + [RFC7518] Jones, M., "JSON Web Algorithms (JWA)", RFC 7518, 4228 + DOI 10.17487/RFC7518, May 2015, 4229 + <https://www.rfc-editor.org/info/rfc7518>. 4230 + 4231 + [RFC8017] Moriarty, K., Ed., Kaliski, B., Jonsson, J., and A. Rusch, 4232 + "PKCS #1: RSA Cryptography Specifications Version 2.2", 4233 + RFC 8017, DOI 10.17487/RFC8017, November 2016, 4234 + <https://www.rfc-editor.org/info/rfc8017>. 4235 + 4236 + [RFC8032] Josefsson, S. and I. Liusvaara, "Edwards-Curve Digital 4237 + Signature Algorithm (EdDSA)", RFC 8032, 4238 + DOI 10.17487/RFC8032, January 2017, 4239 + <https://www.rfc-editor.org/info/rfc8032>. 4240 + 4241 + [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 4242 + 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, 4243 + May 2017, <https://www.rfc-editor.org/info/rfc8174>. 4244 + 4245 + [STRUCTURED-FIELDS] 4246 + Nottingham, M. and P. Kamp, "Structured Field Values for 4247 + HTTP", RFC 8941, DOI 10.17487/RFC8941, February 2021, 4248 + <https://www.rfc-editor.org/info/rfc8941>. 4249 + 4250 + [URI] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform 4251 + Resource Identifier (URI): Generic Syntax", STD 66, 4252 + RFC 3986, DOI 10.17487/RFC3986, January 2005, 4253 + <https://www.rfc-editor.org/info/rfc3986>. 4254 + 4255 + 9.2. Informative References 4256 + 4257 + [AWS-SIGv4] 4258 + Amazon Simple Storage Service, "Authenticating Requests 4259 + (AWS Signature Version 4)", March 2006, 4260 + <https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4- 4261 + authenticating-requests.html>. 4262 + 4263 + [BCP195] Moriarty, K. and S. Farrell, "Deprecating TLS 1.0 and TLS 4264 + 1.1", BCP 195, RFC 8996, March 2021. 4265 + 4266 + Sheffer, Y., Saint-Andre, P., and T. Fossati, 4267 + "Recommendations for Secure Use of Transport Layer 4268 + Security (TLS) and Datagram Transport Layer Security 4269 + (DTLS)", BCP 195, RFC 9325, November 2022. 4270 + 4271 + <https://www.rfc-editor.org/info/bcp195> 4272 + 4273 + [CLIENT-CERT] 4274 + Campbell, B. and M. Bishop, "Client-Cert HTTP Header 4275 + Field", RFC 9440, DOI 10.17487/RFC9440, July 2023, 4276 + <https://www.rfc-editor.org/info/rfc9440>. 4277 + 4278 + [COOKIE] Barth, A., "HTTP State Management Mechanism", RFC 6265, 4279 + DOI 10.17487/RFC6265, April 2011, 4280 + <https://www.rfc-editor.org/info/rfc6265>. 4281 + 4282 + [DIGEST] Polli, R. and L. Pardue, "Digest Fields", RFC 9530, 4283 + DOI 10.17487/RFC9530, February 2024, 4284 + <https://www.rfc-editor.org/info/rfc9530>. 4285 + 4286 + [JACKSON2019] 4287 + Jackson, D., Cremers, C., Cohn-Gordon, K., and R. Sasse, 4288 + "Seems Legit: Automated Analysis of Subtle Attacks on 4289 + Protocols that Use Signatures", CCS '19: Proceedings of 4290 + the 2019 ACM SIGSAC Conference on Computer and 4291 + Communications Security, pp. 2165-2180, 4292 + DOI 10.1145/3319535.3339813, November 2019, 4293 + <https://dl.acm.org/doi/10.1145/3319535.3339813>. 4294 + 4295 + [JWS] Jones, M., Bradley, J., and N. Sakimura, "JSON Web 4296 + Signature (JWS)", RFC 7515, DOI 10.17487/RFC7515, May 4297 + 2015, <https://www.rfc-editor.org/info/rfc7515>. 4298 + 4299 + [RFC7239] Petersson, A. and M. Nilsson, "Forwarded HTTP Extension", 4300 + RFC 7239, DOI 10.17487/RFC7239, June 2014, 4301 + <https://www.rfc-editor.org/info/rfc7239>. 4302 + 4303 + [RFC7468] Josefsson, S. and S. Leonard, "Textual Encodings of PKIX, 4304 + PKCS, and CMS Structures", RFC 7468, DOI 10.17487/RFC7468, 4305 + April 2015, <https://www.rfc-editor.org/info/rfc7468>. 4306 + 4307 + [RFC7807] Nottingham, M. and E. Wilde, "Problem Details for HTTP 4308 + APIs", RFC 7807, DOI 10.17487/RFC7807, March 2016, 4309 + <https://www.rfc-editor.org/info/rfc7807>. 4310 + 4311 + [RFC8126] Cotton, M., Leiba, B., and T. Narten, "Guidelines for 4312 + Writing an IANA Considerations Section in RFCs", BCP 26, 4313 + RFC 8126, DOI 10.17487/RFC8126, June 2017, 4314 + <https://www.rfc-editor.org/info/rfc8126>. 4315 + 4316 + [RFC8792] Watsen, K., Auerswald, E., Farrel, A., and Q. Wu, 4317 + "Handling Long Lines in Content of Internet-Drafts and 4318 + RFCs", RFC 8792, DOI 10.17487/RFC8792, June 2020, 4319 + <https://www.rfc-editor.org/info/rfc8792>. 4320 + 4321 + [RFC9457] Nottingham, M., Wilde, E., and S. Dalal, "Problem Details 4322 + for HTTP APIs", RFC 9457, DOI 10.17487/RFC9457, July 2023, 4323 + <https://www.rfc-editor.org/info/rfc9457>. 4324 + 4325 + [SIGNING-HTTP-MESSAGES] 4326 + Cavage, M. and M. Sporny, "Signing HTTP Messages", Work in 4327 + Progress, Internet-Draft, draft-cavage-http-signatures-12, 4328 + 21 October 2019, <https://datatracker.ietf.org/doc/html/ 4329 + draft-cavage-http-signatures-12>. 4330 + 4331 + [SIGNING-HTTP-REQS-OAUTH] 4332 + Richer, J., Ed., Bradley, J., and H. Tschofenig, "A Method 4333 + for Signing HTTP Requests for OAuth", Work in Progress, 4334 + Internet-Draft, draft-ietf-oauth-signed-http-request-03, 8 4335 + August 2016, <https://datatracker.ietf.org/doc/html/draft- 4336 + ietf-oauth-signed-http-request-03>. 4337 + 4338 + [TLS] Rescorla, E., "The Transport Layer Security (TLS) Protocol 4339 + Version 1.3", RFC 8446, DOI 10.17487/RFC8446, August 2018, 4340 + <https://www.rfc-editor.org/info/rfc8446>. 4341 + 4342 + Appendix A. Detecting HTTP Message Signatures 4343 + 4344 + There have been many attempts to create signed HTTP messages in the 4345 + past, including other non-standardized definitions of the Signature 4346 + field that is used within this specification. It is recommended that 4347 + developers wishing to support this specification, other published 4348 + documents, or other historical drafts do so carefully and 4349 + deliberately, as incompatibilities between this specification and 4350 + other documents or various versions of other drafts could lead to 4351 + unexpected problems. 4352 + 4353 + It is recommended that implementors first detect and validate the 4354 + Signature-Input field defined in this specification to detect that 4355 + the mechanism described in this document is in use and not an 4356 + alternative. If the Signature-Input field is present, all Signature 4357 + fields can be parsed and interpreted in the context of this 4358 + specification. 4359 + 4360 + Appendix B. Examples 4361 + 4362 + The following non-normative examples are provided as a means of 4363 + testing implementations of HTTP message signatures. The signed 4364 + messages given can be used to create the signature base with the 4365 + stated parameters, creating signatures using the stated algorithms 4366 + and keys. 4367 + 4368 + The private keys given can be used to generate signatures, though 4369 + since several of the demonstrated algorithms are non-deterministic, 4370 + the results of a signature are expected to be different from the 4371 + exact bytes of the examples. The public keys given can be used to 4372 + validate all signed examples. 4373 + 4374 + B.1. Example Keys 4375 + 4376 + This section provides cryptographic keys that are referenced in 4377 + example signatures throughout this document. These keys MUST NOT be 4378 + used for any purpose other than testing. 4379 + 4380 + The key identifiers for each key are used throughout the examples in 4381 + this specification. It is assumed for these examples that the signer 4382 + and verifier can unambiguously dereference all key identifiers used 4383 + here and that the keys and algorithms used are appropriate for the 4384 + context in which the signature is presented. 4385 + 4386 + The components for each private key, in PEM format [RFC7468], can be 4387 + displayed by executing the following OpenSSL command: 4388 + 4389 + openssl pkey -text 4390 + 4391 + This command was tested with all the example keys on OpenSSL version 4392 + 1.1.1m. Note that some systems cannot produce or use all of these 4393 + keys directly and may require additional processing. All keys are 4394 + also made available in JWK format. 4395 + 4396 + B.1.1. Example RSA Key 4397 + 4398 + The following key is a 2048-bit RSA public and private key pair, 4399 + referred to in this document as test-key-rsa. This key is encoded in 4400 + PEM format, with no encryption. 4401 + 4402 + -----BEGIN RSA PUBLIC KEY----- 4403 + MIIBCgKCAQEAhAKYdtoeoy8zcAcR874L8cnZxKzAGwd7v36APp7Pv6Q2jdsPBRrw 4404 + WEBnez6d0UDKDwGbc6nxfEXAy5mbhgajzrw3MOEt8uA5txSKobBpKDeBLOsdJKFq 4405 + MGmXCQvEG7YemcxDTRPxAleIAgYYRjTSd/QBwVW9OwNFhekro3RtlinV0a75jfZg 4406 + kne/YiktSvLG34lw2zqXBDTC5NHROUqGTlML4PlNZS5Ri2U4aCNx2rUPRcKIlE0P 4407 + uKxI4T+HIaFpv8+rdV6eUgOrB2xeI1dSFFn/nnv5OoZJEIB+VmuKn3DCUcCZSFlQ 4408 + PSXSfBDiUGhwOw76WuSSsf1D4b/vLoJ10wIDAQAB 4409 + -----END RSA PUBLIC KEY----- 4410 + 4411 + -----BEGIN RSA PRIVATE KEY----- 4412 + MIIEqAIBAAKCAQEAhAKYdtoeoy8zcAcR874L8cnZxKzAGwd7v36APp7Pv6Q2jdsP 4413 + BRrwWEBnez6d0UDKDwGbc6nxfEXAy5mbhgajzrw3MOEt8uA5txSKobBpKDeBLOsd 4414 + JKFqMGmXCQvEG7YemcxDTRPxAleIAgYYRjTSd/QBwVW9OwNFhekro3RtlinV0a75 4415 + jfZgkne/YiktSvLG34lw2zqXBDTC5NHROUqGTlML4PlNZS5Ri2U4aCNx2rUPRcKI 4416 + lE0PuKxI4T+HIaFpv8+rdV6eUgOrB2xeI1dSFFn/nnv5OoZJEIB+VmuKn3DCUcCZ 4417 + SFlQPSXSfBDiUGhwOw76WuSSsf1D4b/vLoJ10wIDAQABAoIBAG/JZuSWdoVHbi56 4418 + vjgCgkjg3lkO1KrO3nrdm6nrgA9P9qaPjxuKoWaKO1cBQlE1pSWp/cKncYgD5WxE 4419 + CpAnRUXG2pG4zdkzCYzAh1i+c34L6oZoHsirK6oNcEnHveydfzJL5934egm6p8DW 4420 + +m1RQ70yUt4uRc0YSor+q1LGJvGQHReF0WmJBZHrhz5e63Pq7lE0gIwuBqL8SMaA 4421 + yRXtK+JGxZpImTq+NHvEWWCu09SCq0r838ceQI55SvzmTkwqtC+8AT2zFviMZkKR 4422 + Qo6SPsrqItxZWRty2izawTF0Bf5S2VAx7O+6t3wBsQ1sLptoSgX3QblELY5asI0J 4423 + YFz7LJECgYkAsqeUJmqXE3LP8tYoIjMIAKiTm9o6psPlc8CrLI9CH0UbuaA2JCOM 4424 + cCNq8SyYbTqgnWlB9ZfcAm/cFpA8tYci9m5vYK8HNxQr+8FS3Qo8N9RJ8d0U5Csw 4425 + DzMYfRghAfUGwmlWj5hp1pQzAuhwbOXFtxKHVsMPhz1IBtF9Y8jvgqgYHLbmyiu1 4426 + mwJ5AL0pYF0G7x81prlARURwHo0Yf52kEw1dxpx+JXER7hQRWQki5/NsUEtv+8RT 4427 + qn2m6qte5DXLyn83b1qRscSdnCCwKtKWUug5q2ZbwVOCJCtmRwmnP131lWRYfj67 4428 + B/xJ1ZA6X3GEf4sNReNAtaucPEelgR2nsN0gKQKBiGoqHWbK1qYvBxX2X3kbPDkv 4429 + 9C+celgZd2PW7aGYLCHq7nPbmfDV0yHcWjOhXZ8jRMjmANVR/eLQ2EfsRLdW69bn 4430 + f3ZD7JS1fwGnO3exGmHO3HZG+6AvberKYVYNHahNFEw5TsAcQWDLRpkGybBcxqZo 4431 + 81YCqlqidwfeO5YtlO7etx1xLyqa2NsCeG9A86UjG+aeNnXEIDk1PDK+EuiThIUa 4432 + /2IxKzJKWl1BKr2d4xAfR0ZnEYuRrbeDQYgTImOlfW6/GuYIxKYgEKCFHFqJATAG 4433 + IxHrq1PDOiSwXd2GmVVYyEmhZnbcp8CxaEMQoevxAta0ssMK3w6UsDtvUvYvF22m 4434 + qQKBiD5GwESzsFPy3Ga0MvZpn3D6EJQLgsnrtUPZx+z2Ep2x0xc5orneB5fGyF1P 4435 + WtP+fG5Q6Dpdz3LRfm+KwBCWFKQjg7uTxcjerhBWEYPmEMKYwTJF5PBG9/ddvHLQ 4436 + EQeNC8fHGg4UXU8mhHnSBt3EA10qQJfRDs15M38eG2cYwB1PZpDHScDnDA0= 4437 + -----END RSA PRIVATE KEY----- 4438 + 4439 + The same public and private key pair in JWK format: 4440 + 4441 + NOTE: '\' line wrapping per RFC 8792 4442 + 4443 + { 4444 + "kty": "RSA", 4445 + "kid": "test-key-rsa", 4446 + "p": "sqeUJmqXE3LP8tYoIjMIAKiTm9o6psPlc8CrLI9CH0UbuaA2JCOMcCNq8Sy\ 4447 + YbTqgnWlB9ZfcAm_cFpA8tYci9m5vYK8HNxQr-8FS3Qo8N9RJ8d0U5CswDzMYfRgh\ 4448 + AfUGwmlWj5hp1pQzAuhwbOXFtxKHVsMPhz1IBtF9Y8jvgqgYHLbmyiu1mw", 4449 + "q": "vSlgXQbvHzWmuUBFRHAejRh_naQTDV3GnH4lcRHuFBFZCSLn82xQS2_7xFO\ 4450 + qfabqq17kNcvKfzdvWpGxxJ2cILAq0pZS6DmrZlvBU4IkK2ZHCac_XfWVZFh-PrsH\ 4451 + _EnVkDpfcYR_iw1F40C1q5w8R6WBHaew3SAp", 4452 + "d": "b8lm5JZ2hUduLnq-OAKCSODeWQ7Uqs7eet2bqeuAD0_2po-PG4qhZoo7VwF\ 4453 + CUTWlJan9wqdxiAPlbEQKkCdFRcbakbjN2TMJjMCHWL5zfgvqhmgeyKsrqg1wSce9\ 4454 + 7J1_Mkvn3fh6CbqnwNb6bVFDvTJS3i5FzRhKiv6rUsYm8ZAdF4XRaYkFkeuHPl7rc\ 4455 + -ruUTSAjC4GovxIxoDJFe0r4kbFmkiZOr40e8RZYK7T1IKrSvzfxx5AjnlK_OZOTC\ 4456 + q0L7wBPbMW-IxmQpFCjpI-yuoi3FlZG3LaLNrBMXQF_lLZUDHs77q3fAGxDWwum2h\ 4457 + KBfdBuUQtjlqwjQlgXPsskQ", 4458 + "e": "AQAB", 4459 + "qi": "PkbARLOwU_LcZrQy9mmfcPoQlAuCyeu1Q9nH7PYSnbHTFzmiud4Hl8bIXU\ 4460 + 9a0_58blDoOl3PctF-b4rAEJYUpCODu5PFyN6uEFYRg-YQwpjBMkXk8Eb39128ctA\ 4461 + RB40Lx8caDhRdTyaEedIG3cQDXSpAl9EOzXkzfx4bZxjAHU9mkMdJwOcMDQ", 4462 + "dp": "aiodZsrWpi8HFfZfeRs8OS_0L5x6WBl3Y9btoZgsIeruc9uZ8NXTIdxaM6\ 4463 + FdnyNEyOYA1VH94tDYR-xEt1br1ud_dkPslLV_Aac7d7EaYc7cdkb7oC9t6sphVg0\ 4464 + dqE0UTDlOwBxBYMtGmQbJsFzGpmjzVgKqWqJ3B947li2U7t63HXEvKprY2w", 4465 + "dq": "b0DzpSMb5p42dcQgOTU8Mr4S6JOEhRr_YjErMkpaXUEqvZ3jEB9HRmcRi5\ 4466 + Gtt4NBiBMiY6V9br8a5gjEpiAQoIUcWokBMAYjEeurU8M6JLBd3YaZVVjISaFmdty\ 4467 + nwLFoQxCh6_EC1rSywwrfDpSwO29S9i8Xbaap", 4468 + "n": "hAKYdtoeoy8zcAcR874L8cnZxKzAGwd7v36APp7Pv6Q2jdsPBRrwWEBnez6\ 4469 + d0UDKDwGbc6nxfEXAy5mbhgajzrw3MOEt8uA5txSKobBpKDeBLOsdJKFqMGmXCQvE\ 4470 + G7YemcxDTRPxAleIAgYYRjTSd_QBwVW9OwNFhekro3RtlinV0a75jfZgkne_YiktS\ 4471 + vLG34lw2zqXBDTC5NHROUqGTlML4PlNZS5Ri2U4aCNx2rUPRcKIlE0PuKxI4T-HIa\ 4472 + Fpv8-rdV6eUgOrB2xeI1dSFFn_nnv5OoZJEIB-VmuKn3DCUcCZSFlQPSXSfBDiUGh\ 4473 + wOw76WuSSsf1D4b_vLoJ10w" 4474 + } 4475 + 4476 + B.1.2. Example RSA-PSS Key 4477 + 4478 + The following key is a 2048-bit RSA public and private key pair, 4479 + referred to in this document as test-key-rsa-pss. This key is PKCS 4480 + #8 encoded in PEM format, with no encryption. 4481 + 4482 + -----BEGIN PUBLIC KEY----- 4483 + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr4tmm3r20Wd/PbqvP1s2 4484 + +QEtvpuRaV8Yq40gjUR8y2Rjxa6dpG2GXHbPfvMs8ct+Lh1GH45x28Rw3Ry53mm+ 4485 + oAXjyQ86OnDkZ5N8lYbggD4O3w6M6pAvLkhk95AndTrifbIFPNU8PPMO7OyrFAHq 4486 + gDsznjPFmTOtCEcN2Z1FpWgchwuYLPL+Wokqltd11nqqzi+bJ9cvSKADYdUAAN5W 4487 + Utzdpiy6LbTgSxP7ociU4Tn0g5I6aDZJ7A8Lzo0KSyZYoA485mqcO0GVAdVw9lq4 4488 + aOT9v6d+nb4bnNkQVklLQ3fVAvJm+xdDOp9LCNCN48V2pnDOkFV6+U9nV5oyc6XI 4489 + 2wIDAQAB 4490 + -----END PUBLIC KEY----- 4491 + 4492 + -----BEGIN PRIVATE KEY----- 4493 + MIIEvgIBADALBgkqhkiG9w0BAQoEggSqMIIEpgIBAAKCAQEAr4tmm3r20Wd/Pbqv 4494 + P1s2+QEtvpuRaV8Yq40gjUR8y2Rjxa6dpG2GXHbPfvMs8ct+Lh1GH45x28Rw3Ry5 4495 + 3mm+oAXjyQ86OnDkZ5N8lYbggD4O3w6M6pAvLkhk95AndTrifbIFPNU8PPMO7Oyr 4496 + FAHqgDsznjPFmTOtCEcN2Z1FpWgchwuYLPL+Wokqltd11nqqzi+bJ9cvSKADYdUA 4497 + AN5WUtzdpiy6LbTgSxP7ociU4Tn0g5I6aDZJ7A8Lzo0KSyZYoA485mqcO0GVAdVw 4498 + 9lq4aOT9v6d+nb4bnNkQVklLQ3fVAvJm+xdDOp9LCNCN48V2pnDOkFV6+U9nV5oy 4499 + c6XI2wIDAQABAoIBAQCUB8ip+kJiiZVKF8AqfB/aUP0jTAqOQewK1kKJ/iQCXBCq 4500 + pbo360gvdt05H5VZ/RDVkEgO2k73VSsbulqezKs8RFs2tEmU+JgTI9MeQJPWcP6X 4501 + aKy6LIYs0E2cWgp8GADgoBs8llBq0UhX0KffglIeek3n7Z6Gt4YFge2TAcW2WbN4 4502 + XfK7lupFyo6HHyWRiYHMMARQXLJeOSdTn5aMBP0PO4bQyk5ORxTUSeOciPJUFktQ 4503 + HkvGbym7KryEfwH8Tks0L7WhzyP60PL3xS9FNOJi9m+zztwYIXGDQuKM2GDsITeD 4504 + 2mI2oHoPMyAD0wdI7BwSVW18p1h+jgfc4dlexKYRAoGBAOVfuiEiOchGghV5vn5N 4505 + RDNscAFnpHj1QgMr6/UG05RTgmcLfVsI1I4bSkbrIuVKviGGf7atlkROALOG/xRx 4506 + DLadgBEeNyHL5lz6ihQaFJLVQ0u3U4SB67J0YtVO3R6lXcIjBDHuY8SjYJ7Ci6Z6 4507 + vuDcoaEujnlrtUhaMxvSfcUJAoGBAMPsCHXte1uWNAqYad2WdLjPDlKtQJK1diCm 4508 + rqmB2g8QE99hDOHItjDBEdpyFBKOIP+NpVtM2KLhRajjcL9Ph8jrID6XUqikQuVi 4509 + 4J9FV2m42jXMuioTT13idAILanYg8D3idvy/3isDVkON0X3UAVKrgMEne0hJpkPL 4510 + FYqgetvDAoGBAKLQ6JZMbSe0pPIJkSamQhsehgL5Rs51iX4m1z7+sYFAJfhvN3Q/ 4511 + OGIHDRp6HjMUcxHpHw7U+S1TETxePwKLnLKj6hw8jnX2/nZRgWHzgVcY+sPsReRx 4512 + NJVf+Cfh6yOtznfX00p+JWOXdSY8glSSHJwRAMog+hFGW1AYdt7w80XBAoGBAImR 4513 + NUugqapgaEA8TrFxkJmngXYaAqpA0iYRA7kv3S4QavPBUGtFJHBNULzitydkNtVZ 4514 + 3w6hgce0h9YThTo/nKc+OZDZbgfN9s7cQ75x0PQCAO4fx2P91Q+mDzDUVTeG30mE 4515 + t2m3S0dGe47JiJxifV9P3wNBNrZGSIF3mrORBVNDAoGBAI0QKn2Iv7Sgo4T/XjND 4516 + dl2kZTXqGAk8dOhpUiw/HdM3OGWbhHj2NdCzBliOmPyQtAr770GITWvbAI+IRYyF 4517 + S7Fnk6ZVVVHsxjtaHy1uJGFlaZzKR4AGNaUTOJMs6NadzCmGPAxNQQOCqoUjn4XR 4518 + rOjr9w349JooGXhOxbu8nOxX 4519 + -----END PRIVATE KEY----- 4520 + 4521 + The same public and private key pair in JWK format: 4522 + 4523 + NOTE: '\' line wrapping per RFC 8792 4524 + 4525 + { 4526 + "kty": "RSA", 4527 + "kid": "test-key-rsa-pss", 4528 + "p": "5V-6ISI5yEaCFXm-fk1EM2xwAWekePVCAyvr9QbTlFOCZwt9WwjUjhtKRus\ 4529 + i5Uq-IYZ_tq2WRE4As4b_FHEMtp2AER43IcvmXPqKFBoUktVDS7dThIHrsnRi1U7d\ 4530 + HqVdwiMEMe5jxKNgnsKLpnq-4NyhoS6OeWu1SFozG9J9xQk", 4531 + "q": "w-wIde17W5Y0Cphp3ZZ0uM8OUq1AkrV2IKauqYHaDxAT32EM4ci2MMER2nI\ 4532 + UEo4g_42lW0zYouFFqONwv0-HyOsgPpdSqKRC5WLgn0VXabjaNcy6KhNPXeJ0Agtq\ 4533 + diDwPeJ2_L_eKwNWQ43RfdQBUquAwSd7SEmmQ8sViqB628M", 4534 + "d": "lAfIqfpCYomVShfAKnwf2lD9I0wKjkHsCtZCif4kAlwQqqW6N-tIL3bdOR-\ 4535 + VWf0Q1ZBIDtpO91UrG7pansyrPERbNrRJlPiYEyPTHkCT1nD-l2isuiyGLNBNnFoK\ 4536 + fBgA4KAbPJZQatFIV9Cn34JSHnpN5-2ehreGBYHtkwHFtlmzeF3yu5bqRcqOhx8lk\ 4537 + YmBzDAEUFyyXjknU5-WjAT9DzuG0MpOTkcU1EnjnIjyVBZLUB5Lxm8puyq8hH8B_E\ 4538 + 5LNC-1oc8j-tDy98UvRTTiYvZvs87cGCFxg0LijNhg7CE3g9piNqB6DzMgA9MHSOw\ 4539 + cElVtfKdYfo4H3OHZXsSmEQ", 4540 + "e": "AQAB", 4541 + "qi": "jRAqfYi_tKCjhP9eM0N2XaRlNeoYCTx06GlSLD8d0zc4ZZuEePY10LMGWI\ 4542 + 6Y_JC0CvvvQYhNa9sAj4hFjIVLsWeTplVVUezGO1ofLW4kYWVpnMpHgAY1pRM4kyz\ 4543 + o1p3MKYY8DE1BA4KqhSOfhdGs6Ov3Dfj0migZeE7Fu7yc7Fc", 4544 + "dp": "otDolkxtJ7Sk8gmRJqZCGx6GAvlGznWJfibXPv6xgUAl-G83dD84YgcNGn\ 4545 + oeMxRzEekfDtT5LVMRPF4_AoucsqPqHDyOdfb-dlGBYfOBVxj6w-xF5HE0lV_4J-H\ 4546 + rI63Od9fTSn4lY5d1JjyCVJIcnBEAyiD6EUZbUBh23vDzRcE", 4547 + "dq": "iZE1S6CpqmBoQDxOsXGQmaeBdhoCqkDSJhEDuS_dLhBq88FQa0UkcE1QvO\ 4548 + K3J2Q21VnfDqGBx7SH1hOFOj-cpz45kNluB832ztxDvnHQ9AIA7h_HY_3VD6YPMNR\ 4549 + VN4bfSYS3abdLR0Z7jsmInGJ9X0_fA0E2tkZIgXeas5EFU0M", 4550 + "n": "r4tmm3r20Wd_PbqvP1s2-QEtvpuRaV8Yq40gjUR8y2Rjxa6dpG2GXHbPfvM\ 4551 + s8ct-Lh1GH45x28Rw3Ry53mm-oAXjyQ86OnDkZ5N8lYbggD4O3w6M6pAvLkhk95An\ 4552 + dTrifbIFPNU8PPMO7OyrFAHqgDsznjPFmTOtCEcN2Z1FpWgchwuYLPL-Wokqltd11\ 4553 + nqqzi-bJ9cvSKADYdUAAN5WUtzdpiy6LbTgSxP7ociU4Tn0g5I6aDZJ7A8Lzo0KSy\ 4554 + ZYoA485mqcO0GVAdVw9lq4aOT9v6d-nb4bnNkQVklLQ3fVAvJm-xdDOp9LCNCN48V\ 4555 + 2pnDOkFV6-U9nV5oyc6XI2w" 4556 + } 4557 + 4558 + B.1.3. Example ECC P-256 Test Key 4559 + 4560 + The following key is a public and private elliptical curve key pair 4561 + over the curve P-256, referred to in this document as test-key-ecc- 4562 + p256. This key is encoded in PEM format, with no encryption. 4563 + 4564 + -----BEGIN PUBLIC KEY----- 4565 + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqIVYZVLCrPZHGHjP17CTW0/+D9Lf 4566 + w0EkjqF7xB4FivAxzic30tMM4GF+hR6Dxh71Z50VGGdldkkDXZCnTNnoXQ== 4567 + -----END PUBLIC KEY----- 4568 + 4569 + -----BEGIN EC PRIVATE KEY----- 4570 + MHcCAQEEIFKbhfNZfpDsW43+0+JjUr9K+bTeuxopu653+hBaXGA7oAoGCCqGSM49 4571 + AwEHoUQDQgAEqIVYZVLCrPZHGHjP17CTW0/+D9Lfw0EkjqF7xB4FivAxzic30tMM 4572 + 4GF+hR6Dxh71Z50VGGdldkkDXZCnTNnoXQ== 4573 + -----END EC PRIVATE KEY----- 4574 + 4575 + The same public and private key pair in JWK format: 4576 + 4577 + { 4578 + "kty": "EC", 4579 + "crv": "P-256", 4580 + "kid": "test-key-ecc-p256", 4581 + "d": "UpuF81l-kOxbjf7T4mNSv0r5tN67Gim7rnf6EFpcYDs", 4582 + "x": "qIVYZVLCrPZHGHjP17CTW0_-D9Lfw0EkjqF7xB4FivA", 4583 + "y": "Mc4nN9LTDOBhfoUeg8Ye9WedFRhnZXZJA12Qp0zZ6F0" 4584 + } 4585 + 4586 + B.1.4. Example Ed25519 Test Key 4587 + 4588 + The following key is an elliptical curve key over the Edwards curve 4589 + ed25519, referred to in this document as test-key-ed25519. This key 4590 + is PKCS #8 encoded in PEM format, with no encryption. 4591 + 4592 + -----BEGIN PUBLIC KEY----- 4593 + MCowBQYDK2VwAyEAJrQLj5P/89iXES9+vFgrIy29clF9CC/oPPsw3c5D0bs= 4594 + -----END PUBLIC KEY----- 4595 + 4596 + -----BEGIN PRIVATE KEY----- 4597 + MC4CAQAwBQYDK2VwBCIEIJ+DYvh6SEqVTm50DFtMDoQikTmiCqirVv9mWG9qfSnF 4598 + -----END PRIVATE KEY----- 4599 + 4600 + The same public and private key pair in JWK format: 4601 + 4602 + { 4603 + "kty": "OKP", 4604 + "crv": "Ed25519", 4605 + "kid": "test-key-ed25519", 4606 + "d": "n4Ni-HpISpVObnQMW0wOhCKROaIKqKtW_2ZYb2p9KcU", 4607 + "x": "JrQLj5P_89iXES9-vFgrIy29clF9CC_oPPsw3c5D0bs" 4608 + } 4609 + 4610 + B.1.5. Example Shared Secret 4611 + 4612 + The following shared secret is 64 randomly generated bytes encoded in 4613 + Base64, referred to in this document as test-shared-secret: 4614 + 4615 + NOTE: '\' line wrapping per RFC 8792 4616 + 4617 + uzvJfB4u3N0Jy4T7NZ75MDVcr8zSTInedJtkgcu46YW4XByzNJjxBdtjUkdJPBt\ 4618 + bmHhIDi6pcl8jsasjlTMtDQ== 4619 + 4620 + B.2. Test Cases 4621 + 4622 + This section provides non-normative examples that may be used as test 4623 + cases to validate implementation correctness. These examples are 4624 + based on the following HTTP messages: 4625 + 4626 + For requests, this test-request message is used: 4627 + 4628 + NOTE: '\' line wrapping per RFC 8792 4629 + 4630 + POST /foo?param=Value&Pet=dog HTTP/1.1 4631 + Host: example.com 4632 + Date: Tue, 20 Apr 2021 02:07:55 GMT 4633 + Content-Type: application/json 4634 + Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+T\ 4635 + aPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: 4636 + Content-Length: 18 4637 + 4638 + {"hello": "world"} 4639 + 4640 + For responses, this test-response message is used: 4641 + 4642 + NOTE: '\' line wrapping per RFC 8792 4643 + 4644 + HTTP/1.1 200 OK 4645 + Date: Tue, 20 Apr 2021 02:07:56 GMT 4646 + Content-Type: application/json 4647 + Content-Digest: sha-512=:mEWXIS7MaLRuGgxOBdODa3xqM1XdEvxoYhvlCFJ41Q\ 4648 + JgJc4GTsPp29l5oGX69wWdXymyU0rjJuahq4l5aGgfLQ==: 4649 + Content-Length: 23 4650 + 4651 + {"message": "good dog"} 4652 + 4653 + B.2.1. Minimal Signature Using rsa-pss-sha512 4654 + 4655 + This example presents a minimal signature using the rsa-pss-sha512 4656 + algorithm over test-request, covering none of the components of the 4657 + HTTP message but providing a timestamped signature proof of 4658 + possession of the key with a signer-provided nonce. 4659 + 4660 + The corresponding signature base is: 4661 + 4662 + NOTE: '\' line wrapping per RFC 8792 4663 + 4664 + "@signature-params": ();created=1618884473;keyid="test-key-rsa-pss"\ 4665 + ;nonce="b3k2pp5k7z-50gnwp.yemd" 4666 + 4667 + This results in the following Signature-Input and Signature header 4668 + fields being added to the message under the signature label sig-b21: 4669 + 4670 + NOTE: '\' line wrapping per RFC 8792 4671 + 4672 + Signature-Input: sig-b21=();created=1618884473\ 4673 + ;keyid="test-key-rsa-pss";nonce="b3k2pp5k7z-50gnwp.yemd" 4674 + Signature: sig-b21=:d2pmTvmbncD3xQm8E9ZV2828BjQWGgiwAaw5bAkgibUopem\ 4675 + LJcWDy/lkbbHAve4cRAtx31Iq786U7it++wgGxbtRxf8Udx7zFZsckzXaJMkA7ChG\ 4676 + 52eSkFxykJeNqsrWH5S+oxNFlD4dzVuwe8DhTSja8xxbR/Z2cOGdCbzR72rgFWhzx\ 4677 + 2VjBqJzsPLMIQKhO4DGezXehhWwE56YCE+O6c0mKZsfxVrogUvA4HELjVKWmAvtl6\ 4678 + UnCh8jYzuVG5WSb/QEVPnP5TmcAnLH1g+s++v6d4s8m0gCw1fV5/SITLq9mhho8K3\ 4679 + +7EPYTU8IU1bLhdxO5Nyt8C8ssinQ98Xw9Q==: 4680 + 4681 + Note that since the covered components list is empty, this signature 4682 + could be applied by an attacker to an unrelated HTTP message. In 4683 + this example, the nonce parameter is included to prevent the same 4684 + signature from being replayed more than once, but if an attacker 4685 + intercepts the signature and prevents its delivery to the verifier, 4686 + the attacker could apply this signature to another message. 4687 + Therefore, the use of an empty covered components set is discouraged. 4688 + See Section 7.2.1 for more discussion. 4689 + 4690 + Note that the RSA-PSS algorithm in use here is non-deterministic, 4691 + meaning that a different signature value will be created every time 4692 + the algorithm is run. The signature value provided here can be 4693 + validated against the given keys, but newly generated signature 4694 + values are not expected to match the example. See Section 7.3.5. 4695 + 4696 + B.2.2. Selective Covered Components Using rsa-pss-sha512 4697 + 4698 + This example covers additional components (the authority, the 4699 + Content-Digest header field, and a single named query parameter) in 4700 + test-request using the rsa-pss-sha512 algorithm. This example also 4701 + adds a tag parameter with the application-specific value of header- 4702 + example. 4703 + 4704 + The corresponding signature base is: 4705 + 4706 + NOTE: '\' line wrapping per RFC 8792 4707 + 4708 + "@authority": example.com 4709 + "content-digest": sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX\ 4710 + +TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: 4711 + "@query-param";name="Pet": dog 4712 + "@signature-params": ("@authority" "content-digest" \ 4713 + "@query-param";name="Pet")\ 4714 + ;created=1618884473;keyid="test-key-rsa-pss"\ 4715 + ;tag="header-example" 4716 + 4717 + This results in the following Signature-Input and Signature header 4718 + fields being added to the message under the label sig-b22: 4719 + 4720 + NOTE: '\' line wrapping per RFC 8792 4721 + 4722 + Signature-Input: sig-b22=("@authority" "content-digest" \ 4723 + "@query-param";name="Pet");created=1618884473\ 4724 + ;keyid="test-key-rsa-pss";tag="header-example" 4725 + Signature: sig-b22=:LjbtqUbfmvjj5C5kr1Ugj4PmLYvx9wVjZvD9GsTT4F7GrcQ\ 4726 + EdJzgI9qHxICagShLRiLMlAJjtq6N4CDfKtjvuJyE5qH7KT8UCMkSowOB4+ECxCmT\ 4727 + 8rtAmj/0PIXxi0A0nxKyB09RNrCQibbUjsLS/2YyFYXEu4TRJQzRw1rLEuEfY17SA\ 4728 + RYhpTlaqwZVtR8NV7+4UKkjqpcAoFqWFQh62s7Cl+H2fjBSpqfZUJcsIk4N6wiKYd\ 4729 + 4je2U/lankenQ99PZfB4jY3I5rSV2DSBVkSFsURIjYErOs0tFTQosMTAoxk//0RoK\ 4730 + UqiYY8Bh0aaUEb0rQl3/XaVe4bXTugEjHSw==: 4731 + 4732 + Note that the RSA-PSS algorithm in use here is non-deterministic, 4733 + meaning that a different signature value will be created every time 4734 + the algorithm is run. The signature value provided here can be 4735 + validated against the given keys, but newly generated signature 4736 + values are not expected to match the example. See Section 7.3.5. 4737 + 4738 + B.2.3. Full Coverage Using rsa-pss-sha512 4739 + 4740 + This example covers all applicable message components in test-request 4741 + (including the content type and length) plus many derived components, 4742 + again using the rsa-pss-sha512 algorithm. Note that the Host header 4743 + field is not covered because the @authority derived component is 4744 + included instead. 4745 + 4746 + The corresponding signature base is: 4747 + 4748 + NOTE: '\' line wrapping per RFC 8792 4749 + 4750 + "date": Tue, 20 Apr 2021 02:07:55 GMT 4751 + "@method": POST 4752 + "@path": /foo 4753 + "@query": ?param=Value&Pet=dog 4754 + "@authority": example.com 4755 + "content-type": application/json 4756 + "content-digest": sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX\ 4757 + +TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: 4758 + "content-length": 18 4759 + "@signature-params": ("date" "@method" "@path" "@query" \ 4760 + "@authority" "content-type" "content-digest" "content-length")\ 4761 + ;created=1618884473;keyid="test-key-rsa-pss" 4762 + 4763 + This results in the following Signature-Input and Signature header 4764 + fields being added to the message under the label sig-b23: 4765 + 4766 + NOTE: '\' line wrapping per RFC 8792 4767 + 4768 + Signature-Input: sig-b23=("date" "@method" "@path" "@query" \ 4769 + "@authority" "content-type" "content-digest" "content-length")\ 4770 + ;created=1618884473;keyid="test-key-rsa-pss" 4771 + Signature: sig-b23=:bbN8oArOxYoyylQQUU6QYwrTuaxLwjAC9fbY2F6SVWvh0yB\ 4772 + iMIRGOnMYwZ/5MR6fb0Kh1rIRASVxFkeGt683+qRpRRU5p2voTp768ZrCUb38K0fU\ 4773 + xN0O0iC59DzYx8DFll5GmydPxSmme9v6ULbMFkl+V5B1TP/yPViV7KsLNmvKiLJH1\ 4774 + pFkh/aYA2HXXZzNBXmIkoQoLd7YfW91kE9o/CCoC1xMy7JA1ipwvKvfrs65ldmlu9\ 4775 + bpG6A9BmzhuzF8Eim5f8ui9eH8LZH896+QIF61ka39VBrohr9iyMUJpvRX2Zbhl5Z\ 4776 + JzSRxpJyoEZAFL2FUo5fTIztsDZKEgM4cUA==: 4777 + 4778 + Note in this example that the value of the Date header field and the 4779 + value of the created signature parameter need not be the same. This 4780 + is due to the fact that the Date header field is added when creating 4781 + the HTTP message and the created parameter is populated when creating 4782 + the signature over that message, and these two times could vary. If 4783 + the Date header field is covered by the signature, it is up to the 4784 + verifier to determine whether its value has to match that of the 4785 + created parameter or not. See Section 7.2.4 for more discussion. 4786 + 4787 + Note that the RSA-PSS algorithm in use here is non-deterministic, 4788 + meaning that a different signature value will be created every time 4789 + the algorithm is run. The signature value provided here can be 4790 + validated against the given keys, but newly generated signature 4791 + values are not expected to match the example. See Section 7.3.5. 4792 + 4793 + B.2.4. Signing a Response Using ecdsa-p256-sha256 4794 + 4795 + This example covers portions of the test-response message using the 4796 + ecdsa-p256-sha256 algorithm and the key test-key-ecc-p256. 4797 + 4798 + The corresponding signature base is: 4799 + 4800 + NOTE: '\' line wrapping per RFC 8792 4801 + 4802 + "@status": 200 4803 + "content-type": application/json 4804 + "content-digest": sha-512=:mEWXIS7MaLRuGgxOBdODa3xqM1XdEvxoYhvlCFJ4\ 4805 + 1QJgJc4GTsPp29l5oGX69wWdXymyU0rjJuahq4l5aGgfLQ==: 4806 + "content-length": 23 4807 + "@signature-params": ("@status" "content-type" "content-digest" \ 4808 + "content-length");created=1618884473;keyid="test-key-ecc-p256" 4809 + 4810 + This results in the following Signature-Input and Signature header 4811 + fields being added to the message under the label sig-b24: 4812 + 4813 + NOTE: '\' line wrapping per RFC 8792 4814 + 4815 + Signature-Input: sig-b24=("@status" "content-type" \ 4816 + "content-digest" "content-length");created=1618884473\ 4817 + ;keyid="test-key-ecc-p256" 4818 + Signature: sig-b24=:wNmSUAhwb5LxtOtOpNa6W5xj067m5hFrj0XQ4fvpaCLx0NK\ 4819 + ocgPquLgyahnzDnDAUy5eCdlYUEkLIj+32oiasw==: 4820 + 4821 + Note that the ECDSA signature algorithm in use here is non- 4822 + deterministic, meaning that a different signature value will be 4823 + created every time the algorithm is run. The signature value 4824 + provided here can be validated against the given keys, but newly 4825 + generated signature values are not expected to match the example. 4826 + See Section 7.3.5. 4827 + 4828 + B.2.5. Signing a Request Using hmac-sha256 4829 + 4830 + This example covers portions of the test-request message using the 4831 + hmac-sha256 algorithm and the secret test-shared-secret. 4832 + 4833 + The corresponding signature base is: 4834 + 4835 + NOTE: '\' line wrapping per RFC 8792 4836 + 4837 + "date": Tue, 20 Apr 2021 02:07:55 GMT 4838 + "@authority": example.com 4839 + "content-type": application/json 4840 + "@signature-params": ("date" "@authority" "content-type")\ 4841 + ;created=1618884473;keyid="test-shared-secret" 4842 + 4843 + This results in the following Signature-Input and Signature header 4844 + fields being added to the message under the label sig-b25: 4845 + 4846 + NOTE: '\' line wrapping per RFC 8792 4847 + 4848 + Signature-Input: sig-b25=("date" "@authority" "content-type")\ 4849 + ;created=1618884473;keyid="test-shared-secret" 4850 + Signature: sig-b25=:pxcQw6G3AjtMBQjwo8XzkZf/bws5LelbaMk5rGIGtE8=: 4851 + 4852 + Before using symmetric signatures in practice, see the discussion 4853 + regarding security trade-offs in Section 7.3.3. 4854 + 4855 + B.2.6. Signing a Request Using ed25519 4856 + 4857 + This example covers portions of the test-request message using the 4858 + Ed25519 algorithm and the key test-key-ed25519. 4859 + 4860 + The corresponding signature base is: 4861 + 4862 + NOTE: '\' line wrapping per RFC 8792 4863 + 4864 + "date": Tue, 20 Apr 2021 02:07:55 GMT 4865 + "@method": POST 4866 + "@path": /foo 4867 + "@authority": example.com 4868 + "content-type": application/json 4869 + "content-length": 18 4870 + "@signature-params": ("date" "@method" "@path" "@authority" \ 4871 + "content-type" "content-length");created=1618884473\ 4872 + ;keyid="test-key-ed25519" 4873 + 4874 + This results in the following Signature-Input and Signature header 4875 + fields being added to the message under the label sig-b26: 4876 + 4877 + NOTE: '\' line wrapping per RFC 8792 4878 + 4879 + Signature-Input: sig-b26=("date" "@method" "@path" "@authority" \ 4880 + "content-type" "content-length");created=1618884473\ 4881 + ;keyid="test-key-ed25519" 4882 + Signature: sig-b26=:wqcAqbmYJ2ji2glfAMaRy4gruYYnx2nEFN2HN6jrnDnQCK1\ 4883 + u02Gb04v9EDgwUPiu4A0w6vuQv5lIp5WPpBKRCw==: 4884 + 4885 + B.3. TLS-Terminating Proxies 4886 + 4887 + In this example, there is a TLS-terminating reverse proxy sitting in 4888 + front of the resource. The client does not sign the request but 4889 + instead uses mutual TLS to make its call. The terminating proxy 4890 + validates the TLS stream and injects a Client-Cert header field 4891 + according to [CLIENT-CERT], and then applies a signature to this 4892 + field. By signing this header field, a reverse proxy not only can 4893 + attest to its own validation of the initial request's TLS parameters 4894 + but can also authenticate itself to the backend system independently 4895 + of the client's actions. 4896 + 4897 + The client makes the following request to the TLS-terminating proxy 4898 + using mutual TLS: 4899 + 4900 + POST /foo?param=Value&Pet=dog HTTP/1.1 4901 + Host: example.com 4902 + Date: Tue, 20 Apr 2021 02:07:55 GMT 4903 + Content-Type: application/json 4904 + Content-Length: 18 4905 + 4906 + {"hello": "world"} 4907 + 4908 + The proxy processes the TLS connection and extracts the client's TLS 4909 + certificate to a Client-Cert header field and passes it along to the 4910 + internal service hosted at service.internal.example. This results in 4911 + the following unsigned request: 4912 + 4913 + NOTE: '\' line wrapping per RFC 8792 4914 + 4915 + POST /foo?param=Value&Pet=dog HTTP/1.1 4916 + Host: service.internal.example 4917 + Date: Tue, 20 Apr 2021 02:07:55 GMT 4918 + Content-Type: application/json 4919 + Content-Length: 18 4920 + Client-Cert: :MIIBqDCCAU6gAwIBAgIBBzAKBggqhkjOPQQDAjA6MRswGQYDVQQKD\ 4921 + BJMZXQncyBBdXRoZW50aWNhdGUxGzAZBgNVBAMMEkxBIEludGVybWVkaWF0ZSBDQT\ 4922 + AeFw0yMDAxMTQyMjU1MzNaFw0yMTAxMjMyMjU1MzNaMA0xCzAJBgNVBAMMAkJDMFk\ 4923 + wEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8YnXXfaUgmnMtOXU/IncWalRhebrXmck\ 4924 + C8vdgJ1p5Be5F/3YC8OthxM4+k1M6aEAEFcGzkJiNy6J84y7uzo9M6NyMHAwCQYDV\ 4925 + R0TBAIwADAfBgNVHSMEGDAWgBRm3WjLa38lbEYCuiCPct0ZaSED2DAOBgNVHQ8BAf\ 4926 + 8EBAMCBsAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwHQYDVR0RAQH/BBMwEYEPYmRjQGV\ 4927 + 4YW1wbGUuY29tMAoGCCqGSM49BAMCA0gAMEUCIBHda/r1vaL6G3VliL4/Di6YK0Q6\ 4928 + bMjeSkC3dFCOOB8TAiEAx/kHSB4urmiZ0NX5r5XarmPk0wmuydBVoU4hBVZ1yhk=: 4929 + 4930 + {"hello": "world"} 4931 + 4932 + Without a signature, the internal service would need to trust that 4933 + the incoming connection has the right information. By signing the 4934 + Client-Cert header field and other portions of the internal request, 4935 + the internal service can be assured that the correct party, the 4936 + trusted proxy, has processed the request and presented it to the 4937 + correct service. The proxy's signature base consists of the 4938 + following: 4939 + 4940 + NOTE: '\' line wrapping per RFC 8792 4941 + 4942 + "@path": /foo 4943 + "@query": ?param=Value&Pet=dog 4944 + "@method": POST 4945 + "@authority": service.internal.example 4946 + "client-cert": :MIIBqDCCAU6gAwIBAgIBBzAKBggqhkjOPQQDAjA6MRswGQYDVQQ\ 4947 + KDBJMZXQncyBBdXRoZW50aWNhdGUxGzAZBgNVBAMMEkxBIEludGVybWVkaWF0ZSBD\ 4948 + QTAeFw0yMDAxMTQyMjU1MzNaFw0yMTAxMjMyMjU1MzNaMA0xCzAJBgNVBAMMAkJDM\ 4949 + FkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8YnXXfaUgmnMtOXU/IncWalRhebrXm\ 4950 + ckC8vdgJ1p5Be5F/3YC8OthxM4+k1M6aEAEFcGzkJiNy6J84y7uzo9M6NyMHAwCQY\ 4951 + DVR0TBAIwADAfBgNVHSMEGDAWgBRm3WjLa38lbEYCuiCPct0ZaSED2DAOBgNVHQ8B\ 4952 + Af8EBAMCBsAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwHQYDVR0RAQH/BBMwEYEPYmRjQ\ 4953 + GV4YW1wbGUuY29tMAoGCCqGSM49BAMCA0gAMEUCIBHda/r1vaL6G3VliL4/Di6YK0\ 4954 + Q6bMjeSkC3dFCOOB8TAiEAx/kHSB4urmiZ0NX5r5XarmPk0wmuydBVoU4hBVZ1yhk=: 4955 + "@signature-params": ("@path" "@query" "@method" "@authority" \ 4956 + "client-cert");created=1618884473;keyid="test-key-ecc-p256" 4957 + 4958 + This results in the following signature: 4959 + 4960 + NOTE: '\' line wrapping per RFC 8792 4961 + 4962 + xVMHVpawaAC/0SbHrKRs9i8I3eOs5RtTMGCWXm/9nvZzoHsIg6Mce9315T6xoklyy0y\ 4963 + zhD9ah4JHRwMLOgmizw== 4964 + 4965 + which results in the following signed request sent from the proxy to 4966 + the internal service with the proxy's signature under the label ttrp: 4967 + 4968 + NOTE: '\' line wrapping per RFC 8792 4969 + 4970 + POST /foo?param=Value&Pet=dog HTTP/1.1 4971 + Host: service.internal.example 4972 + Date: Tue, 20 Apr 2021 02:07:55 GMT 4973 + Content-Type: application/json 4974 + Content-Length: 18 4975 + Client-Cert: :MIIBqDCCAU6gAwIBAgIBBzAKBggqhkjOPQQDAjA6MRswGQYDVQQKD\ 4976 + BJMZXQncyBBdXRoZW50aWNhdGUxGzAZBgNVBAMMEkxBIEludGVybWVkaWF0ZSBDQT\ 4977 + AeFw0yMDAxMTQyMjU1MzNaFw0yMTAxMjMyMjU1MzNaMA0xCzAJBgNVBAMMAkJDMFk\ 4978 + wEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8YnXXfaUgmnMtOXU/IncWalRhebrXmck\ 4979 + C8vdgJ1p5Be5F/3YC8OthxM4+k1M6aEAEFcGzkJiNy6J84y7uzo9M6NyMHAwCQYDV\ 4980 + R0TBAIwADAfBgNVHSMEGDAWgBRm3WjLa38lbEYCuiCPct0ZaSED2DAOBgNVHQ8BAf\ 4981 + 8EBAMCBsAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwHQYDVR0RAQH/BBMwEYEPYmRjQGV\ 4982 + 4YW1wbGUuY29tMAoGCCqGSM49BAMCA0gAMEUCIBHda/r1vaL6G3VliL4/Di6YK0Q6\ 4983 + bMjeSkC3dFCOOB8TAiEAx/kHSB4urmiZ0NX5r5XarmPk0wmuydBVoU4hBVZ1yhk=: 4984 + Signature-Input: ttrp=("@path" "@query" "@method" "@authority" \ 4985 + "client-cert");created=1618884473;keyid="test-key-ecc-p256" 4986 + Signature: ttrp=:xVMHVpawaAC/0SbHrKRs9i8I3eOs5RtTMGCWXm/9nvZzoHsIg6\ 4987 + Mce9315T6xoklyy0yzhD9ah4JHRwMLOgmizw==: 4988 + 4989 + {"hello": "world"} 4990 + 4991 + The internal service can validate the proxy's signature and therefore 4992 + be able to trust that the client's certificate has been appropriately 4993 + processed. 4994 + 4995 + B.4. HTTP Message Transformations 4996 + 4997 + HTTP allows intermediaries and applications to transform an HTTP 4998 + message without affecting the semantics of the message itself. HTTP 4999 + message signatures are designed to be robust against many of these 5000 + transformations in different circumstances. 5001 + 5002 + For example, the following HTTP request message has been signed using 5003 + the Ed25519 algorithm and the key test-key-ed25519: 5004 + 5005 + NOTE: '\' line wrapping per RFC 8792 5006 + 5007 + GET /demo?name1=Value1&Name2=value2 HTTP/1.1 5008 + Host: example.org 5009 + Date: Fri, 15 Jul 2022 14:24:55 GMT 5010 + Accept: application/json 5011 + Accept: */* 5012 + Signature-Input: transform=("@method" "@path" "@authority" \ 5013 + "accept");created=1618884473;keyid="test-key-ed25519" 5014 + Signature: transform=:ZT1kooQsEHpZ0I1IjCqtQppOmIqlJPeo7DHR3SoMn0s5J\ 5015 + Z1eRGS0A+vyYP9t/LXlh5QMFFQ6cpLt2m0pmj3NDA==: 5016 + 5017 + The signature base string for this message is: 5018 + 5019 + "@method": GET 5020 + "@path": /demo 5021 + "@authority": example.org 5022 + "accept": application/json, */* 5023 + "@signature-params": ("@method" "@path" "@authority" "accept")\ 5024 + ;created=1618884473;keyid="test-key-ed25519" 5025 + 5026 + The following message has been altered by adding the Accept-Language 5027 + header field as well as adding a query parameter. However, since 5028 + neither the Accept-Language header field nor the query is covered by 5029 + the signature, the same signature is still valid: 5030 + 5031 + NOTE: '\' line wrapping per RFC 8792 5032 + 5033 + GET /demo?name1=Value1&Name2=value2&param=added HTTP/1.1 5034 + Host: example.org 5035 + Date: Fri, 15 Jul 2022 14:24:55 GMT 5036 + Accept: application/json 5037 + Accept: */* 5038 + Accept-Language: en-US,en;q=0.5 5039 + Signature-Input: transform=("@method" "@path" "@authority" \ 5040 + "accept");created=1618884473;keyid="test-key-ed25519" 5041 + Signature: transform=:ZT1kooQsEHpZ0I1IjCqtQppOmIqlJPeo7DHR3SoMn0s5J\ 5042 + Z1eRGS0A+vyYP9t/LXlh5QMFFQ6cpLt2m0pmj3NDA==: 5043 + 5044 + The following message has been altered by removing the Date header 5045 + field, adding a Referer header field, and collapsing the Accept 5046 + header field into a single line. The Date and Referer header fields 5047 + are not covered by the signature, and the collapsing of the Accept 5048 + header field is an allowed transformation that is already accounted 5049 + for by the canonicalization algorithm for HTTP field values. The 5050 + same signature is still valid: 5051 + 5052 + NOTE: '\' line wrapping per RFC 8792 5053 + 5054 + GET /demo?name1=Value1&Name2=value2 HTTP/1.1 5055 + Host: example.org 5056 + Referer: https://developer.example.org/demo 5057 + Accept: application/json, */* 5058 + Signature-Input: transform=("@method" "@path" "@authority" \ 5059 + "accept");created=1618884473;keyid="test-key-ed25519" 5060 + Signature: transform=:ZT1kooQsEHpZ0I1IjCqtQppOmIqlJPeo7DHR3SoMn0s5J\ 5061 + Z1eRGS0A+vyYP9t/LXlh5QMFFQ6cpLt2m0pmj3NDA==: 5062 + 5063 + The following message has been altered by reordering the field values 5064 + of the original message but not reordering the individual Accept 5065 + header fields. The same signature is still valid: 5066 + 5067 + NOTE: '\' line wrapping per RFC 8792 5068 + 5069 + GET /demo?name1=Value1&Name2=value2 HTTP/1.1 5070 + Accept: application/json 5071 + Accept: */* 5072 + Date: Fri, 15 Jul 2022 14:24:55 GMT 5073 + Host: example.org 5074 + Signature-Input: transform=("@method" "@path" "@authority" \ 5075 + "accept");created=1618884473;keyid="test-key-ed25519" 5076 + Signature: transform=:ZT1kooQsEHpZ0I1IjCqtQppOmIqlJPeo7DHR3SoMn0s5J\ 5077 + Z1eRGS0A+vyYP9t/LXlh5QMFFQ6cpLt2m0pmj3NDA==: 5078 + 5079 + The following message has been altered by changing the method to POST 5080 + and the authority to "example.com" (inside the Host header field). 5081 + Since both the method and authority are covered by the signature, the 5082 + same signature is NOT still valid: 5083 + 5084 + NOTE: '\' line wrapping per RFC 8792 5085 + 5086 + POST /demo?name1=Value1&Name2=value2 HTTP/1.1 5087 + Host: example.com 5088 + Date: Fri, 15 Jul 2022 14:24:55 GMT 5089 + Accept: application/json 5090 + Accept: */* 5091 + Signature-Input: transform=("@method" "@path" "@authority" \ 5092 + "accept");created=1618884473;keyid="test-key-ed25519" 5093 + Signature: transform=:ZT1kooQsEHpZ0I1IjCqtQppOmIqlJPeo7DHR3SoMn0s5J\ 5094 + Z1eRGS0A+vyYP9t/LXlh5QMFFQ6cpLt2m0pmj3NDA==: 5095 + 5096 + The following message has been altered by changing the order of the 5097 + two instances of the Accept header field. Since the order of fields 5098 + with the same name is semantically significant in HTTP, this changes 5099 + the value used in the signature base, and the same signature is NOT 5100 + still valid: 5101 + 5102 + NOTE: '\' line wrapping per RFC 8792 5103 + 5104 + GET /demo?name1=Value1&Name2=value2 HTTP/1.1 5105 + Host: example.org 5106 + Date: Fri, 15 Jul 2022 14:24:55 GMT 5107 + Accept: */* 5108 + Accept: application/json 5109 + Signature-Input: transform=("@method" "@path" "@authority" \ 5110 + "accept");created=1618884473;keyid="test-key-ed25519" 5111 + Signature: transform=:ZT1kooQsEHpZ0I1IjCqtQppOmIqlJPeo7DHR3SoMn0s5J\ 5112 + Z1eRGS0A+vyYP9t/LXlh5QMFFQ6cpLt2m0pmj3NDA==: 5113 + 5114 + Acknowledgements 5115 + 5116 + This specification was initially based on [SIGNING-HTTP-MESSAGES]. 5117 + The editors would like to thank the authors of 5118 + [SIGNING-HTTP-MESSAGES] -- Mark Cavage and Manu Sporny -- for their 5119 + work on that Internet-Draft and their continuing contributions. This 5120 + specification also includes contributions from 5121 + [SIGNING-HTTP-REQS-OAUTH] and other similar efforts. 5122 + 5123 + The editors would also like to thank the following individuals 5124 + (listed in alphabetical order) for feedback, insight, and 5125 + implementation of this document and its predecessors: Mark Adamcin, 5126 + Mark Allen, Paul Annesley, Karl Böhlmark, Stéphane Bortzmeyer, Sarven 5127 + Capadisli, Liam Dennehy, Stephen Farrell, Phillip Hallam-Baker, Tyler 5128 + Ham, Eric Holmes, Andrey Kislyuk, Adam Knight, Dave Lehn, Ilari 5129 + Liusvaara, Dave Longley, James H. Manger, Kathleen Moriarty, Yoav 5130 + Nir, Mark Nottingham, Adrian Palmer, Lucas Pardue, Roberto Polli, 5131 + Julian Reschke, Michael Richardson, Wojciech Rygielski, Rich Salz, 5132 + Adam Scarr, Cory J. Slep, Dirk Stein, Henry Story, Lukasz Szewc, 5133 + Chris Webber, and Jeffrey Yasskin. 5134 + 5135 + Authors' Addresses 5136 + 5137 + Annabelle Backman (editor) 5138 + Amazon 5139 + P.O. Box 81226 5140 + Seattle, WA 98108-1226 5141 + United States of America 5142 + Email: richanna@amazon.com 5143 + URI: https://www.amazon.com/ 5144 + 5145 + 5146 + Justin Richer (editor) 5147 + Bespoke Engineering 5148 + Email: ietf@justin.richer.org 5149 + URI: https://bspk.io/ 5150 + 5151 + 5152 + Manu Sporny 5153 + Digital Bazaar 5154 + 203 Roanoke Street W. 5155 + Blacksburg, VA 24060 5156 + United States of America 5157 + Email: msporny@digitalbazaar.com
+8
test/dune
··· 26 26 (name test_websocket) 27 27 (libraries requests alcotest base64 fmt fmt.tty logs logs.fmt mirage-crypto-rng.unix)) 28 28 29 + (executable 30 + (name test_signature) 31 + (libraries requests alcotest fmt fmt.tty logs logs.fmt mirage-crypto-rng.unix mirage-crypto-ec)) 32 + 33 + (executable 34 + (name test_rfc9421_vectors) 35 + (libraries requests alcotest base64 digestif fmt fmt.tty logs logs.fmt mirage-crypto-rng.unix mirage-crypto-ec)) 36 + 29 37 (cram 30 38 (deps %{bin:ocurl}))
+370
test/test_rfc9421_vectors.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** RFC 9421 Appendix B Test Vectors 7 + 8 + These tests verify our implementation against the official test vectors 9 + from {{:https://datatracker.ietf.org/doc/html/rfc9421#appendix-B}RFC 9421 Appendix B}. *) 10 + 11 + module Signature = Requests.Signature 12 + module Headers = Requests.Headers 13 + module Uri = Requests__Uri 14 + 15 + (* ========================================================================= *) 16 + (* RFC B.1 Test Keys *) 17 + (* ========================================================================= *) 18 + 19 + (** Base64url decode (RFC 4648 Section 5) *) 20 + let base64url_decode s = 21 + (* Replace URL-safe chars with standard base64 chars and add padding *) 22 + let s = String.map (function '-' -> '+' | '_' -> '/' | c -> c) s in 23 + let pad = (4 - (String.length s mod 4)) mod 4 in 24 + let s = s ^ String.make pad '=' in 25 + Base64.decode_exn s 26 + 27 + (** B.1.4 test-key-ed25519 28 + 29 + The Ed25519 key pair from RFC 9421 Appendix B.1.4. 30 + JWK format: 31 + {[ 32 + { 33 + "kty": "OKP", 34 + "crv": "Ed25519", 35 + "kid": "test-key-ed25519", 36 + "d": "n4Ni-HpISpVObnQMW0wOhCKROaIKqKtW_2ZYb2p9KcU", 37 + "x": "JrQLj5P_89iXES9-vFgrIy29clF9CC_oPPsw3c5D0bs" 38 + } 39 + ]} *) 40 + let test_key_ed25519_priv = base64url_decode "n4Ni-HpISpVObnQMW0wOhCKROaIKqKtW_2ZYb2p9KcU" 41 + let test_key_ed25519_pub = base64url_decode "JrQLj5P_89iXES9-vFgrIy29clF9CC_oPPsw3c5D0bs" 42 + 43 + (** B.1.5 test-shared-secret 44 + 45 + 64 random bytes for HMAC-SHA256 testing. 46 + Base64: uzvJfB4u3N0Jy4T7NZ75MDVcr8zSTInedJtkgcu46YW4XByzNJjxBdtjUkdJPBtbmHhIDi6pcl8jsasjlTMtDQ== *) 47 + let test_shared_secret = 48 + Base64.decode_exn "uzvJfB4u3N0Jy4T7NZ75MDVcr8zSTInedJtkgcu46YW4XByzNJjxBdtjUkdJPBtbmHhIDi6pcl8jsasjlTMtDQ==" 49 + 50 + (* ========================================================================= *) 51 + (* RFC B.2 Test Request *) 52 + (* ========================================================================= *) 53 + 54 + (** The test-request from RFC 9421 Appendix B.2: 55 + 56 + POST /foo?param=Value&Pet=dog HTTP/1.1 57 + Host: example.com 58 + Date: Tue, 20 Apr 2021 02:07:55 GMT 59 + Content-Type: application/json 60 + Content-Digest: sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: 61 + Content-Length: 18 62 + 63 + {"hello": "world"} *) 64 + let test_request_uri = Uri.of_string "https://example.com/foo?param=Value&Pet=dog" 65 + let test_request_headers = 66 + Headers.empty 67 + |> Headers.set `Date "Tue, 20 Apr 2021 02:07:55 GMT" 68 + |> Headers.set `Content_type "application/json" 69 + |> Headers.set `Content_digest "sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:" 70 + |> Headers.set `Content_length "18" 71 + 72 + (* ========================================================================= *) 73 + (* B.2.5 Signing a Request Using hmac-sha256 *) 74 + (* ========================================================================= *) 75 + 76 + (** Test case B.2.5: HMAC-SHA256 signature 77 + 78 + Signature base: 79 + {v 80 + "date": Tue, 20 Apr 2021 02:07:55 GMT 81 + "@authority": example.com 82 + "content-type": application/json 83 + "@signature-params": ("date" "@authority" "content-type");created=1618884473;keyid="test-shared-secret" 84 + v} 85 + 86 + Expected Signature-Input: sig-b25=("date" "@authority" "content-type");created=1618884473;keyid="test-shared-secret" 87 + Expected Signature: sig-b25=:pxcQw6G3AjtMBQjwo8XzkZf/bws5LelbaMk5rGIGtE8=: 88 + *) 89 + let test_b25_hmac_signature_base () = 90 + (* Build the expected signature base string exactly as the RFC specifies *) 91 + let expected_base = 92 + "\"date\": Tue, 20 Apr 2021 02:07:55 GMT\n" ^ 93 + "\"@authority\": example.com\n" ^ 94 + "\"content-type\": application/json\n" ^ 95 + "\"@signature-params\": (\"date\" \"@authority\" \"content-type\");created=1618884473;keyid=\"test-shared-secret\"" 96 + in 97 + (* Compute HMAC-SHA256 over the expected signature base *) 98 + let mac = Digestif.SHA256.hmac_string ~key:test_shared_secret expected_base in 99 + let sig_bytes = Digestif.SHA256.to_raw_string mac in 100 + let sig_b64 = Base64.encode_string sig_bytes in 101 + (* RFC expected signature *) 102 + let expected_sig = "pxcQw6G3AjtMBQjwo8XzkZf/bws5LelbaMk5rGIGtE8=" in 103 + Alcotest.(check string) "B.2.5 HMAC signature" expected_sig sig_b64 104 + 105 + let test_b25_hmac_verify () = 106 + (* Create verification context and verify the RFC's signature *) 107 + let key = Signature.Key.symmetric test_shared_secret in 108 + let headers = 109 + test_request_headers 110 + |> Headers.set `Signature_input "sig-b25=(\"date\" \"@authority\" \"content-type\");created=1618884473;keyid=\"test-shared-secret\"" 111 + |> Headers.set `Signature "sig-b25=:pxcQw6G3AjtMBQjwo8XzkZf/bws5LelbaMk5rGIGtE8=:" 112 + in 113 + let context = Signature.Context.request ~method_:`POST ~uri:test_request_uri ~headers in 114 + match Signature.verify ~key ~label:"sig-b25" ~context ~headers () with 115 + | Error e -> 116 + Alcotest.fail ("B.2.5 HMAC verification failed: " ^ Signature.verify_error_to_string e) 117 + | Ok result -> 118 + Alcotest.(check (option string)) "keyid matches" 119 + (Some "test-shared-secret") result.keyid; 120 + Alcotest.(check int) "verified 3 components" 121 + 3 (List.length result.verified_components) 122 + 123 + (* ========================================================================= *) 124 + (* B.2.6 Signing a Request Using ed25519 *) 125 + (* ========================================================================= *) 126 + 127 + (** Test case B.2.6: Ed25519 signature 128 + 129 + Signature base: 130 + {v 131 + "date": Tue, 20 Apr 2021 02:07:55 GMT 132 + "@method": POST 133 + "@path": /foo 134 + "@authority": example.com 135 + "content-type": application/json 136 + "content-length": 18 137 + "@signature-params": ("date" "@method" "@path" "@authority" "content-type" "content-length");created=1618884473;keyid="test-key-ed25519" 138 + v} 139 + 140 + Expected Signature-Input: sig-b26=("date" "@method" "@path" "@authority" "content-type" "content-length");created=1618884473;keyid="test-key-ed25519" 141 + Expected Signature: sig-b26=:wqcAqbmYJ2ji2glfAMaRy4gruYYnx2nEFN2HN6jrnDnQCK1u02Gb04v9EDgwUPiu4A0w6vuQv5lIp5WPpBKRCw==: 142 + *) 143 + let test_b26_ed25519_signature_base () = 144 + (* Build the expected signature base string exactly as the RFC specifies *) 145 + let expected_base = 146 + "\"date\": Tue, 20 Apr 2021 02:07:55 GMT\n" ^ 147 + "\"@method\": POST\n" ^ 148 + "\"@path\": /foo\n" ^ 149 + "\"@authority\": example.com\n" ^ 150 + "\"content-type\": application/json\n" ^ 151 + "\"content-length\": 18\n" ^ 152 + "\"@signature-params\": (\"date\" \"@method\" \"@path\" \"@authority\" \"content-type\" \"content-length\");created=1618884473;keyid=\"test-key-ed25519\"" 153 + in 154 + (* Sign with Ed25519 *) 155 + match Mirage_crypto_ec.Ed25519.priv_of_octets test_key_ed25519_priv with 156 + | Error _ -> Alcotest.fail "Invalid Ed25519 private key" 157 + | Ok priv_key -> 158 + let sig_bytes = Mirage_crypto_ec.Ed25519.sign ~key:priv_key expected_base in 159 + let sig_b64 = Base64.encode_string sig_bytes in 160 + (* RFC expected signature *) 161 + let expected_sig = "wqcAqbmYJ2ji2glfAMaRy4gruYYnx2nEFN2HN6jrnDnQCK1u02Gb04v9EDgwUPiu4A0w6vuQv5lIp5WPpBKRCw==" in 162 + Alcotest.(check string) "B.2.6 Ed25519 signature" expected_sig sig_b64 163 + 164 + let test_b26_ed25519_verify () = 165 + (* Create verification context and verify the RFC's signature *) 166 + let key = Signature.Key.ed25519 ~priv:test_key_ed25519_priv ~pub:test_key_ed25519_pub in 167 + let headers = 168 + test_request_headers 169 + |> Headers.set `Signature_input "sig-b26=(\"date\" \"@method\" \"@path\" \"@authority\" \"content-type\" \"content-length\");created=1618884473;keyid=\"test-key-ed25519\"" 170 + |> Headers.set `Signature "sig-b26=:wqcAqbmYJ2ji2glfAMaRy4gruYYnx2nEFN2HN6jrnDnQCK1u02Gb04v9EDgwUPiu4A0w6vuQv5lIp5WPpBKRCw==:" 171 + in 172 + let context = Signature.Context.request ~method_:`POST ~uri:test_request_uri ~headers in 173 + match Signature.verify ~key ~label:"sig-b26" ~context ~headers () with 174 + | Error e -> 175 + Alcotest.fail ("B.2.6 Ed25519 verification failed: " ^ Signature.verify_error_to_string e) 176 + | Ok result -> 177 + Alcotest.(check (option string)) "keyid matches" 178 + (Some "test-key-ed25519") result.keyid; 179 + Alcotest.(check int) "verified 6 components" 180 + 6 (List.length result.verified_components) 181 + 182 + (* ========================================================================= *) 183 + (* B.4 HTTP Message Transformations (Ed25519) *) 184 + (* ========================================================================= *) 185 + 186 + (** Test case B.4: Message transformation test 187 + 188 + Original request: 189 + {v 190 + GET /demo?name1=Value1&Name2=value2 HTTP/1.1 191 + Host: example.org 192 + Date: Fri, 15 Jul 2022 14:24:55 GMT 193 + Accept: application/json 194 + Accept: */* 195 + v} 196 + 197 + Signature base: 198 + {v 199 + "@method": GET 200 + "@path": /demo 201 + "@authority": example.org 202 + "accept": application/json, */* 203 + "@signature-params": ("@method" "@path" "@authority" "accept");created=1618884473;keyid="test-key-ed25519" 204 + v} 205 + 206 + Expected Signature: transform=:ZT1kooQsEHpZ0I1IjCqtQppOmIqlJPeo7DHR3SoMn0s5JZ1eRGS0A+vyYP9t/LXlh5QMFFQ6cpLt2m0pmj3NDA==: 207 + *) 208 + let test_b4_transform_signature_base () = 209 + let expected_base = 210 + "\"@method\": GET\n" ^ 211 + "\"@path\": /demo\n" ^ 212 + "\"@authority\": example.org\n" ^ 213 + "\"accept\": application/json, */*\n" ^ 214 + "\"@signature-params\": (\"@method\" \"@path\" \"@authority\" \"accept\");created=1618884473;keyid=\"test-key-ed25519\"" 215 + in 216 + match Mirage_crypto_ec.Ed25519.priv_of_octets test_key_ed25519_priv with 217 + | Error _ -> Alcotest.fail "Invalid Ed25519 private key" 218 + | Ok priv_key -> 219 + let sig_bytes = Mirage_crypto_ec.Ed25519.sign ~key:priv_key expected_base in 220 + let sig_b64 = Base64.encode_string sig_bytes in 221 + let expected_sig = "ZT1kooQsEHpZ0I1IjCqtQppOmIqlJPeo7DHR3SoMn0s5JZ1eRGS0A+vyYP9t/LXlh5QMFFQ6cpLt2m0pmj3NDA==" in 222 + Alcotest.(check string) "B.4 Transform Ed25519 signature" expected_sig sig_b64 223 + 224 + let test_b4_transform_verify () = 225 + let key = Signature.Key.ed25519 ~priv:test_key_ed25519_priv ~pub:test_key_ed25519_pub in 226 + let uri = Uri.of_string "https://example.org/demo?name1=Value1&Name2=value2" in 227 + let headers = 228 + Headers.empty 229 + |> Headers.set `Date "Fri, 15 Jul 2022 14:24:55 GMT" 230 + |> Headers.set `Accept "application/json, */*" 231 + |> Headers.set `Signature_input "transform=(\"@method\" \"@path\" \"@authority\" \"accept\");created=1618884473;keyid=\"test-key-ed25519\"" 232 + |> Headers.set `Signature "transform=:ZT1kooQsEHpZ0I1IjCqtQppOmIqlJPeo7DHR3SoMn0s5JZ1eRGS0A+vyYP9t/LXlh5QMFFQ6cpLt2m0pmj3NDA==:" 233 + in 234 + let context = Signature.Context.request ~method_:`GET ~uri ~headers in 235 + match Signature.verify ~key ~label:"transform" ~context ~headers () with 236 + | Error e -> 237 + Alcotest.fail ("B.4 Transform verification failed: " ^ Signature.verify_error_to_string e) 238 + | Ok result -> 239 + Alcotest.(check (option string)) "keyid matches" 240 + (Some "test-key-ed25519") result.keyid; 241 + Alcotest.(check int) "verified 4 components" 242 + 4 (List.length result.verified_components) 243 + 244 + (** Test that modified messages fail verification - B.4 example where 245 + method changed from GET to POST should fail *) 246 + let test_b4_transform_modified_fails () = 247 + let key = Signature.Key.ed25519 ~priv:test_key_ed25519_priv ~pub:test_key_ed25519_pub in 248 + let uri = Uri.of_string "https://example.org/demo?name1=Value1&Name2=value2" in 249 + let headers = 250 + Headers.empty 251 + |> Headers.set `Date "Fri, 15 Jul 2022 14:24:55 GMT" 252 + |> Headers.set `Accept "application/json, */*" 253 + |> Headers.set `Signature_input "transform=(\"@method\" \"@path\" \"@authority\" \"accept\");created=1618884473;keyid=\"test-key-ed25519\"" 254 + |> Headers.set `Signature "transform=:ZT1kooQsEHpZ0I1IjCqtQppOmIqlJPeo7DHR3SoMn0s5JZ1eRGS0A+vyYP9t/LXlh5QMFFQ6cpLt2m0pmj3NDA==:" 255 + in 256 + (* Use POST instead of GET - should fail verification *) 257 + let context = Signature.Context.request ~method_:`POST ~uri ~headers in 258 + match Signature.verify ~key ~label:"transform" ~context ~headers () with 259 + | Error _ -> () (* Expected - signature should not verify *) 260 + | Ok _ -> Alcotest.fail "B.4 Modified message should have failed verification" 261 + 262 + (** Test that authority modification fails verification *) 263 + let test_b4_transform_authority_modified_fails () = 264 + let key = Signature.Key.ed25519 ~priv:test_key_ed25519_priv ~pub:test_key_ed25519_pub in 265 + (* Changed authority from example.org to example.com *) 266 + let uri = Uri.of_string "https://example.com/demo?name1=Value1&Name2=value2" in 267 + let headers = 268 + Headers.empty 269 + |> Headers.set `Date "Fri, 15 Jul 2022 14:24:55 GMT" 270 + |> Headers.set `Accept "application/json, */*" 271 + |> Headers.set `Signature_input "transform=(\"@method\" \"@path\" \"@authority\" \"accept\");created=1618884473;keyid=\"test-key-ed25519\"" 272 + |> Headers.set `Signature "transform=:ZT1kooQsEHpZ0I1IjCqtQppOmIqlJPeo7DHR3SoMn0s5JZ1eRGS0A+vyYP9t/LXlh5QMFFQ6cpLt2m0pmj3NDA==:" 273 + in 274 + let context = Signature.Context.request ~method_:`GET ~uri ~headers in 275 + match Signature.verify ~key ~label:"transform" ~context ~headers () with 276 + | Error _ -> () (* Expected - signature should not verify *) 277 + | Ok _ -> Alcotest.fail "B.4 Authority-modified message should have failed verification" 278 + 279 + (* ========================================================================= *) 280 + (* Signature Base Format Tests *) 281 + (* ========================================================================= *) 282 + 283 + (** Test that we correctly format derived components *) 284 + let test_derived_component_format () = 285 + let tests = [ 286 + (Signature.Component.method_, "\"@method\""); 287 + (Signature.Component.authority, "\"@authority\""); 288 + (Signature.Component.path, "\"@path\""); 289 + (Signature.Component.query, "\"@query\""); 290 + (Signature.Component.target_uri, "\"@target-uri\""); 291 + (Signature.Component.status, "\"@status\""); 292 + (Signature.Component.query_param "Pet", "\"@query-param\";name=\"Pet\""); 293 + ] in 294 + List.iter (fun (comp, expected) -> 295 + let result = Signature.Component.to_identifier comp in 296 + Alcotest.(check string) ("Component: " ^ expected) expected result 297 + ) tests 298 + 299 + (** Test header field component format *) 300 + let test_field_component_format () = 301 + let tests = [ 302 + (Signature.Component.field "content-type", "\"content-type\""); 303 + (Signature.Component.field "Content-Type", "\"content-type\""); (* Should lowercase *) 304 + (Signature.Component.field_sf "accept", "\"accept\";sf"); 305 + (Signature.Component.field_bs "x-custom", "\"x-custom\";bs"); 306 + ] in 307 + List.iter (fun (comp, expected) -> 308 + let result = Signature.Component.to_identifier comp in 309 + Alcotest.(check string) ("Field: " ^ expected) expected result 310 + ) tests 311 + 312 + (* ========================================================================= *) 313 + (* Content-Digest Tests (RFC 9530 integration) *) 314 + (* ========================================================================= *) 315 + 316 + (** Verify Content-Digest from the RFC test-request: 317 + sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==: *) 318 + let test_content_digest_rfc_body () = 319 + let body = "{\"hello\": \"world\"}" in 320 + let expected = "sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:" in 321 + let computed = Signature.Content_digest.compute ~algorithm:`Sha512 ~body in 322 + Alcotest.(check string) "RFC test body Content-Digest" expected computed 323 + 324 + let test_content_digest_verify_rfc () = 325 + let body = "{\"hello\": \"world\"}" in 326 + let digest = "sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:" in 327 + match Signature.Content_digest.verify ~header:digest ~body with 328 + | Ok () -> () 329 + | Error e -> Alcotest.fail ("RFC Content-Digest verification failed: " ^ e) 330 + 331 + (* ========================================================================= *) 332 + (* Test Suite *) 333 + (* ========================================================================= *) 334 + 335 + let hmac_tests = [ 336 + "B.2.5 HMAC signature base", `Quick, test_b25_hmac_signature_base; 337 + "B.2.5 HMAC verify", `Quick, test_b25_hmac_verify; 338 + ] 339 + 340 + let ed25519_tests = [ 341 + "B.2.6 Ed25519 signature base", `Quick, test_b26_ed25519_signature_base; 342 + "B.2.6 Ed25519 verify", `Quick, test_b26_ed25519_verify; 343 + ] 344 + 345 + let transform_tests = [ 346 + "B.4 Transform signature base", `Quick, test_b4_transform_signature_base; 347 + "B.4 Transform verify", `Quick, test_b4_transform_verify; 348 + "B.4 Modified method fails", `Quick, test_b4_transform_modified_fails; 349 + "B.4 Modified authority fails", `Quick, test_b4_transform_authority_modified_fails; 350 + ] 351 + 352 + let component_tests = [ 353 + "Derived component format", `Quick, test_derived_component_format; 354 + "Field component format", `Quick, test_field_component_format; 355 + ] 356 + 357 + let digest_tests = [ 358 + "RFC test body Content-Digest", `Quick, test_content_digest_rfc_body; 359 + "RFC Content-Digest verify", `Quick, test_content_digest_verify_rfc; 360 + ] 361 + 362 + let () = 363 + Mirage_crypto_rng_unix.use_default (); 364 + Alcotest.run "RFC 9421 Test Vectors" [ 365 + "B.2.5 HMAC-SHA256", hmac_tests; 366 + "B.2.6 Ed25519", ed25519_tests; 367 + "B.4 Transformations", transform_tests; 368 + "Components", component_tests; 369 + "Content-Digest", digest_tests; 370 + ]
+206
test/test_signature.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (** Tests for RFC 9421 HTTP Message Signatures *) 7 + 8 + module Signature = Requests.Signature 9 + module Headers = Requests.Headers 10 + module Uri = Requests__Uri 11 + 12 + (** {1 Component Tests} *) 13 + 14 + let test_component_identifiers () = 15 + let tests = [ 16 + (Signature.Component.method_, "\"@method\""); 17 + (Signature.Component.authority, "\"@authority\""); 18 + (Signature.Component.path, "\"@path\""); 19 + (Signature.Component.query, "\"@query\""); 20 + (Signature.Component.target_uri, "\"@target-uri\""); 21 + (Signature.Component.field "content-type", "\"content-type\""); 22 + (Signature.Component.field_sf "content-type", "\"content-type\";sf"); 23 + (Signature.Component.query_param "foo", "\"@query-param\";name=\"foo\""); 24 + ] in 25 + List.iter (fun (component, expected) -> 26 + let result = Signature.Component.to_identifier component in 27 + Alcotest.(check string) ("Component: " ^ expected) expected result 28 + ) tests 29 + 30 + let test_component_parse () = 31 + let tests = [ 32 + ("@method", true); 33 + ("@authority", true); 34 + ("content-type", true); 35 + ("content-type;sf", true); 36 + ] in 37 + List.iter (fun (input, should_succeed) -> 38 + match Signature.Component.of_identifier input with 39 + | Ok _ when should_succeed -> () 40 + | Error _ when not should_succeed -> () 41 + | Ok _ -> Alcotest.fail ("Should have failed: " ^ input) 42 + | Error e -> Alcotest.fail ("Should have succeeded: " ^ input ^ " - " ^ e) 43 + ) tests 44 + 45 + (** {1 Algorithm Tests} *) 46 + 47 + let test_algorithm_roundtrip () = 48 + let algs : Signature.Algorithm.t list = [ 49 + `Ed25519; 50 + `Hmac_sha256; 51 + `Ecdsa_p256_sha256; 52 + `Ecdsa_p384_sha384; 53 + `Rsa_pss_sha512; 54 + `Rsa_v1_5_sha256; 55 + ] in 56 + List.iter (fun alg -> 57 + let s = Signature.Algorithm.to_string alg in 58 + match Signature.Algorithm.of_string s with 59 + | Some alg' when alg = alg' -> () 60 + | Some _ -> Alcotest.fail ("Algorithm mismatch for " ^ s) 61 + | None -> Alcotest.fail ("Failed to parse algorithm: " ^ s) 62 + ) algs 63 + 64 + (** {1 Content-Digest Tests} *) 65 + 66 + let test_content_digest_sha256 () = 67 + let body = "Hello, World!" in 68 + let digest = Signature.Content_digest.compute ~algorithm:`Sha256 ~body in 69 + (* Verify it starts with sha-256= *) 70 + Alcotest.(check bool) "Has sha-256 prefix" 71 + true (String.length digest > 8 && String.sub digest 0 8 = "sha-256=") 72 + 73 + let test_content_digest_verify () = 74 + let body = "Test body content" in 75 + let digest = Signature.Content_digest.compute ~algorithm:`Sha256 ~body in 76 + match Signature.Content_digest.verify ~header:digest ~body with 77 + | Ok () -> () 78 + | Error e -> Alcotest.fail ("Verification failed: " ^ e) 79 + 80 + let test_content_digest_verify_mismatch () = 81 + let body = "Original content" in 82 + let digest = Signature.Content_digest.compute ~algorithm:`Sha256 ~body in 83 + match Signature.Content_digest.verify ~header:digest ~body:"Different content" with 84 + | Ok () -> Alcotest.fail "Should have failed verification" 85 + | Error _ -> () (* Expected *) 86 + 87 + (** {1 HMAC Signing Tests} *) 88 + 89 + let test_hmac_sign_verify () = 90 + let secret = "my-secret-key-for-hmac-signing" in 91 + let key = Signature.Key.symmetric secret in 92 + let config = Signature.config ~key ~keyid:"test-key-1" 93 + ~components:[Signature.Component.method_; Signature.Component.authority] 94 + () in 95 + let headers = Headers.empty in 96 + let uri = Uri.of_string "https://example.com/api/test" in 97 + let context = Signature.Context.request ~method_:`GET ~uri ~headers in 98 + 99 + match Signature.sign ~config ~context ~headers with 100 + | Error e -> Alcotest.fail ("Signing failed: " ^ Signature.sign_error_to_string e) 101 + | Ok signed_headers -> 102 + (* Verify the signature was added *) 103 + Alcotest.(check bool) "Has Signature header" 104 + true (Option.is_some (Headers.get `Signature signed_headers)); 105 + Alcotest.(check bool) "Has Signature-Input header" 106 + true (Option.is_some (Headers.get `Signature_input signed_headers)); 107 + 108 + (* Verify the signature *) 109 + match Signature.verify ~key ~context ~headers:signed_headers () with 110 + | Error e -> Alcotest.fail ("Verification failed: " ^ Signature.verify_error_to_string e) 111 + | Ok result -> 112 + Alcotest.(check (option string)) "Keyid matches" 113 + (Some "test-key-1") result.keyid 114 + 115 + (** {1 Ed25519 Signing Tests} *) 116 + 117 + let test_ed25519_sign_verify () = 118 + (* Generate a test Ed25519 key pair *) 119 + let priv, pub = Mirage_crypto_ec.Ed25519.generate () in 120 + let priv_bytes = Mirage_crypto_ec.Ed25519.priv_to_octets priv in 121 + let pub_bytes = Mirage_crypto_ec.Ed25519.pub_to_octets pub in 122 + 123 + let key = Signature.Key.ed25519 ~priv:priv_bytes ~pub:pub_bytes in 124 + let config = Signature.config ~key ~keyid:"ed25519-key" 125 + ~components:[ 126 + Signature.Component.method_; 127 + Signature.Component.path; 128 + Signature.Component.authority; 129 + ] 130 + () in 131 + 132 + let headers = Headers.empty |> Headers.set `Content_type "application/json" in 133 + let uri = Uri.of_string "https://api.example.com/v1/resource?id=123" in 134 + let context = Signature.Context.request ~method_:`POST ~uri ~headers in 135 + 136 + match Signature.sign ~config ~context ~headers with 137 + | Error e -> Alcotest.fail ("Ed25519 signing failed: " ^ Signature.sign_error_to_string e) 138 + | Ok signed_headers -> 139 + (* Verify we can verify with the same key *) 140 + match Signature.verify ~key ~context ~headers:signed_headers () with 141 + | Error e -> Alcotest.fail ("Ed25519 verification failed: " ^ Signature.verify_error_to_string e) 142 + | Ok result -> 143 + Alcotest.(check (option string)) "Keyid matches" 144 + (Some "ed25519-key") result.keyid; 145 + Alcotest.(check int) "Verified 3 components" 146 + 3 (List.length result.verified_components) 147 + 148 + (** {1 Signature Base Construction Tests} *) 149 + 150 + let test_signature_base_format () = 151 + (* Test that the signature base has the correct format *) 152 + let key = Signature.Key.symmetric "test" in 153 + let config = Signature.config ~key ~keyid:"test" 154 + ~components:[Signature.Component.method_; Signature.Component.authority] 155 + ~include_created:false (* Don't include created for deterministic test *) 156 + () in 157 + 158 + let headers = Headers.empty in 159 + let uri = Uri.of_string "https://example.com/path" in 160 + let context = Signature.Context.request ~method_:`GET ~uri ~headers in 161 + 162 + match Signature.sign ~config ~context ~headers with 163 + | Error e -> Alcotest.fail ("Signing failed: " ^ Signature.sign_error_to_string e) 164 + | Ok signed_headers -> 165 + (* Check Signature-Input format *) 166 + match Headers.get `Signature_input signed_headers with 167 + | None -> Alcotest.fail "Missing Signature-Input" 168 + | Some input -> 169 + (* Should contain the components *) 170 + Alcotest.(check bool) "Contains @method" 171 + true (String.length input > 0 && 172 + (String.sub input 0 4 = "sig1" || String.sub input 0 4 = "sig=")) 173 + 174 + (** {1 Test Suite} *) 175 + 176 + let component_tests = [ 177 + "Component identifiers", `Quick, test_component_identifiers; 178 + "Component parsing", `Quick, test_component_parse; 179 + ] 180 + 181 + let algorithm_tests = [ 182 + "Algorithm roundtrip", `Quick, test_algorithm_roundtrip; 183 + ] 184 + 185 + let content_digest_tests = [ 186 + "SHA-256 digest", `Quick, test_content_digest_sha256; 187 + "Digest verification", `Quick, test_content_digest_verify; 188 + "Digest mismatch", `Quick, test_content_digest_verify_mismatch; 189 + ] 190 + 191 + let signing_tests = [ 192 + "HMAC sign and verify", `Quick, test_hmac_sign_verify; 193 + "Ed25519 sign and verify", `Quick, test_ed25519_sign_verify; 194 + "Signature base format", `Quick, test_signature_base_format; 195 + ] 196 + 197 + let () = 198 + (* Initialize RNG for crypto operations *) 199 + Mirage_crypto_rng_unix.use_default (); 200 + 201 + Alcotest.run "HTTP Message Signatures (RFC 9421)" [ 202 + "Components", component_tests; 203 + "Algorithms", algorithm_tests; 204 + "Content-Digest", content_digest_tests; 205 + "Signing", signing_tests; 206 + ]