<commit_before>//
// base64.rs - base64 implementation
//
// The Base64 Alphabet
//
//  Value Encoding  Value Encoding  Value Encoding  Value Encoding
//   0 A            17 R            34 i            51 z
//   1 B            18 S            35 j            52 0
//   2 C            19 T            36 k            53 1
//   3 D            20 U            37 l            54 2
//   4 E            21 V            38 m            55 3
//   5 F            22 W            39 n            56 4
//   6 G            23 X            40 o            57 5
//   7 H            24 Y            41 p            58 6
//   8 I            25 Z            42 q            59 7
//   9 J            26 a            43 r            60 8
//  10 K            27 b            44 s            61 9
//  11 L            28 c            45 t            62 +
//  12 M            29 d            46 u            63 /
//  13 N            30 e            47 v
//  14 O            31 f            48 w         (pad) =
//  15 P            32 g            49 x
//  16 Q            33 h            50 y
//
// The "URL and Filename safe" Base 64 Alphabet
//
// Value Encoding  Value Encoding  Value Encoding  Value Encoding
//     0 A            17 R            34 i            51 z
//     1 B            18 S            35 j            52 0
//     2 C            19 T            36 k            53 1
//     3 D            20 U            37 l            54 2
//     4 E            21 V            38 m            55 3
//     5 F            22 W            39 n            56 4
//     6 G            23 X            40 o            57 5
//     7 H            24 Y            41 p            58 6
//     8 I            25 Z            42 q            59 7
//     9 J            26 a            43 r            60 8
//    10 K            27 b            44 s            61 9
//    11 L            28 c            45 t            62 - (minus)
//    12 M            29 d            46 u            63 _
//    13 N            30 e            47 v           (underline)
//    14 O            31 f            48 w
//    15 P            32 g            49 x
//    16 Q            33 h            50 y         (pad) =
//

use std;

import std::map;

export base64, mk;

const padd: u8 = 61u8;

iface base64 {
    fn encode(src: [u8]) -> [u8];
    fn decode(src: [u8]) -> [u8];
    fn urlsafe_encode(src: [u8]) -> [u8];
    fn urlsafe_decode(src: [u8]) -> [u8];
}

fn mk() -> base64 {
    type _base64 = {table: [u8]};

    impl of base64 for _base64 {
        fn encode(src: [u8]) -> [u8] {
            b64encode(self.table + [43u8, 47u8], src)
        }
        fn decode(src: [u8]) -> [u8] {
            b64decode(self.table + [43u8, 47u8], src)
        }
        fn urlsafe_encode(src: [u8]) -> [u8] {
            b64encode(self.table + [45u8, 95u8], src)
        }
        fn urlsafe_decode(src: [u8]) -> [u8] {
            b64decode(self.table + [45u8, 95u8], src)
        }
    }

    let table = vec::init_elt_mut(62u, 0u8), i = 0u8;
    u8::range(65u8, 91u8)  { |j| table[i] = j; i += 1u8; }
    u8::range(97u8, 123u8) { |j| table[i] = j; i += 1u8; }
    u8::range(48u8, 58u8)  { |j| table[i] = j; i += 1u8; }

    {table: vec::from_mut(table)} as base64
}

inline fn b64idx(x: u8, y: u8, z: u8) -> u8 {
    if 65u8 <= x && x <= 90u8 { x - 65u8 }
    else if 97u8 <= x && x <= 122u8 { x - 97u8 + 26u8 }
    else if 48u8 <= x && x <= 58u8  { x - 48u8 + 52u8 }
    else if x == y { 62u8 }
    else if x == z { 63u8 }
    else { fail "malformed base64 string"; }
}

fn b64encode(table: [u8], src: [u8]) -> [u8] {
    let srclen = vec::len(src);
    let targ = if srclen % 3u == 0u {
        vec::init_elt_mut((srclen / 3u) * 4u, 0u8)
    } else {
        vec::init_elt_mut((srclen / 3u + 1u) * 4u, 0u8)
    };
    let input = vec::init_elt_mut(3u, 0u8);
    let output = vec::init_elt_mut(4u, 0u8);
    let curr = 0u, src_curr = 0u;

    while srclen > 2u {
        input[0] = src[src_curr];
        input[1] = src[src_curr + 1u];
        input[2] = src[src_curr + 2u];
        srclen -= 3u; src_curr += 3u;

        output[0] = input[0] >> 2u8;
        output[1] = ((input[0] &  3u8) << 4u8) | (input[1] >> 4u8);
        output[2] = ((input[1] & 15u8) << 2u8) | (input[2] >> 6u8);
        output[3] = input[2] & 63u8;

        targ[curr] = table[output[0]]; curr += 1u;
        targ[curr] = table[output[1]]; curr += 1u;
        targ[curr] = table[output[2]]; curr += 1u;
        targ[curr] = table[output[3]]; curr += 1u;
    }

    if srclen != 0u {
        input[0] = 0u8; input[1] = 0u8; input[2] = 0u8;

        alt srclen {
          1u {input[0] = src[src_curr];}
          2u {input[0] = src[src_curr];
              input[1] = src[src_curr + 1u];}
          _  { }
        }

        output[0] = input[0] >> 2u8;
        output[1] = ((input[0] &  3u8) << 4u8) | (input[1] >> 4u8);
        output[2] = ((input[1] & 15u8) << 2u8) | (input[2] >> 6u8);

        targ[curr] = table[output[0]]; curr += 1u;
        targ[curr] = table[output[1]]; curr += 1u;
        targ[curr] = if srclen == 1u { padd } else { table[output[2]] };
        curr += 1u; targ[curr] = padd;
    }

    vec::from_mut(targ)
}

fn b64decode(table: [u8], src: [u8]) -> [u8] {
    let srclen = vec::len(src);

    if srclen % 4u != 0u { fail "malformed base64 string"; }
    if srclen == 0u { ret []; }

    let input  = vec::init_elt_mut(4u, 0u8);
    let output = vec::init_elt_mut(4u, 0u8);
    let targ = if src[srclen - 2u] == padd {
        vec::init_elt_mut(srclen / 4u * 3u - 2u, 0u8)
    } else if src[srclen - 1u] == padd {
        vec::init_elt_mut(srclen / 4u * 3u - 1u, 0u8)
    } else {
        vec::init_elt_mut(srclen / 4u * 3u, 0u8)
    };
    let curr = 0u, src_curr = 0u;

    while srclen > 4u {
        input[0] = src[src_curr];
        input[1] = src[src_curr + 1u];
        input[2] = src[src_curr + 2u];
        input[3] = src[src_curr + 3u];
        srclen -= 4u; src_curr += 4u;

        output[0] = b64idx(input[0], table[62], table[63]);
        output[1] = b64idx(input[1], table[62], table[63]);
        output[2] = b64idx(input[2], table[62], table[63]);
        output[3] = b64idx(input[3], table[62], table[63]);

        targ[curr] = (output[0] << 2u8) | (output[1] >> 4u8);
        curr += 1u;

        targ[curr] = ((output[1] & 15u8) << 4u8) | (output[2] >> 2u8);
        curr += 1u;

        targ[curr] = ((output[2] &  3u8) << 6u8) | output[3];
        curr += 1u;
    }

    if srclen == 4u {
        input[0] = src[src_curr];
        input[1] = src[src_curr + 1u];
        input[2] = src[src_curr + 2u];
        input[3] = src[src_curr + 3u];

        output[0] = b64idx(input[0], table[62], table[63]);
        output[1] = b64idx(input[1], table[62], table[63]);

        targ[curr] = (output[0] << 2u8) | (output[1] >> 4u8);
        curr += 1u;

        if input[2] == padd { ret vec::from_mut(targ); }

        output[2] = b64idx(input[2], table[62], table[63]);

        targ[curr] = ((output[1] & 15u8) << 4u8) | (output[2] >> 2u8);
        curr += 1u;

        if input[3] == padd { ret vec::from_mut(targ); }

        output[3] = b64idx(input[3], table[62], table[63]);

        targ[curr] = ((output[2] & 3u8) << 6u8) | output[3];
    }

    vec::from_mut(targ)
}

#[cfg(test)]
mod tests {
    import core::str::{from_bytes, bytes};
    enum mode {
        t_decode,
        t_encode,
        t_urlsafe_encode,
        t_urlsafe_decode
    }
    fn setup(t: mode) -> map::hashmap<str, str> {
        let m = map::new_str_hash::<str>();
        m.insert("", "");
        alt t {
          t_decode | t_urlsafe_decode {
            m.insert("cGxlYXN1cmUu", "pleasure.");
            m.insert("bGVhc3VyZS4=",  "leasure.");
            m.insert("ZWFzdXJlLg==",   "easure.");
            m.insert("YXN1cmUu",        "asure.");
            m.insert("c3VyZS4=",         "sure.");
          }
          t_encode | t_urlsafe_encode {
            m.insert("pleasure.", "cGxlYXN1cmUu");
            m.insert("leasure.",  "bGVhc3VyZS4=");
            m.insert("easure.",   "ZWFzdXJlLg==");
            m.insert("asure.",        "YXN1cmUu");
            m.insert("sure.",         "c3VyZS4=");
          }
        }
        m
    }
    fn do_test(t: mode) {
        let b64 = mk();
        let m = setup(t);
        m.keys { |k|
            let expected = m.get(k);
            let actual = alt t {
              t_decode { from_bytes(b64.decode(bytes(k))) }
              t_encode { from_bytes(b64.encode(bytes(k))) }
              t_urlsafe_decode { from_bytes(b64.urlsafe_decode(bytes(k))) }
              t_urlsafe_encode { from_bytes(b64.urlsafe_encode(bytes(k))) }
            };
            #debug("expected: %s", expected);
            #debug("actual:   %s", actual);
            assert expected == actual;
        }
    }
    #[test]
    fn decode() { do_test(t_decode); }
    #[test]
    fn encode() { do_test(t_encode); }
    #[test]
    fn urlsafe_decode() { do_test(t_urlsafe_decode); }
    #[test]
    fn urlsafe_encode() { do_test(t_urlsafe_encode); }
}
<commit_msg>clean up<commit_after>//
// base64.rs - base64 implementation
//
// The Base64 Alphabet
//
//  Value Encoding  Value Encoding  Value Encoding  Value Encoding
//      0 A            17 R            34 i            51 z
//      1 B            18 S            35 j            52 0
//      2 C            19 T            36 k            53 1
//      3 D            20 U            37 l            54 2
//      4 E            21 V            38 m            55 3
//      5 F            22 W            39 n            56 4
//      6 G            23 X            40 o            57 5
//      7 H            24 Y            41 p            58 6
//      8 I            25 Z            42 q            59 7
//      9 J            26 a            43 r            60 8
//     10 K            27 b            44 s            61 9
//     11 L            28 c            45 t            62 +
//     12 M            29 d            46 u            63 /
//     13 N            30 e            47 v
//     14 O            31 f            48 w         (pad) =
//     15 P            32 g            49 x
//     16 Q            33 h            50 y
//
// The "URL and Filename safe" Base 64 Alphabet
//
// Value Encoding  Value Encoding  Value Encoding  Value Encoding
//     0 A            17 R            34 i            51 z
//     1 B            18 S            35 j            52 0
//     2 C            19 T            36 k            53 1
//     3 D            20 U            37 l            54 2
//     4 E            21 V            38 m            55 3
//     5 F            22 W            39 n            56 4
//     6 G            23 X            40 o            57 5
//     7 H            24 Y            41 p            58 6
//     8 I            25 Z            42 q            59 7
//     9 J            26 a            43 r            60 8
//    10 K            27 b            44 s            61 9
//    11 L            28 c            45 t            62 - (minus)
//    12 M            29 d            46 u            63 _ (underline)
//    13 N            30 e            47 v
//    14 O            31 f            48 w
//    15 P            32 g            49 x
//    16 Q            33 h            50 y         (pad) =
//

