Skip to content

Commit 64d1077

Browse files
authored
Merge pull request #11 from junkurihara/develop
0.0.21
2 parents afa6289 + 826dc28 commit 64d1077

6 files changed

Lines changed: 326 additions & 17 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ resolver = "2"
44

55
[workspace.package]
66
edition = "2021"
7-
version = "0.0.20"
7+
version = "0.0.21"
88
authors = ["Jun Kurihara"]
99
homepage = "https://github.com/junkurihara/httpsig-rs"
1010
repository = "https://github.com/junkurihara/httpsig-rs"

httpsig-hyper/Cargo.toml

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,21 @@ rust-version.workspace = true
1212

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

15+
[features]
16+
default = ["blocking"]
17+
blocking = ["futures/executor"]
18+
19+
1520
[dependencies]
16-
httpsig = { path = "../httpsig", version = "0.0.20" }
21+
httpsig = { path = "../httpsig", version = "0.0.21" }
1722

18-
thiserror = { version = "2.0.16" }
19-
tracing = { version = "0.1.41" }
23+
thiserror = { version = "2.0.18" }
24+
tracing = { version = "0.1.44" }
2025
futures = { version = "0.3.31", default-features = false, features = [
2126
"std",
2227
"async-await",
2328
] }
24-
indexmap = { version = "2.11.1" }
29+
indexmap = { version = "2.11.4" }
2530

2631
# content digest with rfc8941 structured field values
2732
sha2 = { version = "0.10.9", default-features = false }
@@ -31,14 +36,14 @@ sfv = { version = "0.14.0" }
3136
base64 = { version = "0.22.1" }
3237

3338
# for request and response headers
34-
http = { version = "1.3.1" }
39+
http = { version = "1.4.0" }
3540
http-body = { version = "1.0.1" }
3641
http-body-util = { version = "0.1.3" }
37-
bytes = { version = "1.10.1" }
42+
bytes = { version = "1.11.1" }
3843

3944

4045
[dev-dependencies]
41-
tokio = { version = "1.47.1", default-features = false, features = [
46+
tokio = { version = "1.49.0", default-features = false, features = [
4247
"macros",
4348
"rt-multi-thread",
4449
] } # testing only

httpsig-hyper/src/hyper_http.rs

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,103 @@ pub trait MessageSignatureRes {
133133
) -> Result<IndexMap<SignatureName, (HttpSignatureBase, HttpSignatureHeaders)>, Self::Error>;
134134
}
135135

136+
/* --------------------------------------- */
137+
#[cfg(feature = "blocking")]
138+
/// Synchronous counterpart of [`MessageSignatureReq`].
139+
///
140+
/// Every method delegates to the corresponding async method via `futures::executor::block_on`.
141+
///
142+
/// # Panics
143+
///
144+
/// All methods will panic if called from within an async runtime (e.g. a `tokio` task).
145+
/// Use the async [`MessageSignatureReq`] methods instead when you are already in an async context.
146+
pub trait MessageSignatureReqSync: MessageSignatureReq {
147+
fn set_message_signature_sync<T>(
148+
&mut self,
149+
signature_params: &HttpSignatureParams,
150+
signing_key: &T,
151+
signature_name: Option<&str>,
152+
) -> Result<(), Self::Error>
153+
where
154+
Self: Sized,
155+
T: SigningKey + Sync;
156+
157+
fn set_message_signatures_sync<T>(
158+
&mut self,
159+
params_key_name: &[(&HttpSignatureParams, &T, Option<&str>)],
160+
) -> Result<(), Self::Error>
161+
where
162+
Self: Sized,
163+
T: SigningKey + Sync;
164+
165+
fn verify_message_signature_sync<T>(&self, verifying_key: &T, key_id: Option<&str>) -> Result<SignatureName, Self::Error>
166+
where
167+
Self: Sized,
168+
T: VerifyingKey + Sync;
169+
170+
fn verify_message_signatures_sync<T>(
171+
&self,
172+
key_and_id: &[(&T, Option<&str>)],
173+
) -> Result<Vec<Result<SignatureName, Self::Error>>, Self::Error>
174+
where
175+
Self: Sized,
176+
T: VerifyingKey + Sync;
177+
}
178+
179+
#[cfg(feature = "blocking")]
180+
/// Synchronous counterpart of [`MessageSignatureRes`].
181+
///
182+
/// Every method delegates to the corresponding async method via `futures::executor::block_on`.
183+
///
184+
/// # Panics
185+
///
186+
/// All methods will panic if called from within an async runtime (e.g. a `tokio` task).
187+
/// Use the async [`MessageSignatureRes`] methods instead when you are already in an async context.
188+
pub trait MessageSignatureResSync: MessageSignatureRes {
189+
fn set_message_signature_sync<T, B>(
190+
&mut self,
191+
signature_params: &HttpSignatureParams,
192+
signing_key: &T,
193+
signature_name: Option<&str>,
194+
req_for_param: Option<&Request<B>>,
195+
) -> Result<(), Self::Error>
196+
where
197+
Self: Sized,
198+
T: SigningKey + Sync,
199+
B: Sync;
200+
201+
fn set_message_signatures_sync<T, B>(
202+
&mut self,
203+
params_key_name: &[(&HttpSignatureParams, &T, Option<&str>)],
204+
req_for_param: Option<&Request<B>>,
205+
) -> Result<(), Self::Error>
206+
where
207+
Self: Sized,
208+
T: SigningKey + Sync,
209+
B: Sync;
210+
211+
fn verify_message_signature_sync<T, B>(
212+
&self,
213+
verifying_key: &T,
214+
key_id: Option<&str>,
215+
req_for_param: Option<&Request<B>>,
216+
) -> Result<SignatureName, Self::Error>
217+
where
218+
Self: Sized,
219+
T: VerifyingKey + Sync,
220+
B: Sync;
221+
222+
fn verify_message_signatures_sync<T, B>(
223+
&self,
224+
key_and_id: &[(&T, Option<&str>)],
225+
req_for_param: Option<&Request<B>>,
226+
) -> Result<Vec<Result<SignatureName, Self::Error>>, Self::Error>
227+
where
228+
Self: Sized,
229+
T: VerifyingKey + Sync,
230+
B: Sync;
231+
}
232+
136233
/* --------------------------------------- */
137234
impl<D> MessageSignature for Request<D>
138235
where
@@ -378,6 +475,117 @@ where
378475
}
379476
}
380477

