<commit_before>extern crate time;
extern crate curl;
extern crate rustc_serialize;
extern crate openssl;
extern crate url;
#[macro_use]
extern crate log;

use url::Url;
use std::str;
use std::io::prelude::*;

use curl::easy::{Easy, List};

use openssl::crypto::{hmac, hash};
use rustc_serialize::base64::{ToBase64, STANDARD};
use rustc_serialize::hex::ToHex;

pub const LONG_DATE: &'static str = "%Y%m%dT%H%M%SZ";
const SHORT_DATE: &'static str = "%Y%m%d";
const AMZ_EXPIRE: &'static str = "604800";
const AMZ_ALGO: &'static str = "AWS4-HMAC-SHA256";
const AMZ_REGION: &'static str = "eu-west-1";
const AMZ_SERVICE: &'static str = "s3";
const AMZ_PAYLOAD: &'static str = "UNSIGNED-PAYLOAD";
const AMZ_REQ_VER: &'static str = "aws4_request";

pub struct Bucket {
    name: String,
    region: Option<String>,
    access_key: String,
    secret_key: String,
    proto: String,
}

macro_rules! build_headers {
    ($list:ident, $($x:expr),+) => (
      $($list.append($x).unwrap())+
    )
}

macro_rules! unwrap_get {
  ($get:ident) => (
      match $get {
      Some(x) => {
        if x.len() > 0 {
          x
        } else {
          panic!("These are not the droids you are looking for.")
        }
      },
      None => unreachable!()
    }
  )
}

macro_rules! headers {
  ($transfer:ident, $headers:ident) => (
    $transfer.header_function(|header| {
      $headers.push(str::from_utf8(header).unwrap().to_string());
      true
    }).unwrap();
  )
}

enum Command<'a> {
  Put {
    content: &'a [u8]
  },
  Get,
  List {
    prefix: &'a str,
    delimiter: &'a str
  }
}

impl Bucket {
  pub fn new(name: String,
             region: Option<String>,
             access_key: String,
             secret_key: String,
             proto: &str) -> Bucket {
      Bucket {
          name: name,
          region: region,
          access_key: access_key,
          secret_key: secret_key,
          proto: proto.to_string(),
      }
  }

  fn execute(&self,
                 cmd: Command,
                 path: &str ) -> (String, Option<Vec<u8>>) {

    let content_type = "text/plain";

    let path = if path.starts_with("/") {&path[1..]} else {path};
    debug!("s3_path: {}", path);

    let host = self.host();
    debug!("host: {}", host);

    let date = time::now_utc().rfc822z().to_string();
    debug!("date: {}", date);

    let cmd_string = match cmd {
      Command::Put { content } => "PUT",
      _ => "GET"
    };

    debug!("command: {}", cmd_string);
    let a = self.auth(&cmd_string, &date, path, "", &content_type);
    debug!("auth: {}", a);

    // TODO implement delimiter into request for List
    let url = match cmd {
      Command::List { prefix, delimiter } => format!("{}://{}/?prefix={}", self.proto, host, prefix),
      _ => format!("{}://{}/{}", self.proto, host, path)
    };
    debug!("url: {}", url);

    let mut handle = Easy::new();
    let _ = handle.url(&url[..]);

    let mut list = List::new();

    let l = match cmd {
      Command::Put { content } => content.len(),
      _ => 0
    };

    build_headers!(list,
            &format!("Host: {}", &host),
            &format!("Date: {}", &date),
            &format!("Authorization: {}", &a),
            &format!("Content-Type: {}", &content_type),
            &format!("Content-Length: {}", l));

    for l in list.iter(){
      debug!("{:?}", String::from_utf8_lossy(l));
    }

    match cmd {
      Command::Put { content } => {
        handle.put(true).unwrap();
        let _ = handle.post_field_size(l as u64);
      }
      _ => {}
    }

    handle.http_headers(list).unwrap();

    let result = match cmd {
                  Command::Put { content } => self.put(&mut handle, content),
                  Command::Get => self.get(&mut handle),
                  Command::List { delimiter, prefix }=> self.list(&mut handle)
                };
    (url, result)
  }

  pub fn put(&self, handle: &mut Easy, mut content: &[u8]) -> Option<Vec<u8>> {
    let mut headers = Vec::new();
    {
      let mut transfer = handle.transfer();

      transfer.read_function(|buf| {
          Ok(content.read(buf).unwrap())
      }).unwrap();

      headers!(transfer, headers);

      transfer.perform().unwrap();
    }
    debug!("recieved headers: {:?}", headers);
    debug!("response: {}", handle.response_code().unwrap());
    None
  }

  pub fn get(&self, handle: &mut Easy) -> Option<Vec<u8>> {
    let mut headers = Vec::new();
    let mut dst = Vec::new();

    {
      let mut transfer = handle.transfer();
      transfer.write_function(|data| {
          dst.extend_from_slice(data);
          Ok(data.len())
      }).unwrap();

      headers!(transfer, headers);

      transfer.perform().unwrap();
    }
    debug!("recieved headers: {:?}", headers);
    Some(dst)
  }

  pub fn list(&self, handle: &mut Easy) -> Option<Vec<u8>> {
    let mut dst = Vec::new();
    let mut headers = Vec::new();
    {
      let mut transfer = handle.transfer();
      transfer.write_function(|data| {
          dst.extend_from_slice(data);
          Ok(data.len())
      }).unwrap();

      headers!(transfer, headers);

      transfer.perform().unwrap();
    }
    Some(dst)
  }

    // TODO implement DELETE Command

    // pub fn delete<'a, 'b>(&self, handle: &'a mut http::Handle, path: &str)
    //                       -> http::Request<'a, 'b> {
    //     let path = if path.starts_with("/") {&path[1..]} else {path};
    //     let host = self.host();
    //     let date = time::now().rfc822z().to_string();
    //     let auth = self.auth("DELETE", &date, path, "", "");
    //     let url = format!("{}://{}/{}", self.proto, host, path);
    //     handle.delete(&url[..])
    //           .header("Host", &host)
    //           .header("Date", &date)
    //           .header("Authorization", &auth)
    // }

  pub fn host(&self) -> String {
      format!("{}.s3{}.amazonaws.com", self.name,
              match self.region {
                  Some(ref r) => format!("-{}", r),
                  None => String::new(),
              })
  }

  fn auth(&self, verb: &str, date: &str, path: &str,
          md5: &str, content_type: &str) -> String {
      let string = format!("{verb}\n{md5}\n{ty}\n{date}\n{headers}{resource}",
                           verb = verb,
                           md5 = md5,
                           ty = content_type,
                           date = date,
                           headers = "",
                           resource = format!("/{}/{}", self.name, path));
      let signature = {
          let mut hmac = hmac::HMAC::new(hash::Type::SHA1, self.secret_key.as_bytes());
          let _ = hmac.write_all(string.as_bytes());
          hmac.finish().to_base64(STANDARD)
      };
      format!("AWS {}:{}", self.access_key, signature)
  }
}

pub fn get_s3(bucket: &Bucket, s3_path: Option<&str>) -> Vec<u8> {
      let path = match s3_path {
        Some(x) => x,
        None => "/"
      };
      let (_, result) = bucket.execute(Command::Get, path);
      unwrap_get!(result)
    }

// TODO prefix + delimiter support, default delimiter is / ATM
pub fn list_s3(bucket: &Bucket, path: &str, prefix: &str, delimiter: &str) -> Vec<u8> {
      let (_, result) = bucket.execute(Command::List { prefix: prefix, delimiter: delimiter }, path);
      unwrap_get!(result)
    }

fn sign(key: &[u8], msg: &[u8]) -> Vec<u8> {
    let mut hmac = hmac::HMAC::new(hash::Type::SHA256, key);
    let _ = hmac.write_all(msg);
    hmac.finish()
  }

fn get_signature_key(key: &str, date_stamp: &str, region_name: &str, service_name: &str) -> Vec<u8> {
  let kdate = sign(format!("AWS4{}", key).as_bytes(), date_stamp.as_bytes());
  let kregion = sign(kdate.as_slice(), region_name.as_bytes());
  let kservice = sign(kregion.as_slice(), service_name.as_bytes());
  sign(&kservice, AMZ_REQ_VER.as_bytes())
}