use std;

import std::map;

export base64, mk;

const padd: u8 = 61u8;

iface base64 {
    fn encode(src: [u8]) -> [u8];
    fn decode(src: [u8]) -> [u8];
    fn urlsafe_encode(src: [u8]) -> [u8];
    fn urlsafe_decode(src: [u8]) -> [u8];
}

fn mk() -> base64 {
    type _base64 = {table: [u8]};

    impl of base64 for _base64 {
        fn encode(src: [u8]) -> [u8] {
            b64encode(self.table + [43u8, 47u8], src)
        }
        fn decode(src: [u8]) -> [u8] {
            b64decode(self.table + [43u8, 47u8], src)
        }
        fn urlsafe_encode(src: [u8]) -> [u8] {
            b64encode(self.table + [45u8, 95u8], src)
        }
        fn urlsafe_decode(src: [u8]) -> [u8] {
            b64decode(self.table + [45u8, 95u8], src)
        }
    }

    let table = vec::init_elt_mut(62u, 0u8), i = 0u8;
    u8::range(65u8, 91u8)  { |j| table[i] = j; i += 1u8; }
    u8::range(97u8, 123u8) { |j| table[i] = j; i += 1u8; }
    u8::range(48u8, 58u8)  { |j| table[i] = j; i += 1u8; }

    {table: vec::from_mut(table)} as base64
}

inline fn b64idx(x: u8, y: u8, z: u8) -> u8 {
    if 65u8 <= x && x <= 90u8 { x - 65u8 }
    else if 97u8 <= x && x <= 122u8 { x - 97u8 + 26u8 }
    else if 48u8 <= x && x <= 58u8  { x - 48u8 + 52u8 }
    else if x == y { 62u8 }
    else if x == z { 63u8 }
    else { fail "malformed base64 string"; }
}

fn b64encode(table: [u8], src: [u8]) -> [u8] {
    let srclen = vec::len(src);
    let targ = if srclen % 3u == 0u {
        vec::init_elt_mut((srclen / 3u) * 4u, 0u8)
    } else {
        vec::init_elt_mut((srclen / 3u + 1u) * 4u, 0u8)
    };
    let input = vec::init_elt_mut(3u, 0u8);
    let output = vec::init_elt_mut(4u, 0u8);
    let curr = 0u, src_curr = 0u;

    while srclen > 2u {
        input[0] = src[src_curr];
        input[1] = src[src_curr + 1u];
        input[2] = src[src_curr + 2u];
        srclen -= 3u; src_curr += 3u;

        output[0] = input[0] >> 2u8;
        output[1] = ((input[0] &  3u8) << 4u8) | (input[1] >> 4u8);
        output[2] = ((input[1] & 15u8) << 2u8) | (input[2] >> 6u8);
        output[3] = input[2] & 63u8;

        targ[curr] = table[output[0]]; curr += 1u;
        targ[curr] = table[output[1]]; curr += 1u;
        targ[curr] = table[output[2]]; curr += 1u;
        targ[curr] = table[output[3]]; curr += 1u;
    }

    if srclen != 0u {
        input[0] = 0u8; input[1] = 0u8; input[2] = 0u8;

        alt srclen {
          1u {input[0] = src[src_curr];}
          2u {input[0] = src[src_curr];
              input[1] = src[src_curr + 1u];}
          _  { }
        }

        output[0] = input[0] >> 2u8;
        output[1] = ((input[0] &  3u8) << 4u8) | (input[1] >> 4u8);
        output[2] = ((input[1] & 15u8) << 2u8) | (input[2] >> 6u8);

        targ[curr] = table[output[0]]; curr += 1u;
        targ[curr] = table[output[1]]; curr += 1u;
        targ[curr] = if srclen == 1u { padd } else { table[output[2]] };
        curr += 1u; targ[curr] = padd;
    }

    vec::from_mut(targ)
}

fn b64decode(table: [u8], src: [u8]) -> [u8] {
    let srclen = vec::len(src);

    if srclen % 4u != 0u { fail "malformed base64 string"; }
    if srclen == 0u { ret []; }

    let input  = vec::init_elt_mut(4u, 0u8);
    let output = vec::init_elt_mut(4u, 0u8);
    let targ = if src[srclen - 2u] == padd {
        vec::init_elt_mut(srclen / 4u * 3u - 2u, 0u8)
    } else if src[srclen - 1u] == padd {
        vec::init_elt_mut(srclen / 4u * 3u - 1u, 0u8)
    } else {
        vec::init_elt_mut(srclen / 4u * 3u, 0u8)
    };
    let curr = 0u, src_curr = 0u;

    while srclen > 4u {
        input[0] = src[src_curr];
        input[1] = src[src_curr + 1u];
        input[2] = src[src_curr + 2u];
        input[3] = src[src_curr + 3u];
        srclen -= 4u; src_curr += 4u;

        output[0] = b64idx(input[0], table[62], table[63]);
        output[1] = b64idx(input[1], table[62], table[63]);
        output[2] = b64idx(input[2], table[62], table[63]);
        output[3] = b64idx(input[3], table[62], table[63]);

        targ[curr] = (output[0] << 2u8) | (output[1] >> 4u8);
        curr += 1u;

        targ[curr] = ((output[1] & 15u8) << 4u8) | (output[2] >> 2u8);
        curr += 1u;

        targ[curr] = ((output[2] &  3u8) << 6u8) | output[3];
        curr += 1u;
    }

    if srclen == 4u {
        input[0] = src[src_curr];
        input[1] = src[src_curr + 1u];
        input[2] = src[src_curr + 2u];
        input[3] = src[src_curr + 3u];

        output[0] = b64idx(input[0], table[62], table[63]);
        output[1] = b64idx(input[1], table[62], table[63]);

        targ[curr] = (output[0] << 2u8) | (output[1] >> 4u8);
        curr += 1u;

        if input[2] == padd { ret vec::from_mut(targ); }

        output[2] = b64idx(input[2], table[62], table[63]);

        targ[curr] = ((output[1] & 15u8) << 4u8) | (output[2] >> 2u8);
        curr += 1u;

        if input[3] == padd { ret vec::from_mut(targ); }

        output[3] = b64idx(input[3], table[62], table[63]);

        targ[curr] = ((output[2] & 3u8) << 6u8) | output[3];
    }

    vec::from_mut(targ)
}

#[cfg(test)]
mod tests {
    import core::str::{from_bytes, bytes};
    enum mode {
        t_decode,
        t_encode,
        t_urlsafe_encode,
        t_urlsafe_decode
    }
    fn setup(t: mode) -> map::hashmap<str, str> {
        let m = map::new_str_hash::<str>();
        m.insert("", "");
        alt t {
          t_decode | t_urlsafe_decode {
            m.insert("cGxlYXN1cmUu", "pleasure.");
            m.insert("bGVhc3VyZS4=",  "leasure.");
            m.insert("ZWFzdXJlLg==",   "easure.");
            m.insert("YXN1cmUu",        "asure.");
            m.insert("c3VyZS4=",         "sure.");
          }
          t_encode | t_urlsafe_encode {
            m.insert("pleasure.", "cGxlYXN1cmUu");
            m.insert("leasure.",  "bGVhc3VyZS4=");
            m.insert("easure.",   "ZWFzdXJlLg==");
            m.insert("asure.",        "YXN1cmUu");
            m.insert("sure.",         "c3VyZS4=");
          }
        }
        m
    }
    fn do_test(t: mode) {
        let b64 = mk();
        let m = setup(t);
        m.keys { |k|
            let expected = m.get(k);
            let actual = alt t {
              t_decode { from_bytes(b64.decode(bytes(k))) }
              t_encode { from_bytes(b64.encode(bytes(k))) }
              t_urlsafe_decode { from_bytes(b64.urlsafe_decode(bytes(k))) }
              t_urlsafe_encode { from_bytes(b64.urlsafe_encode(bytes(k))) }
            };
            #debug("expected: %s", expected);
            #debug("actual:   %s", actual);
            assert expected == actual;
        }
    }
    #[test]
    fn decode() { do_test(t_decode); }
    #[test]
    fn encode() { do_test(t_encode); }
    #[test]
    fn urlsafe_decode() { do_test(t_urlsafe_decode); }
    #[test]
    fn urlsafe_encode() { do_test(t_urlsafe_encode); }
}
<|endoftext|>


<commit_before>//! System operations instructions

use utils::address::Address;
use utils::bigint::{U256, M256};
use utils::gas::Gas;
use vm::{Memory, Log, Context, Transaction};
use super::State;
use rlp::RlpStream;

use crypto::sha3::Sha3;
use crypto::digest::Digest;
use vm::eval::utils::copy_from_memory;

pub fn suicide<M: Memory + Default>(state: &mut State<M>) {
    pop!(state, address: Address);
    let balance = state.account_state.balance(state.context.address).unwrap();
    state.account_state.increase_balance(address, balance);
    state.account_state.remove(state.context.address).unwrap();
}

pub fn log<M: Memory + Default>(state: &mut State<M>, topic_len: usize) {
    pop!(state, index, len);
    let data = copy_from_memory(&state.memory, index, len);
    let mut topics = Vec::new();
    for _ in 0..topic_len {
        topics.push(state.stack.pop().unwrap());
    }

    state.logs.push(Log {
        address: state.context.address,
        data: data,
        topics: topics,
    });
}

pub fn sha3<M: Memory + Default>(state: &mut State<M>) {
    pop!(state, from, len);
    let data = copy_from_memory(&state.memory, from, len);
    let mut sha3 = Sha3::keccak256();
    sha3.input(data.as_slice());
    let mut ret = [0u8; 32];
    sha3.result(&mut ret);
    push!(state, M256::from(ret.as_ref()));
}

pub fn create<M: Memory + Default>(state: &mut State<M>, after_gas: Gas) -> Option<Context> {
    pop!(state, value: U256);
    pop!(state, init_start, init_len);
    if state.account_state.balance(state.context.address).unwrap() < value {
        push!(state, M256::zero());
        return None;
    }

    let init = copy_from_memory(&state.memory, init_start, init_len);
    let nonce = state.account_state.nonce(state.context.address).unwrap();
    let mut rlp = RlpStream::new();
    rlp.begin_list(2);
    rlp.append(&state.context.address);
    rlp.append(&nonce);
    let mut address_array = [0u8; 32];
    let mut sha3 = Sha3::keccak256();
    sha3.input(rlp.out().as_slice());
    sha3.result(&mut address_array);
    let address = Address::from(M256::from(address_array));
    let context = Context {
        address: address,
        caller: state.context.address,
        code: init,
        data: Vec::new(),
        gas_limit: after_gas,
        gas_price: state.context.gas_price,
        origin: state.context.origin,
        value: value,
    };
    push!(state, address.into());
    Some(context)
}

