use std::collections::HashMap;
use std::convert::{TryFrom, TryInto};
use std::sync::Arc;
use std::io::Read;
use ring::{rand, signature, digest, hmac};
use base64::encode;
use untrusted::Input;
use super::{SignatureAlgorithm, ShaSize};
use error::{Error, CreationError};
pub trait AsHttpSignature<T>
where
T: Read,
{
fn as_http_signature(
&self,
key_id: String,
key: T,
algorithm: SignatureAlgorithm,
) -> Result<HttpSignature<T>, Error>;
fn authorization_header(
&self,
key_id: String,
key: T,
algorithm: SignatureAlgorithm,
) -> Result<String, Error> {
Ok(self.as_http_signature(key_id, key, algorithm)?
.authorization_header()?)
}
fn signature_header(
&self,
key_id: String,
key: T,
algorithm: SignatureAlgorithm,
) -> Result<String, Error> {
Ok(self.as_http_signature(key_id, key, algorithm)?
.signature_header()?)
}
}
pub trait WithHttpSignature<T>: AsHttpSignature<T>
where
T: Read,
{
fn with_authorization_header(
&mut self,
key_id: String,
key: T,
algorithm: SignatureAlgorithm,
) -> Result<&mut Self, Error>;
fn with_signature_header(
&mut self,
key_id: String,
key: T,
algorithm: SignatureAlgorithm,
) -> Result<&mut Self, Error>;
}
pub struct HttpSignature<T>
where
T: Read,
{
key_id: String,
key: T,
algorithm: SignatureAlgorithm,
headers: HashMap<String, Vec<String>>,
}
impl<T> HttpSignature<T>
where
T: Read,
{
pub fn new(
key_id: String,
key: T,
algorithm: SignatureAlgorithm,
headers: HashMap<String, Vec<String>>,
) -> Result<Self, CreationError> {
if headers.is_empty() {
return Err(CreationError::NoHeaders);
}
Ok(HttpSignature {
key_id,
key,
algorithm,
headers,
})
}
pub fn key_id(&self) -> &str {
&self.key_id
}
pub fn algorithm(&self) -> &SignatureAlgorithm {
&self.algorithm
}
pub fn headers(&self) -> &HashMap<String, Vec<String>> {
&self.headers
}
pub fn authorization_header(self) -> Result<String, CreationError> {
Ok(self.signature()?.authorization())
}
pub fn signature_header(self) -> Result<String, CreationError> {
Ok(self.signature()?.signature())
}
pub fn signature(self) -> Result<Signature, CreationError> {
let signing_string: SigningString<T> = self.into();
signing_string.try_into()
}
}
impl<T> AsHttpSignature<T> for HttpSignature<T>
where
T: Read + Clone,
{
fn as_http_signature(&self, _: String, _: T, _: SignatureAlgorithm) -> Result<Self, Error> {
Ok(HttpSignature {
key_id: self.key_id.clone(),
key: self.key.clone(),
algorithm: self.algorithm.clone(),
headers: self.headers.clone(),
})
}
}
pub struct SigningString<T> {
key_id: String,
key: T,
headers: Vec<String>,
algorithm: SignatureAlgorithm,
signing_string: String,
}
impl<T> From<HttpSignature<T>> for SigningString<T>
where
T: Read,
{
fn from(http_signature: HttpSignature<T>) -> Self {
let (header_keys, signing_vec): (Vec<_>, Vec<_>) = http_signature
.headers
.iter()
.map(|(header, values)| {
(
header.to_lowercase(),
format!("{}: {}", header.to_lowercase(), values.join(", ")),
)
})
.unzip();
SigningString {
key_id: http_signature.key_id,
key: http_signature.key,
headers: header_keys,
algorithm: http_signature.algorithm,
signing_string: signing_vec.join("\n"),
}
}
}
pub struct Signature {
sig: String,
key_id: String,
headers: Vec<String>,
algorithm: SignatureAlgorithm,
}
impl Signature {
pub fn authorization(self) -> String {
format!("Signature {}", self.header())
}
pub fn signature(self) -> String {
self.header()
}
fn header(self) -> String {
let alg: &str = self.algorithm.into();
format!("Signature keyId=\"{}\",algorithm=\"{}\",headers=\"{}\",signature=\"{}\"",
self.key_id,
alg,
self.headers.join(" "),
self.sig,
)
}
fn rsa<T>(mut key: T, size: &ShaSize, signing_string: &[u8]) -> Result<String, CreationError>
where
T: Read,
{
let mut private_key_der = Vec::new();
key.read_to_end(&mut private_key_der)?;
let private_key_der = Input::from(&private_key_der);
let key_pair = signature::RSAKeyPair::from_der(private_key_der).map_err(
|_| {
CreationError::BadPrivateKey
},
)?;
let key_pair = Arc::new(key_pair);
let mut signing_state = signature::RSASigningState::new(key_pair).map_err(|_| {
CreationError::SigningError
})?;
let rng = rand::SystemRandom::new();
let mut signature = vec![0; signing_state.key_pair().public_modulus_len()];
signing_state
.sign(
match *size {
ShaSize::TwoFiftySix => &signature::RSA_PKCS1_SHA256,
ShaSize::ThreeEightyFour => &signature::RSA_PKCS1_SHA384,
ShaSize::FiveTwelve => &signature::RSA_PKCS1_SHA512,
},
&rng,
signing_string,
signature.as_mut_slice(),
)
.map_err(|_| CreationError::SigningError)?;
Ok(encode(signature.as_slice()))
}
fn hmac<T>(mut key: T, size: &ShaSize, signing_string: &[u8]) -> Result<String, CreationError>
where
T: Read,
{
let mut hmac_key = Vec::new();
key.read_to_end(&mut hmac_key)?;
let hmac_key = hmac::SigningKey::new(
match *size {
ShaSize::TwoFiftySix => &digest::SHA256,
ShaSize::ThreeEightyFour => &digest::SHA384,
ShaSize::FiveTwelve => &digest::SHA512,
},
&hmac_key,
);
let signature = hmac::sign(&hmac_key, signing_string);
Ok(encode(signature.as_ref()))
}
}
impl<T> TryFrom<SigningString<T>> for Signature
where
T: Read,
{
type Error = CreationError;
fn try_from(signing_string: SigningString<T>) -> Result<Self, Self::Error> {
Ok(match signing_string.algorithm {
SignatureAlgorithm::RSA(size) => {
Signature {
sig: Signature::rsa(
signing_string.key,
&size,
signing_string.signing_string.as_ref(),
)?,
key_id: signing_string.key_id,
headers: signing_string.headers,
algorithm: SignatureAlgorithm::RSA(size),
}
}
SignatureAlgorithm::HMAC(size) => {
Signature {
sig: Signature::hmac(
signing_string.key,
&size,
signing_string.signing_string.as_ref(),
)?,
key_id: signing_string.key_id,
headers: signing_string.headers,
algorithm: SignatureAlgorithm::HMAC(size),
}
}
})
}
}