pub fn put_s3(bucket: &Bucket,
              s3_path: &str,
              output: &[u8]) -> String {

  let (url, _) = bucket.execute(Command::Put { content: output }, &s3_path);
  let t = time::now();
  let method = &"GET";
  let amzdate = match t.strftime(LONG_DATE) {
    Ok(x) => x.to_string(),
    Err(e) => panic!("{:?}", e)
  };
  let datestamp = match t.strftime(SHORT_DATE) {
    Ok(x) => x.to_string(),
    Err(e) => panic!("{:?}", e)
  };
  let url: Url = match url.parse() {
    Ok(x) => x,
    Err(e) => panic!("{:?}", e)
  };
  let canonical_uri = &url.path();
  let host = match url.host_str() {
    Some(x) => x,
    None => panic!("Unable to extract host string from url: {:?}", url)
  };
  let endpoint = format!("https://{}", host);
  let canonical_headers = format!("host:{}\n", host);
  let signed_headers = &"host";
  let payload_hash = AMZ_PAYLOAD;
  let credential_scope = format!("{}/{}/{}/{}",
                                 datestamp,
                                 AMZ_REGION,
                                 AMZ_SERVICE,
                                 AMZ_REQ_VER);
  let canonical_querystring = format!("{}={}&{}={}%2F{}&{}={}&{}={}&{}={}",
                                &"X-Amz-Algorithm", AMZ_ALGO,
                                &"X-Amz-Credential", bucket.access_key, credential_scope.replace("/", "%2F"),
                                &"X-Amz-Date", &amzdate,
                                &"X-Amz-Expires", AMZ_EXPIRE,
                                &"X-Amz-SignedHeaders", signed_headers);
  let canonical_request = format!("{}\n{}\n{}\n{}\n{}\n{}",
                                  method,
                                  &canonical_uri,
                                  canonical_querystring,
                                  canonical_headers,
                                  signed_headers,
                                  payload_hash);
  let string_to_sign = format!("{}\n{}\n{}\n{}",
                           AMZ_ALGO,
                           amzdate,
                           credential_scope,
                           hash::hash(hash::Type::SHA256, canonical_request.as_bytes()).to_hex());
  let signing_key = get_signature_key(&bucket.secret_key, &datestamp, AMZ_REGION, AMZ_SERVICE);
  let signature = sign(&signing_key, &string_to_sign.as_bytes()).to_hex();
  let canonical_querystring = format!("{}&{}={}",
                                      canonical_querystring,
                                      &"X-Amz-Signature", signature);
  format!("{}{}?{}",
                endpoint,
                canonical_uri,
                canonical_querystring)
}

<commit_msg>More friendly configuration of params<commit_after>extern crate time;
extern crate curl;
extern crate rustc_serialize;
extern crate openssl;
extern crate url;
#[macro_use]
extern crate log;

use url::Url;
use std::str;
use std::io::prelude::*;

use curl::easy::{Easy, List};

use openssl::crypto::{hmac, hash};
use rustc_serialize::base64::{ToBase64, STANDARD};
use rustc_serialize::hex::ToHex;

pub struct Link {
  long_date: String,
  short_date: String,
  amz_expire: String,
  amz_algo: String,
  amz_region: String,
  amz_service: String,
  amz_payload: String,
  amz_req_ver: String,
  protocol: String
}

macro_rules! get_set {
  ($x:ident, $name:ident) => {
    /// Set `$x` field for 'Link'
    pub fn $name(&mut self, value: &str) {
      self.$x = value.to_string()
    }
    /// Get `$x` field for 'Link'
    pub fn $x(&self) -> &str {
      &self.$x
    }
  }
}

macro_rules! link_get_set {
  ($x:ident, $name:ident) => {
    /// Set `$x` field for 'Link' for 'Bucket'
    pub fn $name(&mut self, value: &str) {
      self.mut_link().$x = value.to_string()
    }

    /// Get `$x` field for 'Link' for 'Bucket'
    pub fn $x(&self) -> &str {
      &self.link().$x
    }
  }
}

impl Link {
  pub fn default() -> Link {
    Link {
      long_date: "%Y%m%dT%H%M%SZ".to_string(),
      short_date: "%Y%m%d".to_string(),
      amz_expire: "604800".to_string(),
      amz_algo: "AWS4-HMAC-SHA256".to_string(),
      amz_region: "eu-west-1".to_string(),
      amz_service: "s3".to_string(),
      amz_payload: "UNSIGNED-PAYLOAD".to_string(),
      amz_req_ver: "aws4_request".to_string(),
      protocol: "https".to_string()
    }
  }

  pub fn new(long_date: &str,
             short_date: &str,
             amz_expire: &str,
             amz_algo: &str,
             amz_region: &str,
             amz_service: &str,
             amz_payload: &str,
             amz_req_ver: &str,
             protocol: &str) -> Link {
    Link {
      long_date: long_date.to_string(),
      short_date: short_date.to_string(),
      amz_expire: amz_expire.to_string(),
      amz_algo: amz_algo.to_string(),
      amz_region: amz_region.to_string(),
      amz_service: amz_service.to_string(),
      amz_payload: amz_payload.to_string(),
      amz_req_ver: amz_req_ver.to_string(),
      protocol: protocol.to_string()
    }
  }

  get_set!(long_date, set_long_date);
  get_set!(short_date, set_short_date);
  get_set!(amz_expire, set_amz_expire);
  get_set!(amz_algo, set_amz_algo);
  get_set!(amz_region, set_amz_region);
  get_set!(amz_service, set_amz_service);
  get_set!(amz_payload, set_amz_payload);
  get_set!(amz_req_ver, set_amz_req_ver);
  get_set!(protocol, set_protocol);
}

macro_rules! build_headers {
  ($list:ident, $($x:expr),+) => (
    $($list.append($x).unwrap())+
  )
}

macro_rules! unwrap_get {
  ($get:ident) => (
    match $get {
      Some(x) => {
        if x.len() > 0 {
          x
        } else {
          panic!("These are not the droids you are looking for.")
        }
      },
      None => unreachable!()
    }
  )
}

macro_rules! headers {
  ($transfer:ident, $headers:ident) => (
    $transfer.header_function(|header| {
      $headers.push(str::from_utf8(header).unwrap().to_string());
      true
    }).unwrap();
  )
}

enum Command<'a> {
  Put {
    content: &'a [u8]
  },
  Get,
  List {
    prefix: &'a str,
    delimiter: &'a str
  }
}

/// Bucket object for holding info about an S3 bucket
///
/// # Example
/// ```
/// let bucket = Bucket::new(
///               S3_BUCKET.to_string(),
///               None,
///               AWS_ACCESS.to_string(),
///               AWS_SECRET.to_string(),
///               None);
/// ```
pub struct Bucket {
    name: String,
    region: Option<String>,
    access_key: String,
    secret_key: String,
    link: Link
}

impl Bucket {
  pub fn new(name: String,
             region: Option<String>,
             access_key: String,
             secret_key: String,
             link: Option<Link>) -> Bucket {
      Bucket {
          name: name,
          region: region,
          access_key: access_key,
          secret_key: secret_key,
          link: match link {
                  Some(x) => x,
                  None => Link::default()
                }
      }
  }

  pub fn link(&self) -> &Link {
    &self.link
  }

  pub fn mut_link(&mut self) -> &mut Link {
    &mut self.link
  }

  pub fn set_link(&mut self, link: Link) {
    self.link = link
  }

  link_get_set!(long_date, set_long_date);
  link_get_set!(short_date, set_short_date);
  link_get_set!(amz_expire, set_amz_expire);
  link_get_set!(amz_algo, set_amz_algo);
  link_get_set!(amz_region, set_amz_region);
  link_get_set!(amz_service, set_amz_service);
  link_get_set!(amz_payload, set_amz_payload);
  link_get_set!(amz_req_ver, set_amz_req_ver);
  link_get_set!(protocol, set_protocol);

  fn execute(&self,
               cmd: Command,
               path: &str ) -> (String, Option<Vec<u8>>) {

    let content_type = "text/plain";

    let path = if path.starts_with("/") {&path[1..]} else {path};
    debug!("s3_path: {}", path);

    let host = self.host();
    debug!("host: {}", host);

    let date = time::now_utc().rfc822z().to_string();
    debug!("date: {}", date);

    let cmd_string = match cmd {
      Command::Put { content } => "PUT",
      _ => "GET"
    };

    debug!("command: {}", cmd_string);
    let a = self.auth(&cmd_string, &date, path, "", &content_type);
    debug!("auth: {}", a);

    // TODO implement delimiter into request for List
    let url = match cmd {
      Command::List { prefix, delimiter } => format!("{}://{}/?prefix={}", &self.link.protocol, host, prefix),
      _ => format!("{}://{}/{}", &self.link.protocol, host, path)
    };
    debug!("url: {}", url);

    let mut handle = Easy::new();
    let _ = handle.url(&url[..]);

    let mut list = List::new();

    let l = match cmd {
      Command::Put { content } => content.len(),
      _ => 0
    };

    build_headers!(list,
            &format!("Host: {}", &host),
            &format!("Date: {}", &date),
            &format!("Authorization: {}", &a),
            &format!("Content-Type: {}", &content_type),
            &format!("Content-Length: {}", l));

    for l in list.iter(){
      debug!("{:?}", String::from_utf8_lossy(l));
    }

    match cmd {
      Command::Put { content } => {
        handle.put(true).unwrap();
        let _ = handle.post_field_size(l as u64);
      }
      _ => {}
    }

    handle.http_headers(list).unwrap();

    let result = match cmd {
                  Command::Put { content } => self.put(&mut handle, content),
                  Command::Get => self.get(&mut handle),
                  Command::List { delimiter, prefix }=> self.list(&mut handle)
                };
    (url, result)
  }