#[allow(unused_variables)]
pub fn call<M: Memory + Default>(state: &mut State<M>, stipend_gas: Gas, after_gas: Gas) -> Option<(Context, (M256, M256))> {
    pop!(state, gas: Gas, to: Address, value: U256);
    pop!(state, in_start, in_len, out_start, out_len);
    if state.account_state.balance(state.context.address).unwrap() < value {
        push!(state, M256::zero());
        return None;
    }

    let input = copy_from_memory(&state.memory, in_start, in_len);
    let gas_limit = gas + stipend_gas;

    let transaction = Transaction::MessageCall {
        address: to,
        caller: state.context.address,
        gas_price: state.context.gas_price,
        gas_limit: gas_limit,
        value: value,
        data: input,
    };
    let context = transaction.into_context(
        Gas::zero(), Some(state.context.origin), &state.account_state
    ).unwrap();
    push!(state, M256::zero());
    Some((context, (out_start, out_len)))
}

#[allow(unused_variables)]
pub fn callcode<M: Memory + Default>(state: &mut State<M>, stipend_gas: Gas, after_gas: Gas) -> Option<(Context, (M256, M256))> {
    pop!(state, gas: Gas, to: Address, value: U256);
    pop!(state, in_start, in_len, out_start, out_len);
    if state.account_state.balance(state.context.address).unwrap() < value {
        push!(state, M256::zero());
        return None;
    }

    let input = copy_from_memory(&state.memory, in_start, in_len);
    let gas_limit = gas + stipend_gas;
    let transaction = Transaction::MessageCall {
        address: state.context.address,
        caller: state.context.address,
        gas_price: state.context.gas_price,
        gas_limit: gas_limit,
        value: value,
        data: input,
    };
    let context = transaction.into_context(
        Gas::zero(), Some(state.context.origin), &state.account_state
    ).unwrap();
    push!(state, M256::zero());
    Some((context, (out_start, out_len)))
}
<commit_msg>Problem: CREATE opcode does not use Transaction struct<commit_after>//! System operations instructions

use utils::address::Address;
use utils::bigint::{U256, M256};
use utils::gas::Gas;
use vm::{Memory, Log, Context, Transaction};
use super::State;

use crypto::sha3::Sha3;
use crypto::digest::Digest;
use vm::eval::utils::copy_from_memory;

pub fn suicide<M: Memory + Default>(state: &mut State<M>) {
    pop!(state, address: Address);
    let balance = state.account_state.balance(state.context.address).unwrap();
    state.account_state.increase_balance(address, balance);
    state.account_state.remove(state.context.address).unwrap();
}

pub fn log<M: Memory + Default>(state: &mut State<M>, topic_len: usize) {
    pop!(state, index, len);
    let data = copy_from_memory(&state.memory, index, len);
    let mut topics = Vec::new();
    for _ in 0..topic_len {
        topics.push(state.stack.pop().unwrap());
    }

    state.logs.push(Log {
        address: state.context.address,
        data: data,
        topics: topics,
    });
}

pub fn sha3<M: Memory + Default>(state: &mut State<M>) {
    pop!(state, from, len);
    let data = copy_from_memory(&state.memory, from, len);
    let mut sha3 = Sha3::keccak256();
    sha3.input(data.as_slice());
    let mut ret = [0u8; 32];
    sha3.result(&mut ret);
    push!(state, M256::from(ret.as_ref()));
}

pub fn create<M: Memory + Default>(state: &mut State<M>, after_gas: Gas) -> Option<Context> {
    pop!(state, value: U256);
    pop!(state, init_start, init_len);
    if state.account_state.balance(state.context.address).unwrap() < value {
        push!(state, M256::zero());
        return None;
    }

    let init = copy_from_memory(&state.memory, init_start, init_len);
    let transaction = Transaction::ContractCreation {
        caller: state.context.address,
        gas_price: state.context.gas_price,
        gas_limit: after_gas,
        value: value,
        init: init,
    };
    let context = transaction.into_context(
        Gas::zero(), Some(state.context.origin), &state.account_state
    ).unwrap();

    push!(state, context.address.into());
    Some(context)
}

#[allow(unused_variables)]
pub fn call<M: Memory + Default>(state: &mut State<M>, stipend_gas: Gas, after_gas: Gas) -> Option<(Context, (M256, M256))> {
    pop!(state, gas: Gas, to: Address, value: U256);
    pop!(state, in_start, in_len, out_start, out_len);
    if state.account_state.balance(state.context.address).unwrap() < value {
        push!(state, M256::zero());
        return None;
    }

    let input = copy_from_memory(&state.memory, in_start, in_len);
    let gas_limit = gas + stipend_gas;

    let transaction = Transaction::MessageCall {
        address: to,
        caller: state.context.address,
        gas_price: state.context.gas_price,
        gas_limit: gas_limit,
        value: value,
        data: input,
    };
    let context = transaction.into_context(
        Gas::zero(), Some(state.context.origin), &state.account_state
    ).unwrap();
    push!(state, M256::zero());
    Some((context, (out_start, out_len)))
}

#[allow(unused_variables)]
pub fn callcode<M: Memory + Default>(state: &mut State<M>, stipend_gas: Gas, after_gas: Gas) -> Option<(Context, (M256, M256))> {
    pop!(state, gas: Gas, to: Address, value: U256);
    pop!(state, in_start, in_len, out_start, out_len);
    if state.account_state.balance(state.context.address).unwrap() < value {
        push!(state, M256::zero());
        return None;
    }

    let input = copy_from_memory(&state.memory, in_start, in_len);
    let gas_limit = gas + stipend_gas;
    let transaction = Transaction::MessageCall {
        address: state.context.address,
        caller: state.context.address,
        gas_price: state.context.gas_price,
        gas_limit: gas_limit,
        value: value,
        data: input,
    };
    let context = transaction.into_context(
        Gas::zero(), Some(state.context.origin), &state.account_state
    ).unwrap();
    push!(state, M256::zero());
    Some((context, (out_start, out_len)))
}
<|endoftext|>


<commit_before>use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::io::stderr;
use std::path::Path;
use std::process;

const PREVIEW_LEN: usize = 50;

struct Match {
    offset: usize,
    key: u8,
    preview: String,
}

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    if args.len() < 2 {
        writeln!(stderr(), "usage: roxor <file> <crib>").unwrap();
        process::exit(1);
    }

    let path = Path::new(&args[0]);
    let crib = args[1].as_bytes();

    if crib.len() < 2 {
        writeln!(stderr(), "error: crib too short").unwrap();
        process::exit(1);
    }

    let mut file = match File::open(&path) {
        Err(why) => panic!("couldn't open {}: {}", path.display(), why),
        Ok(file) => file,
    };

    let mut ciphertext: Vec<u8> = Vec::new();
    file.read_to_end(&mut ciphertext).unwrap();

    if ciphertext.len() < crib.len() {
        writeln!(stderr(), "error: ciphertext too short").unwrap();
        process::exit(1);
    }

    for m in attack_cipher(&ciphertext[..], crib) {
        println!("Found text at 0x{:x} (XOR key 0x{:02x})", m.offset, m.key);
        println!("  preview: {}", m.preview);
    }
}

fn attack_cipher(ciphertext: &[u8], crib: &[u8]) -> Vec<Match> {
    let mut matches: Vec<Match> = Vec::new();
    for (i, x) in ciphertext.iter().enumerate() {
        let mut old_key = x ^ crib[0];
        let mut j = 1;
        while j < crib.len() && (i+j) < ciphertext.len() {
            let key = ciphertext[i+j] ^ crib[j];
            if key != old_key { break; }
            old_key = key;
            j += 1;
            if j == crib.len() {
                let preview: String = ciphertext[i..]
                    .iter()
                    .take(PREVIEW_LEN)
                    .map(|x| x ^ key)
                    .map(|x| if x >= 32 && x <= 126 {x as char} else {'.'})
                    .collect();
                matches.push(Match { offset: i, key: key, preview: preview });
            }
        }
    }
    return matches;
}
<commit_msg>Working tests!<commit_after>use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::io::stderr;
use std::path::Path;
use std::process;

const PREVIEW_LEN: usize = 50;

#[derive(Debug, PartialEq)]
struct Match {
    offset: usize,
    key: u8,
    preview: String,
}

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    if args.len() < 2 {
        writeln!(stderr(), "usage: roxor <file> <crib>").unwrap();
        process::exit(1);
    }

    let path = Path::new(&args[0]);
    let crib = args[1].as_bytes();

    if crib.len() < 2 {
        writeln!(stderr(), "error: crib too short").unwrap();
        process::exit(1);
    }

    let mut file = match File::open(&path) {
        Err(why) => panic!("couldn't open {}: {}", path.display(), why),
        Ok(file) => file,
    };

    let mut ciphertext: Vec<u8> = Vec::new();
    file.read_to_end(&mut ciphertext).unwrap();

    if ciphertext.len() < crib.len() {
        writeln!(stderr(), "error: ciphertext too short").unwrap();
        process::exit(1);
    }

    for m in attack_cipher(&ciphertext[..], crib) {
        println!("Found text at 0x{:x} (XOR key 0x{:02x})", m.offset, m.key);
        println!("  preview: {}", m.preview);
    }
}

fn attack_cipher(ciphertext: &[u8], crib: &[u8]) -> Vec<Match> {
    let mut matches: Vec<Match> = Vec::new();
    for (i, x) in ciphertext.iter().enumerate() {
        let mut old_key = x ^ crib[0];
        let mut j = 1;
        while j < crib.len() && (i+j) < ciphertext.len() {
            let key = ciphertext[i+j] ^ crib[j];
            if key != old_key { break; }
            old_key = key;
            j += 1;
            if j == crib.len() {
                let preview: String = ciphertext[i..]
                    .iter()
                    .take(PREVIEW_LEN)
                    .map(|x| x ^ key)
                    .map(|x| if x >= 32 && x <= 126 {x as char} else {'.'})
                    .collect();
                matches.push(Match { offset: i, key: key, preview: preview });
            }
        }
    }
    return matches;
}

#[cfg(test)]
mod tests {
    use super::{Match, attack_cipher};

    struct Test {
        ciphertext: Vec<u8>,
        crib: Vec<u8>,
        matches: Vec<Match>,
    }

