diff --git a/ext-hyper/src/hyper_http.rs b/ext-hyper/src/hyper_http.rs index eca4278..e2b7034 100644 --- a/ext-hyper/src/hyper_http.rs +++ b/ext-hyper/src/hyper_http.rs @@ -7,7 +7,7 @@ use httpsig::prelude::{ build_http_message_component, DerivedComponentName, HttpMessageComponent, HttpMessageComponentId, HttpMessageComponentName, HttpMessageComponentParam, }, - signature_params::HttpSignatureParams, + HttpSignatureBase, HttpSignatureParams, }; // @@ -53,6 +53,22 @@ where } } /* --------------------------------------- */ +/// Build signature base from hyper http request and signature params +fn build_signature_base_from_request( + req: &Request, + signature_params: &HttpSignatureParams, +) -> anyhow::Result { + let component_lines = signature_params + .covered_components + .iter() + .map(|component_id| extract_http_message_component_from_request(req, component_id)) + .collect::>(); + ensure!(component_lines.iter().all(|c| c.is_ok()), "Failed to extract component lines"); + let component_lines = component_lines.into_iter().map(|c| c.unwrap()).collect::>(); + + HttpSignatureBase::try_new(&component_lines, signature_params) +} + /// Extract http field from hyper http request fn extract_http_field_from_request( req: &Request, @@ -204,4 +220,23 @@ mod tests { assert_eq!(component.value.as_component_value(), r##"("@method" "@authority")"##); assert_eq!(component.value.key(), Some("sig1")); } + + #[tokio::test] + async fn test_build_signature_base_from_request() { + let req = build_request().await.unwrap(); + + const SIGPARA: &str = r##";created=1704972031;alg="ed25519";keyid="gjrE7ACMxgzYfFHgabgf4kLTg1eKIdsJ94AiFTFj1is""##; + let values = (r##""@method" "content-type" "date" "content-digest""##, SIGPARA); + let signature_params = HttpSignatureParams::try_from(format!("({}){}", values.0, values.1).as_str()).unwrap(); + + let signature_base = build_signature_base_from_request(&req, &signature_params).unwrap(); + assert_eq!( + signature_base.to_string(), + r##""@method": GET +"content-type": application/json, application/json-patch+json +"date": Sun, 09 May 2021 18:30:00 GMT +"content-digest": sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=: +"@signature-params": ("@method" "content-type" "date" "content-digest");created=1704972031;alg="ed25519";keyid="gjrE7ACMxgzYfFHgabgf4kLTg1eKIdsJ94AiFTFj1is""## + ); + } } diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 208a903..34ed5e7 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -18,9 +18,8 @@ pub mod prelude { HttpMessageComponentParam, }; } - pub mod signature_params { - pub use crate::signature_params::HttpSignatureParams; - } + + pub use crate::{signature_base::HttpSignatureBase, signature_params::HttpSignatureParams}; } #[cfg(test)] diff --git a/lib/src/signature_base.rs b/lib/src/signature_base.rs index 9ffcb78..96397f5 100644 --- a/lib/src/signature_base.rs +++ b/lib/src/signature_base.rs @@ -2,23 +2,19 @@ use crate::{message_component::HttpMessageComponent, signature_params::HttpSigna /// Signature Base /// https://www.ietf.org/archive/id/draft-ietf-httpbis-message-signatures-19.html#section-2.5 -pub struct SignatureBase { +pub struct HttpSignatureBase { /// HTTP message field and derived components ordered as in the vector in signature params component_lines: Vec, /// signature params signature_params: HttpSignatureParams, } -impl SignatureBase { +impl HttpSignatureBase { /// Creates a new signature base from component lines and signature params /// This should not be exposed to user and not used directly. /// TODO: Use wrapper functions generating SignatureBase from base HTTP request and Signer itself instead when newly generating signature /// TODO: When verifying signature, use wrapper functions generating SignatureBase from HTTP request containing signature params itself instead. - pub fn try_new( - component_lines: &Vec, - signature_params: &HttpSignatureParams, - signature_key: Option<&str>, - ) -> anyhow::Result { + pub fn try_new(component_lines: &Vec, signature_params: &HttpSignatureParams) -> anyhow::Result { // check if the order of component lines is the same as the order of covered message component ids if component_lines.len() != signature_params.covered_components.len() { anyhow::bail!("The number of component lines is not the same as the number of covered message component ids"); @@ -45,14 +41,14 @@ impl SignatureBase { } } -impl std::fmt::Display for SignatureBase { +impl std::fmt::Display for HttpSignatureBase { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut signature_base = String::new(); for component_line in &self.component_lines { signature_base.push_str(&component_line.to_string()); signature_base.push('\n'); } - signature_base.push_str(&format!("\"@signature-params\": {}", self.signature_params.to_string())); + signature_base.push_str(&format!("\"@signature-params\": {}", self.signature_params)); write!(f, "{}", signature_base) } } @@ -77,7 +73,7 @@ mod test { HttpMessageComponent::from_serialized_str("\"content-digest\": sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:") .unwrap(), ]; - let signature_base = SignatureBase::try_new(&component_lines, &signature_params, None).unwrap(); + let signature_base = HttpSignatureBase::try_new(&component_lines, &signature_params).unwrap(); let test_string = r##""@method": GET "@path": / "date": Tue, 07 Jun 2014 20:51:35 GMT diff --git a/lib/src/signature_params.rs b/lib/src/signature_params.rs index e925ba9..6192699 100644 --- a/lib/src/signature_params.rs +++ b/lib/src/signature_params.rs @@ -17,19 +17,19 @@ const DEFAULT_EXPIRES_IN: u64 = 300; /// https://www.ietf.org/archive/id/draft-ietf-httpbis-message-signatures-19.html#name-signature-parameters pub struct HttpSignatureParams { /// created unix timestamp. - pub(crate) created: Option, + pub created: Option, /// signature expires unix timestamp. - pub(crate) expires: Option, + pub expires: Option, /// nonce - pub(crate) nonce: Option, + pub nonce: Option, /// algorithm name - pub(crate) alg: Option, + pub alg: Option, /// key id. - pub(crate) keyid: Option, + pub keyid: Option, /// tag - pub(crate) tag: Option, + pub tag: Option, /// covered component vector string: ordered message components, i.e., string of http_fields and derived_components - pub(crate) covered_components: Vec, + pub covered_components: Vec, } impl std::fmt::Display for HttpSignatureParams {