1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
use super::{Grant, TokenGenerator};
use chrono::{Utc, TimeZone};
use std::borrow::Cow;
use rand::{thread_rng, Rng};
use ring;
use rmp_serde;
use url::Url;
use base64::{encode, decode};

pub struct RandomGenerator {
    len: usize
}

impl RandomGenerator {
    pub fn new(length: usize) -> RandomGenerator {
        RandomGenerator {len: length}
    }
}

impl TokenGenerator for RandomGenerator {
    fn generate(&self, _grant: &Grant) -> String {
        let result = thread_rng().gen_iter::<u8>().take(self.len).collect::<Vec<u8>>();
        encode(&result)
    }
}

pub struct Assertion {
    secret: ring::hmac::SigningKey,
}

#[derive(Serialize, Deserialize)]
pub struct InternalAssertionGrant<'a>(&'a str, &'a str, &'a str, &'a str, (i64, u32), &'a str);
#[derive(Serialize, Deserialize)]
pub struct AssertGrant(Vec<u8>, Vec<u8>);

pub struct TaggedAssertion<'a>(&'a Assertion, &'a str);

impl Assertion {
    pub fn new(key: ring::hmac::SigningKey) -> Assertion {
        Assertion { secret: key}
    }

    pub fn tag<'a>(&'a self, tag: &'a str) -> TaggedAssertion<'a> {
        TaggedAssertion(self, tag)
    }

    fn extract<'a>(&self, token: &'a str) -> Result<(Grant<'a>, String), ()> {
        let readbytes = decode(token).map_err(|_| ())?;
        let AssertGrant(message, digest) = rmp_serde::from_slice(&readbytes).unwrap();

        ring::hmac::verify_with_own_key(&self.secret, &message, &digest).map_err(|_| ())?;
        let InternalAssertionGrant(owner_id, client_id, redirectbytes, scope, (ts, tsnanos), tag) =
            rmp_serde::from_slice(&message).map_err(|_| ())?;

        let redirect_url = Url::parse(redirectbytes).map_err(|_| ())?;
        let scope = scope.parse().map_err(|_| ())?;
        let until = Utc::timestamp(&Utc, ts, tsnanos);
        Ok((Grant{
            owner_id: Cow::Owned(owner_id.to_string()),
            client_id: Cow::Owned(client_id.to_string()),
            redirect_url: Cow::Owned(redirect_url),
            scope: Cow::Owned(scope),
            until: Cow::Owned(until),
        }, tag.to_string()))
    }

    fn generate_tagged(&self, grant: &Grant, tag: &str) -> String {
        let tosign = rmp_serde::to_vec(&InternalAssertionGrant(
            &grant.owner_id,
            &grant.client_id,
            grant.redirect_url.as_str(),
            &grant.scope.to_string(),
            (grant.until.timestamp(), grant.until.timestamp_subsec_nanos()),
            tag)).unwrap();
        let signature = ring::hmac::sign(&self.secret, &tosign);
        encode(&rmp_serde::to_vec(&AssertGrant(tosign, signature.as_ref().to_vec())).unwrap())
    }
}

impl<'a> TaggedAssertion<'a> {
    pub fn extract<'b>(&self, token: &'b str) -> Result<Grant<'b>, ()> {
        self.0.extract(token).and_then(|(token, tag)| {
            if tag == self.1 {
                Ok(token)
            } else {
                Err(())
            }
        })
    }
}

impl<'a> TokenGenerator for TaggedAssertion<'a> {
    fn generate(&self, grant: &Grant) -> String {
        self.0.generate_tagged(grant, self.1)
    }
}