  pub fn put(&self, handle: &mut Easy, mut content: &[u8]) -> Option<Vec<u8>> {
    let mut headers = Vec::new();
    {
      let mut transfer = handle.transfer();

      transfer.read_function(|buf| {
          Ok(content.read(buf).unwrap())
      }).unwrap();

      headers!(transfer, headers);

      transfer.perform().unwrap();
    }
    debug!("recieved headers: {:?}", headers);
    debug!("response: {}", handle.response_code().unwrap());
    None
  }

  pub fn get(&self, handle: &mut Easy) -> Option<Vec<u8>> {
    let mut headers = Vec::new();
    let mut dst = Vec::new();

    {
      let mut transfer = handle.transfer();
      transfer.write_function(|data| {
          dst.extend_from_slice(data);
          Ok(data.len())
      }).unwrap();

      headers!(transfer, headers);

      transfer.perform().unwrap();
    }
    debug!("recieved headers: {:?}", headers);
    Some(dst)
  }

  pub fn list(&self, handle: &mut Easy) -> Option<Vec<u8>> {
    let mut dst = Vec::new();
    let mut headers = Vec::new();
    {
      let mut transfer = handle.transfer();
      transfer.write_function(|data| {
          dst.extend_from_slice(data);
          Ok(data.len())
      }).unwrap();

      headers!(transfer, headers);

      transfer.perform().unwrap();
    }
    Some(dst)
  }

    // TODO implement DELETE Command

    // pub fn delete<'a, 'b>(&self, handle: &'a mut http::Handle, path: &str)
    //                       -> http::Request<'a, 'b> {
    //     let path = if path.starts_with("/") {&path[1..]} else {path};
    //     let host = self.host();
    //     let date = time::now().rfc822z().to_string();
    //     let auth = self.auth("DELETE", &date, path, "", "");
    //     let url = format!("{}://{}/{}", self.proto, host, path);
    //     handle.delete(&url[..])
    //           .header("Host", &host)
    //           .header("Date", &date)
    //           .header("Authorization", &auth)
    // }

  pub fn host(&self) -> String {
      format!("{}.s3{}.amazonaws.com", self.name,
              match self.region {
                  Some(ref r) => format!("-{}", r),
                  None => String::new(),
              })
  }

  fn auth(&self, verb: &str, date: &str, path: &str,
          md5: &str, content_type: &str) -> String {
      let string = format!("{verb}\n{md5}\n{ty}\n{date}\n{headers}{resource}",
                           verb = verb,
                           md5 = md5,
                           ty = content_type,
                           date = date,
                           headers = "",
                           resource = format!("/{}/{}", self.name, path));
      let signature = {
          let mut hmac = hmac::HMAC::new(hash::Type::SHA1, self.secret_key.as_bytes());
          let _ = hmac.write_all(string.as_bytes());
          hmac.finish().to_base64(STANDARD)
      };
      format!("AWS {}:{}", self.access_key, signature)
  }
}

/// Gets file from an S3 path
///
/// # Example:
///
/// ```
/// let path = &"test_file";
/// let mut buffer = match File::create(path) {
///           Ok(x) => x,
///           Err(e) => panic!("{:?}, {}", e, path)
///         };
/// let bytes = get_s3(&bucket, Some(&path));
/// match buffer.write(&bytes) {
///   Ok(_) => {} // info!("Written {} bytes from {}", x, path),
///   Err(e) => panic!("{:?}", e)
/// }
/// ```
pub fn get_s3(bucket: &Bucket, s3_path: Option<&str>) -> Vec<u8> {
      let path = match s3_path {
        Some(x) => x,
        None => "/"
      };
      let (_, result) = bucket.execute(Command::Get, path);
      unwrap_get!(result)
    }

/// List contents of an S3 bucket, `prefix` and `delimiter` are placeholders for now
///
/// # Example
///
/// ```
/// let bytes = list_s3(&bucket,
///                       &"/",
///                       &"/",
///                       &"/");
/// let string = String::from_utf8_lossy(&bytes);
/// println!("{}", string);
/// ```
pub fn list_s3(bucket: &Bucket, path: &str, prefix: &str, delimiter: &str) -> Vec<u8> {
      // TODO prefix + delimiter support, default delimiter is / ATM
      let (_, result) = bucket.execute(Command::List {
                                          prefix: prefix,
                                          delimiter: delimiter
                                        },
                                        path);
      unwrap_get!(result)
    }

fn sign(key: &[u8], msg: &[u8]) -> Vec<u8> {
    let mut hmac = hmac::HMAC::new(hash::Type::SHA256, key);
    let _ = hmac.write_all(msg);
    hmac.finish()
  }

fn get_signature_key(key: &str, date_stamp: &str, region_name: &str, service_name: &str, version: &str) -> Vec<u8> {
  let kdate = sign(format!("AWS4{}", key).as_bytes(), date_stamp.as_bytes());
  let kregion = sign(kdate.as_slice(), region_name.as_bytes());
  let kservice = sign(kregion.as_slice(), service_name.as_bytes());
  sign(&kservice, version.as_bytes())
}

/// Put into an S3 bucket, get a preauthorized link back.
///
/// # Example
///
/// ```
/// let put_me = "I want to go to S3".to_string();
/// let url = put_s3(&bucket,
///                 &"/",
///                 &put_me.as_bytes());
/// println!("{}", url);
/// ```
pub fn put_s3(bucket: &Bucket,
              s3_path: &str,
              output: &[u8]) -> String {

  let (url, _) = bucket.execute(Command::Put { content: output }, &s3_path);
  let t = time::now();
  let method = &"GET";
  let amzdate = match t.strftime(&bucket.link.long_date) {
    Ok(x) => x.to_string(),
    Err(e) => panic!("{:?}", e)
  };
  let datestamp = match t.strftime(&bucket.link.short_date) {
    Ok(x) => x.to_string(),
    Err(e) => panic!("{:?}", e)
  };
  let url: Url = match url.parse() {
    Ok(x) => x,
    Err(e) => panic!("{:?}", e)
  };
  let canonical_uri = &url.path();
  let host = match url.host_str() {
    Some(x) => x,
    None => panic!("Unable to extract host string from url: {:?}", url)
  };
  let endpoint = format!("https://{}", host);
  let canonical_headers = format!("host:{}\n", host);
  let signed_headers = &"host";
  let payload_hash = &bucket.link.amz_payload;
  let credential_scope = format!("{}/{}/{}/{}",
                                 datestamp,
                                 &bucket.link.amz_region,
                                 &bucket.link.amz_service,
                                 &bucket.link.amz_req_ver);
  let canonical_querystring = format!("{}={}&{}={}%2F{}&{}={}&{}={}&{}={}",
                                &"X-Amz-Algorithm", &bucket.link.amz_algo,
                                &"X-Amz-Credential", bucket.access_key, credential_scope.replace("/", "%2F"),
                                &"X-Amz-Date", &amzdate,
                                &"X-Amz-Expires", bucket.link.amz_expire,
                                &"X-Amz-SignedHeaders", signed_headers);
  let canonical_request = format!("{}\n{}\n{}\n{}\n{}\n{}",
                                  method,
                                  &canonical_uri,
                                  canonical_querystring,
                                  canonical_headers,
                                  signed_headers,
                                  payload_hash);
  let string_to_sign = format!("{}\n{}\n{}\n{}",
                           bucket.link.amz_algo,
                           amzdate,
                           credential_scope,
                           hash::hash(hash::Type::SHA256, canonical_request.as_bytes()).to_hex());
  let signing_key = get_signature_key(&bucket.secret_key,
                                      &datestamp,
                                      &bucket.link.amz_region,
                                      &bucket.link.amz_service,
                                      &bucket.link.amz_req_ver);
  let signature = sign(&signing_key, &string_to_sign.as_bytes()).to_hex();
  let canonical_querystring = format!("{}&{}={}",
                                      canonical_querystring,
                                      &"X-Amz-Signature", signature);
  format!("{}{}?{}",
                endpoint,
                canonical_uri,
                canonical_querystring)
}

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2017 René Richter,
 * Copyright (c) 2015-2017 Alex Maslakov, <http://gildedhonour.com>, <http://alexmaslakov.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * For questions and comments about this product, please see the project page at:
 *
 * https://github.com/Richterrettich/rusty_jwt
 *
 * This was forked from
 * https://github.com/GildedHonour/frank_jwt
 *
 */

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;
extern crate time;
extern crate openssl;
extern crate base64;

use std::collections::BTreeMap;

use openssl::hash::MessageDigest;
use openssl::pkey::PKey;
use openssl::rsa::Rsa;
use openssl::sign::Signer;
use openssl::sign::Verifier;
use openssl::ec::EcKey;

pub type Payload = BTreeMap<String, String>;

#[derive(Serialize, Deserialize)]
pub struct Header {
    alg: Algorithm,
    typ: String,
}

impl Header {
    pub fn new(alg: Algorithm) -> Header {
        Header {
            alg: alg,
            typ: String::from("JWT"),
        }
    }
}

#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Debug)]
pub enum Algorithm {
    HS256,
    HS384,
    HS512,
    RS256,
    RS384,
    RS512,
    ES256,
    ES384,
    ES512,
}

