Skip to content

Commit

Permalink
feat: add signature base for hyper
Browse files Browse the repository at this point in the history
  • Loading branch information
junkurihara committed Feb 4, 2024
1 parent c3cc9b3 commit cbc8110
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 21 deletions.
37 changes: 36 additions & 1 deletion ext-hyper/src/hyper_http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use httpsig::prelude::{
build_http_message_component, DerivedComponentName, HttpMessageComponent, HttpMessageComponentId, HttpMessageComponentName,
HttpMessageComponentParam,
},
signature_params::HttpSignatureParams,
HttpSignatureBase, HttpSignatureParams,
};
//

Expand Down Expand Up @@ -53,6 +53,22 @@ where
}
}
/* --------------------------------------- */
/// Build signature base from hyper http request and signature params
fn build_signature_base_from_request<B>(
req: &Request<B>,
signature_params: &HttpSignatureParams,
) -> anyhow::Result<HttpSignatureBase> {
let component_lines = signature_params
.covered_components
.iter()
.map(|component_id| extract_http_message_component_from_request(req, component_id))
.collect::<Vec<_>>();
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::<Vec<_>>();

HttpSignatureBase::try_new(&component_lines, signature_params)
}

/// Extract http field from hyper http request
fn extract_http_field_from_request<B>(
req: &Request<B>,
Expand Down Expand Up @@ -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""##
);
}
}
5 changes: 2 additions & 3 deletions lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
16 changes: 6 additions & 10 deletions lib/src/signature_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<HttpMessageComponent>,
/// 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<HttpMessageComponent>,
signature_params: &HttpSignatureParams,
signature_key: Option<&str>,
) -> anyhow::Result<Self> {
pub fn try_new(component_lines: &Vec<HttpMessageComponent>, signature_params: &HttpSignatureParams) -> anyhow::Result<Self> {
// 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");
Expand All @@ -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)
}
}
Expand All @@ -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
Expand Down
14 changes: 7 additions & 7 deletions lib/src/signature_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u64>,
pub created: Option<u64>,
/// signature expires unix timestamp.
pub(crate) expires: Option<u64>,
pub expires: Option<u64>,
/// nonce
pub(crate) nonce: Option<String>,
pub nonce: Option<String>,
/// algorithm name
pub(crate) alg: Option<String>,
pub alg: Option<String>,
/// key id.
pub(crate) keyid: Option<String>,
pub keyid: Option<String>,
/// tag
pub(crate) tag: Option<String>,
pub tag: Option<String>,
/// covered component vector string: ordered message components, i.e., string of http_fields and derived_components
pub(crate) covered_components: Vec<HttpMessageComponentId>,
pub covered_components: Vec<HttpMessageComponentId>,
}

impl std::fmt::Display for HttpSignatureParams {
Expand Down

0 comments on commit cbc8110

Please sign in to comment.