httpsig_hyper/
hyper_http.rs

1use crate::error::{HyperSigError, HyperSigResult};
2use http::{HeaderMap, Request, Response};
3use http_body::Body;
4use httpsig::prelude::{
5  message_component::{
6    DerivedComponentName, HttpMessageComponent, HttpMessageComponentId, HttpMessageComponentName, HttpMessageComponentParam,
7  },
8  HttpSignatureBase, HttpSignatureHeaders, HttpSignatureHeadersMap, HttpSignatureParams, SigningKey, VerifyingKey,
9};
10use indexmap::{IndexMap, IndexSet};
11use std::future::Future;
12
13/// A type alias for the signature name
14type SignatureName = String;
15/// A type alias for the key id in base 64
16type KeyId = String;
17
18/* --------------------------------------- */
19/// A trait about the http message signature common to both request and response
20pub trait MessageSignature {
21  type Error;
22
23  /// Check if the request has signature and signature-input headers
24  fn has_message_signature(&self) -> bool;
25
26  /// Extract all key ids for signature bases contained in the request headers
27  fn get_key_ids(&self) -> Result<IndexMap<SignatureName, KeyId>, Self::Error>;
28
29  /// Extract all signature params used to generate signature bases contained in the request headers
30  fn get_signature_params(&self) -> Result<IndexMap<SignatureName, HttpSignatureParams>, Self::Error>;
31}
32
33/// A trait about http message signature for request
34pub trait MessageSignatureReq {
35  type Error;
36  /// Set the http message signature from given http signature params and signing key
37  fn set_message_signature<T>(
38    &mut self,
39    signature_params: &HttpSignatureParams,
40    signing_key: &T,
41    signature_name: Option<&str>,
42  ) -> impl Future<Output = Result<(), Self::Error>> + Send
43  where
44    Self: Sized,
45    T: SigningKey + Sync;
46
47  /// Set the http message signatures from given tuples of (http signature params, signing key, name)
48  fn set_message_signatures<T>(
49    &mut self,
50    params_key_name: &[(&HttpSignatureParams, &T, Option<&str>)],
51  ) -> impl Future<Output = Result<(), Self::Error>> + Send
52  where
53    Self: Sized,
54    T: SigningKey + Sync;
55
56  /// Verify the http message signature with given verifying key if the request has signature and signature-input headers
57  fn verify_message_signature<T>(
58    &self,
59    verifying_key: &T,
60    key_id: Option<&str>,
61  ) -> impl Future<Output = Result<SignatureName, Self::Error>> + Send
62  where
63    Self: Sized,
64    T: VerifyingKey + Sync;
65
66  /// Verify multiple signatures at once
67  fn verify_message_signatures<T>(
68    &self,
69    key_and_id: &[(&T, Option<&str>)],
70  ) -> impl Future<Output = Result<Vec<Result<SignatureName, Self::Error>>, Self::Error>> + Send
71  where
72    Self: Sized,
73    T: VerifyingKey + Sync;
74
75  /// Extract all signature bases contained in the request headers
76  fn extract_signatures(&self) -> Result<IndexMap<SignatureName, (HttpSignatureBase, HttpSignatureHeaders)>, Self::Error>;
77}
78
79/// A trait about http message signature for response
80pub trait MessageSignatureRes {
81  type Error;
82  /// Set the http message signature from given http signature params and signing key
83  fn set_message_signature<T, B>(
84    &mut self,
85    signature_params: &HttpSignatureParams,
86    signing_key: &T,
87    signature_name: Option<&str>,
88    req_for_param: Option<&Request<B>>,
89  ) -> impl Future<Output = Result<(), Self::Error>> + Send
90  where
91    Self: Sized,
92    T: SigningKey + Sync,
93    B: Sync;
94
95  /// Set the http message signatures from given tuples of (http signature params, signing key, name)
96  fn set_message_signatures<T, B>(
97    &mut self,
98    params_key_name: &[(&HttpSignatureParams, &T, Option<&str>)],
99    req_for_param: Option<&Request<B>>,
100  ) -> impl Future<Output = Result<(), Self::Error>> + Send
101  where
102    Self: Sized,
103    T: SigningKey + Sync,
104    B: Sync;
105
106  /// Verify the http message signature with given verifying key if the request has signature and signature-input headers
107  fn verify_message_signature<T, B>(
108    &self,
109    verifying_key: &T,
110    key_id: Option<&str>,
111    req_for_param: Option<&Request<B>>,
112  ) -> impl Future<Output = Result<SignatureName, Self::Error>> + Send
113  where
114    Self: Sized,
115    T: VerifyingKey + Sync,
116    B: Sync;
117
118  /// Verify multiple signatures at once
119  fn verify_message_signatures<T, B>(
120    &self,
121    key_and_id: &[(&T, Option<&str>)],
122    req_for_param: Option<&Request<B>>,
123  ) -> impl Future<Output = Result<Vec<Result<SignatureName, Self::Error>>, Self::Error>> + Send
124  where
125    Self: Sized,
126    T: VerifyingKey + Sync,
127    B: Sync;
128
129  /// Extract all signature bases contained in the request headers
130  fn extract_signatures<B>(
131    &self,
132    req_for_param: Option<&Request<B>>,
133  ) -> Result<IndexMap<SignatureName, (HttpSignatureBase, HttpSignatureHeaders)>, Self::Error>;
134}
135
136/* --------------------------------------- */
137impl<D> MessageSignature for Request<D>
138where
139  D: Send + Body + Sync,
140{
141  type Error = HyperSigError;
142
143  /// Check if the request has signature and signature-input headers
144  fn has_message_signature(&self) -> bool {
145    has_message_signature_inner(self.headers())
146  }
147
148  /// Extract all signature bases contained in the request headers
149  fn get_key_ids(&self) -> HyperSigResult<IndexMap<SignatureName, KeyId>> {
150    let req_or_res = RequestOrResponse::Request(self);
151    get_key_ids_inner(&req_or_res)
152  }
153
154  /// Extract all signature params used to generate signature bases contained in the request headers
155  fn get_signature_params(&self) -> Result<IndexMap<SignatureName, HttpSignatureParams>, Self::Error> {
156    let req_or_res = RequestOrResponse::Request(self);
157    get_signature_params_inner(&req_or_res)
158  }
159}
160
161impl<D> MessageSignatureReq for Request<D>
162where
163  D: Send + Body + Sync,
164{
165  type Error = HyperSigError;
166
167  /// Set the http message signature from given http signature params and signing key
168  async fn set_message_signature<T>(
169    &mut self,
170    signature_params: &HttpSignatureParams,
171    signing_key: &T,
172    signature_name: Option<&str>,
173  ) -> HyperSigResult<()>
174  where
175    Self: Sized,
176    T: SigningKey + Sync,
177  {
178    self
179      .set_message_signatures(&[(signature_params, signing_key, signature_name)])
180      .await
181  }
182
183  async fn set_message_signatures<T>(
184    &mut self,
185    params_key_name: &[(&HttpSignatureParams, &T, Option<&str>)],
186  ) -> Result<(), Self::Error>
187  where
188    Self: Sized,
189    T: SigningKey + Sync,
190  {
191    let req_or_res = RequestOrResponse::Request(self);
192    let vec_signature_headers_fut = params_key_name.iter().flat_map(|(params, key, name)| {
193      build_signature_base(&req_or_res, params, None as Option<&Request<()>>)
194        .map(|base| async move { base.build_signature_headers(*key, *name) })
195    });
196    let vec_signature_headers = futures::future::join_all(vec_signature_headers_fut)
197      .await
198      .into_iter()
199      .collect::<Result<Vec<_>, _>>()?;
200    vec_signature_headers.iter().try_for_each(|headers| {
201      self
202        .headers_mut()
203        .append("signature-input", headers.signature_input_header_value().parse()?);
204      self
205        .headers_mut()
206        .append("signature", headers.signature_header_value().parse()?);
207      Ok(()) as Result<(), HyperSigError>
208    })
209  }
210
211  /// Verify the http message signature with given verifying key if the request has signature and signature-input headers
212  /// Return Ok(()) if the signature is valid.
213  /// If invalid for the given key or error occurs (like the case where the request does not have signature and/or signature-input headers), return Err.
214  /// If key_id is given, it is used to match the key id in signature params
215  async fn verify_message_signature<T>(&self, verifying_key: &T, key_id: Option<&str>) -> HyperSigResult<SignatureName>
216  where
217    Self: Sized,
218    T: VerifyingKey + Sync,
219  {
220    self
221      .verify_message_signatures(&[(verifying_key, key_id)])
222      .await?
223      .pop()
224      .unwrap()
225  }
226
227  async fn verify_message_signatures<T>(
228    &self,
229    key_and_id: &[(&T, Option<&str>)],
230  ) -> Result<Vec<Result<SignatureName, Self::Error>>, Self::Error>
231  where
232    Self: Sized,
233    T: VerifyingKey + Sync,
234  {
235    if !self.has_message_signature() {
236      return Err(HyperSigError::NoSignatureHeaders(
237        "The request does not have signature and signature-input headers".to_string(),
238      ));
239    }
240    let map_signature_with_base = self.extract_signatures()?;
241    verify_message_signatures_inner(&map_signature_with_base, key_and_id).await
242  }
243
244  /// Extract all signature bases contained in the request headers
245  fn extract_signatures(&self) -> Result<IndexMap<SignatureName, (HttpSignatureBase, HttpSignatureHeaders)>, Self::Error> {
246    let req_or_res = RequestOrResponse::Request(self);
247    extract_signatures_inner(&req_or_res, None as Option<&Request<()>>)
248  }
249}
250
251/* --------------------------------------- */
252impl<D> MessageSignature for Response<D>
253where
254  D: Send + Body + Sync,
255{
256  type Error = HyperSigError;
257
258  /// Check if the response has signature and signature-input headers
259  fn has_message_signature(&self) -> bool {
260    has_message_signature_inner(self.headers())
261  }
262
263  /// Extract all key ids for signature bases contained in the response headers
264  fn get_key_ids(&self) -> Result<IndexMap<SignatureName, KeyId>, Self::Error> {
265    let req_or_res = RequestOrResponse::Response(self);
266    get_key_ids_inner(&req_or_res)
267  }
268
269  /// Extract all signature params used to generate signature bases contained in the response headers
270  fn get_signature_params(&self) -> Result<IndexMap<SignatureName, HttpSignatureParams>, Self::Error> {
271    let req_or_res = RequestOrResponse::Response(self);
272    get_signature_params_inner(&req_or_res)
273  }
274}
275
276impl<D> MessageSignatureRes for Response<D>
277where
278  D: Send + Body + Sync,
279{
280  type Error = HyperSigError;
281
282  /// Set the http message signature from given http signature params and signing key
283  async fn set_message_signature<T, B>(
284    &mut self,
285    signature_params: &HttpSignatureParams,
286    signing_key: &T,
287    signature_name: Option<&str>,
288    req_for_param: Option<&Request<B>>,
289  ) -> Result<(), Self::Error>
290  where
291    Self: Sized,
292    T: SigningKey + Sync,
293    B: Sync,
294  {
295    self
296      .set_message_signatures(&[(signature_params, signing_key, signature_name)], req_for_param)
297      .await
298  }
299
300  async fn set_message_signatures<T, B>(
301    &mut self,
302    params_key_name: &[(&HttpSignatureParams, &T, Option<&str>)],
303    req_for_param: Option<&Request<B>>,
304  ) -> Result<(), Self::Error>
305  where
306    Self: Sized,
307    T: SigningKey + Sync,
308  {
309    let req_or_res = RequestOrResponse::Response(self);
310
311    let vec_signature_headers_fut = params_key_name.iter().flat_map(|(params, key, name)| {
312      build_signature_base(&req_or_res, params, req_for_param)
313        .map(|base| async move { base.build_signature_headers(*key, *name) })
314    });
315    let vec_signature_headers = futures::future::join_all(vec_signature_headers_fut)
316      .await
317      .into_iter()
318      .collect::<Result<Vec<_>, _>>()?;
319
320    vec_signature_headers.iter().try_for_each(|headers| {
321      self
322        .headers_mut()
323        .append("signature-input", headers.signature_input_header_value().parse()?);
324      self
325        .headers_mut()
326        .append("signature", headers.signature_header_value().parse()?);
327      Ok(()) as Result<(), HyperSigError>
328    })
329  }
330
331  /// Verify the http message signature with given verifying key if the response has signature and signature-input headers
332  /// Return Ok(()) if the signature is valid.
333  /// If invalid for the given key or error occurs (like the case where the request does not have signature and/or signature-input headers), return Err.
334  /// If key_id is given, it is used to match the key id in signature params
335  async fn verify_message_signature<T, B>(
336    &self,
337    verifying_key: &T,
338    key_id: Option<&str>,
339    req_for_param: Option<&Request<B>>,
340  ) -> Result<SignatureName, Self::Error>
341  where
342    Self: Sized,
343    T: VerifyingKey + Sync,
344    B: Sync,
345  {
346    self
347      .verify_message_signatures(&[(verifying_key, key_id)], req_for_param)
348      .await?
349      .pop()
350      .unwrap()
351  }
352
353  async fn verify_message_signatures<T, B>(
354    &self,
355    key_and_id: &[(&T, Option<&str>)],
356    req_for_param: Option<&Request<B>>,
357  ) -> Result<Vec<Result<SignatureName, Self::Error>>, Self::Error>
358  where
359    Self: Sized,
360    T: VerifyingKey + Sync,
361  {
362    if !self.has_message_signature() {
363      return Err(HyperSigError::NoSignatureHeaders(
364        "The response does not have signature and signature-input headers".to_string(),
365      ));
366    }
367    let map_signature_with_base = self.extract_signatures(req_for_param)?;
368    verify_message_signatures_inner(&map_signature_with_base, key_and_id).await
369  }
370
371  /// Extract all signature bases contained in the response headers
372  fn extract_signatures<B>(
373    &self,
374    req_for_param: Option<&Request<B>>,
375  ) -> Result<IndexMap<SignatureName, (HttpSignatureBase, HttpSignatureHeaders)>, Self::Error> {
376    let req_or_res = RequestOrResponse::Response(self);
377    extract_signatures_inner(&req_or_res, req_for_param)
378  }
379}
380
381/* --------------------------------------- */
382// inner functions
383/// has message signature inner function
384fn has_message_signature_inner(headers: &HeaderMap) -> bool {
385  headers.contains_key("signature") && headers.contains_key("signature-input")
386}
387
388/// get key ids inner function
389fn get_key_ids_inner<B>(req_or_res: &RequestOrResponse<B>) -> HyperSigResult<IndexMap<SignatureName, KeyId>> {
390  let signature_headers_map = extract_signature_headers_with_name(req_or_res)?;
391  let res = signature_headers_map
392    .iter()
393    .filter_map(|(name, headers)| headers.signature_params().keyid.clone().map(|key_id| (name.clone(), key_id)))
394    .collect();
395  Ok(res)
396}
397
398/// get signature params inner function
399fn get_signature_params_inner<B>(
400  req_or_res: &RequestOrResponse<B>,
401) -> HyperSigResult<IndexMap<SignatureName, HttpSignatureParams>> {
402  let signature_headers_map = extract_signature_headers_with_name(req_or_res)?;
403  let res = signature_headers_map
404    .iter()
405    .map(|(name, headers)| (name.clone(), headers.signature_params().clone()))
406    .collect();
407  Ok(res)
408}
409
410/// extract signatures inner function
411fn extract_signatures_inner<B1, B2>(
412  req_or_res: &RequestOrResponse<B1>,
413  req_for_param: Option<&Request<B2>>,
414) -> HyperSigResult<IndexMap<SignatureName, (HttpSignatureBase, HttpSignatureHeaders)>> {
415  let signature_headers_map = extract_signature_headers_with_name(req_or_res)?;
416  let extracted = signature_headers_map
417    .iter()
418    .filter_map(|(name, headers)| {
419      build_signature_base(req_or_res, headers.signature_params(), req_for_param)
420        .ok()
421        .map(|base| (name.clone(), (base, headers.clone())))
422    })
423    .collect();
424  Ok(extracted)
425}
426
427/// Verify multiple signatures inner function
428async fn verify_message_signatures_inner<T>(
429  map_signature_with_base: &IndexMap<String, (HttpSignatureBase, HttpSignatureHeaders)>,
430  key_and_id: &[(&T, Option<&str>)],
431) -> HyperSigResult<Vec<HyperSigResult<SignatureName>>>
432where
433  T: VerifyingKey + Sync,
434{
435  // verify for each key_and_id tuple
436  let res_fut = key_and_id.iter().map(|(key, key_id)| {
437    let filtered = if let Some(key_id) = key_id {
438      map_signature_with_base
439        .iter()
440        .filter(|(_, (base, _))| base.keyid() == Some(key_id))
441        .collect::<IndexMap<_, _>>()
442    } else {
443      map_signature_with_base.iter().collect()
444    };
445
446    // check if any one of the signature headers is valid in async manner
447    async move {
448      if filtered.is_empty() {
449        return Err(HyperSigError::NoSignatureHeaders(
450          "No signature as appropriate target for verification".to_string(),
451        ));
452      }
453      // check if any one of the signature headers is valid
454      let successful_sig_names = filtered
455        .iter()
456        .filter_map(|(&name, (base, headers))| base.verify_signature_headers(*key, headers).ok().map(|_| name.clone()))
457        .collect::<IndexSet<_>>();
458      if !successful_sig_names.is_empty() {
459        Ok(successful_sig_names.first().unwrap().clone())
460      } else {
461        Err(HyperSigError::InvalidSignature(
462          "Invalid signature for the verifying key".to_string(),
463        ))
464      }
465    }
466  });
467  let res = futures::future::join_all(res_fut).await;
468  Ok(res)
469}
470
471/* --------------------------------------- */
472
473/// A type to represent either http request or response
474enum RequestOrResponse<'a, B> {
475  Request(&'a Request<B>),
476  Response(&'a Response<B>),
477}
478
479impl<B> RequestOrResponse<'_, B> {
480  fn method(&self) -> HyperSigResult<&http::Method> {
481    match self {
482      RequestOrResponse::Request(req) => Ok(req.method()),
483      _ => Err(HyperSigError::InvalidComponentName(
484        "`method` is only for request".to_string(),
485      )),
486    }
487  }
488
489  fn uri(&self) -> HyperSigResult<&http::Uri> {
490    match self {
491      RequestOrResponse::Request(req) => Ok(req.uri()),
492      _ => Err(HyperSigError::InvalidComponentName("`uri` is only for request".to_string())),
493    }
494  }
495
496  fn headers(&self) -> &HeaderMap {
497    match self {
498      RequestOrResponse::Request(req) => req.headers(),
499      RequestOrResponse::Response(res) => res.headers(),
500    }
501  }
502
503  fn status(&self) -> HyperSigResult<http::StatusCode> {
504    match self {
505      RequestOrResponse::Response(res) => Ok(res.status()),
506      _ => Err(HyperSigError::InvalidComponentName(
507        "`status` is only for response".to_string(),
508      )),
509    }
510  }
511}
512
513/// Extract signature and signature-input with signature-name indication from http request and response
514fn extract_signature_headers_with_name<B>(req_or_res: &RequestOrResponse<B>) -> HyperSigResult<HttpSignatureHeadersMap> {
515  let headers = req_or_res.headers();
516  if !(headers.contains_key("signature-input") && headers.contains_key("signature")) {
517    return Err(HyperSigError::NoSignatureHeaders(
518      "The request does not have signature and signature-input headers".to_string(),
519    ));
520  };
521
522  let signature_input_strings = headers
523    .get_all("signature-input")
524    .iter()
525    .map(|v| v.to_str())
526    .collect::<Result<Vec<_>, _>>()?
527    .join(", ");
528  let signature_strings = headers
529    .get_all("signature")
530    .iter()
531    .map(|v| v.to_str())
532    .collect::<Result<Vec<_>, _>>()?
533    .join(", ");
534
535  let signature_headers = HttpSignatureHeaders::try_parse(&signature_strings, &signature_input_strings)?;
536  Ok(signature_headers)
537}
538
539/// Build signature base from hyper http request/response and signature params
540/// - req_or_res: the hyper http request or response
541/// - signature_params: the http signature params
542/// - req_for_param: corresponding request to be considered in the signature base in response
543fn build_signature_base<B1, B2>(
544  req_or_res: &RequestOrResponse<B1>,
545  signature_params: &HttpSignatureParams,
546  req_for_param: Option<&Request<B2>>,
547) -> HyperSigResult<HttpSignatureBase> {
548  let component_lines = signature_params
549    .covered_components
550    .iter()
551    .map(|component_id| {
552      if component_id.params.0.contains(&HttpMessageComponentParam::Req) {
553        if matches!(req_or_res, RequestOrResponse::Request(_)) {
554          return Err(HyperSigError::InvalidComponentParam(
555            "`req` is not allowed in request".to_string(),
556          ));
557        }
558        if req_for_param.is_none() {
559          return Err(HyperSigError::InvalidComponentParam(
560            "`req` is required for the param".to_string(),
561          ));
562        }
563        let req = RequestOrResponse::Request(req_for_param.unwrap());
564        extract_http_message_component(&req, component_id)
565      } else {
566        extract_http_message_component(req_or_res, component_id)
567      }
568    })
569    .collect::<Result<Vec<_>, _>>()?;
570
571  HttpSignatureBase::try_new(&component_lines, signature_params).map_err(|e| e.into())
572}
573
574/// Extract http field from hyper http request/response
575fn extract_http_field<B>(req_or_res: &RequestOrResponse<B>, id: &HttpMessageComponentId) -> HyperSigResult<HttpMessageComponent> {
576  let HttpMessageComponentName::HttpField(header_name) = &id.name else {
577    return Err(HyperSigError::InvalidComponentName(
578      "invalid http message component name as http field".to_string(),
579    ));
580  };
581  let headers = match req_or_res {
582    RequestOrResponse::Request(req) => req.headers(),
583    RequestOrResponse::Response(res) => res.headers(),
584  };
585
586  let field_values = headers
587    .get_all(header_name)
588    .iter()
589    .map(|v| v.to_str().map(|s| s.to_owned()))
590    .collect::<Result<Vec<_>, _>>()?;
591
592  HttpMessageComponent::try_from((id, field_values.as_slice())).map_err(|e| e.into())
593}
594
595/// Extract derived component from hyper http request/response
596fn extract_derived_component<B>(
597  req_or_res: &RequestOrResponse<B>,
598  id: &HttpMessageComponentId,
599) -> HyperSigResult<HttpMessageComponent> {
600  let HttpMessageComponentName::Derived(derived_id) = &id.name else {
601    return Err(HyperSigError::InvalidComponentName(
602      "invalid http message component name as derived component".to_string(),
603    ));
604  };
605  if !id.params.0.is_empty()
606    && matches!(req_or_res, RequestOrResponse::Request(_))
607    && !(id.params.0.contains(&HttpMessageComponentParam::Req) && id.params.0.len() == 1)
608  {
609    return Err(HyperSigError::InvalidComponentParam(
610      "derived component does not allow parameters for request".to_string(),
611    ));
612  }
613
614  match req_or_res {
615    RequestOrResponse::Request(_) => {
616      if matches!(derived_id, DerivedComponentName::Status) {
617        return Err(HyperSigError::InvalidComponentName(
618          "`status` is only for response".to_string(),
619        ));
620      }
621    }
622    RequestOrResponse::Response(_) => {
623      if !matches!(derived_id, DerivedComponentName::Status) && !matches!(derived_id, DerivedComponentName::SignatureParams) {
624        return Err(HyperSigError::InvalidComponentName(
625          "Only `status` and `signature-params` are allowed for response".to_string(),
626        ));
627      }
628    }
629  }
630
631  let field_values: Vec<String> = match derived_id {
632    DerivedComponentName::Method => vec![req_or_res.method()?.as_str().to_string()],
633    DerivedComponentName::TargetUri => vec![req_or_res.uri()?.to_string()],
634    DerivedComponentName::Authority => vec![req_or_res.uri()?.authority().map(|s| s.to_string()).unwrap_or("".to_string())],
635    DerivedComponentName::Scheme => vec![req_or_res.uri()?.scheme_str().unwrap_or("").to_string()],
636    DerivedComponentName::RequestTarget => match *req_or_res.method()? {
637      http::Method::CONNECT => vec![req_or_res.uri()?.authority().map(|s| s.to_string()).unwrap_or("".to_string())],
638      http::Method::OPTIONS => vec!["*".to_string()],
639      _ => vec![req_or_res
640        .uri()?
641        .path_and_query()
642        .map(|s| s.to_string())
643        .unwrap_or("".to_string())],
644    },
645    DerivedComponentName::Path => vec![{
646      let p = req_or_res.uri()?.path();
647      if p.is_empty() {
648        "/".to_string()
649      } else {
650        p.to_string()
651      }
652    }],
653    DerivedComponentName::Query => vec![req_or_res.uri()?.query().map(|v| format!("?{v}")).unwrap_or("?".to_string())],
654    DerivedComponentName::QueryParam => {
655      let query = req_or_res.uri()?.query().unwrap_or("");
656      query
657        .split('&')
658        .filter(|s| !s.is_empty())
659        .map(|s| s.to_string())
660        .collect::<Vec<_>>()
661    }
662    DerivedComponentName::Status => vec![req_or_res.status()?.as_str().to_string()],
663    DerivedComponentName::SignatureParams => req_or_res
664      .headers()
665      .get_all("signature-input")
666      .iter()
667      .map(|v| v.to_str().unwrap_or("").to_string())
668      .collect::<Vec<_>>(),
669  };
670
671  HttpMessageComponent::try_from((id, field_values.as_slice())).map_err(|e| e.into())
672}
673
674/* --------------------------------------- */
675/// Extract http message component from hyper http request
676fn extract_http_message_component<B>(
677  req_or_res: &RequestOrResponse<B>,
678  target_component_id: &HttpMessageComponentId,
679) -> HyperSigResult<HttpMessageComponent> {
680  match &target_component_id.name {
681    HttpMessageComponentName::HttpField(_) => extract_http_field(req_or_res, target_component_id),
682    HttpMessageComponentName::Derived(_) => extract_derived_component(req_or_res, target_component_id),
683  }
684}
685
686/* --------------------------------------- */
687#[cfg(test)]
688mod tests {
689
690  use super::{
691    super::{
692      error::HyperDigestError,
693      hyper_content_digest::{RequestContentDigest, ResponseContentDigest},
694      ContentDigestType,
695    },
696    *,
697  };
698  use http_body_util::Full;
699  use httpsig::prelude::{PublicKey, SecretKey, SharedKey};
700
701  type BoxBody = http_body_util::combinators::BoxBody<bytes::Bytes, HyperDigestError>;
702
703  const EDDSA_SECRET_KEY: &str = r##"-----BEGIN PRIVATE KEY-----
704MC4CAQAwBQYDK2VwBCIEIDSHAE++q1BP7T8tk+mJtS+hLf81B0o6CFyWgucDFN/C
705-----END PRIVATE KEY-----
706"##;
707  const EDDSA_PUBLIC_KEY: &str = r##"-----BEGIN PUBLIC KEY-----
708MCowBQYDK2VwAyEA1ixMQcxO46PLlgQfYS46ivFd+n0CcDHSKUnuhm3i1O0=
709-----END PUBLIC KEY-----
710"##;
711  // const EDDSA_KEY_ID: &str = "gjrE7ACMxgzYfFHgabgf4kLTg1eKIdsJ94AiFTFj1is=";
712  const COVERED_COMPONENTS_REQ: &[&str] = &["@method", "date", "content-type", "content-digest"];
713  const COVERED_COMPONENTS_RES: &[&str] = &["@status", "\"@method\";req", "date", "content-type", "\"content-digest\";req"];
714
715  async fn build_request() -> Request<BoxBody> {
716    let body = Full::new(&b"{\"hello\": \"world\"}"[..]);
717    let req = Request::builder()
718      .method("GET")
719      .uri("https://example.com/parameters?var=this%20is%20a%20big%0Amultiline%20value&bar=with+plus+whitespace&fa%C3%A7ade%22%3A%20=something")
720      .header("date", "Sun, 09 May 2021 18:30:00 GMT")
721      .header("content-type", "application/json")
722      .header("content-type", "application/json-patch+json")
723      .body(body)
724      .unwrap();
725    req.set_content_digest(&ContentDigestType::Sha256).await.unwrap()
726  }
727
728  async fn build_response() -> Response<BoxBody> {
729    let body = Full::new(&b"{\"hello\": \"world!!\"}"[..]);
730    let res = Response::builder()
731      .status(200)
732      .header("date", "Sun, 09 May 2021 18:30:00 GMT")
733      .header("content-type", "application/json")
734      .header("content-type", "application/json-patch+json")
735      .body(body)
736      .unwrap();
737    res.set_content_digest(&ContentDigestType::Sha256).await.unwrap()
738  }
739
740  fn build_covered_components_req() -> Vec<HttpMessageComponentId> {
741    COVERED_COMPONENTS_REQ
742      .iter()
743      .map(|&s| HttpMessageComponentId::try_from(s).unwrap())
744      .collect()
745  }
746
747  fn build_covered_components_res() -> Vec<HttpMessageComponentId> {
748    COVERED_COMPONENTS_RES
749      .iter()
750      .map(|&s| HttpMessageComponentId::try_from(s).unwrap())
751      .collect()
752  }
753
754  #[tokio::test]
755  async fn test_extract_component_from_request() {
756    let req = build_request().await;
757    let req_or_res = RequestOrResponse::Request(&req);
758
759    let component_id_method = HttpMessageComponentId::try_from("\"@method\"").unwrap();
760    let component = extract_http_message_component(&req_or_res, &component_id_method).unwrap();
761    assert_eq!(component.to_string(), "\"@method\": GET");
762
763    let component_id = HttpMessageComponentId::try_from("\"date\"").unwrap();
764    let component = extract_http_message_component(&req_or_res, &component_id).unwrap();
765    assert_eq!(component.to_string(), "\"date\": Sun, 09 May 2021 18:30:00 GMT");
766
767    let component_id = HttpMessageComponentId::try_from("content-type").unwrap();
768    let component = extract_http_field(&req_or_res, &component_id).unwrap();
769    assert_eq!(
770      component.to_string(),
771      "\"content-type\": application/json, application/json-patch+json"
772    );
773
774    let component_id = HttpMessageComponentId::try_from("content-digest").unwrap();
775    let component = extract_http_message_component(&req_or_res, &component_id).unwrap();
776    assert_eq!(
777      component.to_string(),
778      "\"content-digest\": sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:"
779    );
780  }
781
782  #[tokio::test]
783  async fn test_extract_signature_params_from_request() {
784    let mut req = build_request().await;
785    let headers = req.headers_mut();
786    headers.insert(
787      "signature-input",
788      http::HeaderValue::from_static(r##"sig1=("@method" "@authority")"##),
789    );
790    let component_id = HttpMessageComponentId::try_from("@signature-params").unwrap();
791    let req_or_res = RequestOrResponse::Request(&req);
792    let component = extract_http_message_component(&req_or_res, &component_id).unwrap();
793    assert_eq!(component.to_string(), "\"@signature-params\": (\"@method\" \"@authority\")");
794    assert_eq!(component.value.to_string(), r##"("@method" "@authority")"##);
795    assert_eq!(component.value.as_field_value(), r##"sig1=("@method" "@authority")"##);
796    assert_eq!(component.value.as_component_value(), r##"("@method" "@authority")"##);
797    assert_eq!(component.value.key(), Some("sig1"));
798  }
799
800  #[tokio::test]
801  async fn test_build_signature_base_from_request() {
802    let req = build_request().await;
803
804    const SIGPARA: &str = r##";created=1704972031;alg="ed25519";keyid="gjrE7ACMxgzYfFHgabgf4kLTg1eKIdsJ94AiFTFj1is=""##;
805    let values = (r##""@method" "content-type" "date" "content-digest""##, SIGPARA);
806    let signature_params = HttpSignatureParams::try_from(format!("({}){}", values.0, values.1).as_str()).unwrap();
807
808    let req_or_res = RequestOrResponse::Request(&req);
809    let signature_base = build_signature_base(&req_or_res, &signature_params, None as Option<&Request<()>>).unwrap();
810    assert_eq!(
811      signature_base.to_string(),
812      r##""@method": GET
813"content-type": application/json, application/json-patch+json
814"date": Sun, 09 May 2021 18:30:00 GMT
815"content-digest": sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:
816"@signature-params": ("@method" "content-type" "date" "content-digest");created=1704972031;alg="ed25519";keyid="gjrE7ACMxgzYfFHgabgf4kLTg1eKIdsJ94AiFTFj1is=""##
817    );
818  }
819
820  #[tokio::test]
821  async fn test_extract_tuples_from_request() {
822    let mut req = build_request().await;
823    let headers = req.headers_mut();
824    headers.insert(
825      "signature-input",
826      http::HeaderValue::from_static(r##"sig11=("@method" "@authority");created=1704972031"##),
827    );
828    headers.insert(
829      "signature",
830      http::HeaderValue::from_static(
831        r##"sig11=:wqcAqbmYJ2ji2glfAMaRy4gruYYnx2nEFN2HN6jrnDnQCK1u02Gb04v9EDgwUPiu4A0w6vuQv5lIp5WPpBKRCw==:"##,
832      ),
833    );
834
835    let req_or_res = RequestOrResponse::Request(&req);
836    let tuples = extract_signature_headers_with_name(&req_or_res).unwrap();
837    assert_eq!(tuples.len(), 1);
838    assert_eq!(tuples.get("sig11").unwrap().signature_name(), "sig11");
839    assert_eq!(
840      tuples.get("sig11").unwrap().signature_params().to_string(),
841      r##"("@method" "@authority");created=1704972031"##
842    );
843  }
844
845  #[tokio::test]
846  async fn test_set_verify_message_signature_req() {
847    let mut req = build_request().await;
848    let secret_key = SecretKey::from_pem(EDDSA_SECRET_KEY).unwrap();
849    let mut signature_params = HttpSignatureParams::try_new(&build_covered_components_req()).unwrap();
850    signature_params.set_key_info(&secret_key);
851
852    req.set_message_signature(&signature_params, &secret_key, None).await.unwrap();
853    let signature_input = req.headers().get("signature-input").unwrap().to_str().unwrap();
854    assert!(signature_input.starts_with(r##"sig=("@method" "date" "content-type" "content-digest")"##));
855    // let signature = req.headers().get("signature").unwrap().to_str().unwrap();
856
857    let public_key = PublicKey::from_pem(EDDSA_PUBLIC_KEY).unwrap();
858    let verification_res = req.verify_message_signature(&public_key, None).await;
859    assert!(verification_res.is_ok());
860  }
861
862  #[tokio::test]
863  async fn test_set_verify_message_signature_res() {
864    let req = build_request().await;
865    let mut res = build_response().await;
866
867    let secret_key = SecretKey::from_pem(EDDSA_SECRET_KEY).unwrap();
868
869    let mut signature_params = HttpSignatureParams::try_new(&build_covered_components_res()).unwrap();
870    signature_params.set_key_info(&secret_key);
871    // let req_or_res = RequestOrResponse::Response(&res);
872    // let base = build_signature_base(&req_or_res, &signature_params, Some(&req));
873    // println!("{}", base.unwrap());
874    // // println!("{:#?}", req);
875
876    res
877      .set_message_signature(&signature_params, &secret_key, None, Some(&req))
878      .await
879      .unwrap();
880    // println!("{:#?}", res.headers());
881    let signature_input = res.headers().get("signature-input").unwrap().to_str().unwrap();
882    assert!(signature_input.starts_with(r##"sig=("@status" "@method";req "date" "content-type" "content-digest";req)"##));
883    // let signature = req.headers().get("signature").unwrap().to_str().unwrap();
884
885    let public_key = PublicKey::from_pem(EDDSA_PUBLIC_KEY).unwrap();
886    let verification_res = res.verify_message_signature(&public_key, None, Some(&req)).await;
887    assert!(verification_res.is_ok());
888  }
889
890  #[tokio::test]
891  async fn test_expired_signature() {
892    let mut req = build_request().await;
893    let secret_key = SecretKey::from_pem(EDDSA_SECRET_KEY).unwrap();
894    let mut signature_params = HttpSignatureParams::try_new(&build_covered_components_req()).unwrap();
895    signature_params.set_key_info(&secret_key);
896    let created = signature_params.created.unwrap();
897    signature_params.set_expires(created - 1);
898    assert!(signature_params.is_expired());
899
900    req.set_message_signature(&signature_params, &secret_key, None).await.unwrap();
901
902    let public_key = PublicKey::from_pem(EDDSA_PUBLIC_KEY).unwrap();
903    let verification_res = req.verify_message_signature(&public_key, None).await;
904    assert!(verification_res.is_err());
905  }
906
907  #[tokio::test]
908  async fn test_set_verify_with_signature_name() {
909    let mut req = build_request().await;
910    let secret_key = SecretKey::from_pem(EDDSA_SECRET_KEY).unwrap();
911    let mut signature_params = HttpSignatureParams::try_new(&build_covered_components_req()).unwrap();
912    signature_params.set_key_info(&secret_key);
913
914    req
915      .set_message_signature(&signature_params, &secret_key, Some("custom_sig_name"))
916      .await
917      .unwrap();
918
919    let req_or_res = RequestOrResponse::Request(&req);
920    let signature_headers_map = extract_signature_headers_with_name(&req_or_res).unwrap();
921    assert_eq!(signature_headers_map.len(), 1);
922    assert_eq!(signature_headers_map[0].signature_name(), "custom_sig_name");
923
924    let public_key = PublicKey::from_pem(EDDSA_PUBLIC_KEY).unwrap();
925    let verification_res = req.verify_message_signature(&public_key, None).await;
926    assert!(verification_res.is_ok());
927  }
928
929  #[tokio::test]
930  async fn test_set_verify_with_key_id() {
931    let mut req = build_request().await;
932    let secret_key = SecretKey::from_pem(EDDSA_SECRET_KEY).unwrap();
933    let mut signature_params = HttpSignatureParams::try_new(&build_covered_components_req()).unwrap();
934    signature_params.set_key_info(&secret_key);
935
936    req.set_message_signature(&signature_params, &secret_key, None).await.unwrap();
937
938    let public_key = PublicKey::from_pem(EDDSA_PUBLIC_KEY).unwrap();
939    let key_id = public_key.key_id();
940    let verification_res = req.verify_message_signature(&public_key, Some(&key_id)).await;
941    assert!(verification_res.is_ok());
942
943    let verification_res = req.verify_message_signature(&public_key, Some("NotFoundKeyId")).await;
944    assert!(verification_res.is_err());
945  }
946
947  const HMACSHA256_SECRET_KEY: &str =
948    r##"uzvJfB4u3N0Jy4T7NZ75MDVcr8zSTInedJtkgcu46YW4XByzNJjxBdtjUkdJPBtbmHhIDi6pcl8jsasjlTMtDQ=="##;
949
950  #[tokio::test]
951  async fn test_set_verify_with_key_id_hmac_sha256() {
952    let mut req = build_request().await;
953    let secret_key = SharedKey::from_base64(HMACSHA256_SECRET_KEY).unwrap();
954    let mut signature_params = HttpSignatureParams::try_new(&build_covered_components_req()).unwrap();
955    signature_params.set_key_info(&secret_key);
956    // Random nonce is highly recommended for HMAC
957    signature_params.set_random_nonce();
958
959    req.set_message_signature(&signature_params, &secret_key, None).await.unwrap();
960
961    let key_id = VerifyingKey::key_id(&secret_key);
962    let verification_res = req.verify_message_signature(&secret_key, Some(&key_id)).await;
963    assert!(verification_res.is_ok());
964
965    let verification_res = req.verify_message_signature(&secret_key, Some("NotFoundKeyId")).await;
966    assert!(verification_res.is_err());
967  }
968
969  #[tokio::test]
970  async fn test_get_key_ids() {
971    let mut req = build_request().await;
972    let secret_key = SecretKey::from_pem(EDDSA_SECRET_KEY).unwrap();
973    let mut signature_params = HttpSignatureParams::try_new(&build_covered_components_req()).unwrap();
974    signature_params.set_key_info(&secret_key);
975
976    req.set_message_signature(&signature_params, &secret_key, None).await.unwrap();
977    let key_ids = req.get_key_ids().unwrap();
978    assert_eq!(key_ids.len(), 1);
979    assert_eq!(key_ids[0], "gjrE7ACMxgzYfFHgabgf4kLTg1eKIdsJ94AiFTFj1is=");
980  }
981
982  const P256_SECERT_KEY: &str = r##"-----BEGIN PRIVATE KEY-----
983MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgv7zxW56ojrWwmSo1
9844uOdbVhUfj9Jd+5aZIB9u8gtWnihRANCAARGYsMe0CT6pIypwRvoJlLNs4+cTh2K
985L7fUNb5i6WbKxkpAoO+6T3pMBG5Yw7+8NuGTvvtrZAXduA2giPxQ8zCf
986-----END PRIVATE KEY-----
987"##;
988  const P256_PUBLIC_KEY: &str = r##"-----BEGIN PUBLIC KEY-----
989MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERmLDHtAk+qSMqcEb6CZSzbOPnE4d
990ii+31DW+YulmysZKQKDvuk96TARuWMO/vDbhk777a2QF3bgNoIj8UPMwnw==
991-----END PUBLIC KEY-----
992"##;
993  #[tokio::test]
994  async fn test_set_verify_multiple_signatures() {
995    let mut req = build_request().await;
996
997    let secret_key_eddsa = SecretKey::from_pem(EDDSA_SECRET_KEY).unwrap();
998    let mut signature_params_eddsa = HttpSignatureParams::try_new(&build_covered_components_req()).unwrap();
999    signature_params_eddsa.set_key_info(&secret_key_eddsa);
1000
1001    let secret_key_p256 = SecretKey::from_pem(P256_SECERT_KEY).unwrap();
1002    let mut signature_params_hmac = HttpSignatureParams::try_new(&build_covered_components_req()).unwrap();
1003    signature_params_hmac.set_key_info(&secret_key_p256);
1004
1005    let params_key_name = &[
1006      (&signature_params_eddsa, &secret_key_eddsa, Some("eddsa_sig")),
1007      (&signature_params_hmac, &secret_key_p256, Some("p256_sig")),
1008    ];
1009
1010    req.set_message_signatures(params_key_name).await.unwrap();
1011
1012    let public_key_eddsa = PublicKey::from_pem(EDDSA_PUBLIC_KEY).unwrap();
1013    let public_key_p256 = PublicKey::from_pem(P256_PUBLIC_KEY).unwrap();
1014    let key_id_eddsa = public_key_eddsa.key_id();
1015    let key_id_p256 = public_key_p256.key_id();
1016
1017    let verification_res = req
1018      .verify_message_signatures(&[
1019        (&public_key_eddsa, Some(&key_id_eddsa)),
1020        (&public_key_p256, Some(&key_id_p256)),
1021      ])
1022      .await
1023      .unwrap();
1024
1025    assert!(verification_res.len() == 2 && verification_res.iter().all(|r| r.is_ok()));
1026    assert!(verification_res[0].as_ref().unwrap() == "eddsa_sig");
1027    assert!(verification_res[1].as_ref().unwrap() == "p256_sig");
1028  }
1029}