impl Algorithm {
    fn get_hash(&self) -> MessageDigest {
        match *self {
            Algorithm::HS256 | Algorithm::RS256 | Algorithm::ES256 => MessageDigest::sha256(),
            Algorithm::HS384 | Algorithm::RS384 | Algorithm::ES384 => MessageDigest::sha384(),
            Algorithm::HS512 | Algorithm::RS512 | Algorithm::ES512 => MessageDigest::sha512(),
        }
    }
}

#[derive(Debug)]
pub enum Error {
    SignatureExpired,
    SignatureInvalid,
    JWTInvalid,
    IssuerInvalid,
    ExpirationInvalid,
    AudienceInvalid,
}

pub fn encode<T: ?Sized + AsRef<[u8]>>(header: Header, key: &T, payload: Payload) -> String {
    let signing_input = get_signing_input(payload, &header.alg);
    let kr = key.as_ref();
    let pkey = match header.alg {
        Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => PKey::hmac(kr).unwrap(),
        Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 => rsa_key(kr),
        Algorithm::ES256 | Algorithm::ES384 | Algorithm::ES512 => ec_key(kr),
    };
    let signature = sign_and_encode(&signing_input, pkey, header.alg.get_hash());
    format!("{}.{}", signing_input, signature)
}

pub fn decode<T, E>(
    encoded_token: E,
    key: &T,
    algorithm: Algorithm,
) -> Result<(Header, Payload), Error>
where
    T: ?Sized + AsRef<[u8]>,
    E: AsRef<str>,
{
    match decode_segments(encoded_token.as_ref()) {
        Some((header, payload, signature, signing_input)) => {
            if !verify_signature(algorithm, signing_input, &signature, key.as_ref()) {
                return Err(Error::SignatureInvalid);
            }
            Ok((header, payload))
        }

        None => Err(Error::JWTInvalid),
    }
}

static SEGMENTS_COUNT: usize = 3;

fn get_signing_input(payload: Payload, algorithm: &Algorithm) -> String {
    let header = Header::new(*algorithm);
    let header_json_str =
        serde_json::to_string(&header).expect("could not convert header to json.");
    let encoded_header = base64::encode_config(header_json_str.as_bytes(), base64::URL_SAFE_NO_PAD)
        .to_string();
    let payload_json = serde_json::to_string(&payload).expect("could not convert payload to json");
    let encoded_payload = base64::encode_config(payload_json.as_bytes(), base64::URL_SAFE_NO_PAD)
        .to_string();
    format!("{}.{}", encoded_header, encoded_payload)
}

fn rsa_key(raw_key: &[u8]) -> PKey {
    let rsa = Rsa::private_key_from_pem(raw_key).unwrap();
    PKey::from_rsa(rsa).unwrap()
}

fn ec_key(raw_key: &[u8]) -> PKey {
    let ec_key =
        EcKey::private_key_from_pem(&raw_key).expect("could not convert to EC private key");
    PKey::from_ec_key(ec_key).expect("could not convert EC private key")
}

fn sign(data: &str, private_key: PKey, digest: MessageDigest) -> Vec<u8> {
    let mut signer = Signer::new(digest, &private_key).unwrap();
    signer.update(data.as_bytes()).unwrap();
    signer.finish().unwrap()
}

fn sign_and_encode(data: &str, private_key: PKey, digest: MessageDigest) -> String {
    let raw = sign(data, private_key, digest);
    base64::encode_config(&raw, base64::URL_SAFE_NO_PAD)
}

fn decode_segments(encoded_token: &str) -> Option<(Header, Payload, Vec<u8>, String)> {
    let raw_segments: Vec<&str> = encoded_token.split(".").collect();
    if raw_segments.len() != SEGMENTS_COUNT {
        return None;
    }

    let header_segment = raw_segments[0];
    let payload_segment = raw_segments[1];
    let crypto_segment = raw_segments[2];
    let (header, payload) = decode_header_and_payload(header_segment, payload_segment);
    let signature = base64::decode_config(crypto_segment, base64::URL_SAFE_NO_PAD)
        .expect("could not decoding base64 signature");
    let signing_input = format!("{}.{}", header_segment, payload_segment);
    Some((header, payload, signature.clone(), signing_input))
}

fn decode_header_and_payload(header_segment: &str, payload_segment: &str) -> (Header, Payload) {
    let headder_bytes = base64::decode(header_segment).expect("could not decoding base64 header");
    let header: Header =
        serde_json::from_slice(&headder_bytes[..]).expect("could not convert header to json");

    let payload_bytes = base64::decode(payload_segment).expect("could not decoding base64 payload");
    let payload: Payload =
        serde_json::from_slice(&payload_bytes[..]).expect("could not convert header to json");
    (header, payload)
}

fn verify_signature(
    algorithm: Algorithm,
    signing_input: String,
    signature: &[u8],
    public_key: &[u8],
) -> bool {
    match algorithm {

        Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => {
            let key = PKey::hmac(public_key).unwrap();
            let signature2 = sign(&signing_input, key, algorithm.get_hash());
            secure_compare(signature, &signature2)
        }
        _ => {
            let key = PKey::public_key_from_pem(public_key).expect("could not convert pem to pkey");
            let mut verifier = Verifier::new(algorithm.get_hash(), &key).unwrap();
            verifier.update(signing_input.as_bytes()).unwrap();
            verifier.finish(&signature).unwrap()
        }
    }
}

fn secure_compare(a: &[u8], b: &[u8]) -> bool {
    if a.len() != b.len() {
        return false;
    }

    let mut res = 0_u8;
    for (&x, &y) in a.iter().zip(b.iter()) {
        res |= x ^ y;
    }

    res == 0
}

#[cfg(test)]
mod tests {
    extern crate time;

    use super::Header;
    use super::Payload;
    use super::encode;
    use super::decode;
    use super::Algorithm;
    use super::secure_compare;
    use std::env;

    #[test]
    fn test_encode_and_decode_jwt_hs256() {
        let mut p1 = Payload::new();
        p1.insert("key1".to_string(), "val1".to_string());
        p1.insert("key2".to_string(), "val2".to_string());
        p1.insert("key3".to_string(), "val3".to_string());

        let secret = "secret123";
        let header = Header::new(Algorithm::HS256);
        let jwt1 = encode(header, secret, p1.clone());
        let maybe_res = decode(jwt1.trim(), secret, Algorithm::HS256);
        assert!(maybe_res.is_ok());
    }

    static HS256_JWT: &'static str = include_str!("../test/valid_hs256.jwt");
    #[test]
    fn test_decode_valid_jwt_hs256() {
        let mut p1 = Payload::new();
        p1.insert("key11".to_string(), "val1".to_string());
        p1.insert("key22".to_string(), "val2".to_string());
        let secret = "secret123";
        let maybe_res = decode(HS256_JWT, secret, Algorithm::HS256);
        assert!(maybe_res.is_ok());
    }

    #[test]
    fn test_secure_compare_same_strings() {
        let str1 = "same same".as_bytes();
        let str2 = "same same".as_bytes();
        let res = secure_compare(str1, str2);
        assert!(res);
    }

    #[test]
    fn test_fails_when_secure_compare_different_strings() {
        let str1 = "same same".as_bytes();
        let str2 = "same same but different".as_bytes();
        let res = secure_compare(str1, str2);
        assert!(!res);
    }

    #[test]
    fn test_encode_and_decode_jwt_hs384() {
        let mut p1 = Payload::new();
        p1.insert("key1".to_string(), "val1".to_string());
        p1.insert("key2".to_string(), "val2".to_string());
        p1.insert("key3".to_string(), "val3".to_string());

        let secret = "secret123";
        let header = Header::new(Algorithm::HS384);
        let jwt1 = encode(header, secret, p1.clone());
        let maybe_res = decode(jwt1, secret, Algorithm::HS384);
        assert!(maybe_res.is_ok());
    }

    #[test]
    fn test_encode_and_decode_jwt_hs512() {
        let mut p1 = Payload::new();
        p1.insert("key12".to_string(), "val1".to_string());
        p1.insert("key22".to_string(), "val2".to_string());
        p1.insert("key33".to_string(), "val3".to_string());

        let secret = "secret123456";
        let header = Header::new(Algorithm::HS512);
        let jwt1 = encode(header, secret, p1.clone());
        let maybe_res = decode(jwt1, secret, Algorithm::HS512);
        assert!(maybe_res.is_ok());
    }

    #[test]
    fn test_encode_and_decode_jwt_rs256() {
        let mut p1 = Payload::new();
        p1.insert("key12".to_string(), "val1".to_string());
        p1.insert("key22".to_string(), "val2".to_string());
        p1.insert("key33".to_string(), "val3".to_string());
        let header = Header::new(Algorithm::RS256);

        let mut path = env::current_dir().unwrap();
        path.push("test");
        path.push("my_rsa_2048_key.pem");
        path.to_str().unwrap().to_string();

        let jwt1 = encode(header, RSA_PRIVATE_KEY, p1.clone());
        let maybe_res = decode(jwt1, RSA_PUBLIC_KEY, Algorithm::RS256);
        assert!(maybe_res.is_ok());
    }