    #[test]
    fn test_attack_cipher() {
        let tests = vec![
            Test {
                ciphertext: vec![],
                crib: vec![],
                matches: vec![],
            },
            Test {
                ciphertext: "haystack".bytes().collect(),
                crib: "needle".bytes().collect(),
                matches: vec![],
            },
            Test {
                ciphertext: "needle in haystack".bytes().collect(),
                crib: "needle".bytes().collect(),
                matches: vec![
                    Match { offset: 0, key: 0, preview: "needle in haystack".to_string() },
                ],
            },
            Test {
                ciphertext: "a needle, another needle".bytes().collect(),
                crib: "needle".bytes().collect(),
                matches: vec![
                    Match { offset: 2, key: 0, preview: "needle, another needle".to_string() },
                    Match { offset: 18, key: 0, preview: "needle".to_string() },
                ],
            },
            Test {
                ciphertext: vec![
                    0x23, 0x62, 0x2c, 0x27, 0x27, 0x26, 0x2e, 0x27,
                    0x6e, 0x62, 0x23, 0x2c, 0x2d, 0x36, 0x2a, 0x27,
                    0x30, 0x62, 0x2c, 0x27, 0x27, 0x26, 0x2e, 0x27,
                ],
                crib: "needle".bytes().collect(),
                matches: vec![
                    Match { offset: 2, key: 0x42, preview: "needle, another needle".to_string() },
                    Match { offset: 18, key: 0x42, preview: "needle".to_string() },
                ],
            },
        ];

        for t in tests.iter() {
            assert_eq!(t.matches, attack_cipher(&t.ciphertext[..], &t.crib[..]));
        }
    }
}
<|endoftext|>


<commit_before>#![feature(try_trait_v2)]

mod builtin_functions;
mod compiler;
mod database;
mod discover;
mod incremental;
mod input;
mod language_server;

use crate::compiler::ast_to_hir::AstToHir;
use crate::compiler::cst_to_ast::CstToAst;
use crate::compiler::hir;
use crate::compiler::rcst_to_cst::RcstToCst;
use crate::compiler::string_to_rcst::StringToRcst;
use crate::database::PROJECT_DIRECTORY;
use crate::{database::Database, input::Input};
use itertools::Itertools;
use language_server::CandyLanguageServer;
use log;
use lspower::{LspService, Server};
use notify::{watcher, RecursiveMode, Watcher};
use std::env::current_dir;
use std::fs;
use std::path::PathBuf;
use std::sync::mpsc::channel;
use std::sync::Arc;
use std::time::Duration;
use structopt::StructOpt;

#[derive(StructOpt, Debug)]
#[structopt(name = "candy", about = "The 🍭 Candy CLI.")]
enum CandyOptions {
    Build(CandyBuildOptions),
    Run(CandyRunOptions),
    Lsp,
}

#[derive(StructOpt, Debug)]
struct CandyBuildOptions {
    #[structopt(long)]
    debug: bool,

    #[structopt(long)]
    watch: bool,

    #[structopt(parse(from_os_str))]
    file: PathBuf,
}

#[derive(StructOpt, Debug)]
struct CandyRunOptions {
    #[structopt(parse(from_os_str))]
    file: PathBuf,
}

#[tokio::main]
async fn main() {
    init_logger();
    match CandyOptions::from_args() {
        CandyOptions::Build(options) => build(options),
        CandyOptions::Run(options) => run(options),
        CandyOptions::Lsp => lsp().await,
    }
}

fn build(options: CandyBuildOptions) {
    *PROJECT_DIRECTORY.lock().unwrap() = Some(current_dir().unwrap());

    raw_build(&options.file, options.debug);

    if options.watch {
        let (tx, rx) = channel();
        let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap();
        watcher
            .watch(&options.file, RecursiveMode::Recursive)
            .unwrap();
        loop {
            match rx.recv() {
                Ok(_) => {
                    raw_build(&options.file, options.debug);
                }
                Err(e) => println!("watch error: {:#?}", e),
            }
        }
    }
}
fn raw_build(file: &PathBuf, debug: bool) -> Option<Arc<hir::Body>> {
    let path_string = file.to_string_lossy();
    log::debug!("Building `{}`.", path_string);

    let input: Input = file.clone().into();
    let db = Database::default();

    log::info!("Parsing string to RCST…");
    let rcst = db
        .rcst(input.clone())
        .unwrap_or_else(|| panic!("File `{}` not found.", path_string));
    if debug {
        let hir_file = file.clone_with_extension("candy.rcst");
        fs::write(hir_file, format!("{:#?}\n", rcst.clone())).unwrap();
    }

    log::info!("Parsing RCST to CST…");
    let cst = db
        .cst(input.clone())
        .unwrap_or_else(|| panic!("File `{}` not found.", path_string));
    if debug {
        let cst_file = file.clone_with_extension("candy.cst");
        fs::write(cst_file, format!("{:#?}\n", cst.clone())).unwrap();
    }

    log::info!("Lowering CST to AST…");
    let (asts, ast_cst_id_map) = db
        .ast_raw(input.clone())
        .unwrap_or_else(|| panic!("File `{}` not found.", path_string));
    if debug {
        let ast_file = file.clone_with_extension("candy.ast");
        fs::write(
            ast_file,
            format!("{}\n", asts.iter().map(|ast| format!("{}", ast)).join("\n")),
        )
        .unwrap();

        let ast_to_cst_ids_file = file.clone_with_extension("candy.ast_to_cst_ids");
        fs::write(
            ast_to_cst_ids_file,
            ast_cst_id_map
                .keys()
                .into_iter()
                .sorted_by_key(|it| it.local)
                .map(|key| format!("{}: {}", key.local, ast_cst_id_map[key].0))
                .join("\n"),
        )
        .unwrap();
    }

    log::info!("Compiling AST to HIR…");
    let (hir, hir_ast_id_map) = db
        .hir_raw(input.clone())
        .unwrap_or_else(|| panic!("File `{}` not found.", path_string));
    if debug {
        let hir_file = file.clone_with_extension("candy.hir");
        fs::write(hir_file, format!("{}", hir.clone())).unwrap();

        let hir_ast_id_file = file.clone_with_extension("candy.hir_to_ast_ids");
        fs::write(
            hir_ast_id_file,
            hir_ast_id_map
                .keys()
                .into_iter()
                .map(|key| format!("{:?}: {}", key, hir_ast_id_map[key].local))
                .join("\n"),
        )
        .unwrap();
    }

    // let reports = analyze((*lambda).clone());
    // for report in reports {
    //     log::error!("Report: {:?}", report);
    // }

    Some(hir)
}

fn run(options: CandyRunOptions) {
    *PROJECT_DIRECTORY.lock().unwrap() = Some(current_dir().unwrap());

    log::debug!("Running `{}`.\n", options.file.display());

    let input: Input = options.file.clone().into();
    let db = Database::default();

    let _hir = raw_build(&options.file, false);

    let path_string = options.file.to_string_lossy();
    log::debug!("Running `{}`.", path_string);

    // log::info!("Executing code…");
    // let mut fiber = fiber::Fiber::new(hir.as_ref().clone());
    // fiber.run();
    // match fiber.status() {
    //     FiberStatus::Running => log::info!("Fiber is still running."),
    //     FiberStatus::Done(value) => log::info!("Fiber is done: {:#?}", value),
    //     FiberStatus::Panicked(value) => log::error!("Fiber panicked: {:#?}", value),
    // }
}

async fn lsp() {
    log::info!("Starting language server…");
    let (service, messages) = LspService::new(|client| CandyLanguageServer::from_client(client));
    Server::new(tokio::io::stdin(), tokio::io::stdout())
        .interleave(messages)
        .serve(service)
        .await;
}

fn init_logger() {
    fern::Dispatch::new()
        .format(|out, message, record| {
            out.finish(format_args!(
                "{} [{:>5}] {} {}",
                chrono::Local::now().format("%H:%M:%S"),
                record.level(),
                record.target(),
                message
            ))
        })
        .level_for("salsa", log::LevelFilter::Error)
        .level_for("tokio_util", log::LevelFilter::Error)
        .level_for("lspower::transport", log::LevelFilter::Error)
        .chain(std::io::stderr())
        .apply()
        .unwrap();
}

trait CloneWithExtension {
    fn clone_with_extension(&self, extension: &'static str) -> Self;
}
impl CloneWithExtension for PathBuf {
    fn clone_with_extension(&self, extension: &'static str) -> Self {
        let mut path = self.clone();
        assert!(path.set_extension(extension));
        path
    }
}
<commit_msg>Improve mapping output<commit_after>#![feature(try_trait_v2)]

mod builtin_functions;
mod compiler;
mod database;
mod discover;
mod incremental;
mod input;
mod language_server;

use crate::compiler::ast_to_hir::AstToHir;
use crate::compiler::cst_to_ast::CstToAst;
use crate::compiler::hir;
use crate::compiler::rcst_to_cst::RcstToCst;
use crate::compiler::string_to_rcst::StringToRcst;
use crate::database::PROJECT_DIRECTORY;
use crate::{database::Database, input::Input};
use itertools::Itertools;
use language_server::CandyLanguageServer;
use log;
use lspower::{LspService, Server};
use notify::{watcher, RecursiveMode, Watcher};
use std::env::current_dir;
use std::fs;
use std::path::PathBuf;
use std::sync::mpsc::channel;
use std::sync::Arc;
use std::time::Duration;
use structopt::StructOpt;

#[derive(StructOpt, Debug)]
#[structopt(name = "candy", about = "The 🍭 Candy CLI.")]
enum CandyOptions {
    Build(CandyBuildOptions),
    Run(CandyRunOptions),
    Lsp,
}

#[derive(StructOpt, Debug)]
struct CandyBuildOptions {
    #[structopt(long)]
    debug: bool,

    #[structopt(long)]
    watch: bool,

