Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ resolver = "2"

[workspace.package]
edition = "2021"
version = "0.0.20"
version = "0.0.21"
authors = ["Jun Kurihara"]
homepage = "https://github.com/junkurihara/httpsig-rs"
repository = "https://github.com/junkurihara/httpsig-rs"
Expand Down
7 changes: 6 additions & 1 deletion httpsig-hyper/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@ rust-version.workspace = true

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
default = ["blocking"]
blocking = ["futures/executor"]


[dependencies]
httpsig = { path = "../httpsig", version = "0.0.20" }
httpsig = { path = "../httpsig", version = "0.0.21" }

thiserror = { version = "2.0.18" }
tracing = { version = "0.1.44" }
Expand Down
240 changes: 240 additions & 0 deletions httpsig-hyper/src/hyper_http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,103 @@ pub trait MessageSignatureRes {
) -> Result<IndexMap<SignatureName, (HttpSignatureBase, HttpSignatureHeaders)>, Self::Error>;
}

/* --------------------------------------- */
#[cfg(feature = "blocking")]
/// Synchronous counterpart of [`MessageSignatureReq`].
///
/// Every method delegates to the corresponding async method via `futures::executor::block_on`.
///
/// # Panics
///
/// All methods will panic if called from within an async runtime (e.g. a `tokio` task).
/// Use the async [`MessageSignatureReq`] methods instead when you are already in an async context.
pub trait MessageSignatureReqSync: MessageSignatureReq {
fn set_message_signature_sync<T>(
&mut self,
signature_params: &HttpSignatureParams,
signing_key: &T,
signature_name: Option<&str>,
) -> Result<(), Self::Error>
where
Self: Sized,
T: SigningKey + Sync;

fn set_message_signatures_sync<T>(
&mut self,
params_key_name: &[(&HttpSignatureParams, &T, Option<&str>)],
) -> Result<(), Self::Error>
where
Self: Sized,
T: SigningKey + Sync;

fn verify_message_signature_sync<T>(&self, verifying_key: &T, key_id: Option<&str>) -> Result<SignatureName, Self::Error>
where
Self: Sized,
T: VerifyingKey + Sync;

fn verify_message_signatures_sync<T>(
&self,
key_and_id: &[(&T, Option<&str>)],
) -> Result<Vec<Result<SignatureName, Self::Error>>, Self::Error>
where
Self: Sized,
T: VerifyingKey + Sync;
}

#[cfg(feature = "blocking")]
/// Synchronous counterpart of [`MessageSignatureRes`].
///
/// Every method delegates to the corresponding async method via `futures::executor::block_on`.
///
/// # Panics
///
/// All methods will panic if called from within an async runtime (e.g. a `tokio` task).
/// Use the async [`MessageSignatureRes`] methods instead when you are already in an async context.
pub trait MessageSignatureResSync: MessageSignatureRes {
fn set_message_signature_sync<T, B>(
&mut self,
signature_params: &HttpSignatureParams,
signing_key: &T,
signature_name: Option<&str>,
req_for_param: Option<&Request<B>>,
) -> Result<(), Self::Error>
where
Self: Sized,
T: SigningKey + Sync,
B: Sync;

fn set_message_signatures_sync<T, B>(
&mut self,
params_key_name: &[(&HttpSignatureParams, &T, Option<&str>)],
req_for_param: Option<&Request<B>>,
) -> Result<(), Self::Error>
where
Self: Sized,
T: SigningKey + Sync,
B: Sync;

fn verify_message_signature_sync<T, B>(
&self,
verifying_key: &T,
key_id: Option<&str>,
req_for_param: Option<&Request<B>>,
) -> Result<SignatureName, Self::Error>
where
Self: Sized,
T: VerifyingKey + Sync,
B: Sync;

fn verify_message_signatures_sync<T, B>(
&self,
key_and_id: &[(&T, Option<&str>)],
req_for_param: Option<&Request<B>>,
) -> Result<Vec<Result<SignatureName, Self::Error>>, Self::Error>
where
Self: Sized,
T: VerifyingKey + Sync,
B: Sync;
}

/* --------------------------------------- */
impl<D> MessageSignature for Request<D>
where
Expand Down Expand Up @@ -378,6 +475,117 @@ where
}
}

