libedgegrid 0.0.1

This library implements an Authentication handler for the Akamai OPEN EdgeGrid Authentication scheme in Rust
use curl;
use rustc_serialize::base64::{ToBase64, STANDARD, 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::RequestVerb::*;
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)
    }
}

#[derive(Debug)]
pub enum RequestVerb {
    GET,
    POST
}

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: RequestVerb,
        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)
    }
}