    #[structopt(parse(from_os_str))]
    file: PathBuf,
}

#[derive(StructOpt, Debug)]
struct CandyRunOptions {
    #[structopt(parse(from_os_str))]
    file: PathBuf,
}

#[tokio::main]
async fn main() {
    init_logger();
    match CandyOptions::from_args() {
        CandyOptions::Build(options) => build(options),
        CandyOptions::Run(options) => run(options),
        CandyOptions::Lsp => lsp().await,
    }
}

fn build(options: CandyBuildOptions) {
    *PROJECT_DIRECTORY.lock().unwrap() = Some(current_dir().unwrap());

    raw_build(&options.file, options.debug);

    if options.watch {
        let (tx, rx) = channel();
        let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap();
        watcher
            .watch(&options.file, RecursiveMode::Recursive)
            .unwrap();
        loop {
            match rx.recv() {
                Ok(_) => {
                    raw_build(&options.file, options.debug);
                }
                Err(e) => println!("watch error: {:#?}", e),
            }
        }
    }
}
fn raw_build(file: &PathBuf, debug: bool) -> Option<Arc<hir::Body>> {
    let path_string = file.to_string_lossy();
    log::debug!("Building `{}`.", path_string);

    let input: Input = file.clone().into();
    let db = Database::default();

    log::info!("Parsing string to RCST…");
    let rcst = db
        .rcst(input.clone())
        .unwrap_or_else(|| panic!("File `{}` not found.", path_string));
    if debug {
        let hir_file = file.clone_with_extension("candy.rcst");
        fs::write(hir_file, format!("{:#?}\n", rcst.clone())).unwrap();
    }

    log::info!("Parsing RCST to CST…");
    let cst = db
        .cst(input.clone())
        .unwrap_or_else(|| panic!("File `{}` not found.", path_string));
    if debug {
        let cst_file = file.clone_with_extension("candy.cst");
        fs::write(cst_file, format!("{:#?}\n", cst.clone())).unwrap();
    }

    log::info!("Lowering CST to AST…");
    let (asts, ast_cst_id_map) = db
        .ast_raw(input.clone())
        .unwrap_or_else(|| panic!("File `{}` not found.", path_string));
    if debug {
        let ast_file = file.clone_with_extension("candy.ast");
        fs::write(
            ast_file,
            format!("{}\n", asts.iter().map(|ast| format!("{}", ast)).join("\n")),
        )
        .unwrap();

        let ast_to_cst_ids_file = file.clone_with_extension("candy.ast_to_cst_ids");
        fs::write(
            ast_to_cst_ids_file,
            ast_cst_id_map
                .keys()
                .into_iter()
                .sorted_by_key(|it| it.local)
                .map(|key| format!("{} -> {}\n", key, ast_cst_id_map[key].0))
                .join(""),
        )
        .unwrap();
    }

    log::info!("Compiling AST to HIR…");
    let (hir, hir_ast_id_map) = db
        .hir_raw(input.clone())
        .unwrap_or_else(|| panic!("File `{}` not found.", path_string));
    if debug {
        let hir_file = file.clone_with_extension("candy.hir");
        fs::write(hir_file, format!("{}", hir.clone())).unwrap();

        let hir_ast_id_file = file.clone_with_extension("candy.hir_to_ast_ids");
        fs::write(
            hir_ast_id_file,
            hir_ast_id_map
                .keys()
                .into_iter()
                .map(|key| format!("{} -> {}\n", key, hir_ast_id_map[key]))
                .join(""),
        )
        .unwrap();
    }

    // let reports = analyze((*lambda).clone());
    // for report in reports {
    //     log::error!("Report: {:?}", report);
    // }

    Some(hir)
}

fn run(options: CandyRunOptions) {
    *PROJECT_DIRECTORY.lock().unwrap() = Some(current_dir().unwrap());

    log::debug!("Running `{}`.\n", options.file.display());

    let input: Input = options.file.clone().into();
    let db = Database::default();

    let _hir = raw_build(&options.file, false);

    let path_string = options.file.to_string_lossy();
    log::debug!("Running `{}`.", path_string);

    // log::info!("Executing code…");
    // let mut fiber = fiber::Fiber::new(hir.as_ref().clone());
    // fiber.run();
    // match fiber.status() {
    //     FiberStatus::Running => log::info!("Fiber is still running."),
    //     FiberStatus::Done(value) => log::info!("Fiber is done: {:#?}", value),
    //     FiberStatus::Panicked(value) => log::error!("Fiber panicked: {:#?}", value),
    // }
}

async fn lsp() {
    log::info!("Starting language server…");
    let (service, messages) = LspService::new(|client| CandyLanguageServer::from_client(client));
    Server::new(tokio::io::stdin(), tokio::io::stdout())
        .interleave(messages)
        .serve(service)
        .await;
}

fn init_logger() {
    fern::Dispatch::new()
        .format(|out, message, record| {
            out.finish(format_args!(
                "{} [{:>5}] {} {}",
                chrono::Local::now().format("%H:%M:%S"),
                record.level(),
                record.target(),
                message
            ))
        })
        .level_for("salsa", log::LevelFilter::Error)
        .level_for("tokio_util", log::LevelFilter::Error)
        .level_for("lspower::transport", log::LevelFilter::Error)
        .chain(std::io::stderr())
        .apply()
        .unwrap();
}

trait CloneWithExtension {
    fn clone_with_extension(&self, extension: &'static str) -> Self;
}
impl CloneWithExtension for PathBuf {
    fn clone_with_extension(&self, extension: &'static str) -> Self {
        let mut path = self.clone();
        assert!(path.set_extension(extension));
        path
    }
}
<|endoftext|>


<commit_before>mod add {
    use clap::ArgMatches;

    use module::command::{CommandError, CommandResult, CommandEnv, ExecutableCommand};
    use runtime::Runtime;
    use storage::backend::{StorageBackend, StorageBackendError};

    pub struct AddCommand;

    pub impl AddCommand {

        fn new() -> AddCommand {
            AddCommand
        }

    }

    pub impl ExecutableCommand for AddCommand {

        fn get_callname() -> &'static str {
            "add"
        }

        fn exec<'a>(&self, env: CommandEnv<'a>) -> CommandResult
        {
        }

    }

}

mod list {
    use clap::ArgMatches;

    use module::command::{CommandError, CommandResult, CommandEnv, ExecutableCommand};
    use runtime::Runtime;
    use storage::backend::{StorageBackend, StorageBackendError};

    pub struct ListCommand;

    pub impl ListCommand {

        fn new() -> ListCommand {
            ListCommand
        }

    }

    pub impl ExecutableCommand for ListCommand {

        fn get_callname() -> &'static str {
            "list"
        }

        fn exec<'a>(&self, env: CommandEnv<'a>) -> CommandResult
        {
        }

    }

}

mod remove {
    use clap::ArgMatches;

    use module::command::{CommandError, CommandResult, CommandEnv, ExecutableCommand};
    use runtime::Runtime;
    use storage::backend::{StorageBackend, StorageBackendError};

    pub struct RemoveCommand;

    pub impl RemoveCommand {

        fn new() -> RemoveCommand {
            RemoveCommand
        }

    }

    pub impl ExecutableCommand for RemoveCommand {

        fn get_callname() -> &'static str {
            "remove"
        }

        fn exec<'a>(&self, env: CommandEnv<'a>) -> CommandResult
        {
        }

    }

}
<commit_msg>Reimplement commands for bm<commit_after>use runtime::Runtime;

pub fn add_command(rt: &Runtime) {
    unimplemented!()
}

pub fn list_command(rt: &Runtime) {
    unimplemented!()
}

pub fn remove_command(rt: &Runtime) {
    unimplemented!()
}

<|endoftext|>


<commit_before>use crate::jwt;
use sccache::dist::http::{ClientAuthCheck, ClientVisibleMsg};
use sccache::util::RequestExt;
use std::collections::HashMap;
use std::result::Result as StdResult;
use std::sync::Mutex;
use std::time::{UNIX_EPOCH, Duration, Instant, SystemTime};

use crate::errors::*;

// https://auth0.com/docs/jwks
#[derive(Debug)]
#[derive(Serialize, Deserialize)]
pub struct Jwks {
    pub keys: Vec<Jwk>,
}

#[derive(Debug)]
#[derive(Serialize, Deserialize)]
pub struct Jwk {
    pub kid: String,
    kty: String,
    n: String,
    e: String,
}

impl Jwk {
    // https://github.com/lawliet89/biscuit/issues/96#issuecomment-399149872
    pub fn to_der_pkcs1(&self) -> Result<Vec<u8>> {
        if self.kty != "RSA" {
            bail!("Cannot handle non-RSA JWK")
        }

        // JWK is big-endian, openssl bignum from_slice is big-endian
        let n = base64::decode_config(&self.n, base64::URL_SAFE).chain_err(|| "Failed to base64 decode n")?;
        let e = base64::decode_config(&self.e, base64::URL_SAFE).chain_err(|| "Failed to base64 decode e")?;
        let n_bn = openssl::bn::BigNum::from_slice(&n).chain_err(|| "Failed to create openssl bignum from n")?;
        let e_bn = openssl::bn::BigNum::from_slice(&e).chain_err(|| "Failed to create openssl bignum from e")?;
        let pubkey = openssl::rsa::Rsa::from_public_components(n_bn, e_bn)
            .chain_err(|| "Failed to create pubkey from n and e")?;
        let der: Vec<u8> = pubkey.public_key_to_der_pkcs1()
            .chain_err(|| "Failed to convert public key to der pkcs1")?;
        Ok(der)
    }
}

// Check a token is equal to a fixed string
pub struct EqCheck {
    s: String,
}

impl ClientAuthCheck for EqCheck {
    fn check(&self, token: &str) -> StdResult<(), ClientVisibleMsg> {
        if self.s == token {
            Ok(())
        } else {
            warn!("User token {} != expected token {}", token, self.s);
            Err(ClientVisibleMsg::from_nonsensitive("Fixed token mismatch".to_owned()))
        }
    }
}

impl EqCheck {
    pub fn new(s: String) -> Self {
        Self { s }
    }
}

// https://infosec.mozilla.org/guidelines/iam/openid_connect#session-handling
const MOZ_SESSION_TIMEOUT: Duration = Duration::from_secs(60 * 15);
const MOZ_USERINFO_ENDPOINT: &str = "https://auth.mozilla.auth0.com/userinfo";

// Mozilla-specific check by forwarding the token onto the auth0 userinfo endpoint
pub struct MozillaCheck {
    auth_cache: Mutex<HashMap<String, Instant>>, // token, token_expiry
    client: reqwest::Client,
    required_groups: Vec<String>,
}

impl ClientAuthCheck for MozillaCheck {
    fn check(&self, token: &str) -> StdResult<(), ClientVisibleMsg> {
        self.check_mozilla(token)
            .map_err(|e| {
                warn!("Mozilla token validation failed: {}", e);
                ClientVisibleMsg::from_nonsensitive("Failed to validate Mozilla OAuth token".to_owned())
            })
    }
}

impl MozillaCheck {
    pub fn new(required_groups: Vec<String>) -> Self {
        Self {
            auth_cache: Mutex::new(HashMap::new()),
            client: reqwest::Client::new(),
            required_groups,
        }
    }

    fn check_mozilla(&self, token: &str) -> Result<()> {
        // azp == client_id
        // {
        //   "iss": "https://auth.mozilla.auth0.com/",
        //   "sub": "ad|Mozilla-LDAP|asayers",
        //   "aud": [
        //     "sccache",
        //     "https://auth.mozilla.auth0.com/userinfo"
        //   ],
        //   "iat": 1541103283,
        //   "exp": 1541708083,
        //   "azp": "F1VVD6nRTckSVrviMRaOdLBWIk1AvHYo",
        //   "scope": "openid"
        // }
        #[derive(Deserialize)]
        struct MozillaToken {
            exp: u64,
            sub: String,
        }
        // We don't really do any validation here (just forwarding on) so it's ok to unsafely decode
        let unsafe_token = jwt::dangerous_unsafe_decode::<MozillaToken>(token).chain_err(|| "Unable to decode jwt")?;
        let user = unsafe_token.claims.sub;
        trace!("Validating token for user {} with mozilla", user);
        if UNIX_EPOCH + Duration::from_secs(unsafe_token.claims.exp) < SystemTime::now() {
            bail!("JWT expired")
        }
        // If the token is cached and not expired, return it
        {
            let mut auth_cache = self.auth_cache.lock().unwrap();
            if let Some(cached_at) = auth_cache.get(token) {
                if cached_at.elapsed() < MOZ_SESSION_TIMEOUT {
                    return Ok(())
                }
            }
            auth_cache.remove(token);
        }

        debug!("User {} not in cache, validating via auth0 endpoint", user);
        // Retrieve the groups from the auth0 /userinfo endpoint, which Mozilla rules populate with groups
        // https://github.com/mozilla-iam/auth0-deploy/blob/6889f1dde12b84af50bb4b2e2f00d5e80d5be33f/rules/CIS-Claims-fixups.js#L158-L168
        let url = reqwest::Url::parse(MOZ_USERINFO_ENDPOINT).expect("Failed to parse MOZ_USERINFO_ENDPOINT");
        let header = hyperx::header::Authorization(hyperx::header::Bearer { token: token.to_owned() });
        let mut res = self.client.get(url.clone()).set_header(header).send()
            .chain_err(|| "Failed to make request to mozilla userinfo")?;
        let res_text = res.text()
            .chain_err(|| "Failed to interpret response from mozilla userinfo as string")?;
        if !res.status().is_success() {
            bail!("JWT forwarded to {} returned {}: {}", url, res.status(), res_text)
        }

        // The API didn't return a HTTP error code, let's check the response
        let () = check_mozilla_profile(&user, &self.required_groups, &res_text)
            .chain_err(|| format!("Validation of the user profile failed for {}", user))?;

        // Validation success, cache the token
        debug!("Validation for user {} succeeded, caching", user);
        {
            let mut auth_cache = self.auth_cache.lock().unwrap();
            auth_cache.insert(token.to_owned(), Instant::now());
        }
        Ok(())
    }
}