/* --------------------------------------- */
#[cfg(feature = "blocking")]
impl<D> MessageSignatureReqSync for Request<D>
where
D: Send + Body + Sync,
{
fn set_message_signature_sync<T>(
&mut self,
signature_params: &HttpSignatureParams,
signing_key: &T,
signature_name: Option<&str>,
) -> Result<(), Self::Error>
where
Self: Sized,
T: SigningKey + Sync,
{
futures::executor::block_on(self.set_message_signature(signature_params, signing_key, signature_name))
}

fn set_message_signatures_sync<T>(
&mut self,
params_key_name: &[(&HttpSignatureParams, &T, Option<&str>)],
) -> Result<(), Self::Error>
where
Self: Sized,
T: SigningKey + Sync,
{
futures::executor::block_on(self.set_message_signatures(params_key_name))
}

fn verify_message_signature_sync<T>(&self, verifying_key: &T, key_id: Option<&str>) -> Result<SignatureName, Self::Error>
where
Self: Sized,
T: VerifyingKey + Sync,
{
futures::executor::block_on(self.verify_message_signature(verifying_key, key_id))
}

fn verify_message_signatures_sync<T>(
&self,
key_and_id: &[(&T, Option<&str>)],
) -> Result<Vec<Result<SignatureName, Self::Error>>, Self::Error>
where
Self: Sized,
T: VerifyingKey + Sync,
{
futures::executor::block_on(self.verify_message_signatures(key_and_id))
}
}

#[cfg(feature = "blocking")]
impl<D> MessageSignatureResSync for Response<D>
where
D: Send + Body + Sync,
{
fn set_message_signature_sync<T, B>(
&mut self,
signature_params: &HttpSignatureParams,
signing_key: &T,
signature_name: Option<&str>,
req_for_param: Option<&Request<B>>,
) -> Result<(), Self::Error>
where
Self: Sized,
T: SigningKey + Sync,
B: Sync,
{
futures::executor::block_on(self.set_message_signature(signature_params, signing_key, signature_name, req_for_param))
}

fn set_message_signatures_sync<T, B>(
&mut self,
params_key_name: &[(&HttpSignatureParams, &T, Option<&str>)],
req_for_param: Option<&Request<B>>,
) -> Result<(), Self::Error>
where
Self: Sized,
T: SigningKey + Sync,
B: Sync,
{
futures::executor::block_on(self.set_message_signatures(params_key_name, req_for_param))
}

fn verify_message_signature_sync<T, B>(
&self,
verifying_key: &T,
key_id: Option<&str>,
req_for_param: Option<&Request<B>>,
) -> Result<SignatureName, Self::Error>
where
Self: Sized,
T: VerifyingKey + Sync,
B: Sync,
{
futures::executor::block_on(self.verify_message_signature(verifying_key, key_id, req_for_param))
}

fn verify_message_signatures_sync<T, B>(
&self,
key_and_id: &[(&T, Option<&str>)],
req_for_param: Option<&Request<B>>,
) -> Result<Vec<Result<SignatureName, Self::Error>>, Self::Error>
where
Self: Sized,
T: VerifyingKey + Sync,
B: Sync,
{
futures::executor::block_on(self.verify_message_signatures(key_and_id, req_for_param))
}
}

/* --------------------------------------- */
// inner functions
/// has message signature inner function
Expand Down Expand Up @@ -1026,4 +1234,36 @@ ii+31DW+YulmysZKQKDvuk96TARuWMO/vDbhk777a2QF3bgNoIj8UPMwnw==
assert!(verification_res[0].as_ref().unwrap() == "eddsa_sig");
assert!(verification_res[1].as_ref().unwrap() == "p256_sig");
}

#[cfg(feature = "blocking")]
#[test]
fn test_blocking_set_verify_message_signature_req() {
let mut req = futures::executor::block_on(build_request());
let secret_key = SecretKey::from_pem(EDDSA_SECRET_KEY).unwrap();
let mut signature_params = HttpSignatureParams::try_new(&build_covered_components_req()).unwrap();
signature_params.set_key_info(&secret_key);

req.set_message_signature_sync(&signature_params, &secret_key, None).unwrap();

let public_key = PublicKey::from_pem(EDDSA_PUBLIC_KEY).unwrap();
let verification_res = req.verify_message_signature_sync(&public_key, None);
assert!(verification_res.is_ok());
}

