use chrono::{DateTime, Utc}; use http::header::{HeaderMap, HeaderName, HeaderValue, InvalidHeaderValue, AUTHORIZATION}; use crate::{ ALGORITHM_FIELD, ALGORITHM_VALUE, CREATED_FIELD, EXPIRES_FIELD, HEADERS_FIELD, KEY_ID_FIELD, SIGNATURE_FIELD, }; const SIGNATURE_HEADER: &'static str = "Signature"; pub struct Signed { signature: String, sig_headers: Vec, created: DateTime, expires: DateTime, key_id: String, } pub struct Unsigned { pub(crate) signing_string: String, pub(crate) sig_headers: Vec, pub(crate) created: DateTime, pub(crate) expires: DateTime, } impl Signed { pub fn signature_header(self, hm: &mut HeaderMap) -> Result<(), InvalidHeaderValue> { hm.insert( AUTHORIZATION, HeaderValue::from_str(&format!("Signature {}", self.into_header()))?, ); Ok(()) } pub fn authorization_header(self, hm: &mut HeaderMap) -> Result<(), InvalidHeaderValue> { hm.insert( HeaderName::from_static(SIGNATURE_HEADER), HeaderValue::from_str(&self.into_header())?, ); Ok(()) } fn into_header(self) -> String { let header_parts = [ (KEY_ID_FIELD, self.key_id), (ALGORITHM_FIELD, ALGORITHM_VALUE.to_owned()), (CREATED_FIELD, self.created.timestamp().to_string()), (EXPIRES_FIELD, self.expires.timestamp().to_string()), (HEADERS_FIELD, self.sig_headers.join(" ")), (SIGNATURE_FIELD, self.signature), ]; header_parts .iter() .map(|(k, v)| format!("{}=\"{}\"", k, v)) .collect::>() .join(",") } } impl Unsigned { pub fn sign(self, key_id: String, f: F) -> Result where F: FnOnce(&str) -> Result, E>, { (f)(&self.signing_string).map(|v| Signed { signature: base64::encode(&v), sig_headers: self.sig_headers, created: self.created, expires: self.expires, key_id, }) } }