478+
/* --------------------------------------- */
479+
#[cfg(feature = "blocking")]
480+
impl<D> MessageSignatureReqSync for Request<D>
481+
where
482+
D: Send + Body + Sync,
483+
{
484+
fn set_message_signature_sync<T>(
485+
&mut self,
486+
signature_params: &HttpSignatureParams,
487+
signing_key: &T,
488+
signature_name: Option<&str>,
489+
) -> Result<(), Self::Error>
490+
where
491+
Self: Sized,
492+
T: SigningKey + Sync,
493+
{
494+
futures::executor::block_on(self.set_message_signature(signature_params, signing_key, signature_name))
495+
}
496+
497+
fn set_message_signatures_sync<T>(
498+
&mut self,
499+
params_key_name: &[(&HttpSignatureParams, &T, Option<&str>)],
500+
) -> Result<(), Self::Error>
501+
where
502+
Self: Sized,
503+
T: SigningKey + Sync,
504+
{
505+
futures::executor::block_on(self.set_message_signatures(params_key_name))
506+
}
507+
508+
fn verify_message_signature_sync<T>(&self, verifying_key: &T, key_id: Option<&str>) -> Result<SignatureName, Self::Error>
509+
where
510+
Self: Sized,
511+
T: VerifyingKey + Sync,
512+
{
513+
futures::executor::block_on(self.verify_message_signature(verifying_key, key_id))
514+
}
515+
516+
fn verify_message_signatures_sync<T>(
517+
&self,
518+
key_and_id: &[(&T, Option<&str>)],
519+
) -> Result<Vec<Result<SignatureName, Self::Error>>, Self::Error>
520+
where
521+
Self: Sized,
522+
T: VerifyingKey + Sync,
523+
{
524+
futures::executor::block_on(self.verify_message_signatures(key_and_id))
525+
}
526+
}
527+
528+
#[cfg(feature = "blocking")]
529+
impl<D> MessageSignatureResSync for Response<D>
530+
where
531+
D: Send + Body + Sync,
532+
{
533+
fn set_message_signature_sync<T, B>(
534+
&mut self,
535+
signature_params: &HttpSignatureParams,
536+
signing_key: &T,
537+
signature_name: Option<&str>,
538+
req_for_param: Option<&Request<B>>,
539+
) -> Result<(), Self::Error>
540+
where
541+
Self: Sized,
542+
T: SigningKey + Sync,
543+
B: Sync,
544+
{
545+
futures::executor::block_on(self.set_message_signature(signature_params, signing_key, signature_name, req_for_param))
546+
}
547+
548+
fn set_message_signatures_sync<T, B>(
549+
&mut self,
550+
params_key_name: &[(&HttpSignatureParams, &T, Option<&str>)],
551+
req_for_param: Option<&Request<B>>,
552+
) -> Result<(), Self::Error>
553+
where
554+
Self: Sized,
555+
T: SigningKey + Sync,
556+
B: Sync,
557+
{
558+
futures::executor::block_on(self.set_message_signatures(params_key_name, req_for_param))
559+
}
560+
561+
fn verify_message_signature_sync<T, B>(
562+
&self,
563+
verifying_key: &T,
564+
key_id: Option<&str>,
565+
req_for_param: Option<&Request<B>>,
566+
) -> Result<SignatureName, Self::Error>
567+
where
568+
Self: Sized,
569+
T: VerifyingKey + Sync,
570+
B: Sync,
571+
{
572+
futures::executor::block_on(self.verify_message_signature(verifying_key, key_id, req_for_param))
573+
}
574+
575+
fn verify_message_signatures_sync<T, B>(
576+
&self,
577+
key_and_id: &[(&T, Option<&str>)],
578+
req_for_param: Option<&Request<B>>,
579+
) -> Result<Vec<Result<SignatureName, Self::Error>>, Self::Error>
580+
where
581+
Self: Sized,
582+
T: VerifyingKey + Sync,
583+
B: Sync,
584+
{
585+
futures::executor::block_on(self.verify_message_signatures(key_and_id, req_for_param))
586+
}
587+
}
588+
381589
/* --------------------------------------- */
382590
// inner functions
383591
/// has message signature inner function
@@ -1026,4 +1234,36 @@ ii+31DW+YulmysZKQKDvuk96TARuWMO/vDbhk777a2QF3bgNoIj8UPMwnw==
10261234
assert!(verification_res[0].as_ref().unwrap() == "eddsa_sig");
10271235
assert!(verification_res[1].as_ref().unwrap() == "p256_sig");
10281236
}
1237+
1238+
#[cfg(feature = "blocking")]
1239+
#[test]
1240+
fn test_blocking_set_verify_message_signature_req() {
1241+
let mut req = futures::executor::block_on(build_request());
1242+
let secret_key = SecretKey::from_pem(EDDSA_SECRET_KEY).unwrap();
1243+
let mut signature_params = HttpSignatureParams::try_new(&build_covered_components_req()).unwrap();
1244+
signature_params.set_key_info(&secret_key);
1245+
1246+
req.set_message_signature_sync(&signature_params, &secret_key, None).unwrap();
1247+
1248+
let public_key = PublicKey::from_pem(EDDSA_PUBLIC_KEY).unwrap();
1249+
let verification_res = req.verify_message_signature_sync(&public_key, None);
1250+
assert!(verification_res.is_ok());
1251+
}
1252+
1253+
#[cfg(feature = "blocking")]
1254+
#[test]
1255+
fn test_blocking_set_verify_message_signature_res() {
1256+
let req = futures::executor::block_on(build_request());
1257+
let mut res = futures::executor::block_on(build_response());
1258+
let secret_key = SecretKey::from_pem(EDDSA_SECRET_KEY).unwrap();
1259+
let mut signature_params = HttpSignatureParams::try_new(&build_covered_components_res()).unwrap();
1260+
signature_params.set_key_info(&secret_key);
1261+
res
1262+
.set_message_signature_sync(&signature_params, &secret_key, None, Some(&req))
1263+
.unwrap();
1264+
1265+
let public_key = PublicKey::from_pem(EDDSA_PUBLIC_KEY).unwrap();
1266+
let verification_res = res.verify_message_signature_sync(&public_key, None, Some(&req));
1267+
assert!(verification_res.is_ok());
1268+
}
10291269
}