    static RS256_JWT: &str = include_str!("../test/valid_rs256.jwt");
    #[test]
    fn test_decode_valid_jwt_rs256() {
        let mut p1 = Payload::new();
        p1.insert("key1".to_string(), "val1".to_string());
        p1.insert("key2".to_string(), "val2".to_string());
        let header = Header::new(Algorithm::RS256);
        let jwt = encode(header, RSA_PRIVATE_KEY, p1.clone());
        assert_eq!(RS256_JWT, jwt);
    }

    #[test]
    fn test_decode_valid_jwt_rs256_and_check_deeply() {
        let mut p1 = Payload::new();
        p1.insert("key1".to_string(), "val1".to_string());
        p1.insert("key2".to_string(), "val2".to_string());
        let h1 = Header::new(Algorithm::RS256);
        let res = decode(RS256_JWT, RSA_PUBLIC_KEY, Algorithm::RS256);
        match res {
            Ok((h2, p2)) => {
                assert_eq!(h1.typ, h2.typ);
                assert_eq!(h1.alg, h2.alg);
                for (k, v) in &p1 {
                    assert_eq!(true, p2.contains_key(k));
                    assert_eq!(v, p2.get(k).unwrap());
                }
            }
            Err(e) => panic!(e),
        }
    }

    #[test]
    fn test_encode_and_decode_jwt_ec() {
        let mut p1 = Payload::new();
        p1.insert("key12".to_string(), "val1".to_string());
        p1.insert("key22".to_string(), "val2".to_string());
        p1.insert("key33".to_string(), "val3".to_string());
        let header = Header::new(Algorithm::ES512);

        let jwt1 = encode(header, EC_PRIVATE_KEY, p1.clone());
        let maybe_res = decode(jwt1, EC_PUBLIC_KEY, Algorithm::ES512);
        assert!(maybe_res.is_ok());
    }

    static EC_PRIVATE_KEY: &str = include_str!("../test/ec_x9_62_prime256v1.private.key.pem");
    static EC_PUBLIC_KEY: &str = include_str!("../test/ec_x9_62_prime256v1.public.key.pem");
    static RSA_PRIVATE_KEY: &str = include_str!("../test/my_rsa_2048_key.pem");
    static RSA_PUBLIC_KEY: &str = include_str!("../test/my_rsa_public_2048_key.pem");
}
<commit_msg>feat: Implement std::error::Error trait for Error<commit_after>/*
 * Copyright (c) 2017 René Richter,
 * Copyright (c) 2015-2017 Alex Maslakov, <http://gildedhonour.com>, <http://alexmaslakov.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * For questions and comments about this product, please see the project page at:
 *
 * https://github.com/Richterrettich/rusty_jwt
 *
 * This was forked from
 * https://github.com/GildedHonour/frank_jwt
 *
 */

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;
extern crate time;
extern crate openssl;
extern crate base64;

use std::collections::BTreeMap;

use openssl::hash::MessageDigest;
use openssl::pkey::PKey;
use openssl::rsa::Rsa;
use openssl::sign::Signer;
use openssl::sign::Verifier;
use openssl::ec::EcKey;

pub type Payload = BTreeMap<String, String>;

#[derive(Serialize, Deserialize)]
pub struct Header {
    alg: Algorithm,
    typ: String,
}

impl Header {
    pub fn new(alg: Algorithm) -> Header {
        Header {
            alg: alg,
            typ: String::from("JWT"),
        }
    }
}

#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Debug)]
pub enum Algorithm {
    HS256,
    HS384,
    HS512,
    RS256,
    RS384,
    RS512,
    ES256,
    ES384,
    ES512,
}

impl Algorithm {
    fn get_hash(&self) -> MessageDigest {
        match *self {
            Algorithm::HS256 | Algorithm::RS256 | Algorithm::ES256 => MessageDigest::sha256(),
            Algorithm::HS384 | Algorithm::RS384 | Algorithm::ES384 => MessageDigest::sha384(),
            Algorithm::HS512 | Algorithm::RS512 | Algorithm::ES512 => MessageDigest::sha512(),
        }
    }
}

#[derive(Debug)]
pub enum Error {
    SignatureExpired,
    SignatureInvalid,
    JWTInvalid,
    IssuerInvalid,
    ExpirationInvalid,
    AudienceInvalid,
}

impl std::error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::SignatureExpired => "signature expired",
            Error::SignatureInvalid => "signature invalid",
            Error::JWTInvalid => "jwt invalid",
            Error::IssuerInvalid => "invalid issuer",
            Error::ExpirationInvalid => "invalid expiration",
            Error::AudienceInvalid => "invalid audience",
        }
    }
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match *self {
            Error::SignatureExpired => write!(f, "signature expired"),
            Error::SignatureInvalid => write!(f, "signature invalid"),
            Error::JWTInvalid => write!(f, "jwt invalid"),
            Error::IssuerInvalid => write!(f, "invalid issuer"),
            Error::ExpirationInvalid => write!(f, "invalid expiration"),
            Error::AudienceInvalid => write!(f, "invalid audience"),
        }
    }
}

pub fn encode<T: ?Sized + AsRef<[u8]>>(header: Header, key: &T, payload: Payload) -> String {
    let signing_input = get_signing_input(payload, &header.alg);
    let kr = key.as_ref();
    let pkey = match header.alg {
        Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => PKey::hmac(kr).unwrap(),
        Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 => rsa_key(kr),
        Algorithm::ES256 | Algorithm::ES384 | Algorithm::ES512 => ec_key(kr),
    };
    let signature = sign_and_encode(&signing_input, pkey, header.alg.get_hash());
    format!("{}.{}", signing_input, signature)
}

pub fn decode<T, E>(
    encoded_token: E,
    key: &T,
    algorithm: Algorithm,
) -> Result<(Header, Payload), Error>
where
    T: ?Sized + AsRef<[u8]>,
    E: AsRef<str>,
{
    match decode_segments(encoded_token.as_ref()) {
        Some((header, payload, signature, signing_input)) => {
            if !verify_signature(algorithm, signing_input, &signature, key.as_ref()) {
                return Err(Error::SignatureInvalid);
            }
            Ok((header, payload))
        }

        None => Err(Error::JWTInvalid),
    }
}

static SEGMENTS_COUNT: usize = 3;

fn get_signing_input(payload: Payload, algorithm: &Algorithm) -> String {
    let header = Header::new(*algorithm);
    let header_json_str =
        serde_json::to_string(&header).expect("could not convert header to json.");
    let encoded_header = base64::encode_config(header_json_str.as_bytes(), base64::URL_SAFE_NO_PAD)
        .to_string();
    let payload_json = serde_json::to_string(&payload).expect("could not convert payload to json");
    let encoded_payload = base64::encode_config(payload_json.as_bytes(), base64::URL_SAFE_NO_PAD)
        .to_string();
    format!("{}.{}", encoded_header, encoded_payload)
}

fn rsa_key(raw_key: &[u8]) -> PKey {
    let rsa = Rsa::private_key_from_pem(raw_key).unwrap();
    PKey::from_rsa(rsa).unwrap()
}

fn ec_key(raw_key: &[u8]) -> PKey {
    let ec_key =
        EcKey::private_key_from_pem(&raw_key).expect("could not convert to EC private key");
    PKey::from_ec_key(ec_key).expect("could not convert EC private key")
}

fn sign(data: &str, private_key: PKey, digest: MessageDigest) -> Vec<u8> {
    let mut signer = Signer::new(digest, &private_key).unwrap();
    signer.update(data.as_bytes()).unwrap();
    signer.finish().unwrap()
}

fn sign_and_encode(data: &str, private_key: PKey, digest: MessageDigest) -> String {
    let raw = sign(data, private_key, digest);
    base64::encode_config(&raw, base64::URL_SAFE_NO_PAD)
}

fn decode_segments(encoded_token: &str) -> Option<(Header, Payload, Vec<u8>, String)> {
    let raw_segments: Vec<&str> = encoded_token.split(".").collect();
    if raw_segments.len() != SEGMENTS_COUNT {
        return None;
    }

    let header_segment = raw_segments[0];
    let payload_segment = raw_segments[1];
    let crypto_segment = raw_segments[2];
    let (header, payload) = decode_header_and_payload(header_segment, payload_segment);
    let signature = base64::decode_config(crypto_segment, base64::URL_SAFE_NO_PAD)
        .expect("could not decoding base64 signature");
    let signing_input = format!("{}.{}", header_segment, payload_segment);
    Some((header, payload, signature.clone(), signing_input))
}

fn decode_header_and_payload(header_segment: &str, payload_segment: &str) -> (Header, Payload) {
    let headder_bytes = base64::decode(header_segment).expect("could not decoding base64 header");
    let header: Header =
        serde_json::from_slice(&headder_bytes[..]).expect("could not convert header to json");

    let payload_bytes = base64::decode(payload_segment).expect("could not decoding base64 payload");
    let payload: Payload =
        serde_json::from_slice(&payload_bytes[..]).expect("could not convert header to json");
    (header, payload)
}