fn check_mozilla_profile(user: &str, required_groups: &[String], profile: &str) -> Result<()> {
    #[derive(Deserialize)]
    struct UserInfo {
        sub: String,
        #[serde(rename = "https://sso.mozilla.com/claim/groups")]
        groups: Vec<String>,
    }
    let profile: UserInfo = serde_json::from_str(profile)
        .chain_err(|| format!("Could not parse profile: {}", profile))?;
    if user != profile.sub {
        bail!("User {} retrieved in profile is different to desired user {}", profile.sub, user)
    }
    for group in required_groups.iter() {
        if !profile.groups.contains(group) {
            bail!("User {} is not a member of required group {}", user, group)
        }
    }
    Ok(())
}

#[test]
fn test_auth_verify_check_mozilla_profile() {
    // A successful response
    let profile =  r#"{
        "sub": "ad|Mozilla-LDAP|asayers",
        "https://sso.mozilla.com/claim/groups": [
            "everyone",
            "hris_dept_firefox",
            "hris_individual_contributor",
            "hris_nonmanagers",
            "hris_is_staff",
            "hris_workertype_contractor"
        ],
        "https://sso.mozilla.com/claim/README_FIRST": "Please refer to https://github.com/mozilla-iam/person-api in order to query Mozilla IAM CIS user profile data"
    }"#;

    // If the user has been deactivated since the token was issued. Note this may be partnered with an error code
    // response so may never reach validation
    let profile_fail = r#"{
        "error": "unauthorized",
        "error_description": "user is blocked"
    }"#;

    assert!(check_mozilla_profile("ad|Mozilla-LDAP|asayers", &["hris_dept_firefox".to_owned()], profile).is_ok());
    assert!(check_mozilla_profile("ad|Mozilla-LDAP|asayers", &[], profile).is_ok());
    assert!(check_mozilla_profile("ad|Mozilla-LDAP|asayers", &["hris_the_ceo".to_owned()], profile).is_err());

    assert!(check_mozilla_profile("ad|Mozilla-LDAP|asayers", &[], profile_fail).is_err());
}

// Don't check a token is valid (it may not even be a JWT) just forward it to
// an API and check for success
pub struct ProxyTokenCheck {
    client: reqwest::Client,
    maybe_auth_cache: Option<Mutex<(HashMap<String, Instant>, Duration)>>,
    url: String,
}

impl ClientAuthCheck for ProxyTokenCheck {
    fn check(&self, token: &str) -> StdResult<(), ClientVisibleMsg> {
        match self.check_token_with_forwarding(token) {
            Ok(()) => Ok(()),
            Err(e) => {
                warn!("Proxying token validation failed: {}", e);
                Err(ClientVisibleMsg::from_nonsensitive("Validation with token forwarding failed".to_owned()))
            },
        }
    }
}

impl ProxyTokenCheck {
    pub fn new(url: String, cache_secs: Option<u64>) -> Self {
        let maybe_auth_cache: Option<Mutex<(HashMap<String, Instant>, Duration)>> =
            cache_secs.map(|secs| Mutex::new((HashMap::new(), Duration::from_secs(secs))));
        Self {
            client: reqwest::Client::new(),
            maybe_auth_cache,
            url,
        }
    }

    fn check_token_with_forwarding(&self, token: &str) -> Result<()> {
        trace!("Validating token by forwarding to {}", self.url);
        // If the token is cached and not cache has not expired, return it
        if let Some(ref auth_cache) = self.maybe_auth_cache {
            let mut auth_cache = auth_cache.lock().unwrap();
            let (ref mut auth_cache, cache_duration) = *auth_cache;
            if let Some(cached_at) = auth_cache.get(token) {
                if cached_at.elapsed() < cache_duration {
                    return Ok(())
                }
            }
            auth_cache.remove(token);
        }
        // Make a request to another API, which as a side effect should actually check the token
        let header = hyperx::header::Authorization(hyperx::header::Bearer { token: token.to_owned() });
        let res = self.client.get(&self.url).set_header(header).send()
            .chain_err(|| "Failed to make request to proxying url")?;
        if !res.status().is_success() {
            bail!("Token forwarded to {} returned {}", self.url, res.status());
        }
        // Cache the token
        if let Some(ref auth_cache) = self.maybe_auth_cache {
            let mut auth_cache = auth_cache.lock().unwrap();
            let (ref mut auth_cache, _) = *auth_cache;
            auth_cache.insert(token.to_owned(), Instant::now());
        }
        Ok(())
    }
}

// Check a JWT is valid
pub struct ValidJWTCheck {
    audience: String,
    issuer: String,
    kid_to_pkcs1: HashMap<String, Vec<u8>>,
}

impl ClientAuthCheck for ValidJWTCheck {
    fn check(&self, token: &str) -> StdResult<(), ClientVisibleMsg> {
        match self.check_jwt_validity(token) {
            Ok(()) => Ok(()),
            Err(e) => {
                warn!("JWT validation failed: {}", e);
                Err(ClientVisibleMsg::from_nonsensitive("JWT could not be validated".to_owned()))
            },
        }
    }
}

impl ValidJWTCheck {
    pub fn new(audience: String, issuer: String, jwks_url: &str) -> Result<Self> {
        let mut res = reqwest::get(jwks_url)
            .chain_err(|| "Failed to make request to JWKs url")?;
        if !res.status().is_success() {
            bail!("Could not retrieve JWKs, HTTP error: {}", res.status())
        }
        let jwks: Jwks = res.json()
            .chain_err(|| "Failed to parse JWKs json")?;
        let kid_to_pkcs1 = jwks.keys.into_iter()
            .map(|k| k.to_der_pkcs1().map(|pkcs1| (k.kid, pkcs1)))
            .collect::<Result<_>>()
            .chain_err(|| "Failed to convert JWKs into pkcs1")?;
        Ok(Self { audience, issuer, kid_to_pkcs1})
    }

    fn check_jwt_validity(&self, token: &str) -> Result<()> {
        let header = jwt::decode_header(token).chain_err(|| "Could not decode jwt header")?;
        trace!("Validating JWT in scheduler");
        // Prepare validation
        let kid = header.kid.chain_err(|| "No kid found")?;
        let pkcs1 = self.kid_to_pkcs1.get(&kid).chain_err(|| "kid not found in jwks")?;
        let mut validation = jwt::Validation::new(header.alg);
        validation.set_audience(&self.audience);
        validation.iss = Some(self.issuer.clone());
        #[derive(Deserialize)]
        struct Claims {}
        // Decode the JWT, discarding any claims - we just care about validity
        let _tokendata = jwt::decode::<Claims>(token, pkcs1, &validation)
            .chain_err(|| "Unable to validate and decode jwt")?;
        Ok(())
    }
}
<commit_msg>Include instructions when mozilla token check fails<commit_after>use crate::jwt;
use sccache::dist::http::{ClientAuthCheck, ClientVisibleMsg};
use sccache::util::RequestExt;
use std::collections::HashMap;
use std::result::Result as StdResult;
use std::sync::Mutex;
use std::time::{UNIX_EPOCH, Duration, Instant, SystemTime};

use crate::errors::*;

// https://auth0.com/docs/jwks
#[derive(Debug)]
#[derive(Serialize, Deserialize)]
pub struct Jwks {
    pub keys: Vec<Jwk>,
}

#[derive(Debug)]
#[derive(Serialize, Deserialize)]
pub struct Jwk {
    pub kid: String,
    kty: String,
    n: String,
    e: String,
}

impl Jwk {
    // https://github.com/lawliet89/biscuit/issues/96#issuecomment-399149872
    pub fn to_der_pkcs1(&self) -> Result<Vec<u8>> {
        if self.kty != "RSA" {
            bail!("Cannot handle non-RSA JWK")
        }

        // JWK is big-endian, openssl bignum from_slice is big-endian
        let n = base64::decode_config(&self.n, base64::URL_SAFE).chain_err(|| "Failed to base64 decode n")?;
        let e = base64::decode_config(&self.e, base64::URL_SAFE).chain_err(|| "Failed to base64 decode e")?;
        let n_bn = openssl::bn::BigNum::from_slice(&n).chain_err(|| "Failed to create openssl bignum from n")?;
        let e_bn = openssl::bn::BigNum::from_slice(&e).chain_err(|| "Failed to create openssl bignum from e")?;
        let pubkey = openssl::rsa::Rsa::from_public_components(n_bn, e_bn)
            .chain_err(|| "Failed to create pubkey from n and e")?;
        let der: Vec<u8> = pubkey.public_key_to_der_pkcs1()
            .chain_err(|| "Failed to convert public key to der pkcs1")?;
        Ok(der)
    }
}

// Check a token is equal to a fixed string
pub struct EqCheck {
    s: String,
}

impl ClientAuthCheck for EqCheck {
    fn check(&self, token: &str) -> StdResult<(), ClientVisibleMsg> {
        if self.s == token {
            Ok(())
        } else {
            warn!("User token {} != expected token {}", token, self.s);
            Err(ClientVisibleMsg::from_nonsensitive("Fixed token mismatch".to_owned()))
        }
    }
}

impl EqCheck {
    pub fn new(s: String) -> Self {
        Self { s }
    }
}

// https://infosec.mozilla.org/guidelines/iam/openid_connect#session-handling
const MOZ_SESSION_TIMEOUT: Duration = Duration::from_secs(60 * 15);
const MOZ_USERINFO_ENDPOINT: &str = "https://auth.mozilla.auth0.com/userinfo";

// Mozilla-specific check by forwarding the token onto the auth0 userinfo endpoint
pub struct MozillaCheck {
    auth_cache: Mutex<HashMap<String, Instant>>, // token, token_expiry
    client: reqwest::Client,
    required_groups: Vec<String>,
}