httpsig-hyper/src/lib.rs

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,24 @@
11
//! # httpsig-hyper
22
//!
33
//! `httpsig-hyper` is a crate that provides a convenient API for `Hyper` users to handle HTTP signatures.
4-
//! This crate extends hyper's https request and response messages with the ability to generate and verify HTTP signatures.
4+
//! This crate extends hyper's http request and response messages with the ability to generate and verify HTTP signatures.
55
//! Additionally it also provides a way to set and verify content-digest header.
6+
//!
7+
//! ## Async-first design
8+
//!
9+
//! The primary API is fully async, allowing concurrent processing of multiple signatures via
10+
//! [`MessageSignatureReq`] and [`MessageSignatureRes`].
11+
//!
12+
//! ## Blocking API
13+
//!
14+
//! When the `blocking` feature is enabled (on by default), synchronous wrappers are provided via
15+
//! [`MessageSignatureReqSync`] and [`MessageSignatureResSync`]. These use `futures::executor::block_on`
16+
//! internally and are intended **exclusively for non-async contexts**.
17+
//!
18+
//! # Panics
19+
//!
20+
//! Calling any `*_sync` method from within an async runtime (e.g. inside a `tokio::spawn` task)
21+
//! will panic. If you are already in an async context, use the async methods directly.
622
723
mod error;
824
mod hyper_content_digest;
@@ -42,7 +58,9 @@ impl std::str::FromStr for ContentDigestType {
4258
pub use error::{HyperDigestError, HyperDigestResult, HyperSigError, HyperSigResult};
4359
pub use httpsig::prelude;
4460
pub use hyper_content_digest::{ContentDigest, RequestContentDigest, ResponseContentDigest};
45-
pub use hyper_http::{MessageSignature, MessageSignatureReq, MessageSignatureRes};
61+
pub use hyper_http::{
62+
MessageSignature, MessageSignatureReq, MessageSignatureReqSync, MessageSignatureRes, MessageSignatureResSync,
63+
};
4664

4765
/* ----------------------------------------------------------------- */
4866
#[cfg(test)]
@@ -188,4 +206,50 @@ MCowBQYDK2VwAyEA1ixMQcxO46PLlgQfYS46ivFd+n0CcDHSKUnuhm3i1O0=
188206
.await;
189207
assert!(verification_res.is_err());
190208
}
209+
210+
#[cfg(feature = "blocking")]
211+
#[test]
212+
fn test_set_verify_request_sync() {
213+
// show usage of set_message_signature_sync and verify_message_signature_sync
214+
215+
let mut req = futures::executor::block_on(build_request());
216+
let secret_key = SecretKey::from_pem(EDDSA_SECRET_KEY).unwrap();
217+
let covered_components = COVERED_COMPONENTS_REQ
218+
.iter()
219+
.map(|v| message_component::HttpMessageComponentId::try_from(*v))
220+
.collect::<Result<Vec<_>, _>>()
221+
.unwrap();
222+
let mut signature_params = HttpSignatureParams::try_new(&covered_components).unwrap();
223+
// set key information, alg and keyid
224+
signature_params.set_key_info(&secret_key);
225+
// set signature
226+
req.set_message_signature_sync(&signature_params, &secret_key, None).unwrap();
227+
let public_key = PublicKey::from_pem(EDDSA_PUBLIC_KEY).unwrap();
228+
let verification_res = req.verify_message_signature_sync(&public_key, None);
229+
assert!(verification_res.is_ok());
230+
}
231+
232+
#[cfg(feature = "blocking")]
233+
#[test]
234+
fn test_set_verify_response_sync() {
235+
// show usage of set_message_signature_sync and verify_message_signature_sync
236+
let req = futures::executor::block_on(build_request());
237+
let mut res = futures::executor::block_on(build_response());
238+
let secret_key = SecretKey::from_pem(EDDSA_SECRET_KEY).unwrap();
239+
let covered_components = COVERED_COMPONENTS_RES
240+
.iter()
241+
.map(|v| message_component::HttpMessageComponentId::try_from(*v))
242+
.collect::<Result<Vec<_>, _>>()
243+
.unwrap();
244+
let mut signature_params = HttpSignatureParams::try_new(&covered_components).unwrap();
245+
// set key information, alg and keyid
246+
signature_params.set_key_info(&secret_key);
247+
// set signature
248+
res
249+
.set_message_signature_sync(&signature_params, &secret_key, None, Some(&req))
250+
.unwrap();
251+
let public_key = PublicKey::from_pem(EDDSA_PUBLIC_KEY).unwrap();
252+
let verification_res = res.verify_message_signature_sync(&public_key, None, Some(&req));
253+
assert!(verification_res.is_ok());
254+
}
191255
}

0 commit comments

Comments
 (0)