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
13type SignatureName = String;
15type KeyId = String;
17
18pub trait MessageSignature {
21 type Error;
22
23 fn has_message_signature(&self) -> bool;
25
26 fn get_key_ids(&self) -> Result<IndexMap<SignatureName, KeyId>, Self::Error>;
28
29 fn get_signature_params(&self) -> Result<IndexMap<SignatureName, HttpSignatureParams>, Self::Error>;
31}
32
33pub trait MessageSignatureReq {
35 type Error;
36 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 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 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 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 fn extract_signatures(&self) -> Result<IndexMap<SignatureName, (HttpSignatureBase, HttpSignatureHeaders)>, Self::Error>;
77}
78
79pub trait MessageSignatureRes {
81 type Error;
82 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 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 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 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 fn extract_signatures<B>(
131 &self,
132 req_for_param: Option<&Request<B>>,
133 ) -> Result<IndexMap<SignatureName, (HttpSignatureBase, HttpSignatureHeaders)>, Self::Error>;
134}
135
136impl<D> MessageSignature for Request<D>
138where
139 D: Send + Body + Sync,
140{
141 type Error = HyperSigError;
142
143 fn has_message_signature(&self) -> bool {
145 has_message_signature_inner(self.headers())
146 }
147
148 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 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 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 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 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
251impl<D> MessageSignature for Response<D>
253where
254 D: Send + Body + Sync,
255{
256 type Error = HyperSigError;
257
258 fn has_message_signature(&self) -> bool {
260 has_message_signature_inner(self.headers())
261 }
262
263 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 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 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 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 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
381fn has_message_signature_inner(headers: &HeaderMap) -> bool {
385 headers.contains_key("signature") && headers.contains_key("signature-input")
386}
387
388fn 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
398fn 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
410fn 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
427async 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 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 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 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
471enum 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
513fn 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
539fn 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
574fn 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
595fn 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
674fn 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#[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 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 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 res
877 .set_message_signature(&signature_params, &secret_key, None, Some(&req))
878 .await
879 .unwrap();
880 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 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 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}