diff --git a/httpsig-hyper/Cargo.toml b/httpsig-hyper/Cargo.toml index 0c5b0f1..7d1b8f1 100644 --- a/httpsig-hyper/Cargo.toml +++ b/httpsig-hyper/Cargo.toml @@ -13,7 +13,9 @@ rust-version.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -default = ["blocking"] +default = ["blocking", "digest-sha256", "digest-sha512"] +digest-sha256 = [] +digest-sha512 = [] blocking = ["futures/executor"] rsa-signature = ["httpsig/rsa-signature"] @@ -49,3 +51,12 @@ tokio = { version = "1.49.0", default-features = false, features = [ "macros", "rt-multi-thread", ] } # testing only + +[[example]] +name = "hyper-response" +required-features = ["digest-sha256"] + + +[[example]] +name = "hyper-request" +required-features = ["digest-sha256"] diff --git a/httpsig-hyper/src/hyper_content_digest.rs b/httpsig-hyper/src/hyper_content_digest.rs index efecb18..05e95de 100644 --- a/httpsig-hyper/src/hyper_content_digest.rs +++ b/httpsig-hyper/src/hyper_content_digest.rs @@ -44,12 +44,14 @@ pub trait ContentDigest: http_body::Body { /// Returns the digest of the given body in Vec fn derive_digest(body_bytes: &Bytes, cd_type: &ContentDigestType) -> Vec { match cd_type { + #[cfg(feature = "digest-sha256")] ContentDigestType::Sha256 => { let mut hasher = sha2::Sha256::new(); hasher.update(body_bytes); hasher.finalize().to_vec() } + #[cfg(feature = "digest-sha512")] ContentDigestType::Sha512 => { let mut hasher = sha2::Sha512::new(); hasher.update(body_bytes); @@ -263,17 +265,30 @@ mod tests { #[tokio::test] async fn content_digest() { let body = Full::new(&b"{\"hello\": \"world\"}"[..]); - let (_body_bytes, digest) = body.into_bytes_with_digest(&ContentDigestType::Sha256).await.unwrap(); - - assert_eq!(digest, "X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE="); + #[cfg(feature = "digest-sha256")] + { + let (_body_bytes, digest) = body + .into_bytes_with_digest(&ContentDigestType::Sha256) + .await + .unwrap(); + + assert_eq!(digest, "X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE="); + } - let (_body_bytes, digest) = body.into_bytes_with_digest(&ContentDigestType::Sha512).await.unwrap(); - assert_eq!( - digest, - "WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==" - ); + #[cfg(feature = "digest-sha512")] + { + let (_body_bytes, digest) = body + .into_bytes_with_digest(&ContentDigestType::Sha512) + .await + .unwrap(); + assert_eq!( + digest, + "WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==" + ); + } } + #[cfg(feature = "digest-sha256")] #[tokio::test] async fn hyper_request_test() { let body = Full::new(&b"{\"hello\": \"world\"}"[..]); @@ -295,6 +310,7 @@ mod tests { assert!(verified.is_ok()); } + #[cfg(feature = "digest-sha256")] #[tokio::test] async fn hyper_response_test() { let body = Full::new(&b"{\"hello\": \"world\"}"[..]); @@ -315,6 +331,7 @@ mod tests { assert!(verified.is_ok()); } + #[cfg(feature = "digest-sha256")] #[tokio::test] async fn hyper_request_digest_mismatch_by_body_tamper_should_fail() { // 1) Create a request and set a correct Content-Digest for the original body @@ -344,6 +361,7 @@ mod tests { } } + #[cfg(feature = "digest-sha256")] #[tokio::test] async fn hyper_response_digest_mismatch_by_header_tamper_should_fail() { // 1) Create a response and set a correct Content-Digest @@ -397,6 +415,7 @@ mod tests { } } + #[cfg(feature = "digest-sha256")] #[tokio::test] async fn hyper_request_digest_length_mismatch_should_fail() { // 1) Create a request and attach a valid Content-Digest header diff --git a/httpsig-hyper/src/hyper_http.rs b/httpsig-hyper/src/hyper_http.rs index 9f1a6c0..d45a052 100644 --- a/httpsig-hyper/src/hyper_http.rs +++ b/httpsig-hyper/src/hyper_http.rs @@ -935,11 +935,13 @@ fn extract_http_message_component( ) -> HyperSigResult { match &target_component_id.name { HttpMessageComponentName::HttpField(_) => extract_http_field(req_or_res, target_component_id), - HttpMessageComponentName::Derived(_) => extract_derived_component(req_or_res, target_component_id), + HttpMessageComponentName::Derived(_) => { + extract_derived_component(req_or_res, target_component_id) + } } } /* --------------------------------------- */ -#[cfg(test)] +#[cfg(all(test, feature = "digest-sha256"))] #[path = "hyper_http_tests.rs"] mod tests; diff --git a/httpsig-hyper/src/lib.rs b/httpsig-hyper/src/lib.rs index 691a392..998c678 100644 --- a/httpsig-hyper/src/lib.rs +++ b/httpsig-hyper/src/lib.rs @@ -21,34 +21,45 @@ //! will panic. If you are already in an async context, use the async methods directly. mod error; +#[cfg(any(feature = "digest-sha256", feature = "digest-sha512"))] mod hyper_content_digest; mod hyper_http; // hyper's http specific extension to generate and verify http signature /// content-digest header name +#[cfg(any(feature = "digest-sha256", feature = "digest-sha512"))] const CONTENT_DIGEST_HEADER: &str = "content-digest"; /// content-digest header type +#[cfg(any(feature = "digest-sha256", feature = "digest-sha512"))] pub enum ContentDigestType { + #[cfg(feature = "digest-sha256")] Sha256, + #[cfg(feature = "digest-sha512")] Sha512, } +#[cfg(any(feature = "digest-sha256", feature = "digest-sha512"))] impl std::fmt::Display for ContentDigestType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + #[cfg(feature = "digest-sha256")] ContentDigestType::Sha256 => write!(f, "sha-256"), + #[cfg(feature = "digest-sha512")] ContentDigestType::Sha512 => write!(f, "sha-512"), } } } +#[cfg(any(feature = "digest-sha256", feature = "digest-sha512"))] impl std::str::FromStr for ContentDigestType { type Err = error::HyperDigestError; fn from_str(s: &str) -> Result { match s { + #[cfg(feature = "digest-sha256")] "sha-256" => Ok(ContentDigestType::Sha256), + #[cfg(feature = "digest-sha512")] "sha-512" => Ok(ContentDigestType::Sha512), _ => Err(error::HyperDigestError::InvalidContentDigestType(s.to_string())), } @@ -57,13 +68,14 @@ impl std::str::FromStr for ContentDigestType { pub use error::{HyperDigestError, HyperDigestResult, HyperSigError, HyperSigResult}; pub use httpsig::prelude; +#[cfg(any(feature = "digest-sha256", feature = "digest-sha512"))] pub use hyper_content_digest::{ContentDigest, RequestContentDigest, ResponseContentDigest}; pub use hyper_http::{ MessageSignature, MessageSignatureReq, MessageSignatureReqSync, MessageSignatureRes, MessageSignatureResSync, }; /* ----------------------------------------------------------------- */ -#[cfg(test)] +#[cfg(all(test, feature = "digest-sha256"))] mod tests { use super::{prelude::*, *}; use http::{Request, Response}; @@ -113,6 +125,7 @@ MCowBQYDK2VwAyEA1ixMQcxO46PLlgQfYS46ivFd+n0CcDHSKUnuhm3i1O0= #[test] fn test_content_digest_type() { assert_eq!(ContentDigestType::Sha256.to_string(), "sha-256"); + #[cfg(feature = "digest-sha512")] assert_eq!(ContentDigestType::Sha512.to_string(), "sha-512"); }