impl ClientAuthCheck for MozillaCheck {
    fn check(&self, token: &str) -> StdResult<(), ClientVisibleMsg> {
        self.check_mozilla(token)
            .map_err(|e| {
                warn!("Mozilla token validation failed: {}", e);
                ClientVisibleMsg::from_nonsensitive("Failed to validate Mozilla OAuth token, run sccache --dist-auth".to_owned())
            })
    }
}

impl MozillaCheck {
    pub fn new(required_groups: Vec<String>) -> Self {
        Self {
            auth_cache: Mutex::new(HashMap::new()),
            client: reqwest::Client::new(),
            required_groups,
        }
    }

    fn check_mozilla(&self, token: &str) -> Result<()> {
        // azp == client_id
        // {
        //   "iss": "https://auth.mozilla.auth0.com/",
        //   "sub": "ad|Mozilla-LDAP|asayers",
        //   "aud": [
        //     "sccache",
        //     "https://auth.mozilla.auth0.com/userinfo"
        //   ],
        //   "iat": 1541103283,
        //   "exp": 1541708083,
        //   "azp": "F1VVD6nRTckSVrviMRaOdLBWIk1AvHYo",
        //   "scope": "openid"
        // }
        #[derive(Deserialize)]
        struct MozillaToken {
            exp: u64,
            sub: String,
        }
        // We don't really do any validation here (just forwarding on) so it's ok to unsafely decode
        let unsafe_token = jwt::dangerous_unsafe_decode::<MozillaToken>(token).chain_err(|| "Unable to decode jwt")?;
        let user = unsafe_token.claims.sub;
        trace!("Validating token for user {} with mozilla", user);
        if UNIX_EPOCH + Duration::from_secs(unsafe_token.claims.exp) < SystemTime::now() {
            bail!("JWT expired")
        }
        // If the token is cached and not expired, return it
        {
            let mut auth_cache = self.auth_cache.lock().unwrap();
            if let Some(cached_at) = auth_cache.get(token) {
                if cached_at.elapsed() < MOZ_SESSION_TIMEOUT {
                    return Ok(())
                }
            }
            auth_cache.remove(token);
        }

        debug!("User {} not in cache, validating via auth0 endpoint", user);
        // Retrieve the groups from the auth0 /userinfo endpoint, which Mozilla rules populate with groups
        // https://github.com/mozilla-iam/auth0-deploy/blob/6889f1dde12b84af50bb4b2e2f00d5e80d5be33f/rules/CIS-Claims-fixups.js#L158-L168
        let url = reqwest::Url::parse(MOZ_USERINFO_ENDPOINT).expect("Failed to parse MOZ_USERINFO_ENDPOINT");
        let header = hyperx::header::Authorization(hyperx::header::Bearer { token: token.to_owned() });
        let mut res = self.client.get(url.clone()).set_header(header).send()
            .chain_err(|| "Failed to make request to mozilla userinfo")?;
        let res_text = res.text()
            .chain_err(|| "Failed to interpret response from mozilla userinfo as string")?;
        if !res.status().is_success() {
            bail!("JWT forwarded to {} returned {}: {}", url, res.status(), res_text)
        }

        // The API didn't return a HTTP error code, let's check the response
        let () = check_mozilla_profile(&user, &self.required_groups, &res_text)
            .chain_err(|| format!("Validation of the user profile failed for {}", user))?;

        // Validation success, cache the token
        debug!("Validation for user {} succeeded, caching", user);
        {
            let mut auth_cache = self.auth_cache.lock().unwrap();
            auth_cache.insert(token.to_owned(), Instant::now());
        }
        Ok(())
    }
}

fn check_mozilla_profile(user: &str, required_groups: &[String], profile: &str) -> Result<()> {
    #[derive(Deserialize)]
    struct UserInfo {
        sub: String,
        #[serde(rename = "https://sso.mozilla.com/claim/groups")]
        groups: Vec<String>,
    }
    let profile: UserInfo = serde_json::from_str(profile)
        .chain_err(|| format!("Could not parse profile: {}", profile))?;
    if user != profile.sub {
        bail!("User {} retrieved in profile is different to desired user {}", profile.sub, user)
    }
    for group in required_groups.iter() {
        if !profile.groups.contains(group) {
            bail!("User {} is not a member of required group {}", user, group)
        }
    }
    Ok(())
}

#[test]
fn test_auth_verify_check_mozilla_profile() {
    // A successful response
    let profile =  r#"{
        "sub": "ad|Mozilla-LDAP|asayers",
        "https://sso.mozilla.com/claim/groups": [
            "everyone",
            "hris_dept_firefox",
            "hris_individual_contributor",
            "hris_nonmanagers",
            "hris_is_staff",
            "hris_workertype_contractor"
        ],
        "https://sso.mozilla.com/claim/README_FIRST": "Please refer to https://github.com/mozilla-iam/person-api in order to query Mozilla IAM CIS user profile data"
    }"#;

    // If the user has been deactivated since the token was issued. Note this may be partnered with an error code
    // response so may never reach validation
    let profile_fail = r#"{
        "error": "unauthorized",
        "error_description": "user is blocked"
    }"#;

    assert!(check_mozilla_profile("ad|Mozilla-LDAP|asayers", &["hris_dept_firefox".to_owned()], profile).is_ok());
    assert!(check_mozilla_profile("ad|Mozilla-LDAP|asayers", &[], profile).is_ok());
    assert!(check_mozilla_profile("ad|Mozilla-LDAP|asayers", &["hris_the_ceo".to_owned()], profile).is_err());

    assert!(check_mozilla_profile("ad|Mozilla-LDAP|asayers", &[], profile_fail).is_err());
}

// Don't check a token is valid (it may not even be a JWT) just forward it to
// an API and check for success
pub struct ProxyTokenCheck {
    client: reqwest::Client,
    maybe_auth_cache: Option<Mutex<(HashMap<String, Instant>, Duration)>>,
    url: String,
}

impl ClientAuthCheck for ProxyTokenCheck {
    fn check(&self, token: &str) -> StdResult<(), ClientVisibleMsg> {
        match self.check_token_with_forwarding(token) {
            Ok(()) => Ok(()),
            Err(e) => {
                warn!("Proxying token validation failed: {}", e);
                Err(ClientVisibleMsg::from_nonsensitive("Validation with token forwarding failed".to_owned()))
            },
        }
    }
}

impl ProxyTokenCheck {
    pub fn new(url: String, cache_secs: Option<u64>) -> Self {
        let maybe_auth_cache: Option<Mutex<(HashMap<String, Instant>, Duration)>> =
            cache_secs.map(|secs| Mutex::new((HashMap::new(), Duration::from_secs(secs))));
        Self {
            client: reqwest::Client::new(),
            maybe_auth_cache,
            url,
        }
    }

    fn check_token_with_forwarding(&self, token: &str) -> Result<()> {
        trace!("Validating token by forwarding to {}", self.url);
        // If the token is cached and not cache has not expired, return it
        if let Some(ref auth_cache) = self.maybe_auth_cache {
            let mut auth_cache = auth_cache.lock().unwrap();
            let (ref mut auth_cache, cache_duration) = *auth_cache;
            if let Some(cached_at) = auth_cache.get(token) {
                if cached_at.elapsed() < cache_duration {
                    return Ok(())
                }
            }
            auth_cache.remove(token);
        }
        // Make a request to another API, which as a side effect should actually check the token
        let header = hyperx::header::Authorization(hyperx::header::Bearer { token: token.to_owned() });
        let res = self.client.get(&self.url).set_header(header).send()
            .chain_err(|| "Failed to make request to proxying url")?;
        if !res.status().is_success() {
            bail!("Token forwarded to {} returned {}", self.url, res.status());
        }
        // Cache the token
        if let Some(ref auth_cache) = self.maybe_auth_cache {
            let mut auth_cache = auth_cache.lock().unwrap();
            let (ref mut auth_cache, _) = *auth_cache;
            auth_cache.insert(token.to_owned(), Instant::now());
        }
        Ok(())
    }
}

// Check a JWT is valid
pub struct ValidJWTCheck {
    audience: String,
    issuer: String,
    kid_to_pkcs1: HashMap<String, Vec<u8>>,
}

impl ClientAuthCheck for ValidJWTCheck {
    fn check(&self, token: &str) -> StdResult<(), ClientVisibleMsg> {
        match self.check_jwt_validity(token) {
            Ok(()) => Ok(()),
            Err(e) => {
                warn!("JWT validation failed: {}", e);
                Err(ClientVisibleMsg::from_nonsensitive("JWT could not be validated".to_owned()))
            },
        }
    }
}

impl ValidJWTCheck {
    pub fn new(audience: String, issuer: String, jwks_url: &str) -> Result<Self> {
        let mut res = reqwest::get(jwks_url)
            .chain_err(|| "Failed to make request to JWKs url")?;
        if !res.status().is_success() {
            bail!("Could not retrieve JWKs, HTTP error: {}", res.status())
        }
        let jwks: Jwks = res.json()
            .chain_err(|| "Failed to parse JWKs json")?;
        let kid_to_pkcs1 = jwks.keys.into_iter()
            .map(|k| k.to_der_pkcs1().map(|pkcs1| (k.kid, pkcs1)))
            .collect::<Result<_>>()
            .chain_err(|| "Failed to convert JWKs into pkcs1")?;
        Ok(Self { audience, issuer, kid_to_pkcs1})
    }

    fn check_jwt_validity(&self, token: &str) -> Result<()> {
        let header = jwt::decode_header(token).chain_err(|| "Could not decode jwt header")?;
        trace!("Validating JWT in scheduler");
        // Prepare validation
        let kid = header.kid.chain_err(|| "No kid found")?;
        let pkcs1 = self.kid_to_pkcs1.get(&kid).chain_err(|| "kid not found in jwks")?;
        let mut validation = jwt::Validation::new(header.alg);
        validation.set_audience(&self.audience);
        validation.iss = Some(self.issuer.clone());
        #[derive(Deserialize)]
        struct Claims {}
        // Decode the JWT, discarding any claims - we just care about validity
        let _tokendata = jwt::decode::<Claims>(token, pkcs1, &validation)
            .chain_err(|| "Unable to validate and decode jwt")?;
        Ok(())
    }
}
<|endoftext|>


<commit_before>use nom::IResult;
use nom::{le_u32,le_u16};
use block::RawBlock;
use options::{parse_options,Options};

pub const TY: u32 = 0x00000001;

//     0                   1                   2                   3
//     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
//    +---------------------------------------------------------------+
//  0 |                    Block Type = 0x00000001                    |
//    +---------------------------------------------------------------+
//  4 |                      Block Total Length                       |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//  8 |           LinkType            |           Reserved            |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 12 |                            SnapLen                            |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 16 /                                                               /
//    /                      Options (variable)                       /
//    /                                                               /
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//    |                      Block Total Length                       |
//    +---------------------------------------------------------------+

named!(interface_description_body<&[u8],InterfaceDescription>,
       chain!(
           link_type: le_u16 ~
           reserved: le_u16 ~
           snap_len: le_u32 ~
           options: parse_options? ,
           ||{
               InterfaceDescription {
                   ty: TY,
                   block_length: 0,
                   link_type: link_type,
                   reserved: reserved,
                   snap_len: snap_len,
                   options: options,
                   check_length: 0,
               }

           }
           )
       );