fn verify_signature(
    algorithm: Algorithm,
    signing_input: String,
    signature: &[u8],
    public_key: &[u8],
) -> bool {
    match algorithm {

        Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => {
            let key = PKey::hmac(public_key).unwrap();
            let signature2 = sign(&signing_input, key, algorithm.get_hash());
            secure_compare(signature, &signature2)
        }
        _ => {
            let key = PKey::public_key_from_pem(public_key).expect("could not convert pem to pkey");
            let mut verifier = Verifier::new(algorithm.get_hash(), &key).unwrap();
            verifier.update(signing_input.as_bytes()).unwrap();
            verifier.finish(&signature).unwrap()
        }
    }
}

fn secure_compare(a: &[u8], b: &[u8]) -> bool {
    if a.len() != b.len() {
        return false;
    }

    let mut res = 0_u8;
    for (&x, &y) in a.iter().zip(b.iter()) {
        res |= x ^ y;
    }

    res == 0
}

#[cfg(test)]
mod tests {
    extern crate time;

    use super::Header;
    use super::Payload;
    use super::encode;
    use super::decode;
    use super::Algorithm;
    use super::secure_compare;
    use std::env;

    #[test]
    fn test_encode_and_decode_jwt_hs256() {
        let mut p1 = Payload::new();
        p1.insert("key1".to_string(), "val1".to_string());
        p1.insert("key2".to_string(), "val2".to_string());
        p1.insert("key3".to_string(), "val3".to_string());

        let secret = "secret123";
        let header = Header::new(Algorithm::HS256);
        let jwt1 = encode(header, secret, p1.clone());
        let maybe_res = decode(jwt1.trim(), secret, Algorithm::HS256);
        assert!(maybe_res.is_ok());
    }

    static HS256_JWT: &'static str = include_str!("../test/valid_hs256.jwt");
    #[test]
    fn test_decode_valid_jwt_hs256() {
        let mut p1 = Payload::new();
        p1.insert("key11".to_string(), "val1".to_string());
        p1.insert("key22".to_string(), "val2".to_string());
        let secret = "secret123";
        let maybe_res = decode(HS256_JWT, secret, Algorithm::HS256);
        assert!(maybe_res.is_ok());
    }

    #[test]
    fn test_secure_compare_same_strings() {
        let str1 = "same same".as_bytes();
        let str2 = "same same".as_bytes();
        let res = secure_compare(str1, str2);
        assert!(res);
    }

    #[test]
    fn test_fails_when_secure_compare_different_strings() {
        let str1 = "same same".as_bytes();
        let str2 = "same same but different".as_bytes();
        let res = secure_compare(str1, str2);
        assert!(!res);
    }

    #[test]
    fn test_encode_and_decode_jwt_hs384() {
        let mut p1 = Payload::new();
        p1.insert("key1".to_string(), "val1".to_string());
        p1.insert("key2".to_string(), "val2".to_string());
        p1.insert("key3".to_string(), "val3".to_string());

        let secret = "secret123";
        let header = Header::new(Algorithm::HS384);
        let jwt1 = encode(header, secret, p1.clone());
        let maybe_res = decode(jwt1, secret, Algorithm::HS384);
        assert!(maybe_res.is_ok());
    }

    #[test]
    fn test_encode_and_decode_jwt_hs512() {
        let mut p1 = Payload::new();
        p1.insert("key12".to_string(), "val1".to_string());
        p1.insert("key22".to_string(), "val2".to_string());
        p1.insert("key33".to_string(), "val3".to_string());

        let secret = "secret123456";
        let header = Header::new(Algorithm::HS512);
        let jwt1 = encode(header, secret, p1.clone());
        let maybe_res = decode(jwt1, secret, Algorithm::HS512);
        assert!(maybe_res.is_ok());
    }

    #[test]
    fn test_encode_and_decode_jwt_rs256() {
        let mut p1 = Payload::new();
        p1.insert("key12".to_string(), "val1".to_string());
        p1.insert("key22".to_string(), "val2".to_string());
        p1.insert("key33".to_string(), "val3".to_string());
        let header = Header::new(Algorithm::RS256);

        let mut path = env::current_dir().unwrap();
        path.push("test");
        path.push("my_rsa_2048_key.pem");
        path.to_str().unwrap().to_string();

        let jwt1 = encode(header, RSA_PRIVATE_KEY, p1.clone());
        let maybe_res = decode(jwt1, RSA_PUBLIC_KEY, Algorithm::RS256);
        assert!(maybe_res.is_ok());
    }

    static RS256_JWT: &str = include_str!("../test/valid_rs256.jwt");
    #[test]
    fn test_decode_valid_jwt_rs256() {
        let mut p1 = Payload::new();
        p1.insert("key1".to_string(), "val1".to_string());
        p1.insert("key2".to_string(), "val2".to_string());
        let header = Header::new(Algorithm::RS256);
        let jwt = encode(header, RSA_PRIVATE_KEY, p1.clone());
        assert_eq!(RS256_JWT, jwt);
    }

    #[test]
    fn test_decode_valid_jwt_rs256_and_check_deeply() {
        let mut p1 = Payload::new();
        p1.insert("key1".to_string(), "val1".to_string());
        p1.insert("key2".to_string(), "val2".to_string());
        let h1 = Header::new(Algorithm::RS256);
        let res = decode(RS256_JWT, RSA_PUBLIC_KEY, Algorithm::RS256);
        match res {
            Ok((h2, p2)) => {
                assert_eq!(h1.typ, h2.typ);
                assert_eq!(h1.alg, h2.alg);
                for (k, v) in &p1 {
                    assert_eq!(true, p2.contains_key(k));
                    assert_eq!(v, p2.get(k).unwrap());
                }
            }
            Err(e) => panic!(e),
        }
    }

    #[test]
    fn test_encode_and_decode_jwt_ec() {
        let mut p1 = Payload::new();
        p1.insert("key12".to_string(), "val1".to_string());
        p1.insert("key22".to_string(), "val2".to_string());
        p1.insert("key33".to_string(), "val3".to_string());
        let header = Header::new(Algorithm::ES512);

        let jwt1 = encode(header, EC_PRIVATE_KEY, p1.clone());
        let maybe_res = decode(jwt1, EC_PUBLIC_KEY, Algorithm::ES512);
        assert!(maybe_res.is_ok());
    }

    static EC_PRIVATE_KEY: &str = include_str!("../test/ec_x9_62_prime256v1.private.key.pem");
    static EC_PUBLIC_KEY: &str = include_str!("../test/ec_x9_62_prime256v1.public.key.pem");
    static RSA_PRIVATE_KEY: &str = include_str!("../test/my_rsa_2048_key.pem");
    static RSA_PUBLIC_KEY: &str = include_str!("../test/my_rsa_public_2048_key.pem");
}
<|endoftext|>


<commit_before>pub type Float = f32;
pub type Point = [Float; 3];

/// Generic grid type.
pub trait Grid {
    /// Cell coordinate on the grid.
    type Coordinate;
    /// Direciton on the grid.
    type Direction;
    /// Get the center point of a cell.
    fn get_cell_center(&self, Self::Coordinate) -> Point;
    /// Get the closest coordinate of a point.
    fn get_coordinate(&self, &Point) -> Self::Coordinate;
    /// Get the cell neighbor in a direction.
    fn get_neighbour(&self, Self::Coordinate, Self::Direction) -> Self::Coordinate;
    /// Fold over all neighbours.
    fn fold_neighbours<U, F: Fn(U, Self::Coordinate) -> U>(&self, Self::Coordinate, U) -> U;
    /// Fold over all cells in a radius.
    fn fold_in_radius<U, F: Fn(U, Self::Coordinate) -> U>(&self, Self::Coordinate, Float, U) -> U;
}

/// Position on the grid.
pub struct Position<G: Grid> {
    /// Coordinate.
    pub coord: G::Coordinate,
    /// Direction.
    pub dir: G::Direction,
}
<commit_msg>Fixed the fold definitions<commit_after>pub mod quad;

pub type Float = f32;
pub type Point = [Float; 3];

/// Generic grid type.
pub trait Grid {
    /// Cell coordinate on the grid.
    type Coordinate;
    /// Direciton on the grid.
    type Direction;
    /// Get the center point of a cell.
    fn get_cell_center(&self, Self::Coordinate) -> Point;
    /// Get the closest coordinate of a point.
    fn get_coordinate(&self, &Point) -> Self::Coordinate;
    /// Get the cell neighbor in a direction.
    fn get_neighbour(&self, Self::Coordinate, Self::Direction) -> Self::Coordinate;
    /// Fold over all neighbours.
    fn fold_neighbours<U, F: Fn(U, Self::Coordinate, Self::Direction) -> U>(&self, Self::Coordinate, F, U) -> U;
    /// Fold over all cells in a radius.
    fn fold_in_radius<U, F: Fn(U, Self::Coordinate) -> U>(&self, Self::Coordinate, Float, F, U) -> U;
}

/// Position on the grid.
pub struct Position<G: Grid> {
    /// Coordinate.
    pub coord: G::Coordinate,
    /// Direction.
    pub dir: G::Direction,
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
pub mod stm {
    use std::result::Result as Either;
    use std::mem::{transmute, forget, uninitialized};
    use std::ptr::swap;
    /// We cannot make a real monad, so this is going to be a fake linear type a la Clean.
    pub struct Result(Either);

