use curl;
use request::HttpRequestVerb;
use rustc_serialize::base64::{STANDARD, ToBase64, URL_SAFE};
use rustc_serialize::json::EncoderError;
use sodium_sys::SSError;
use sodium_sys::crypto::hash::sha2;
use sodium_sys::crypto::utils;
use sodium_sys::crypto::symmetrickey::hmacsha2;
use sodium_sys::crypto::symmetrickey::hmacsha2::Family::*;
use std::default::Default;
use std::fmt;
use time;
use self::EdgeGridAuthError::*;
use self::SAM::*;
static TIME_FMT: &'static str = "%Y%m%dT%H:%M:%S+0000";
#[derive(Debug)]
pub enum EdgeGridAuthError {
Curl(curl::ErrCode),
Json(EncoderError),
ParseError(time::ParseError),
SignError(SSError),
}
impl From<time::ParseError> for EdgeGridAuthError {
fn from(e: time::ParseError) -> EdgeGridAuthError {
ParseError(e)
}
}
impl From<SSError> for EdgeGridAuthError {
fn from(e: SSError) -> EdgeGridAuthError {
SignError(e)
}
}
impl From<curl::ErrCode> for EdgeGridAuthError {
fn from(e: curl::ErrCode) -> EdgeGridAuthError {
Curl(e)
}
}
pub enum SAM {
EG1HMACSHA256,
}
impl Default for SAM {
fn default() -> SAM {
EG1HMACSHA256
}
}
impl fmt::Display for SAM {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let display = match *self {
EG1HMACSHA256 => "EG1-HMAC-SHA256",
};
write!(f, "{}", display)
}
}
pub struct EdgeGridAuth {
name: String,
baseurl: String,
sam: SAM,
client_token: String,
access_token: String,
client_secret: String,
timeout: usize,
}
impl Default for EdgeGridAuth {
fn default() -> EdgeGridAuth {
EdgeGridAuth {
name: String::new(),
baseurl: String::new(),
sam: EG1HMACSHA256,
client_token: String::new(),
access_token: String::new(),
client_secret: String::new(),
timeout: 30000,
}
}
}
impl EdgeGridAuth {
pub fn new(name: &str, baseurl: &str) -> EdgeGridAuth {
EdgeGridAuth {
name: String::from(name),
baseurl: String::from(baseurl),
..Default::default()
}
}
pub fn name(&self) -> &str {
&self.name[..]
}
pub fn baseurl(&self) -> &str {
&self.baseurl[..]
}
pub fn timeout(&self) -> usize {
self.timeout
}
pub fn set_client_token(&mut self, client_token: &str) -> &mut EdgeGridAuth {
self.client_token = String::from(client_token);
self
}
pub fn set_access_token(&mut self, access_token: &str) -> &mut EdgeGridAuth {
self.access_token = String::from(access_token);
self
}
pub fn set_client_secret(&mut self, client_secret: &str) -> &mut EdgeGridAuth {
self.client_secret = String::from(client_secret);
self
}
pub fn set_timeout(&mut self, timeout: usize) -> &mut EdgeGridAuth {
self.timeout = timeout;
self
}
fn signing_key(&self, nowfmt: &time::TmFmt) -> Result<String, EdgeGridAuthError> {
let state_size = hmacsha2::statebytes(SHA256);
let mut state = utils::secmem::malloc(state_size);
let cs = self.client_secret.as_bytes();
let now = format!("{}", nowfmt);
let nowb = now.as_bytes();
let _ = try!(hmacsha2::init(&mut state, cs, SHA256));
let _ = try!(hmacsha2::update(&mut state, nowb, SHA256));
let mac = try!(hmacsha2::finalize(&mut state, SHA256));
Ok(mac.to_base64(STANDARD))
}
fn signature(&self,
signing_key: &str,
data_to_sign: &str)
-> Result<String, EdgeGridAuthError> {
let state_size = hmacsha2::statebytes(SHA256);
let mut state = utils::secmem::malloc(state_size);
let sk = signing_key.as_bytes();
let dts = data_to_sign.as_bytes();
let _ = try!(hmacsha2::init(&mut state, sk, SHA256));
let _ = try!(hmacsha2::update(&mut state, dts, SHA256));
let mac = try!(hmacsha2::finalize(&mut state, SHA256));
Ok(mac.to_base64(STANDARD))
}
fn post_data_hash(&self, post_data: &str) -> Result<String, EdgeGridAuthError> {
let state_size = try!(sha2::state_size_256());
let mut state = utils::secmem::malloc(state_size);
let _ = try!(sha2::init256(&mut state));
let _ = try!(sha2::update256(&mut state, post_data.as_bytes()));
let hash = try!(sha2::finalize256(&mut state));
Ok(hash.to_base64(STANDARD))
}
pub fn auth_header(&self,
reqtype: HttpRequestVerb,
relurl: &str,
post_data: Option<&str>)
-> Result<String, EdgeGridAuthError> {
::init();
let now = time::now_utc();
let nowfmt = try!(now.strftime(TIME_FMT));
let signing_key = &try!(self.signing_key(&nowfmt))[..];
let nonce = utils::nonce::Nonce::new(64);
nonce.activate();
let mut auth_data = format!("{} client_token={};access_token={};timestamp={};nonce={};",
self.sam,
self.client_token,
self.access_token,
nowfmt,
nonce.bytes().to_base64(URL_SAFE));
let data_to_sign = &{
let parts: Vec<&str> = self.baseurl.split("://").collect();
let mut dts = format!("{:?}\t{}\t{}\t{}\t\t", reqtype, parts[0], parts[1], relurl);
if post_data.is_some() {
let post_hash = try!(self.post_data_hash(post_data.unwrap()));
dts.push_str(&post_hash[..]);
}
dts.push_str("\t");
dts.push_str(&auth_data[..]);
dts
}[..];
let signature = try!(self.signature(signing_key, data_to_sign));
auth_data.push_str(&format!("signature={}", signature)[..]);
Ok(auth_data)
}
}