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 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 if !(self.exact.contains(&fp.predicate())
48 || self.verify_general(&fp.predicate()))
49 {
50 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 root_sig == &sig {
63 return Ok(());
64 }
65 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 let verifier = Verifier::default();
322 verifier.verify(&macaroon, &root_key, vec![]).unwrap();
323
324 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}