http-signature-normalization/src/create.rs

93 lines
3.1 KiB
Rust
Raw Permalink Normal View History

2019-09-21 16:26:11 +00:00
//! Types and logic for creating signature and authorization headers
2019-09-11 05:17:30 +00:00
use crate::{
2022-01-17 22:49:01 +00:00
unix_timestamp, ALGORITHM_FIELD, ALGORITHM_VALUE, CREATED_FIELD, EXPIRES_FIELD, HEADERS_FIELD,
KEY_ID_FIELD, SIGNATURE_FIELD,
2019-09-11 05:17:30 +00:00
};
2022-01-17 22:49:01 +00:00
use std::time::SystemTime;
2019-09-11 05:17:30 +00:00
2019-09-11 06:24:51 +00:00
#[derive(Debug)]
2019-09-21 16:26:11 +00:00
/// The signed stage of creating a signature
///
/// From here, the Signature or Authorization headers can be generated as string
2019-09-11 05:17:30 +00:00
pub struct Signed {
signature: String,
sig_headers: Vec<String>,
2022-01-17 22:49:01 +00:00
created: Option<SystemTime>,
expires: Option<SystemTime>,
2019-09-11 05:17:30 +00:00
key_id: String,
}
2019-09-11 06:24:51 +00:00
#[derive(Debug)]
2019-09-21 16:26:11 +00:00
/// The Unsigned stage of creating a signature
///
/// From here, the `sign` method can be used to sign the signing_string, producing a [`Signed`]
/// type.
2019-09-11 05:17:30 +00:00
pub struct Unsigned {
pub(crate) signing_string: String,
pub(crate) sig_headers: Vec<String>,
2022-01-17 22:49:01 +00:00
pub(crate) created: Option<SystemTime>,
pub(crate) expires: Option<SystemTime>,
2019-09-11 05:17:30 +00:00
}
impl Signed {
2019-09-21 16:26:11 +00:00
/// Turn the Signed type into a String that can be used as the Signature Header
///
/// Done manually, it would look like `format!("Signature: {}", signed.signature_header())`
2019-09-11 05:24:04 +00:00
pub fn signature_header(self) -> String {
2020-01-18 00:35:30 +00:00
self.into_header()
2019-09-11 05:17:30 +00:00
}
2019-09-21 16:26:11 +00:00
/// Turn the Signed type into a String that can be used as the Authorization Header
///
/// Done manually, it would look like `format!("Authorization: {}", signed.authorization_header())`
2019-09-11 05:24:04 +00:00
pub fn authorization_header(self) -> String {
2020-01-18 00:35:30 +00:00
format!("Signature {}", self.into_header())
2019-09-11 05:17:30 +00:00
}
fn into_header(self) -> String {
let mapped = self.created.and_then(|c| self.expires.map(|e| (c, e)));
let header_parts = if let Some((created, expires)) = mapped {
vec![
(KEY_ID_FIELD, self.key_id),
(ALGORITHM_FIELD, ALGORITHM_VALUE.to_owned()),
2022-01-17 22:49:01 +00:00
(CREATED_FIELD, unix_timestamp(created).to_string()),
(EXPIRES_FIELD, unix_timestamp(expires).to_string()),
(HEADERS_FIELD, self.sig_headers.join(" ")),
(SIGNATURE_FIELD, self.signature),
]
} else {
vec![
(KEY_ID_FIELD, self.key_id),
(ALGORITHM_FIELD, ALGORITHM_VALUE.to_owned()),
(HEADERS_FIELD, self.sig_headers.join(" ")),
(SIGNATURE_FIELD, self.signature),
]
};
2019-09-11 05:17:30 +00:00
header_parts
.iter()
.map(|(k, v)| format!("{}=\"{}\"", k, v))
.collect::<Vec<_>>()
.join(",")
}
}
impl Unsigned {
2019-09-21 16:26:11 +00:00
/// Sign the signing string, producing a String that can be used in an HTTP Header
///
/// When using RSA or HMAC to sign the string, be sure to base64-encode the result to produce a
/// String.
2019-09-11 05:17:30 +00:00
pub fn sign<F, E>(self, key_id: String, f: F) -> Result<Signed, E>
where
F: FnOnce(&str) -> Result<String, E>,
2019-09-11 05:17:30 +00:00
{
(f)(&self.signing_string).map(|signature| Signed {
signature,
2019-09-11 05:17:30 +00:00
sig_headers: self.sig_headers,
created: self.created,
expires: self.expires,
key_id,
})
}
}