#[cfg(feature = "blocking")]
#[test]
fn test_blocking_set_verify_message_signature_res() {
let req = futures::executor::block_on(build_request());
let mut res = futures::executor::block_on(build_response());
let secret_key = SecretKey::from_pem(EDDSA_SECRET_KEY).unwrap();
let mut signature_params = HttpSignatureParams::try_new(&build_covered_components_res()).unwrap();
signature_params.set_key_info(&secret_key);
res
.set_message_signature_sync(&signature_params, &secret_key, None, Some(&req))
.unwrap();

let public_key = PublicKey::from_pem(EDDSA_PUBLIC_KEY).unwrap();
let verification_res = res.verify_message_signature_sync(&public_key, None, Some(&req));
assert!(verification_res.is_ok());
}
}
68 changes: 66 additions & 2 deletions httpsig-hyper/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
//! # httpsig-hyper
//!
//! `httpsig-hyper` is a crate that provides a convenient API for `Hyper` users to handle HTTP signatures.
//! This crate extends hyper's https request and response messages with the ability to generate and verify HTTP signatures.
//! This crate extends hyper's http request and response messages with the ability to generate and verify HTTP signatures.
//! Additionally it also provides a way to set and verify content-digest header.
//!
//! ## Async-first design
//!
//! The primary API is fully async, allowing concurrent processing of multiple signatures via
//! [`MessageSignatureReq`] and [`MessageSignatureRes`].
//!
//! ## Blocking API
//!
//! When the `blocking` feature is enabled (on by default), synchronous wrappers are provided via
//! [`MessageSignatureReqSync`] and [`MessageSignatureResSync`]. These use `futures::executor::block_on`
//! internally and are intended **exclusively for non-async contexts**.
//!
//! # Panics
//!
//! Calling any `*_sync` method from within an async runtime (e.g. inside a `tokio::spawn` task)
//! will panic. If you are already in an async context, use the async methods directly.

mod error;
mod hyper_content_digest;
Expand Down Expand Up @@ -42,7 +58,9 @@ impl std::str::FromStr for ContentDigestType {
pub use error::{HyperDigestError, HyperDigestResult, HyperSigError, HyperSigResult};
pub use httpsig::prelude;
pub use hyper_content_digest::{ContentDigest, RequestContentDigest, ResponseContentDigest};
pub use hyper_http::{MessageSignature, MessageSignatureReq, MessageSignatureRes};
pub use hyper_http::{
MessageSignature, MessageSignatureReq, MessageSignatureReqSync, MessageSignatureRes, MessageSignatureResSync,
};

/* ----------------------------------------------------------------- */
#[cfg(test)]
Expand Down Expand Up @@ -188,4 +206,50 @@ MCowBQYDK2VwAyEA1ixMQcxO46PLlgQfYS46ivFd+n0CcDHSKUnuhm3i1O0=
.await;
assert!(verification_res.is_err());
}

#[cfg(feature = "blocking")]
#[test]
fn test_set_verify_request_sync() {
// show usage of set_message_signature_sync and verify_message_signature_sync

let mut req = futures::executor::block_on(build_request());
let secret_key = SecretKey::from_pem(EDDSA_SECRET_KEY).unwrap();
let covered_components = COVERED_COMPONENTS_REQ
.iter()
.map(|v| message_component::HttpMessageComponentId::try_from(*v))
.collect::<Result<Vec<_>, _>>()
.unwrap();
let mut signature_params = HttpSignatureParams::try_new(&covered_components).unwrap();
// set key information, alg and keyid
signature_params.set_key_info(&secret_key);
// set signature
req.set_message_signature_sync(&signature_params, &secret_key, None).unwrap();
let public_key = PublicKey::from_pem(EDDSA_PUBLIC_KEY).unwrap();
let verification_res = req.verify_message_signature_sync(&public_key, None);
assert!(verification_res.is_ok());
}

#[cfg(feature = "blocking")]
#[test]
fn test_set_verify_response_sync() {
// show usage of set_message_signature_sync and verify_message_signature_sync
let req = futures::executor::block_on(build_request());
let mut res = futures::executor::block_on(build_response());
let secret_key = SecretKey::from_pem(EDDSA_SECRET_KEY).unwrap();
let covered_components = COVERED_COMPONENTS_RES
.iter()
.map(|v| message_component::HttpMessageComponentId::try_from(*v))
.collect::<Result<Vec<_>, _>>()
.unwrap();
let mut signature_params = HttpSignatureParams::try_new(&covered_components).unwrap();
// set key information, alg and keyid
signature_params.set_key_info(&secret_key);
// set signature
res
.set_message_signature_sync(&signature_params, &secret_key, None, Some(&req))
.unwrap();
let public_key = PublicKey::from_pem(EDDSA_PUBLIC_KEY).unwrap();
let verification_res = res.verify_message_signature_sync(&public_key, None, Some(&req));
assert!(verification_res.is_ok());
}
}