diff --git a/README.md b/README.md index d01e5f0e..d01f7aaa 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ impl GetKey for MyKeyGetter { type Key = Cursor>; type Error = ..; - fn get_key(self, _key_id: String) -> Result { + fn get_key(self, _key_id: &str) -> Result { Ok(Cursor::new(self.key.clone())) } } @@ -170,7 +170,7 @@ impl GetKey for MyKeyGetter { type Key = Cursor>; type Error = ..; - fn get_key(self, _key_id: String) -> Result { + fn get_key(self, _key_id: &str) -> Result { Ok(Cursor::new(self.key.clone())) } } diff --git a/examples/hyper_server.rs b/examples/hyper_server.rs index 8883e820..9a549138 100644 --- a/examples/hyper_server.rs +++ b/examples/hyper_server.rs @@ -51,7 +51,7 @@ impl GetKey for MyKeyGetter { type Key = Cursor>; type Error = Error; - fn get_key(self, _key_id: String) -> Result { + fn get_key(self, _key_id: &str) -> Result { Ok(Cursor::new(self.key.clone())) } } diff --git a/examples/rocket.rs b/examples/rocket.rs index f076d88d..de910770 100644 --- a/examples/rocket.rs +++ b/examples/rocket.rs @@ -53,7 +53,7 @@ impl GetKey for MyKeyGetter { type Key = Cursor>; type Error = Error; - fn get_key(self, _key_id: String) -> Result { + fn get_key(self, _key_id: &str) -> Result { Ok(Cursor::new(self.key.clone())) } } diff --git a/src/lib.rs b/src/lib.rs index 4acbf711..4699b746 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,7 @@ // along with HTTP Signatures If not, see . #![feature(try_from)] +#![feature(underscore_lifetimes)] #[cfg(feature = "use_hyper")] extern crate hyper; @@ -44,6 +45,8 @@ pub use create::{AsHttpSignature, WithHttpSignature, HttpSignature, SigningStrin pub use verify::{AuthorizationHeader, VerifyAuthorizationHeader, GetKey}; pub use error::{Error, DecodeError, VerificationError}; +const REQUEST_TARGET: &'static str = "(request-target)"; + #[derive(Debug)] pub enum ShaSize { TwoFiftySix, @@ -103,13 +106,14 @@ mod tests { use std::io::Cursor; use std::fs::File; - use super::HttpSignature; use super::SignatureAlgorithm; - use super::SigningString; - use super::GetKey; - use super::AuthorizationHeader; use super::ShaSize; - use super::VerificationError; + use super::REQUEST_TARGET; + use create::HttpSignature; + use create::SigningString; + use verify::GetKey; + use verify::AuthorizationHeader; + use error::VerificationError; use create::Signature; struct HmacKeyGetter { @@ -120,7 +124,7 @@ mod tests { type Key = Cursor>; type Error = VerificationError; - fn get_key(self, _: String) -> Result { + fn get_key(self, _: &str) -> Result { Ok(Cursor::new(self.key)) } } @@ -133,7 +137,7 @@ mod tests { type Key = File; type Error = VerificationError; - fn get_key(self, _: String) -> Result { + fn get_key(self, _: &str) -> Result { Ok(self.key) } } @@ -187,7 +191,7 @@ mod tests { let mut headers_one: HashMap> = HashMap::new(); headers_one.insert("Accept".into(), vec!["application/json".into()]); headers_one.insert( - "(request-target)".into(), + REQUEST_TARGET.into(), vec![format!("{} {}?{}", method.to_lowercase(), path, query)], ); @@ -204,7 +208,7 @@ mod tests { let signature: Signature = signing_string.try_into().unwrap(); let auth_header = signature.authorization(); - let auth_header = AuthorizationHeader::new(auth_header).unwrap(); + let auth_header = AuthorizationHeader::new(&auth_header).unwrap(); auth_header .verify(&headers_two, method, path, Some(query), key_getter) @@ -224,7 +228,7 @@ mod tests { let mut headers_one: HashMap> = HashMap::new(); headers_one.insert("Accept".into(), vec!["application/json".into()]); headers_one.insert( - "(request-target)".into(), + REQUEST_TARGET.into(), vec![format!("{} {}?{}", method.to_lowercase(), path, query)], ); @@ -240,7 +244,7 @@ mod tests { let signature: Signature = signing_string.try_into().unwrap(); let auth_header = signature.authorization(); - let auth_header = AuthorizationHeader::new(auth_header).unwrap(); + let auth_header = AuthorizationHeader::new(&auth_header).unwrap(); auth_header .verify(&headers_two, method, path, Some(query), key_getter) diff --git a/src/use_hyper_client.rs b/src/use_hyper_client.rs index 814aba24..e2ce82a4 100644 --- a/src/use_hyper_client.rs +++ b/src/use_hyper_client.rs @@ -17,7 +17,7 @@ use std::io::Read; use std::collections::HashMap; use error::Error; -use super::SignatureAlgorithm; +use super::{SignatureAlgorithm, REQUEST_TARGET}; use create::{AsHttpSignature, WithHttpSignature, HttpSignature}; use hyper::Request as HyperRequest; @@ -34,7 +34,7 @@ where ) -> Result, Error> { let mut headers = HashMap::new(); headers.insert( - "(request-target)".into(), + REQUEST_TARGET.into(), vec![ if let Some(ref query) = self.uri().query() { format!( diff --git a/src/use_hyper_server.rs b/src/use_hyper_server.rs index 62c4046d..9ffefc34 100644 --- a/src/use_hyper_server.rs +++ b/src/use_hyper_server.rs @@ -28,17 +28,22 @@ impl VerifyAuthorizationHeader for Request { VerificationError::HeaderNotPresent, )?; - let auth_header = AuthorizationHeader::new(auth_header.clone())?; + let auth_header = AuthorizationHeader::new(auth_header)?; - let headers: Vec<(String, String)> = self.headers() + let headers: Vec<(&str, String)> = self.headers() .iter() .map(|header_view| { - (header_view.name().into(), header_view.value_string()) + (header_view.name(), header_view.value_string()) }) .collect(); + let headers_borrowed: Vec<(&str, &str)> = headers + .iter() + .map(|&(key, ref val)| (key, val.as_ref())) + .collect(); + auth_header.verify( - headers.as_ref(), + headers_borrowed.as_ref(), &self.method().as_ref().to_lowercase(), self.path(), self.query(), diff --git a/src/use_reqwest.rs b/src/use_reqwest.rs index 372ac3fc..7c1326a8 100644 --- a/src/use_reqwest.rs +++ b/src/use_reqwest.rs @@ -17,7 +17,7 @@ use std::io::Read; use std::collections::HashMap; use error::Error; -use super::SignatureAlgorithm; +use super::{SignatureAlgorithm, REQUEST_TARGET}; use create::{AsHttpSignature, WithHttpSignature, HttpSignature}; use reqwest::Request as ReqwestRequest; @@ -34,7 +34,7 @@ where ) -> Result, Error> { let mut headers = HashMap::new(); headers.insert( - "(request-target)".into(), + REQUEST_TARGET.into(), vec![ if let Some(ref query) = self.url().query() { format!( diff --git a/src/use_rocket.rs b/src/use_rocket.rs index 5439f6a7..43e9342b 100644 --- a/src/use_rocket.rs +++ b/src/use_rocket.rs @@ -27,15 +27,20 @@ impl<'r> VerifyAuthorizationHeader for Request<'r> { VerificationError::HeaderNotPresent, )?; - let auth_header = AuthorizationHeader::new(String::from(auth_header))?; + let auth_header = AuthorizationHeader::new(auth_header)?; let headers: Vec<(String, String)> = self.headers() .iter() .map(|header| (header.name().into(), header.value().into())) .collect(); + let headers_borrowed: Vec<(&str, &str)> = headers + .iter() + .map(|&(ref key, ref val)| (key.as_ref(), val.as_ref())) + .collect(); + auth_header.verify( - headers.as_ref(), + headers_borrowed.as_ref(), self.method().as_str(), self.uri().path(), self.uri().query(), diff --git a/src/verify.rs b/src/verify.rs index 7f7a3bfc..20b06e52 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -22,14 +22,20 @@ use ring::error::Unspecified; use base64::decode; use untrusted::Input; -use super::{SignatureAlgorithm, ShaSize}; +use super::{SignatureAlgorithm, ShaSize, REQUEST_TARGET}; use error::{DecodeError, VerificationError}; +const KEY_ID: &'static str = "keyId"; +const HEADERS: &'static str = "headers"; +const ALGORITHM: &'static str = "algorithm"; +const DATE: &'static str = "date"; +const SIGNATURE: &'static str = "signature"; + pub trait GetKey { type Key: Read; type Error; - fn get_key(self, key_id: String) -> Result; + fn get_key(self, key_id: &str) -> Result; } pub trait VerifyAuthorizationHeader { @@ -39,21 +45,21 @@ pub trait VerifyAuthorizationHeader { ) -> Result<(), VerificationError>; } -pub struct AuthorizationHeader { - key_id: String, - header_keys: Vec, +pub struct AuthorizationHeader<'a> { + key_id: &'a str, + header_keys: Vec<&'a str>, algorithm: SignatureAlgorithm, signature: Vec, } -impl AuthorizationHeader { - pub fn new(s: String) -> Result { +impl<'a> AuthorizationHeader<'a> { + pub fn new(s: &'a str) -> Result { s.try_into() } pub fn verify( self, - headers: &[(String, String)], + headers: &[(&str, &str)], method: &str, path: &str, query: Option<&str>, @@ -74,49 +80,44 @@ impl AuthorizationHeader { } } -impl TryFrom for AuthorizationHeader { +impl<'a> TryFrom<&'a str> for AuthorizationHeader<'a> { type Error = DecodeError; - fn try_from(s: String) -> Result { + fn try_from(s: &'a str) -> Result { let s = s.trim_left_matches("Signature: "); let key_value = s.split(',') .filter_map(|item| { - let key_value_slice: Vec<_> = item.split('=').collect(); - - if key_value_slice.len() >= 2 { - Some((key_value_slice[0], key_value_slice[1..].join("="))) - } else { - None - } + let eq_index = item.find("=")?; + let tup = item.split_at(eq_index); + let val = tup.1.get(1..)?; + Some((tup.0, val)) }) - .collect::>(); + .collect::>(); - let key_id = (*key_value - .get("keyId") - .ok_or(DecodeError::MissingKey("keyId"))? - .trim_left_matches("\"") - .trim_right_matches("\"")) - .into(); + let key_id = key_value + .get(KEY_ID) + .ok_or(DecodeError::MissingKey(KEY_ID))? + .trim_left_matches("\"") + .trim_right_matches("\""); let header_keys = key_value - .get("headers") - .unwrap_or(&"date".into()) + .get(HEADERS) + .unwrap_or(&DATE) .trim_left_matches("\"") .trim_right_matches("\"") .split(' ') - .map(|header| header.into()) .collect(); let algorithm = (*key_value - .get("algorithm") - .ok_or(DecodeError::MissingKey("algorithm"))? + .get(ALGORITHM) + .ok_or(DecodeError::MissingKey(ALGORITHM))? .trim_left_matches("\"") .trim_right_matches("\"")) .try_into()?; let sig_string: String = key_value - .get("signature") - .ok_or(DecodeError::MissingKey("signature"))? + .get(SIGNATURE) + .ok_or(DecodeError::MissingKey(SIGNATURE))? .trim_left_matches("\"") .trim_right_matches("\"") .into(); @@ -133,15 +134,15 @@ impl TryFrom for AuthorizationHeader { } pub struct CheckAuthorizationHeader<'a> { - auth_header: AuthorizationHeader, - headers: &'a [(String, String)], + auth_header: AuthorizationHeader<'a>, + headers: &'a [(&'a str, &'a str)], method: &'a str, path: &'a str, query: Option<&'a str>, } impl<'a> CheckAuthorizationHeader<'a> { - pub fn verify(self, key_getter: G) -> Result<(), VerificationError> + pub fn verify(&self, key_getter: G) -> Result<(), VerificationError> where G: GetKey, { @@ -149,23 +150,23 @@ impl<'a> CheckAuthorizationHeader<'a> { VerificationError::GetKey })?; - let headers: HashMap> = + let headers: HashMap> = self.headers.iter().fold(HashMap::new(), |mut acc, &(ref key, ref value)| { - acc.entry(key.clone().to_lowercase()) - .or_insert(Vec::new()) - .push(value.clone()); + acc.entry(key.to_lowercase()).or_insert(Vec::new()).push( + value, + ); acc }); - let mut headers: HashMap = headers + let mut headers: HashMap<&str, String> = headers .iter() - .map(|(key, value)| (key.clone(), value.join(", "))) + .map(|(key, value)| (key.as_ref(), value.join(", "))) .collect(); headers.insert( - "(request-target)".into(), + REQUEST_TARGET.into(), if let Some(ref query) = self.query { format!( "{} {}?{}", @@ -202,7 +203,7 @@ impl<'a> CheckAuthorizationHeader<'a> { let signing_string = signing_vec.0.join("\n"); match self.auth_header.algorithm { - SignatureAlgorithm::RSA(sha_size) => { + SignatureAlgorithm::RSA(ref sha_size) => { Self::verify_rsa( key, sha_size, @@ -210,7 +211,7 @@ impl<'a> CheckAuthorizationHeader<'a> { self.auth_header.signature.as_ref(), ) } - SignatureAlgorithm::HMAC(sha_size) => { + SignatureAlgorithm::HMAC(ref sha_size) => { Self::verify_hmac( key, sha_size, @@ -223,7 +224,7 @@ impl<'a> CheckAuthorizationHeader<'a> { fn verify_rsa( mut key: T, - sha_size: ShaSize, + sha_size: &ShaSize, signing_string: String, sig: &[u8], ) -> Result<(), VerificationError> @@ -239,7 +240,7 @@ impl<'a> CheckAuthorizationHeader<'a> { let message = Input::from(signing_string.as_ref()); let signature = Input::from(sig); - match sha_size { + match *sha_size { ShaSize::TwoFiftySix => { signature::verify( &signature::RSA_PKCS1_2048_8192_SHA256, @@ -271,7 +272,7 @@ impl<'a> CheckAuthorizationHeader<'a> { fn verify_hmac( mut key: T, - sha_size: ShaSize, + sha_size: &ShaSize, signing_string: String, sig: &[u8], ) -> Result<(), VerificationError> @@ -283,7 +284,7 @@ impl<'a> CheckAuthorizationHeader<'a> { |_| VerificationError::ReadKey, )?; let hmac_key = hmac::SigningKey::new( - match sha_size { + match *sha_size { ShaSize::TwoFiftySix => &digest::SHA256, ShaSize::ThreeEightyFour => &digest::SHA384, ShaSize::FiveTwelve => &digest::SHA512,