macaroon/
verifier.rs

1use crate::crypto;
2use crate::{ByteString, Caveat, Macaroon, MacaroonError, MacaroonKey, Result};
3use std::collections::BTreeSet;
4use std::collections::HashMap;
5
6pub type VerifyFunc = fn(&ByteString) -> bool;
7
8#[derive(Default)]
9pub struct Verifier {
10    exact: BTreeSet<ByteString>,
11    general: Vec<VerifyFunc>,
12}
13
14impl Verifier {
15    pub fn verify(&self, m: &Macaroon, key: &MacaroonKey, discharges: Vec<Macaroon>) -> Result<()> {
16        let mut discharge_set = discharges
17            .iter()
18            .map(|d| (d.identifier.clone(), d.clone()))
19            .collect::<HashMap<ByteString, Macaroon>>();
20        self.verify_with_sig(&m.signature, m, key, &mut discharge_set)?;
21        // Now check that all discharges were used
22        if !discharge_set.is_empty() {
23            return Err(MacaroonError::DischargeNotUsed);
24        }
25        Ok(())
26    }
27
28    fn verify_with_sig(
29        &self,
30        root_sig: &MacaroonKey,
31        m: &Macaroon,
32        key: &MacaroonKey,
33        discharge_set: &mut HashMap<ByteString, Macaroon>,
34    ) -> Result<()> {
35        let mut sig = crypto::hmac(key, &m.identifier());
36        for c in m.caveats() {
37            sig = match &c {
38                Caveat::ThirdParty(tp) => {
39                    let caveat_key = crypto::decrypt_key(&sig, &tp.verifier_id().0)?;
40                    let dm = discharge_set.remove(&tp.id()).ok_or_else(|| MacaroonError::CaveatNotSatisfied("no discharge macaroon found (or discharge has already been used) for third-party caveat".to_string()))?;
41                    self.verify_with_sig(root_sig, &dm, &caveat_key, discharge_set)?;
42                    c.sign(&sig)
43                }
44                Caveat::FirstParty(fp) => {
45                    // This checks exact caveats first and then general second
46                    // if it fails due to logic short circuiting
47                    if !(self.exact.contains(&fp.predicate())
48                        || self.verify_general(&fp.predicate()))
49                    {
50                        // If both failed, it means we weren't successful at either
51                        return Err(MacaroonError::CaveatNotSatisfied(format!(
52                            "first party caveat not satisfied: {}",
53                            String::from_utf8_lossy(fp.predicate().as_ref())
54                        )));
55                    }
56                    c.sign(&sig)
57                }
58            };
59        }
60        // If the root sig equals the newly generated sig, that means we reached
61        // the end of the line and we are ok to return
62        if root_sig == &sig {
63            return Ok(());
64        }
65        // Check the bound signature equals the signature of the discharge
66        // macaroon
67        let zero_key: MacaroonKey = [0; 32].into();
68        let bound_sig = crypto::hmac2(&zero_key, &ByteString(root_sig.to_vec()), &sig.into());
69        if bound_sig != m.signature {
70            return Err(MacaroonError::InvalidSignature);
71        }
72        Ok(())
73    }
74
75    pub fn satisfy_exact(&mut self, b: ByteString) {
76        self.exact.insert(b);
77    }
78
79    pub fn satisfy_general(&mut self, f: VerifyFunc) {
80        self.general.push(f)
81    }
82
83    fn verify_general(&self, value: &ByteString) -> bool {
84        for f in self.general.iter() {
85            if f(value) {
86                return true;
87            }
88        }
89        false
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    extern crate time;
96
97    use super::Verifier;
98    use crate::{ByteString, Macaroon, MacaroonError, MacaroonKey};
99
100    #[test]
101    fn test_simple_macaroon() {
102        let key = MacaroonKey::generate(b"this is the key");
103        let macaroon = Macaroon::create(None, &key, "testing".into()).unwrap();
104        let verifier = Verifier::default();
105        verifier
106            .verify(&macaroon, &key, Default::default())
107            .unwrap();
108    }
109
110    #[test]
111    fn test_simple_macaroon_bad_verifier_key() {
112        let macaroon =
113            Macaroon::create(None, &MacaroonKey::generate(b"key"), "testing".into()).unwrap();
114        let key = MacaroonKey::generate(b"this is not the key");
115        let verifier = Verifier::default();
116        verifier
117            .verify(&macaroon, &key, Default::default())
118            .unwrap_err();
119    }
120
121    #[test]
122    fn test_macaroon_exact_caveat() {
123        let key = MacaroonKey::generate(b"this is the key");
124        let mut macaroon = Macaroon::create(None, &key, "testing".into()).unwrap();
125        macaroon.add_first_party_caveat("account = 3735928559".into());
126        let mut verifier = Verifier::default();
127        verifier.satisfy_exact("account = 3735928559".into());
128        verifier
129            .verify(&macaroon, &key, Default::default())
130            .unwrap()
131    }
132
133    #[test]
134    fn test_macaroon_exact_caveat_wrong_verifier() {
135        let key = MacaroonKey::generate(b"this is the key");
136        let mut macaroon = Macaroon::create(None, &key, "testing".into()).unwrap();
137        macaroon.add_first_party_caveat("account = 3735928559".into());
138        let mut verifier = Verifier::default();
139        verifier.satisfy_exact("account = 0000000000".into());
140        verifier
141            .verify(&macaroon, &key, Default::default())
142            .unwrap_err();
143    }
144
145    #[test]
146    fn test_macaroon_exact_caveat_wrong_context() {
147        let key = MacaroonKey::generate(b"this is the key");
148        let mut macaroon = Macaroon::create(None, &key, "testing".into()).unwrap();
149        macaroon.add_first_party_caveat("account = 3735928559".into());
150        let verifier = Verifier::default();
151        verifier
152            .verify(&macaroon, &key, Default::default())
153            .unwrap_err();
154    }
155
156    #[test]
157    fn test_macaroon_two_exact_caveats() {
158        let key = MacaroonKey::generate(b"this is the key");
159        let mut macaroon = Macaroon::create(None, &key, "testing".into()).unwrap();
160        macaroon.add_first_party_caveat("account = 3735928559".into());
161        macaroon.add_first_party_caveat("user = alice".into());
162        let mut verifier = Verifier::default();
163        verifier.satisfy_exact("account = 3735928559".into());
164        verifier.satisfy_exact("user = alice".into());
165        verifier
166            .verify(&macaroon, &key, Default::default())
167            .unwrap()
168    }
169
170    #[test]
171    fn test_macaroon_two_exact_caveats_incomplete_verifier() {
172        let key = MacaroonKey::generate(b"this is the key");
173        let mut macaroon = Macaroon::create(None, &key, "testing".into()).unwrap();
174        macaroon.add_first_party_caveat("account = 3735928559".into());
175        macaroon.add_first_party_caveat("user = alice".into());
176        let mut verifier = Verifier::default();
177        verifier.satisfy_exact("account = 3735928559".into());
178        verifier
179            .verify(&macaroon, &key, Default::default())
180            .unwrap_err();
181        let mut verifier = Verifier::default();
182        verifier.satisfy_exact("user = alice".into());
183        verifier
184            .verify(&macaroon, &key, Default::default())
185            .unwrap_err();
186    }
187
188    fn after_time_verifier(caveat: &ByteString) -> bool {
189        if !caveat.0.starts_with(b"time > ") {
190            return false;
191        }
192        let strcaveat = match std::str::from_utf8(&caveat.0) {
193            Ok(s) => s,
194            Err(_) => return false,
195        };
196
197        let format = time::format_description::parse(
198            "[year]-[month]-[day]T[hour]:[minute][offset_hour sign:mandatory][offset_minute]",
199        )
200        .unwrap();
201        match time::OffsetDateTime::parse(&strcaveat[7..], &format) {
202            Ok(compare) => time::OffsetDateTime::now_utc() > compare,
203            Err(_) => false,
204        }
205    }
206
207    #[test]
208    fn test_macaroon_two_exact_and_one_general_caveat() {
209        let key = MacaroonKey::generate(b"this is the key");
210        let mut macaroon =
211            Macaroon::create(Some("http://example.org/".into()), &key, "keyid".into()).unwrap();
212        macaroon.add_first_party_caveat("account = 3735928559".into());
213        macaroon.add_first_party_caveat("user = alice".into());
214        macaroon.add_first_party_caveat("time > 2010-01-01T00:00+0000".into());
215        let mut verifier = Verifier::default();
216        verifier.satisfy_exact("account = 3735928559".into());
217        verifier.satisfy_exact("user = alice".into());
218        verifier.satisfy_general(after_time_verifier);
219        verifier
220            .verify(&macaroon, &key, Default::default())
221            .unwrap()
222    }
223
224    #[test]
225    fn test_macaroon_two_exact_and_one_general_fails_general() {
226        let key = MacaroonKey::generate(b"this is the key");
227        let mut macaroon =
228            Macaroon::create(Some("http://example.org/".into()), &key, "keyid".into()).unwrap();
229        macaroon.add_first_party_caveat("account = 3735928559".into());
230        macaroon.add_first_party_caveat("user = alice".into());
231        macaroon.add_first_party_caveat("time > 3010-01-01T00:00+0000".into());
232        let mut verifier = Verifier::default();
233        verifier.satisfy_exact("account = 3735928559".into());
234        verifier.satisfy_exact("user = alice".into());
235        verifier.satisfy_general(after_time_verifier);
236        verifier
237            .verify(&macaroon, &key, Default::default())
238            .unwrap_err();
239    }
240
241    #[test]
242    fn test_macaroon_two_exact_and_one_general_incomplete_verifier() {
243        let key = MacaroonKey::generate(b"this is the key");
244        let mut macaroon =
245            Macaroon::create(Some("http://example.org/".into()), &key, "keyid".into()).unwrap();
246        macaroon.add_first_party_caveat("account = 3735928559".into());
247        macaroon.add_first_party_caveat("user = alice".into());
248        macaroon.add_first_party_caveat("time > 2010-01-01T00:00+0000".into());
249        let mut verifier = Verifier::default();
250        verifier.satisfy_exact("account = 3735928559".into());
251        verifier.satisfy_exact("user = alice".into());
252        verifier
253            .verify(&macaroon, &key, Default::default())
254            .unwrap_err();
255    }
256
257    #[test]
258    fn test_macaroon_third_party_caveat() {
259        let root_key = MacaroonKey::generate(b"this is the key");
260        let another_key = MacaroonKey::generate(b"this is another key");
261        let mut macaroon = Macaroon::create(
262            Some("http://example.org/".into()),
263            &root_key,
264            "keyid".into(),
265        )
266        .unwrap();
267        macaroon.add_third_party_caveat("http://auth.mybank/", &another_key, "other keyid".into());
268        let mut discharge = Macaroon::create(
269            Some("http://auth.mybank/".into()),
270            &another_key,
271            "other keyid".into(),
272        )
273        .unwrap();
274        discharge.add_first_party_caveat("time > 2010-01-01T00:00+0000".into());
275        macaroon.bind(&mut discharge);
276        let mut verifier = Verifier::default();
277        verifier.satisfy_general(after_time_verifier);
278        verifier
279            .verify(&macaroon, &root_key, vec![discharge])
280            .unwrap()
281    }
282
283    #[test]
284    fn test_macaroon_third_party_caveat_with_cycle() {
285        let root_key = MacaroonKey::generate(b"this is the key");
286        let another_key = MacaroonKey::generate(b"this is another key");
287        let mut macaroon = Macaroon::create(
288            Some("http://example.org/".into()),
289            &root_key,
290            "keyid".into(),
291        )
292        .unwrap();
293        macaroon.add_third_party_caveat("http://auth.mybank/", &another_key, "other keyid".into());
294        let mut discharge = Macaroon::create(
295            Some("http://auth.mybank/".into()),
296            &another_key,
297            "other keyid".into(),
298        )
299        .unwrap();
300        discharge.add_third_party_caveat("http://auth.mybank/", &another_key, "other keyid".into());
301        macaroon.bind(&mut discharge);
302        let mut verifier = Verifier::default();
303        verifier.satisfy_general(after_time_verifier);
304        verifier
305            .verify(&macaroon, &root_key, vec![discharge])
306            .unwrap_err();
307    }
308
309    #[test]
310    fn test_macaroon_third_party_unsatisfied() {
311        let root_key = MacaroonKey::generate(b"this is the key");
312        let another_key = MacaroonKey::generate(b"this is another key");
313        let mut macaroon = Macaroon::create(
314            Some("http://example.org/".into()),
315            &root_key,
316            "keyid".into(),
317        )
318        .unwrap();
319
320        // with no caveats, should verify fine
321        let verifier = Verifier::default();
322        verifier.verify(&macaroon, &root_key, vec![]).unwrap();
323
324        // add a third party caveat but no satisfier, should fail
325        macaroon.add_third_party_caveat("http://auth.mybank/", &another_key, "other keyid".into());
326        assert!(matches!(
327            verifier.verify(&macaroon, &root_key, vec![]),
328            Err(MacaroonError::CaveatNotSatisfied(_))
329        ));
330    }
331}