use std::{collections::HashMap, str::FromStr};
use base64::decode;
use failure::Fail;
use ring::{error::Unspecified, hmac, signature};
use untrusted::Input;
use crate::{
error::{DecodeError, VerificationError},
ShaSize, SignatureAlgorithm, REQUEST_TARGET,
};
const KEY_ID: &str = "keyId";
const HEADERS: &str = "headers";
const ALGORITHM: &str = "algorithm";
const DATE: &str = "date";
const SIGNATURE: &str = "signature";
#[derive(Clone)]
pub struct VerifyKey(Vec<u8>);
impl VerifyKey {
pub fn unchecked_from_slice(s: &[u8]) -> Self {
VerifyKey(s.into())
}
pub fn unchecked_from_vec(v: Vec<u8>) -> Self {
VerifyKey(v)
}
}
pub trait AsVerifyKey {
type Error: Fail;
fn as_verify_key(&self) -> Result<VerifyKey, Self::Error>;
}
#[cfg(feature = "openssl")]
impl AsVerifyKey for openssl::rsa::Rsa<openssl::pkey::Public> {
type Error = openssl::error::ErrorStack;
fn as_verify_key(&self) -> Result<VerifyKey, Self::Error> {
Ok(VerifyKey(self.public_key_to_der_pkcs1()?))
}
}
#[cfg(feature = "openssl")]
impl AsVerifyKey for openssl::rsa::Rsa<openssl::pkey::Private> {
type Error = openssl::error::ErrorStack;
fn as_verify_key(&self) -> Result<VerifyKey, Self::Error> {
Ok(VerifyKey(self.public_key_to_der_pkcs1()?))
}
}
#[derive(Debug)]
pub struct SignedHeader {
pub key_id: String,
header_keys: Vec<String>,
algorithm: SignatureAlgorithm,
signature: Vec<u8>,
}
impl SignedHeader {
pub fn new(s: &str) -> Result<Self, DecodeError> {
s.parse()
}
pub fn verify(
self,
headers: &[(&str, &str)],
method: &str,
path: &str,
query: Option<&str>,
key: VerifyKey,
) -> Result<(), VerificationError> {
let vah = CheckSignedHeader {
auth_header: self,
headers: headers,
method: method,
path: path,
query: query,
};
let key = key.0;
vah.verify(Input::from(&key))
}
}
impl FromStr for SignedHeader {
type Err = DecodeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim_start_matches("Signature ");
let key_value = s
.split(',')
.filter_map(|item| {
let eq_index = item.find('=')?;
let tup = item.split_at(eq_index);
let val = tup.1.get(1..)?;
Some((tup.0, val))
})
.collect::<HashMap<&str, &str>>();
let key_id = key_value
.get(KEY_ID)
.ok_or(DecodeError::MissingKey(KEY_ID))?
.trim_start_matches('"')
.trim_end_matches('"')
.to_owned();
let header_keys = key_value
.get(HEADERS)
.unwrap_or(&DATE)
.trim_start_matches('"')
.trim_end_matches('"')
.split(' ')
.map(String::from)
.collect();
let algorithm = (*key_value
.get(ALGORITHM)
.ok_or(DecodeError::MissingKey(ALGORITHM))?
.trim_start_matches('"')
.trim_end_matches('"'))
.parse()?;
let sig_string: String = key_value
.get(SIGNATURE)
.ok_or(DecodeError::MissingKey(SIGNATURE))?
.trim_start_matches('"')
.trim_end_matches('"')
.into();
let signature = decode(&sig_string).map_err(|_| DecodeError::NotBase64)?;
Ok(SignedHeader {
key_id,
header_keys,
algorithm,
signature,
})
}
}
#[derive(Debug)]
struct CheckSignedHeader<'a> {
auth_header: SignedHeader,
headers: &'a [(&'a str, &'a str)],
method: &'a str,
path: &'a str,
query: Option<&'a str>,
}
impl<'a> CheckSignedHeader<'a> {
pub fn verify(&self, public_key_der: Input) -> Result<(), VerificationError> {
let headers: HashMap<String, Vec<&str>> =
self.headers
.iter()
.fold(HashMap::new(), |mut acc, &(key, value)| {
acc.entry(key.to_lowercase())
.or_insert_with(Vec::new)
.push(value);
acc
});
let mut headers: HashMap<&str, String> = headers
.iter()
.map(|(key, value)| (key.as_ref(), value.join(", ")))
.collect();
headers.insert(
REQUEST_TARGET,
if let Some(query) = self.query {
format!("{} {}?{}", self.method.to_lowercase(), self.path, query,)
} else {
format!("{} {}", self.method.to_lowercase(), self.path,)
},
);
let signing_vec = self.auth_header.header_keys.iter().fold(
(Vec::new(), Vec::new()),
|mut acc, header_key| {
if let Some(header) = headers.get(header_key.to_owned().as_str()) {
acc.0.push(format!("{}: {}", header_key, header));
} else {
acc.1.push(header_key.to_owned());
}
acc
},
);
if !signing_vec.1.is_empty() {
return Err(VerificationError::MissingHeaders(signing_vec.1.join(", ")));
}
let signing_string = signing_vec.0.join("\n");
match self.auth_header.algorithm {
SignatureAlgorithm::RSA(sha_size) => verify_rsa(
public_key_der,
sha_size,
signing_string.as_bytes(),
&self.auth_header.signature,
),
SignatureAlgorithm::HMAC(sha_size) => verify_hmac(
public_key_der,
sha_size,
signing_string.as_bytes(),
&self.auth_header.signature,
),
}
}
}
fn verify_rsa<'a>(
public_key_der: Input,
sha_size: ShaSize,
message: &'a [u8],
signature: &'a [u8],
) -> Result<(), VerificationError> {
signature::verify(
sha_size.verification_algorithm(),
public_key_der,
Input::from(message),
Input::from(signature),
)
.map_err(|Unspecified| VerificationError::BadSignature)?;
Ok(())
}
fn verify_hmac<'a>(
hmac_key: Input,
sha_size: ShaSize,
signing_string: &'a [u8],
sig: &'a [u8],
) -> Result<(), VerificationError> {
let hmac_key = hmac::SigningKey::new(sha_size.hmac_algorithm(), hmac_key.as_slice_less_safe());
hmac::verify_with_own_key(&hmac_key, signing_string, sig)
.map_err(|_| VerificationError::BadSignature)?;
Ok(())
}