macaroons 0.3.2

Cookie-like bearer credentials with caveats for distributed authorization
Documentation
extern crate macaroons;

use macaroons::caveat::Caveat;
use macaroons::token::Token;
use macaroons::v1::V1Token;
use macaroons::verifier::{Func, LinkVerifier};

const EMPTY_TAG: [u8; 32] = [0xe3, 0xd9, 0xe0, 0x29, 0x08, 0x52, 0x6c, 0x4c, 0x00, 0x39, 0xae,
                             0x15, 0x11, 0x41, 0x15, 0xd9, 0x7f, 0xdd, 0x68, 0xbf, 0x2b, 0xa3,
                             0x79, 0xb3, 0x42, 0xaa, 0xf0, 0xf6, 0x17, 0xd0, 0x55, 0x2f];

const EXPECTED_TAG_WITH_FIRST_PARTY_CAVEATS: [u8; 32] = [0x19, 0x7b, 0xac, 0x7a, 0x04, 0x4a, 0xf3,
                                                         0x33, 0x32, 0x86, 0x5b, 0x92, 0x66, 0xe2,
                                                         0x6d, 0x49, 0x3b, 0xdd, 0x66, 0x8a, 0x66,
                                                         0x0e, 0x44, 0xd8, 0x8c, 0xe1, 0xa9, 0x98,
                                                         0xc2, 0x3d, 0xbd, 0x67];

fn example_key() -> Vec<u8> {
    Vec::from("this is our super secret key; only we should know it")
}

fn invalid_key() -> Vec<u8> {
    Vec::from("this is not the key you are looking for; it is evil")
}

fn example_id() -> Vec<u8> {
    Vec::from("we used our secret key")
}

fn example_uri() -> Vec<u8> {
    Vec::from("http://mybank/")
}

fn example_first_party_caveat() -> Caveat {
    Caveat::first_party(Vec::from("test = caveat"))
}

fn example_first_party_caveat_different_prefix() -> Caveat {
    Caveat::first_party(Vec::from("other = test"))
}

fn verify_caveat(predicate: &str) -> bool {
    let (prefix, value) = predicate.split_at(7);
    
    if prefix != "test = " {
        return true;
    }

    value == "caveat"
}

fn verify_wrong_value(predicate: &str) -> bool {
    let (prefix, value) = predicate.split_at(7);
    
    if prefix != "test = " {
        return true;
    }

    value == "wrong"
}

fn verify_other(predicate: &str) -> bool {
    let (prefix, value) = predicate.split_at(7);
    
    if prefix != "other = " {
        return true;
    }

    value == "caveat"
}

fn example_caveat_key() -> Vec<u8> {
    Vec::from("4; guaranteed random by a fair toss of the dice")
}

fn example_third_party_caveat_id() -> Vec<u8> {
    Vec::from("this was how we remind auth of key/pred")
}

fn example_third_party_caveat_location() -> Vec<u8> {
    Vec::from("http://auth.mybank/")
}

fn example_third_party_caveat() -> Caveat {
    Caveat::third_party(example_caveat_key(),
                        example_third_party_caveat_id(),
                        example_third_party_caveat_location())
}

fn example_token() -> V1Token {
    V1Token::new(&example_key(), example_id(), Some(example_uri()))
}

fn example_serialized_with_first_party_caveats() -> Vec<u8> {
    Vec::from("MDAxY2xvY2F0aW9uIGh0dHA6Ly9teWJhbmsvCjAwMjZpZGVudGlmaWVyIHdlIHVzZWQgb3VyIHNlY3JldCB\
               rZXkKMDAxNmNpZCB0ZXN0ID0gY2F2ZWF0CjAwMmZzaWduYXR1cmUgGXusegRK8zMyhluSZuJtSTvdZopmDk\
               TYjOGpmMI9vWcK")
}

#[test]
fn empty_macaroon_signature() {
    let token = V1Token::new(&example_key(), example_id(), Some(example_uri()));
    assert_eq!(EMPTY_TAG, token.tag)
}

#[test]
fn signature_with_first_party_caveat() {
    let token = example_token().add_caveat(&example_first_party_caveat());
    assert_eq!(EXPECTED_TAG_WITH_FIRST_PARTY_CAVEATS, token.tag)
}

#[test]
fn signature_with_third_party_caveat() {
    let mut token = example_token();

    token = token.add_caveat(&example_first_party_caveat());
    token = token.add_caveat(&example_third_party_caveat());

    let token_serialized = token.serialize().unwrap();
    let parsed_token = V1Token::deserialize(token_serialized).unwrap();
    let third_party_caveat = &parsed_token.caveats[1];

    assert_eq!(third_party_caveat.caveat_id,
               example_third_party_caveat_id());
    assert_eq!(third_party_caveat.caveat_location,
               Some(example_third_party_caveat_location()));
}

#[test]
fn binary_serialization() {
    let token = example_token().add_caveat(&example_first_party_caveat());
    assert_eq!(example_serialized_with_first_party_caveats(),
               token.serialize().unwrap());
}

#[test]
fn binary_deserialization() {
    let token = V1Token::deserialize(example_serialized_with_first_party_caveats()).unwrap();

    assert_eq!(example_uri(), token.location.unwrap());
    assert_eq!(example_id(), token.identifier);

    assert_eq!(EXPECTED_TAG_WITH_FIRST_PARTY_CAVEATS, token.tag)
}

#[test]
fn simple_verification() {
    let token = example_token().add_caveat(&example_first_party_caveat());

    assert!(token.authenticate_without_verifying(&example_key()).is_ok(), "verifies with valid key");
    assert!(token.authenticate_without_verifying(&invalid_key()).is_err(), "doesn't verify with invalid key");
}

#[test]
fn verifying_predicates() {
    let token = example_token()
        .add_caveat(&example_first_party_caveat())
        .add_caveat(&example_first_party_caveat_different_prefix());

    let matching_verifier = Func(verify_caveat);
    assert!(token.verify(&example_key(), &matching_verifier).is_ok());
    assert!(token.verify(&invalid_key(), &matching_verifier).is_err());
    
    let non_matching_verifier = Func(verify_wrong_value);
    assert!(token.verify(&example_key(), &non_matching_verifier).is_err());
    assert!(token.verify(&invalid_key(), &non_matching_verifier).is_err());

    let multiple_verifier = Func(verify_caveat).link(Func(verify_other));
    assert!(token.verify(&example_key(), &multiple_verifier).is_ok());
}