    impl Result {
        pub fn ret(a: A) -> Result {
            Result(Either::Ok(a))
        }

        pub fn abort() -> Result {
            Result(Either::Err(()))
        }

        /// just don't look
        fn spec_move(mut self) -> Either {
            unsafe {
                let mut x = uninitialized();
                swap(&mut x, transmute(&mut self));
                forget(self);
                x
            }
        }

        pub fn bind(self, f : F) -> Result where F : FnOnce(A) -> Result {
            Result(self.spec_move().and_then(|x| f(x).spec_move()))
        }
    }

    impl Drop for Result {
        // Best we can do absent a true linear type
        fn drop(&mut self) {
            panic!()
        }
    }

    pub trait GlobalState {
        /// A hook for transaction-local state
        type LocalState;

        fn run(&self, F) -> A
            where F: FnMut(&mut Self::LocalState) -> Result;

        fn or_else(&self, F, G) -> Result
            where F: FnMut(&mut Self::LocalState) -> Result,
                  G: FnMut(&mut Self::LocalState) -> Result;
    }

    pub trait TVar {
        /// A wrapper around an object whose access is mediated by the STM implementation
        type LocalState;

        fn read(&mut self, Self::LocalState) -> Result;

        fn write(&mut self, Self::LocalState, A) -> Result<()>;
    }
}
pub mod no_rec {
    use std::sync::atomic::{AtomicUsize, Ordering};
    use std::vec::Vec;
    use std::collections::HashMap;
    use std::cell::UnsafeCell;
    use std::marker::PhantomData;
    use stm::{Result};

    pub struct GlobalState {
        version : AtomicUsize
    }

    #[allow(raw_pointer_derive)]
    #[derive(Hash, Eq, PartialEq, Debug, Copy, Clone)]
    /// Lies... Lies
    struct TVarAddr<'a, 'b : 'a>(*mut TVar<'b, ()>, PhantomData<&'a TVar<'b, ()>>);

    impl<'a, 'b : 'a> TVarAddr<'a, 'b> {
        fn version(self) -> &'a AtomicUsize {
            unsafe { &(*self.0).version }
        }
    }

    pub struct LocalState<'v, 'g : 'v> {
        /// the norec paper has the reads stored as a list of <address, value> pairs. For us it
        /// would probably be better to have versioned TVars. That way we always have a fast
        /// comparison operation and avoid doing extra dynamic dispatch when validating reads.
        reads : Vec<(TVarAddr<'v, 'g>, usize)>,

        /// for writes we will probably have to cast unsafely to get this to work. TVars just hold
        /// arbitrary types and we only have one table.  XXX: gross
        writes : HashMap<TVarAddr<'v, 'g>, Box<[u8]>>,

        /// STM environment we are using
        global : &'g GlobalState,

        /// last version at which our view of the global state was valid
        snapshot : usize
    }

    impl<'v, 'g : 'v> LocalState<'v, 'g> {
        /*
         * while (true)
         * time = global lock
         * if ((time&1)!=0)
         *  continue
         * for each (addr, val) in reads
         *      if (∗addr != val)
         *          TXAbort() // abort will longjmp if (time == global lock)
         * return time
         */
        fn validate(&mut self) -> Result<usize> {
            loop {
                let time = self.global.version.load(Ordering::SeqCst);
                if (time & 1) == 0 {
                    continue;
                }
                for &(addr, version) in &self.reads {
                    if addr.version().load(Ordering::SeqCst) != version {
                        return Result::abort();
                    }
                }
                return Result::ret(time);
            }
        }
    }

    pub struct TVar<'a, A> {
        version : AtomicUsize,
        val : UnsafeCell,
        global : &'a GlobalState
    }

    impl<'a, A> TVar<'a, A> {
        unsafe fn get_val(&self) -> &A {
            &*self.val.get()
        }

        unsafe fn get_val_mut(&self) -> &mut A {
            &mut *self.val.get()
        }
    }

    //impl<'a, A> stm::TVar for TVar<'a, A> {
    //}
}

#[test]
fn it_works() {
}
<commit_msg>working commit -- added comments regarding potential issues<commit_after>#![allow(dead_code)]
pub mod stm {
    use std::result::Result as Either;
    use std::mem::{transmute, forget, uninitialized};
    use std::ptr::swap;
    /// We cannot make a real monad, so this is going to be a fake linear type a la Clean.
    pub struct Result(Either);

    impl Result {
        pub fn ret(a: A) -> Result {
            Result(Either::Ok(a))
        }

        pub fn abort() -> Result {
            Result(Either::Err(()))
        }

        /// just don't look
        fn spec_move(mut self) -> Either {
            unsafe {
                let mut x = uninitialized();
                swap(&mut x, transmute(&mut self));
                forget(self);
                x
            }
        }

        pub fn bind(self, f : F) -> Result where F : FnOnce(A) -> Result {
            Result(self.spec_move().and_then(|x| f(x).spec_move()))
        }
    }

    impl Drop for Result {
        // Best we can do absent a true linear type
        fn drop(&mut self) {
            panic!()
        }
    }

    pub trait GlobalState {
        /// A hook for transaction-local state
        type LocalState;

        fn run(&self, F) -> A
            where F: FnMut(&mut Self::LocalState) -> Result;

        fn or_else(&self, F, G) -> Result
            where F: FnMut(&mut Self::LocalState) -> Result,
                  G: FnMut(&mut Self::LocalState) -> Result;
    }

    pub trait TVar {
        /// A wrapper around an object whose access is mediated by the STM implementation
        type LocalState;

        fn read(&mut self, Self::LocalState) -> Result;

        fn write(&mut self, Self::LocalState, A) -> Result<()>;
    }
}
pub mod no_rec {
    use std::sync::atomic::{AtomicUsize, Ordering};
    use std::vec::Vec;
    use std::collections::HashMap;
    use std::cell::UnsafeCell;
    use std::marker::PhantomData;
    use std::mem::transmute;
    use stm::Result;

    pub struct GlobalState {
        version : AtomicUsize
    }

    #[allow(raw_pointer_derive)]
    #[derive(Hash, Eq, PartialEq, Debug, Copy, Clone)]
    /// Lies... Lies
    struct TVarAddr<'a, 'b : 'a>(*mut TVar<'b, ()>, PhantomData<&'a TVar<'b, ()>>);

    impl<'a, 'b : 'a> TVarAddr<'a, 'b> {
        fn version(self) -> &'a AtomicUsize {
            unsafe { &(*self.0).version }
        }
    }

    pub struct LocalState<'v, 'g : 'v> {
        /// the norec paper has the reads stored as a list of <address, value> pairs. For us it
        /// would probably be better to have versioned TVars. That way we always have a fast
        /// comparison operation and avoid doing extra dynamic dispatch when validating reads.
        reads : Vec<(TVarAddr<'v, 'g>, usize)>,

        /// for writes we will probably have to cast unsafely to get this to work. TVars just hold
        /// arbitrary types and we only have one table.  XXX: gross
        writes : HashMap<TVarAddr<'v, 'g>, Box<[u8]>>,

        /// STM environment we are using
        global : &'g GlobalState,

        /// last version at which our view of the global state was valid
        snapshot : usize
    }

    impl<'v, 'g : 'v> LocalState<'v, 'g> {

        /// Validate the current local state against the state of the world,
        /// Aborts if a TVar in our readset has been written, otherwise
        /// returns the version number to with we are consistent
        fn validate(&self) -> Result<usize> {
            loop {
                let time = self.global.version.load(Ordering::SeqCst);
                if (time & 1) == 0 {
                    continue;
                }
                for &(addr, version) in &self.reads {
                    if addr.version().load(Ordering::SeqCst) != version {
                        return Result::abort();
                    }
                }
                return Result::ret(time);
            }
        }

        /// Acquire reference to value in TVar within the transaction
        fn read(&mut self, tvar : &'v TVar<'g, A>) -> Result<&A> {
            let addr = unsafe { tvar.to_addr() } ;
            if let Some(v) =  self.writes.get(&addr) {
                // TODO(ezrosent) may not be transmuting the right thing here
                return unsafe { Result::ret(transmute(v)) };
            }

            if self.snapshot == self.global.version.load(Ordering::SeqCst) {
                self.reads.push((addr, tvar.version.load(Ordering::SeqCst)));
                Result::ret(unsafe {tvar.get_val()})
            } else {
                self.validate().bind( | v | {
                    self.snapshot = v;
                    self.reads.push((addr, tvar.version.load(Ordering::SeqCst)));
                    Result::ret(unsafe {tvar.get_val()})
                })
            }
        }

        /// Add the TVar and value to our writeset, these are thread-local until the
        /// transaction commits
        fn write(&mut self, tvar : &'v mut TVar<'g, A>, val : A) -> Result<()> {
            //XXX: transmute doesn't work here. It says that Box<[u8]> could be 128 bits, while
            //Box could be 64 bits.

            /*
            unsafe {
                self.writes.insert(tvar.to_addr(), transmute(Box::new(val.clone())));
            }
            */

            Result::ret(())
        }

        /// if (read−only transaction)
        ///     return
        /// while (!CAS(&global lock, snapshot, snapshot + 1))
        ///     snapshot = Validate()
        /// for each (addr, val) in writes
        ///     ∗addr = val
        /// global lock = snapshot + 2 // one more than CAS above
        fn commit(&mut self) -> Result<()> {
            fn commit_loop(l : &mut LocalState) -> Result<()> {
               if l.global.version.compare_and_swap(l.snapshot, l.snapshot + 1, Ordering::SeqCst)
                   == l.snapshot {
                       Result::ret(())
               } else {
                   l.validate().bind(|v| {
                       l.snapshot = v;
                       commit_loop(l)
                   })
               }
            }
            if self.writes.is_empty() {
                Result::ret(())
            } else {
                commit_loop(self).bind(| _ | {
                    for (addr, value) in &self.writes {
                        //XXX: how do we safely write back the values now? we need to
                        //store some size/type-related information now.
                        //maybe we do want Any? or are we better off just with a
                        //struct {
                        //  size
                        //  A /* size of A is size */
                        //}
                        let tvar : &mut TVar<()> =  unsafe { transmute(addr.0) };
                        tvar.version.fetch_add(1, Ordering::SeqCst);
                    }
                    self.global.version.compare_and_swap(self.snapshot + 1,
                                                         self.snapshot + 2, Ordering::SeqCst);
                    Result::ret(())
                })
            }
        }
    }

    /// Haskell-style container of mutable state within a transaction
    pub struct TVar<'a, A> {
        version : AtomicUsize,
        val : UnsafeCell,
        global : &'a GlobalState
    }

    impl<'a, A> TVar<'a, A> {
        unsafe fn get_val(&self) -> &A {
            &*self.val.get()
        }

        unsafe fn get_val_mut(&self) -> &mut A {
            &mut *self.val.get()
        }
        unsafe fn to_addr<'b>(&'b self) -> TVarAddr<'b, 'a> {
            let ptr : *mut TVar<'a, ()> = transmute(self);
            TVarAddr(ptr, PhantomData)
        }
    }

}