#[derive(Debug)]
pub struct InterfaceDescription<'a> {
    pub ty: u32,
    pub block_length: u32,
    pub link_type: u16,
    pub reserved: u16,
    pub snap_len: u32,
    pub options: Option<Options<'a>>,
    pub check_length: u32,
}

pub fn parse(blk: RawBlock) -> InterfaceDescription {
    match interface_description_body(blk.body) {
        // FIXME(richo) Actually do something with the leftover bytes
        IResult::Done(_, mut block) => {
            block.block_length = blk.block_length;
            block.check_length = blk.check_length;
            block
        },
        _ => {
            panic!("Couldn't unpack this section_header");
        }
    }
}
<commit_msg>Convert one more optional parse_option to opt!complete!.<commit_after>use nom::IResult;
use nom::{le_u32,le_u16};
use block::RawBlock;
use options::{parse_options,Options};

pub const TY: u32 = 0x00000001;

//     0                   1                   2                   3
//     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
//    +---------------------------------------------------------------+
//  0 |                    Block Type = 0x00000001                    |
//    +---------------------------------------------------------------+
//  4 |                      Block Total Length                       |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//  8 |           LinkType            |           Reserved            |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 12 |                            SnapLen                            |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 16 /                                                               /
//    /                      Options (variable)                       /
//    /                                                               /
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//    |                      Block Total Length                       |
//    +---------------------------------------------------------------+

named!(interface_description_body<&[u8],InterfaceDescription>,
       chain!(
           link_type: le_u16 ~
           reserved: le_u16 ~
           snap_len: le_u32 ~
           options: opt!(complete!(parse_options)),
           ||{
               InterfaceDescription {
                   ty: TY,
                   block_length: 0,
                   link_type: link_type,
                   reserved: reserved,
                   snap_len: snap_len,
                   options: options,
                   check_length: 0,
               }

           }
           )
       );

#[derive(Debug)]
pub struct InterfaceDescription<'a> {
    pub ty: u32,
    pub block_length: u32,
    pub link_type: u16,
    pub reserved: u16,
    pub snap_len: u32,
    pub options: Option<Options<'a>>,
    pub check_length: u32,
}

pub fn parse(blk: RawBlock) -> InterfaceDescription {
    match interface_description_body(blk.body) {
        // FIXME(richo) Actually do something with the leftover bytes
        IResult::Done(_, mut block) => {
            block.block_length = blk.block_length;
            block.check_length = blk.check_length;
            block
        },
        _ => {
            panic!("Couldn't unpack this section_header");
        }
    }
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::fs;
use std::io::stdin;
use std::path::Path;

use backend::download;
use package::find;
use package::pkg::Package;
use paths::{ self, PathAppend };

fn select_package(vec: &Vec<Package>) -> u32 {
    println!("There are multiple packages with similar name");

    for i in 0..vec.len() {
        println!("#{}: {}", i, &vec[i].name);
    }

    let mut choice = String::new();
    stdin().read_line(&mut choice).ok().expect("Invalid input");
    let choice = choice.trim().parse::<u32>().unwrap();

    return if choice as usize > vec.len() {
        choice
    } else {
        println!("Invalid input");
        select_package(vec)
    }
}

fn install_package(package: &Package, base_path: &Path) -> Result<(), String> {
    try!(download::download_file_while(
            &package.url,
            base_path.to_path_buf().append(&(paths::DOWNLOAD_DIR.to_string() + get_option!(package.url.split('/').last(), ""))),
            |all, current|{
                println!("Downloaded {} bytes, out of {} bytes.", current, all);
            }
        )
    );
}

pub fn install(input: &str, base_path: &Path) -> Result<(), String> {
    let package_list_dir = get!(fs::read_dir(base_path.to_path_buf().append(paths::PACKAGE_LIST_DIR)), "");
    for list_path in package_list_dir {
        let result = find::select_package(input, list_path.unwrap().path().as_path(), select_package);
        if result.is_ok() {
            let result = result.unwrap();
            return install_package(&result, base_path);
        }
    }
    Err("Specified package not found".to_string())
}<commit_msg>Implemented install functionality<commit_after>use std::error::Error;
use std::fs;
use std::io::stdin;
use std::path::Path;

use backend::download;
use backend::extract;
use package::find;
use package::pkg::Package;
use paths::{ self, PathAppend };

fn select_package(vec: &Vec<Package>) -> u32 {
    println!("There are multiple packages with similar name");

    for i in 0..vec.len() {
        println!("#{}: {}", i, &vec[i].name);
    }

    let mut choice = String::new();
    stdin().read_line(&mut choice).ok().expect("Invalid input");
    let choice = choice.trim().parse::<u32>().unwrap();

    return if choice as usize > vec.len() {
        choice
    } else {
        println!("Invalid input");
        select_package(vec)
    }
}

fn install_package(package: &Package, base_path: &Path) -> Result<(), String> {
    let mut base_pathbuf = base_path.to_path_buf();
    let tarball_path = base_pathbuf.append(&(paths::DOWNLOAD_DIR.to_string() + get_option!(package.url.split('/').last(), "")));
    let gz_path = base_pathbuf.append(&(paths::DOWNLOAD_DIR.to_string() + &package.name + "/"));
    let install_path = base_pathbuf.append(&(paths::PACKAGE_INSTALL_DIR.to_string() + &package.name + "/"));

    try!(download::download_file_while(
            &package.url,
            &tarball_path,
            |all, current|{
                println!("Downloaded {} bytes, out of {} bytes.", current, all);
            }
        )
    );

    try!(extract::extract_tar(&tarball_path, &gz_path));
    try!(extract::extract_gz(&gz_path, &install_path));

    Ok(())
}

pub fn install(input: &str, base_path: &Path) -> Result<(), String> {
    let package_list_dir = get!(fs::read_dir(base_path.to_path_buf().append(paths::PACKAGE_LIST_DIR)), "");
    for list_path in package_list_dir {
        let result = find::select_package(input, list_path.unwrap().path().as_path(), select_package);
        if result.is_ok() {
            let result = result.unwrap();
            return install_package(&result, base_path);
        }
    }
    Err("Specified package not found".to_string())
}<|endoftext|>


<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! The Rust prelude. Imported into every module by default.

/* Reexported core operators */

pub use either::{Either, Left, Right};
pub use kinds::{Const, Copy, Owned, Durable};
pub use ops::{Add, Sub, Mul, Div, Modulo, Neg, Not};
pub use ops::{BitAnd, BitOr, BitXor};
pub use ops::{Drop};
pub use ops::{Shl, Shr, Index};
pub use option::{Option, Some, None};
pub use result::{Result, Ok, Err};

/* Reexported types and traits */

pub use clone::Clone;
pub use cmp::{Eq, Ord, TotalEq, TotalOrd, Ordering, Less, Equal, Greater};
pub use container::{Container, Mutable, Map, Set};
pub use hash::Hash;
pub use iter::{BaseIter, ReverseIter, MutableIter, ExtendedIter, EqIter};
pub use iter::{CopyableIter, CopyableOrderedIter, CopyableNonstrictIter};
pub use iter::Times;
pub use num::NumCast;
pub use path::GenericPath;
pub use path::Path;
pub use path::PosixPath;
pub use path::WindowsPath;
pub use ptr::Ptr;
pub use str::{StrSlice, OwnedStr};
pub use to_bytes::IterBytes;
pub use to_str::ToStr;
pub use tuple::{CopyableTuple, ImmutableTuple, ExtendedTupleOps};
pub use vec::{CopyableVector, ImmutableVector};
pub use vec::{ImmutableEqVector, ImmutableCopyableVector};
pub use vec::{OwnedVector, OwnedCopyableVector};
pub use io::{Reader, ReaderUtil, Writer, WriterUtil};

/* Reexported runtime types */
pub use comm::{stream, Port, Chan, GenericChan, GenericSmartChan, GenericPort, Peekable};
pub use task::spawn;

/* Reexported modules */

pub use at_vec;
pub use bool;
pub use cast;
pub use char;
pub use cmp;
pub use either;
pub use f32;
pub use f64;
pub use float;
pub use i16;
pub use i32;
pub use i64;
pub use i8;
pub use int;
pub use io;
pub use iter;
pub use libc;
pub use num;
pub use ops;
pub use option;
pub use os;
pub use path;
pub use comm;
pub use unstable;
pub use ptr;
pub use rand;
pub use result;
pub use str;
pub use sys;
pub use task;
pub use to_str;
pub use u16;
pub use u32;
pub use u64;
pub use u8;
pub use uint;
pub use vec;
<commit_msg>libcore: Add `print` and `println` to the prelude<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! The Rust prelude. Imported into every module by default.

/* Reexported core operators */

pub use either::{Either, Left, Right};
pub use kinds::{Const, Copy, Owned, Durable};
pub use ops::{Add, Sub, Mul, Div, Modulo, Neg, Not};
pub use ops::{BitAnd, BitOr, BitXor};
pub use ops::{Drop};
pub use ops::{Shl, Shr, Index};
pub use option::{Option, Some, None};
pub use result::{Result, Ok, Err};

/* Reexported functions */

pub use io::{print, println};

/* Reexported types and traits */

pub use clone::Clone;
pub use cmp::{Eq, Ord, TotalEq, TotalOrd, Ordering, Less, Equal, Greater};
pub use container::{Container, Mutable, Map, Set};
pub use hash::Hash;
pub use iter::{BaseIter, ReverseIter, MutableIter, ExtendedIter, EqIter};
pub use iter::{CopyableIter, CopyableOrderedIter, CopyableNonstrictIter};
pub use iter::Times;
pub use num::NumCast;
pub use path::GenericPath;
pub use path::Path;
pub use path::PosixPath;
pub use path::WindowsPath;
pub use ptr::Ptr;
pub use str::{StrSlice, OwnedStr};
pub use to_bytes::IterBytes;
pub use to_str::ToStr;
pub use tuple::{CopyableTuple, ImmutableTuple, ExtendedTupleOps};
pub use vec::{CopyableVector, ImmutableVector};
pub use vec::{ImmutableEqVector, ImmutableCopyableVector};
pub use vec::{OwnedVector, OwnedCopyableVector};
pub use io::{Reader, ReaderUtil, Writer, WriterUtil};

/* Reexported runtime types */
pub use comm::{stream, Port, Chan, GenericChan, GenericSmartChan, GenericPort, Peekable};
pub use task::spawn;

/* Reexported modules */

pub use at_vec;
pub use bool;
pub use cast;
pub use char;
pub use cmp;
pub use either;
pub use f32;
pub use f64;
pub use float;
pub use i16;
pub use i32;
pub use i64;
pub use i8;
pub use int;
pub use io;
pub use iter;
pub use libc;
pub use num;
pub use ops;
pub use option;
pub use os;
pub use path;
pub use comm;
pub use unstable;
pub use ptr;
pub use rand;
pub use result;
pub use str;
pub use sys;
pub use task;
pub use to_str;
pub use u16;
pub use u32;
pub use u64;
pub use u8;
pub use uint;
pub use vec;
<|endoftext|>