#[test]
fn it_works() {
}
<|endoftext|>


<commit_before>

/// Used to ensure an abort on panic.
/// Call .done() to consume without aborting
#[derive(Debug)]
struct AbortOnSuddenDrop {
    finished: bool
}

impl AbortOnSuddenDrop {

    pub fn new() -> Self {
        AbortOnSuddenDrop { finished: false }
    }
    /// Consume `self` without aborting
    pub fn done(mut self) {
        self.finished = true;
    }
}

impl Drop for AbortOnSuddenDrop {
    fn drop(&mut self) {
        if !self.finished {
            std::process::exit(-1);
        }
    }
}

/// Allows use of a value inside a `&mut T` as though it was owned by the closure
///
/// The closure must return a valid T
/// # Aborts
/// Will abort the program (exiting with status code -1) if the closure panics.
pub fn take<T, F>(mut_ref: &mut T, closure: F)
  where F: FnOnce(T) -> T {
    use std::mem;
    let aborter = AbortOnSuddenDrop::new();
    unsafe {
        let old_t = mem::replace(mut_ref, mem::uninitialized());
        let new_t = closure(old_t);
        let garbage = mem::replace(mut_ref, new_t);
        mem::forget(garbage);
    }
    aborter.done();
}

use std::cell::Cell;

pub struct Scope {
    active_holes: Cell<usize>
}

impl Scope {
    pub fn scope<F>(f: F)
    where F: FnOnce(&Scope) {
        let aborter = AbortOnSuddenDrop::new();
        let this = Scope { active_holes: Cell::new(0) };
        f(&this);
        if this.active_holes.get() != 0 {
            panic!("There are still unfilled Holes!");
        }
        aborter.done();
    }
    // TODO: NEED TO GUARANTEE THAT MUTABLE OBJECT IN QUESTION IS NOT A SMALLER LIFETIME
    pub fn take<'s, 'm: 's, T: 'm>(&'s self, mut_ref: &'m mut T) -> (T, Hole<'s, 'm, T>) {
        use std::mem;

        let num_holes = self.active_holes.get();
        if num_holes == std::usize::MAX {
            panic!("Failed to create new Hole, already usize::MAX unfilled holes.");
        }
        self.active_holes.set(num_holes + 1);
        let t: T;
        let hole: Hole<'s, 'm, T>;
        unsafe {
            t = mem::replace(mut_ref, mem::uninitialized());
            hole = Hole { scope: self, hole: mut_ref };
        };
        (t, hole)
    }
}

pub struct Hole<'scope, 'm, T: 'm> {
    scope: &'scope Scope,
    hole: &'m mut T
}

impl<'scope, 'm, T: 'm> Hole<'scope, 'm, T> {
    pub fn fill(self, t: T) {
        use std::ptr;

        unsafe {
            ptr::write(self.hole, t);
        }

        self.scope.active_holes.set(self.scope.active_holes.get() - 1);
    }
}

#[test]
fn it_works() {
    enum Foo {A, B};
    impl Drop for Foo {
        fn drop(&mut self) {
            match *self {
                Foo::A => println!("Foo::A dropped"),
                Foo::B => println!("Foo::B dropped")
            }
        }
    }
    let mut foo = Foo::A;
    take(&mut foo, |mut f| {
       drop(f);
       Foo::B
    });
}
<commit_msg>Add test/example for Scope mechanism<commit_after>

/// Used to ensure an abort on panic.
/// Call .done() to consume without aborting
#[derive(Debug)]
struct AbortOnSuddenDrop {
    finished: bool
}

impl AbortOnSuddenDrop {

    pub fn new() -> Self {
        AbortOnSuddenDrop { finished: false }
    }
    /// Consume `self` without aborting
    pub fn done(mut self) {
        self.finished = true;
    }
}

impl Drop for AbortOnSuddenDrop {
    fn drop(&mut self) {
        if !self.finished {
            std::process::exit(-1);
        }
    }
}

/// Allows use of a value inside a `&mut T` as though it was owned by the closure
///
/// The closure must return a valid T
/// # Aborts
/// Will abort the program (exiting with status code -1) if the closure panics.
pub fn take<T, F>(mut_ref: &mut T, closure: F)
  where F: FnOnce(T) -> T {
    use std::mem;
    let aborter = AbortOnSuddenDrop::new();
    unsafe {
        let old_t = mem::replace(mut_ref, mem::uninitialized());
        let new_t = closure(old_t);
        let garbage = mem::replace(mut_ref, new_t);
        mem::forget(garbage);
    }
    aborter.done();
}

use std::cell::Cell;

pub struct Scope {
    active_holes: Cell<usize>
}

impl Scope {
    pub fn scope<F>(f: F)
    where F: FnOnce(&Scope) {
        let aborter = AbortOnSuddenDrop::new();
        let this = Scope { active_holes: Cell::new(0) };
        f(&this);
        if this.active_holes.get() != 0 {
            panic!("There are still unfilled Holes!");
        }
        aborter.done();
    }
    // TODO: NEED TO GUARANTEE THAT MUTABLE OBJECT IN QUESTION IS NOT A SMALLER LIFETIME
    pub fn take<'s, 'm: 's, T: 'm>(&'s self, mut_ref: &'m mut T) -> (T, Hole<'s, 'm, T>) {
        use std::mem;

        let num_holes = self.active_holes.get();
        if num_holes == std::usize::MAX {
            panic!("Failed to create new Hole, already usize::MAX unfilled holes.");
        }
        self.active_holes.set(num_holes + 1);
        let t: T;
        let hole: Hole<'s, 'm, T>;
        unsafe {
            t = mem::replace(mut_ref, mem::uninitialized());
            hole = Hole { scope: self, hole: mut_ref };
        };
        (t, hole)
    }
}

pub struct Hole<'scope, 'm, T: 'm> {
    scope: &'scope Scope,
    hole: &'m mut T
}

impl<'scope, 'm, T: 'm> Hole<'scope, 'm, T> {
    pub fn fill(self, t: T) {
        use std::ptr;

        unsafe {
            ptr::write(self.hole, t);
        }

        self.scope.active_holes.set(self.scope.active_holes.get() - 1);
    }
}

#[test]
fn it_works() {
    enum Foo {A, B};
    impl Drop for Foo {
        fn drop(&mut self) {
            match *self {
                Foo::A => println!("Foo::A dropped"),
                Foo::B => println!("Foo::B dropped")
            }
        }
    }
    let mut foo = Foo::A;
    take(&mut foo, |mut f| {
       drop(f);
       Foo::B
    });
}

#[test]
fn scope_based_take() {
    #[derive(Debug)]
    struct Foo;

    #[derive(Debug)]
    struct Bar {
        a: Foo,
        b: Foo
    }
    let mut bar = Bar { a: Foo, b: Foo };
    Scope::scope(|scope| {
        let (a, a_hole) = scope.take(&mut bar.a);
        let (b, b_hole) = scope.take(&mut bar.b);
        // Imagine consuming a and b
        a_hole.fill(Foo);
        b_hole.fill(Foo);
    });
    println!("{:?}", &bar);
}<|endoftext|>