sequoia_wot/network/path.rs
1use std::borrow::Borrow;
2use std::time::SystemTime;
3use std::sync::Arc;
4
5use sequoia_openpgp as openpgp;
6
7use openpgp::KeyHandle;
8use openpgp::KeyID;
9use openpgp::Result;
10use openpgp::cert::prelude::*;
11use openpgp::packet::prelude::*;
12use openpgp::policy::NullPolicy;
13use openpgp::policy::Policy;
14use openpgp::types::ReasonForRevocation;
15use openpgp::types::RevocationStatus;
16
17use sequoia_cert_store as cert_store;
18use cert_store::store::StoreError;
19use cert_store::LazyCert;
20
21use crate::CertSynopsis;
22use crate::Certification;
23use crate::CertificationSet;
24use crate::Depth;
25use crate::format_time;
26use crate::Network;
27use crate::Path;
28use crate::store::Backend;
29use crate::store::Store;
30
31use super::TRACE;
32
33const NP: NullPolicy = unsafe { NullPolicy::new() };
34
35/// [`Network::lint_path`] specific error codes.
36#[non_exhaustive]
37#[derive(thiserror::Error, Debug)]
38pub enum PathError {
39 /// A path consists of at least one node.
40 #[error("A path consists of at least one node.")]
41 EmptyPath,
42
43 /// There is a path, but the trust amount is insufficient.
44 #[error("The path {path}, {1:?} exists, but its trust amount \
45 is too low ({2}, required: {3}).",
46 path=.0.iter()
47 .map(|kh| KeyID::from(kh).to_hex())
48 .collect::<Vec<String>>()
49 .join(" -> "))]
50 PathInadequate(Vec<KeyHandle>, UserID, usize, usize),
51
52 /// Missing the issuer's certificate.
53 #[error("Can't check certification: \
54 missing the alleged issuer's certificate ({0})")]
55 MissingIssuer(KeyHandle),
56
57 /// Missing the target's certificate.
58 #[error("Can't check certification: \
59 missing the target's certificate ({0})")]
60 MissingTarget(KeyHandle),
61
62 /// The target certificate is expired.
63 #[error("The target ({0}) is expired ({time1}) \
64 as of the reference time ({time2})",
65 time1=format_time(.1), time2=format_time(.2))]
66 TargetExpired(KeyHandle, SystemTime, SystemTime),
67
68 /// The target certificate is revoked.
69 ///
70 /// 0. Target cert, 1. revocation code, 2. revocation reason,
71 /// 3. revocation time, 4. reference time.
72 #[error("The target ({0}) is revoked ({time1}, {1}, {msg}) \
73 as of the reference time ({time2})",
74 time1=format_time(.3),
75 msg=String::from_utf8_lossy(.2),
76 time2=format_time(.4))]
77 TargetRevoked(KeyHandle, ReasonForRevocation, Vec<u8>, SystemTime, SystemTime),
78
79 /// The target User ID is revoked.
80 ///
81 /// 0. Target cert, 1. target user id, 2. revocation code,
82 /// 3. revocation reason, 4. revocation time, 5. reference time.
83 #[error("The target's ({0}) User ID ({userid:?}) is revoked \
84 ({time1}, {2}, {msg}) as of the reference time ({time2})",
85 userid=String::from_utf8_lossy(.1.value()),
86 time1=format_time(.4),
87 msg=String::from_utf8_lossy(.3),
88 time2=format_time(.5))]
89 TargetUserIDRevoked(KeyHandle, UserID, ReasonForRevocation, Vec<u8>,
90 SystemTime, SystemTime),
91
92 /// None of this certificate's regular expressions match the
93 /// target User ID.
94 #[error("None of the certification's ({0}) regular expressions ({regex:?}) \
95 match the target User ID ({userid:?})",
96 regex=.0.regular_expressions_bytes()
97 .iter()
98 .map(|re| {
99 String::from_utf8_lossy(re).into_owned()
100 })
101 .collect::<Vec<String>>()
102 .join(", "),
103 userid=String::from_utf8_lossy(.1.value()))]
104 RegexMismatch(Certification, UserID),
105
106 /// The certification's target does not have a sufficiently high
107 /// trust depth to authenticate the rest of the path.
108 #[error("The path requires that {keyid} be a level-{1} \
109 trusted introducer, the certification {0} only makes it \
110 a level-{depth} trusted introducer",
111 keyid=.0.target().keyid().to_hex(),
112 depth=.0.depth())]
113 InsufficientTrustDepth(Certification, Depth),
114
115 /// The certification does not have a sufficient trust amount.
116 #[error("The path requires a trust amount of {1}, but \
117 this certification ({0}) only has a trust amount of \
118 {amount}",
119 amount=.0.amount())]
120 InsufficientTrustAmount(Certification, usize),
121
122 /// The certificate did not issue a certification for the
123 /// specified binding.
124 #[error("{} did not certify <{}, {:?}>",
125 .0.keyid(), .1.keyid(),
126 String::from_utf8_lossy(.2.value()))]
127 NoCertification(CertSynopsis, CertSynopsis, UserID),
128
129 /// The certificate did not delegate to the target.
130 #[error("{} did not certify {}",
131 .0.keyid(), .1.keyid())]
132 NoDelegation(CertSynopsis, CertSynopsis),
133
134 /// None of the active certifications were adequate for
135 /// authenticating the target.
136 #[error("No active certifications by {keyid1} for \
137 <{keyid2}, {userid:?}> had a trust amount of at least {3}",
138 keyid1=.0.keyid(), keyid2=.1.keyid(),
139 userid=String::from_utf8_lossy(.2.value()))]
140 NoAdequateCertification(CertSynopsis, CertSynopsis, UserID, usize),
141
142 /// None of the active delegations were adequate for the path
143 /// suffix.
144 #[error("No active certifications by {keyid1} for {keyid2} \
145 that make it at least a level-{2} trusted introducer \
146 with a trust amount of at least {3}",
147 keyid1=.0.keyid(), keyid2=.1.keyid())]
148 NoAdequateDelegation(CertSynopsis, CertSynopsis, Depth, usize),
149
150 /// A certification would be adequate, but it is not active.
151 #[error("Certification ({0}) is adequate, but it is not active")]
152 AdequateButNotActive(Certification),
153
154 /// A certification would be adequate, but it is not valid.
155 #[error("Certification ({0}) is adequate, but it is not valid")]
156 AdequateButNotValid(Certification, #[source] anyhow::Error),
157}
158
159/// What we know about a certificate.
160///
161/// Sometimes we only know its Key ID.
162///
163/// The lifetimes `'a` and `'b` have the same meaning as for
164/// [`sequoia_wot::store::Store`]: `'a` is the lifetime of the object
165/// on the backend, `'b` is the lifetime of the reference to the
166/// object, and the object (`'a`) must outlive the reference (`'b`).
167#[derive(Debug, Clone)]
168enum CertVariants<'a> {
169 KeyHandle(KeyHandle),
170 Cert(Arc<LazyCert<'a>>),
171 CertSynopsis(CertSynopsis),
172}
173
174impl<'a> CertVariants<'a> {
175 /// Returns the certificate's KeyHandle.
176 pub fn key_handle(&self) -> KeyHandle {
177 match self {
178 CertVariants::KeyHandle(ref kh) => kh.clone(),
179 CertVariants::Cert(ref cert) => cert.key_handle(),
180 CertVariants::CertSynopsis(ref cert) => cert.fingerprint().into(),
181 }
182 }
183
184 /// Returns the certificate's primary User ID, if known.
185 ///
186 /// Note: this is best effort, and the User ID may not be valid
187 /// under the policy.
188 pub fn primary_userid(&self) -> Option<UserID> {
189 match self {
190 CertVariants::KeyHandle(_) => None,
191 CertVariants::Cert(ref cert) => {
192 // We can't get a ValidCert (otherwise we'd have a
193 // CertVariants::CertSynopsis). This is best effort.
194 cert.with_policy(&NP, None)
195 .and_then(|vc| {
196 vc.primary_userid().map(|ua| ua.userid().clone())
197 })
198 .ok()
199 .or_else(|| {
200 cert.userids().next()
201 })
202 }
203 CertVariants::CertSynopsis(ref cert) => {
204 cert.primary_userid().map(|u| u.userid().clone())
205 }
206 }
207 }
208
209 /// Returns the CertSynopsis, if it is available.
210 pub fn cert(&self) -> Option<&CertSynopsis> {
211 if let CertVariants::CertSynopsis(ref c) = self {
212 Some(c)
213 } else {
214 None
215 }
216 }
217}
218
219/// What we know about a certificate, and any errors or lints.
220///
221/// Sometimes we only know its Key ID.
222///
223/// This is indirectly returned by [`Network::lint_path`].
224///
225/// The lifetimes `'a` and `'b` have the same meaning as for
226/// [`Store`]: `'a` is the lifetime of the object on the backend, `'b`
227/// is the lifetime of the reference to the object, and the object
228/// (`'a`) must outlive the reference (`'b`).
229#[derive(Debug)]
230pub struct CertLints<'a> {
231 cert: CertVariants<'a>,
232 errors: Vec<anyhow::Error>,
233 lints: Vec<anyhow::Error>,
234}
235
236impl<'a> CertLints<'a> {
237 fn from_key_handle(kh: KeyHandle) -> Self {
238 Self {
239 cert: CertVariants::KeyHandle(kh),
240 errors: Vec::new(),
241 lints: Vec::new(),
242 }
243 }
244
245 fn from_lazy_cert(lc: Arc<LazyCert<'a>>) -> Self {
246 Self {
247 cert: CertVariants::Cert(lc),
248 errors: Vec::new(),
249 lints: Vec::new(),
250 }
251 }
252
253 fn from_cert_synopsis(cert: CertSynopsis) -> Self {
254 Self {
255 cert: CertVariants::CertSynopsis(cert),
256 errors: Vec::new(),
257 lints: Vec::new(),
258 }
259 }
260
261 /// Returns the certificate's KeyHandle.
262 pub fn key_handle(&self) -> KeyHandle {
263 self.cert.key_handle()
264 }
265
266 /// Returns the certificate's primary User ID, if known.
267 ///
268 /// Note: this is best effort, and the User ID may not be valid
269 /// under the policy.
270 pub fn primary_userid(&self) -> Option<UserID> {
271 self.cert.primary_userid()
272 }
273
274 /// Returns the CertSynopsis, if it is available.
275 pub fn cert(&self) -> Option<&CertSynopsis> {
276 self.cert.cert()
277 }
278
279 /// Returns any errors.
280 ///
281 /// Errors are fatal in the sense that the path is not valid.
282 pub fn errors(&self) -> &[anyhow::Error] {
283 &self.errors
284 }
285
286 /// Returns any lints.
287 ///
288 /// Lints are not fatal in the sense that the lint does not
289 /// necessarily invalidate the path.
290 pub fn lints(&self) -> &[anyhow::Error] {
291 &self.lints
292 }
293}
294
295/// What we know about a certificate.
296///
297/// Sometimes we only know the Key ID of the issuer and the Key ID of
298/// the target.
299#[derive(Debug, Clone)]
300enum CertificationVariants {
301 KeyHandles(KeyHandle, KeyHandle),
302 Certs(KeyHandle, Option<CertSynopsis>, KeyHandle, Option<CertSynopsis>),
303 Certification(Box<Certification>),
304}
305
306/// What we know about a certification, and any errors or lints.
307///
308/// Sometimes we only know the Key ID of the issuer and the Key ID of
309/// the target.
310///
311/// This is indirectly returned by [`Network::lint_path`].
312#[derive(Debug)]
313pub struct CertificationLints {
314 certification: CertificationVariants,
315 userid: Option<UserID>,
316 errors: Vec<anyhow::Error>,
317 lints: Vec<anyhow::Error>,
318}
319
320impl CertificationLints {
321 fn from_key_handles(issuer: KeyHandle, target: KeyHandle,
322 userid: Option<UserID>)
323 -> Self
324 {
325 CertificationLints {
326 certification:
327 CertificationVariants::KeyHandles(issuer, target),
328 userid: userid,
329 errors: Vec::new(),
330 lints: Vec::new(),
331 }
332 }
333
334 fn from_certs<I, T>(issuer: I, target: T, userid: Option<UserID>)
335 -> Self
336 where I: Into<CertSynopsis>,
337 T: Into<CertSynopsis>,
338 {
339 let issuer = issuer.into();
340 let target = target.into();
341
342 CertificationLints {
343 certification:
344 CertificationVariants::Certs(
345 issuer.key_handle(), Some(issuer),
346 target.key_handle(), Some(target)),
347 userid: userid,
348 errors: Vec::new(),
349 lints: Vec::new(),
350 }
351 }
352
353 fn from_certification(certification: Certification)
354 -> Self
355 {
356 let certification = certification.into();
357
358 CertificationLints {
359 certification:
360 CertificationVariants::Certification(certification),
361 userid: None,
362 errors: Vec::new(),
363 lints: Vec::new(),
364 }
365 }
366
367 /// Returns the issuer's KeyHandle.
368 pub fn issuer(&self) -> KeyHandle {
369 match self.certification {
370 CertificationVariants::KeyHandles(ref issuer, _) =>
371 issuer.clone(),
372 CertificationVariants::Certs(ref issuer, _, _, _) =>
373 issuer.clone(),
374 CertificationVariants::Certification(ref c) =>
375 KeyHandle::from(c.issuer().fingerprint()),
376 }
377 }
378
379 /// Returns the issuer's CertSynopsis, if known.
380 pub fn issuer_cert(&self) -> Option<&CertSynopsis> {
381 match self.certification {
382 CertificationVariants::KeyHandles(_, _) => None,
383 CertificationVariants::Certs(_, ref i, _, _) => i.as_ref(),
384 CertificationVariants::Certification(ref c) =>
385 Some(c.issuer()),
386 }
387 }
388
389 /// Returns the target's KeyHandle.
390 pub fn target(&self) -> KeyHandle {
391 match self.certification {
392 CertificationVariants::KeyHandles(_, ref target) =>
393 target.clone(),
394 CertificationVariants::Certs(_, _, ref target, _) =>
395 target.clone(),
396 CertificationVariants::Certification(ref c) =>
397 KeyHandle::from(c.target().fingerprint()),
398 }
399 }
400
401 /// Returns the target's CertSynopsis, if known.
402 pub fn target_cert(&self) -> Option<&CertSynopsis> {
403 match self.certification {
404 CertificationVariants::KeyHandles(_, _) =>
405 None,
406 CertificationVariants::Certs(_, _, _, ref t) =>
407 t.as_ref(),
408 CertificationVariants::Certification(ref c) =>
409 Some(c.target()),
410 }
411 }
412
413 /// Returns the certification's trust amount, if known.
414 pub fn amount(&self) -> Option<usize> {
415 self.certification().map(|c| c.amount())
416 }
417
418 /// Returns the certification's trust depth, if known.
419 pub fn depth(&self) -> Option<Depth> {
420 self.certification().map(|c| c.depth())
421 }
422
423 /// Returns the certification's creation time, if known.
424 pub fn creation_time(&self) -> Option<SystemTime> {
425 self.certification().map(|c| c.creation_time())
426 }
427
428 /// Returns the certification's creation time, if known.
429 pub fn expiration_time(&self) -> Option<Option<SystemTime>> {
430 self.certification().map(|c| c.expiration_time())
431 }
432
433 /// Returns the User ID that is being certified.
434 ///
435 /// This may be `None` if it is not known, or if this is a
436 /// delegation (i.e., a third-party direct key signature).
437 pub fn userid(&self) -> Option<&UserID> {
438 if let CertificationVariants::Certification(ref c) = self.certification {
439 c.userid()
440 } else {
441 self.userid.as_ref()
442 }
443 }
444
445 /// Returns the Certification, if it is known.
446 pub fn certification(&self) -> Option<&Certification> {
447 if let CertificationVariants::Certification(ref c)
448 = self.certification
449 {
450 Some(c)
451 } else {
452 None
453 }
454 }
455
456 /// Returns any errors.
457 ///
458 /// Errors are fatal in the sense that the path is not valid.
459 pub fn errors(&self) -> &[anyhow::Error] {
460 &self.errors
461 }
462
463 /// Returns any lints.
464 ///
465 /// Lints are not fatal in the sense that the lint does not
466 /// necessarily invalidate the path.
467 pub fn lints(&self) -> &[anyhow::Error] {
468 &self.lints
469 }
470}
471
472/// A linted path.
473///
474/// This is returned by [`Network::lint_path`].
475///
476/// The lifetimes `'a` and `'b` have the same meaning as for
477/// [`Store`]: `'a` is the lifetime of the object on the backend, `'b`
478/// is the lifetime of the reference to the object, and the object
479/// (`'a`) must outlive the reference (`'b`).
480#[derive(Debug)]
481pub struct PathLints<'a> {
482 certs: Vec<CertLints<'a>>,
483 certifications: Vec<CertificationLints>,
484 certification_network: bool,
485}
486
487impl<'a> PathLints<'a> {
488 /// Returns whether the path is in a certification network.
489 ///
490 /// In a certification network, depth constraints and regular
491 /// expressions are ignored.
492 pub fn certification_network(&self) -> bool {
493 self.certification_network
494 }
495
496 /// Returns the path's root.
497 pub fn root(&self) -> &CertLints<'a> {
498 &self.certs[0]
499 }
500
501 /// Returns the last node in the path.
502 pub fn target(&self) -> &CertLints<'a> {
503 &self.certs[self.certs.len() - 1]
504 }
505
506 /// Returns an iterator over the certificates.
507 pub fn certs(&self) -> impl Iterator<Item=&CertLints<'a>> {
508 self.certs.iter()
509 }
510
511 /// Returns the number of nodes (certificates) in the path.
512 pub fn len(&self) -> usize {
513 self.certs.len()
514 }
515
516 /// Returns an iterator over the certifications.
517 pub fn certifications(&self) -> impl Iterator<Item=&CertificationLints> {
518 self.certifications.iter()
519 }
520
521 /// Returns the amount that the target is trusted.
522 ///
523 /// 120 usually means fully trusted. This function checks that
524 /// there are no errors, that each certification's depth parameter
525 /// is sufficient for the rest of the path, and that the regular
526 /// expression constraints are respected.
527 pub fn amount(&self) -> usize {
528 tracer!(TRACE, "PathLint::amount");
529
530 // If there are any errors, we return 0.
531 if self.certs.iter().any(|c| ! c.errors().is_empty()) {
532 return 0;
533 }
534
535 let userid = if let Some(userid)
536 = self.certifications.last().expect("have one").userid() {
537 userid
538 } else {
539 // This is an invalid path: the last certification
540 // doesn't have a User ID.
541 t!("Invalid path: no target User ID");
542 return 0;
543 };
544
545 self.certifications.iter()
546 // The required depth for this path to be valid.
547 .zip((0..self.certifications.len()).rev())
548 .map(|(c, required_depth)| {
549 if ! c.errors.is_empty() {
550 return 0;
551 }
552
553 if let Some(c) = c.certification() {
554 if self.certification_network {
555 c.amount()
556 } else if c.depth() < required_depth.into() {
557 0
558 } else if required_depth > 0
559 && (! c.regular_expressions()
560 .map(|re_set| {
561 let matches = re_set.matches_userid(&userid);
562 t!("Certification's regular expression \
563 {} target user ID {}",
564 if matches {
565 "matches"
566 } else {
567 "does not match"
568 },
569 String::from_utf8_lossy(userid.value()));
570 matches
571 })
572 // Invalid => assume everything matches.
573 .unwrap_or(true))
574 {
575 // We check that the current certificate's
576 // regular expressions match the target user
577 // ID UNLESS (`required_depth == 0`) this is
578 // the certification that introduces the
579 // target user ID.
580 //
581 // Consider: Alice delegates to 'Bob
582 // <bob@other.org>', but only for "some.org".
583 // Bob's email address (`bob@other.org`) is
584 // not in some.org, but that doesn't matter
585 // when considering Alice's introduction of
586 // Bob; the regular expressions only scopes
587 // what user IDs Bob can introduce!
588 //
589 // ```
590 // Alice <alice@example.org>
591 // |
592 // | Authorization (depth: 1, amount: 120),
593 // | Regular expression: some.org
594 // v
595 // Bob <bob@other.org>
596 // / \
597 // / Certification \ Certification
598 // v v
599 // Carol <carol@some.org> Dave <dave@other.org>
600 // ```
601 0
602 } else {
603 c.amount()
604 }
605 } else {
606 0
607 }
608 }).min().unwrap_or(120) as usize
609 }
610
611 /// Converts the `PathLints` into a `Path`.
612 ///
613 /// This fails if the path is invalid. Note: the path is still
614 /// considered valid even if it doesn't have the required trust
615 /// amount as passed to [`Network::lint_path`].
616 ///
617 /// There may be multiple reasons why a path is invalid. This
618 /// function tries to return the first (when checking from the
619 /// root towards the target) reason why it is not valid.
620 pub fn to_path(mut self) -> Result<Path> {
621 let root = self.certifications.get(0)
622 .and_then(|c| c.issuer_cert())
623 .ok_or(PathError::EmptyPath)?;
624
625 let singleton = self.certs.len() == 1;
626
627 // If we're only checking a self signature, then we don't have
628 // a separate target.
629 let target_error = if singleton {
630 None
631 } else {
632 let target = self.certs.pop().expect("have one");
633 target.errors.into_iter().next()
634 };
635
636 let mut path = Path::new(root.clone());
637 path.set_certification_network(self.certification_network);
638 for (certification_lints, cert_lints)
639 in self.certifications.into_iter()
640 .zip(self.certs.into_iter())
641 {
642 // Issuer.
643 if let Some(err) = cert_lints.errors.into_iter().next() {
644 return Err(err);
645 }
646 // Certification.
647 if let Some(err) = certification_lints.errors.into_iter().next() {
648 return Err(err);
649 }
650 if let CertificationVariants::Certification(c)
651 = certification_lints.certification
652 {
653 if ! singleton {
654 path.try_append(*c)?;
655 }
656 } else {
657 unreachable!("If there's an error, \
658 we would have recorded it");
659 }
660 }
661
662 if let Some(err) = target_error {
663 return Err(err)
664 }
665
666 Ok(path)
667 }
668}
669
670impl<'a> From<&Path> for PathLints<'a> {
671 fn from(path: &Path) -> PathLints<'a> {
672 let mut certs: Vec<CertLints> = Vec::new();
673 let mut certifications: Vec<CertificationLints> = Vec::new();
674
675 for c in path.certifications() {
676 certs.push(CertLints::from_cert_synopsis(c.issuer().clone()));
677 certifications.push(
678 CertificationLints::from_certification(c.clone()));
679 }
680 certs.push(CertLints::from_cert_synopsis(path.target().clone()));
681
682 PathLints {
683 certs,
684 certifications,
685 certification_network: path.certification_network(),
686 }
687 }
688}
689
690/// The lifetimes `'a` and `'b` have the same meaning as for
691/// [`Store`]: `'a` is the lifetime of the object on the backend, `'b`
692/// is the lifetime of the reference to the object, and the object
693/// (`'a`) must outlive the reference (`'b`).
694impl<'a, S> Network<S>
695 where S: Store + Backend<'a>
696{
697 /// Authenticates a path in the network.
698 ///
699 /// This checks that there are valid certifications from the first
700 /// certificate in `khs` to the last over the User ID, `userid`
701 /// for the specified trust amount.
702 ///
703 /// This function will return `Ok` if a path with the required
704 /// trust amount can be found.
705 ///
706 /// Unlike [`Network::lint_path`], this function returns as soon
707 /// as an error is encountered.
708 ///
709 /// This function requires that the [`Network`] object implement
710 /// [`Backend`] in addition to [`Store`]. This is technically
711 /// needed by [`Network::lint_path`] to provide better diagnostics,
712 /// but it is not strictly required by [`Network::path`], which
713 /// only needs active certifications. This requirement exists,
714 /// because [`Network::path`] and [`Network::lint_path`] share a
715 /// fair amount of code. This bound may be lifted in the future.
716 ///
717 /// [`Network`]: crate::Network
718 /// [`Backend`]: crate::store::Backend
719 /// [`Store`]: crate::store::Store
720 pub fn path<U>(&self, khs: &[KeyHandle], userid: U,
721 required_amount: usize,
722 policy: &dyn Policy)
723 -> Result<Path>
724 where U: Borrow<UserID>
725 {
726 let userid = userid.borrow();
727
728 self.path_internal(khs, userid, required_amount, policy, false)
729 .and_then(|path_info| {
730 let path = path_info.to_path()?;
731 let amount = path.amount();
732 if amount < required_amount {
733 Err(PathError::PathInadequate(
734 khs.to_vec(), userid.clone(),
735 amount, required_amount)
736 .into())
737 } else {
738 Ok(path)
739 }
740 })
741 }
742
743 /// Lints a path in the network.
744 ///
745 /// This checks that the there are valid certifications from the
746 /// first certificate in `khs` to the last over the User ID,
747 /// `userid`.
748 ///
749 /// This function almost always returns `Ok`; it only returns an
750 /// error in an extraordinary circumstance.
751 ///
752 /// Unlike [`Network::path`], this function does extra work to
753 /// identify reasons why a path is invalid. For instance, if
754 /// there is no valid certification for a path segment, but there
755 /// is an expired certification that is expired, this function
756 /// will indicate that.
757 pub fn lint_path<U>(&self, khs: &[KeyHandle], userid: U,
758 required_amount: usize,
759 policy: &dyn Policy)
760 -> Result<PathLints<'a>>
761 where U: Borrow<UserID>
762 {
763 self.path_internal(khs, userid, required_amount, policy, true)
764 }
765
766 /// Authenticates a path in the network.
767 fn path_internal<U>(&self, khs: &[KeyHandle], userid: U,
768 required_amount: usize,
769 policy: &dyn Policy,
770 lint: bool)
771 -> Result<PathLints<'a>>
772 where U: Borrow<UserID>
773 {
774 let userid = userid.borrow();
775
776 tracer!(TRACE, "Network::path_internal");
777 t!("Checking path {} {}",
778 khs.iter()
779 .map(|kh| KeyID::from(kh).to_hex())
780 .collect::<Vec<String>>()
781 .join(" "),
782 String::from_utf8_lossy(userid.value()));
783
784 if khs.len() == 0 {
785 return Err(PathError::EmptyPath.into());
786 }
787
788 // Change the lifetime.
789 let khs: &[KeyHandle] = khs;
790
791 // XXX: let policy = self.policy();
792 let reference_time = self.reference_time();
793
794 // Look up the certificates.
795 let mut cert_lints: Vec<CertLints> = Vec::with_capacity(khs.len());
796 let mut certs: Vec<Option<Arc<LazyCert>>>
797 = Vec::with_capacity(khs.len());
798 for kh in khs.iter() {
799 cert_lints.push(CertLints::from_key_handle(kh.clone()));
800 let cl = cert_lints.last_mut().expect("have one");
801
802 certs.push(None);
803 let cert = certs.last_mut().expect("have one");
804
805 match self.lookup_by_cert(kh) {
806 Ok(certs) => {
807 if certs.len() > 1 {
808 // XXX: keyid collision :/. Silently ignore for
809 // now.
810 t!("Store returned multiple certificates \
811 for {}: {}",
812 kh,
813 certs.iter()
814 .map(|c| c.fingerprint().to_hex())
815 .collect::<Vec<String>>()
816 .join(", "));
817 }
818
819 if let Some(c) = certs.into_iter().next() {
820 t!("Looking up {}: hit!", kh);
821 *cl = CertLints::from_lazy_cert(c.clone());
822 *cert = Some(c);
823 } else {
824 let err = StoreError::NotFound(kh.clone()).into();
825 t!("Looking up {}: {}", kh, err);
826 if lint {
827 // We'll transform this to a valid
828 // cert below.
829 cl.errors.push(err);
830 } else {
831 return Err(err);
832 }
833 }
834 }
835 Err(err) => {
836 t!("Looking up {}: {}", kh, err);
837 if lint {
838 cl.errors.push(err);
839 } else {
840 return Err(err);
841 }
842 }
843 }
844 }
845
846 assert_eq!(certs.len(), khs.len());
847 assert_eq!(cert_lints.len(), khs.len());
848
849 // Convert them to ValidCerts (if the policy allows it). Due
850 // to lifetimes, we don't do this in the previous loop.
851 let certs: Vec<Option<ValidCert>> = certs.iter()
852 .zip(cert_lints.iter_mut())
853 .map(|(c, cl)| {
854 if let Some(c) = c {
855 match c.with_policy(policy, reference_time) {
856 Ok(c) => Ok(Some(c)),
857 Err(err) => {
858 if lint {
859 // We're linting. Just keep going.
860 cl.errors.push(err);
861 Ok(None)
862 } else {
863 Err(err)
864 }
865 }
866 }
867 } else {
868 Ok(None)
869 }
870 })
871 .collect::<Result<Vec<_>>>()?;
872
873
874 // If we were only give a single node, we assume the caller is
875 // asking the question: does this node have this self
876 // signature? In that case, we run one iteration of the where
877 // the issuer and target are the same.
878 let singleton = khs.len() == 1;
879
880 // Look for a valid certification for each piece of the path.
881 let mut certification_lints = Vec::with_capacity(certs.len() - 1);
882 'certification: for i in 0..(certs.len() - 1).max(1) {
883 // Whether this is the last path segment.
884 let last = singleton || (i == certs.len() - 2);
885
886 let issuer: Option<&ValidCert> = certs[i].as_ref();
887 let issuer_kh = issuer.map(|c| c.key_handle())
888 .unwrap_or_else(|| khs[i].clone());
889
890 // Carefully handle the singleton case:
891 let target: Option<&ValidCert> = if singleton {
892 issuer
893 } else {
894 certs[i + 1].as_ref()
895 };
896 let target_kh = target.map(|c| c.key_handle())
897 .unwrap_or_else(|| {
898 if singleton {
899 issuer_kh.clone()
900 } else {
901 khs[i + 1].clone()
902 }
903 });
904 t!("Considering {} -> {}", issuer_kh, target_kh);
905
906 certification_lints.push(
907 CertificationLints::from_key_handles(
908 issuer_kh.clone(), target_kh.clone(),
909 if last { Some(userid.clone()) } else { None }));
910 let mut cl = certification_lints.last_mut().expect("have one");
911
912 if issuer.is_none() {
913 let err = PathError::MissingIssuer(issuer_kh.clone());
914 t!(" {} -> {}: {}",
915 KeyID::from(&issuer_kh), KeyID::from(&target_kh),
916 err);
917 cl.errors.push(err.into());
918 }
919 if target.is_none() {
920 let err = PathError::MissingTarget(target_kh.clone());
921 t!(" {} -> {}: {}",
922 KeyID::from(&issuer_kh), KeyID::from(&target_kh),
923 err);
924 cl.errors.push(err.into());
925 }
926
927 let target: &ValidCert = if let Some(target) = target {
928 target
929 } else {
930 // We already emitted a lint.
931 continue 'certification;
932 };
933
934 if last {
935 // We need to check that the target is valid (i.e., it
936 // is not expired, and not revoked) at the reference
937 // time. Note: Certification::try_from_signature
938 // already checks that the certification and the
939 // certificates are valid at the *certification* time.
940
941 match target.clone().with_policy(target.policy(), reference_time) {
942 Err(err) => {
943 // Target is invalid at the reference time.
944 if lint {
945 cert_lints.last_mut().expect("have one")
946 .errors.push(err.into());
947 } else {
948 return Err(err.into());
949 }
950 }
951 Ok(vc) => {
952 // Check that the target is not revoked.
953 if let RevocationStatus::Revoked(revs)
954 = vc.revocation_status()
955 {
956 let rev = revs.iter().next().expect("have one");
957
958 // We know we have at least one revocation.
959 let reason = rev.reason_for_revocation();
960 let msg = reason
961 .map(|r| r.1.to_vec())
962 .unwrap_or(Vec::new());
963 let code = reason
964 .map(|r| r.0)
965 .unwrap_or(ReasonForRevocation::Unspecified);
966
967 let err = PathError::TargetRevoked(
968 target.key_handle(), code, msg,
969 rev.signature_creation_time()
970 .unwrap_or(std::time::UNIX_EPOCH),
971 reference_time);
972 t!("{}", err);
973 if lint {
974 cert_lints.last_mut().expect("have one")
975 .errors.push(err.into());
976 } else {
977 return Err(err.into());
978 }
979 }
980
981 // Check that the target certificate is not expired.
982 if let Some(e) = vc.primary_key().key_expiration_time() {
983 if e <= reference_time {
984 let err = PathError::TargetExpired(
985 target.key_handle(), e, reference_time);
986 t!("{}", err);
987 if lint {
988 cert_lints.last_mut().expect("have one")
989 .errors.push(err.into());
990 } else {
991 return Err(err.into());
992 }
993 }
994 }
995
996 // The target doesn't need to have self signed
997 // the User ID to authenticate the User ID.
998 // But if the target has revoked it, then it
999 // can't be authenticated.
1000 if let Some(ua) = vc.userids().find(|u| {
1001 u.userid() == userid
1002 })
1003 {
1004 if let RevocationStatus::Revoked(revs)
1005 = ua.revocation_status()
1006 {
1007 let rev = revs.iter().next().expect("have one");
1008
1009 // We know we have at least one revocation.
1010 let reason = rev.reason_for_revocation();
1011 let msg = reason
1012 .map(|r| r.1.to_vec())
1013 .unwrap_or(Vec::new());
1014 let code = reason
1015 .map(|r| r.0)
1016 .unwrap_or(ReasonForRevocation::Unspecified);
1017
1018 let err = PathError::TargetUserIDRevoked(
1019 target.key_handle(), userid.clone(),
1020 code, msg,
1021 rev.signature_creation_time()
1022 .unwrap_or(std::time::UNIX_EPOCH),
1023 reference_time);
1024 t!("{}", err);
1025 if lint {
1026 cert_lints.last_mut().expect("have one")
1027 .errors.push(err.into());
1028 } else {
1029 return Err(err.into());
1030 }
1031 }
1032 }
1033 }
1034 }
1035 }
1036
1037 let issuer: &ValidCert = if let Some(issuer) = issuer {
1038 issuer
1039 } else {
1040 // We already emitted a lint.
1041 continue 'certification;
1042 };
1043 let issuer_kh = KeyHandle::from(issuer.fingerprint());
1044
1045 *cl = CertificationLints::from_certs(
1046 issuer, target,
1047 if last { Some(userid.clone()) } else { None });
1048
1049 // We iterate over all of the certifications, take
1050 // those whose issuer, target and target User ID
1051 // match what we are looking for and partition
1052 // them into:
1053 //
1054 // - valid certifications: Those that
1055 // Certification::try_from_signature says are
1056 // valid. Note: not all of these are
1057 // necessarily *active*.
1058 //
1059 // - invalid_certifications: Those that are
1060 // invalid, because they violate the policy, are
1061 // revoked, etc.
1062 let mut valid_certifications: Vec<Certification>
1063 = Vec::new();
1064 let mut invalid_certifications:
1065 Vec<(Signature, Option<UserID>, anyhow::Error)>
1066 = Vec::new();
1067
1068 let mut parse_component = |ua: Option<&UserIDAmalgamation>| {
1069 if let Some(ua) = ua {
1070 t!("parse_component({}, {} self certifications, \
1071 {} third-party certifications)",
1072 String::from_utf8_lossy(ua.userid().value()),
1073 ua.self_signatures().count(),
1074 ua.certifications().count());
1075 } else {
1076 t!("parse_component(primary)");
1077 }
1078
1079 let valid_certifications
1080 = if issuer.fingerprint() == target.fingerprint()
1081 {
1082 t!("Returning self signatures.");
1083 if let Some(ua) = ua {
1084 Box::new(ua.self_signatures())
1085 as Box<dyn Iterator<Item=&Signature>>
1086 } else {
1087 // This is non-sense: a certificate never
1088 // has to delegate to itself. That would
1089 // create a cycle, anyway.
1090 Box::new(std::iter::empty())
1091 }
1092 } else {
1093 Box::new(if let Some(ua) = ua {
1094 Box::new(ua.certifications())
1095 as Box<dyn Iterator<Item=&Signature>>
1096 } else {
1097 Box::new(target.primary_key().certifications())
1098 }
1099 .filter(|c| {
1100 c.get_issuers()
1101 .into_iter().any(|i| {
1102 i.aliases(&issuer_kh)
1103 })
1104 }))
1105 }
1106 .filter_map(|c| {
1107 match Certification::try_from_signature(
1108 &issuer, ua, &target, c)
1109 {
1110 Ok(c) => Some(c),
1111 Err(err) => {
1112 t!(" {}", err);
1113 if lint {
1114 // Only save the error if we
1115 // are linting.
1116 invalid_certifications.push(
1117 (c.clone(),
1118 ua.map(|ua| ua.userid().clone()),
1119 err));
1120 }
1121 None
1122 }
1123 }
1124 })
1125 .collect::<Vec<_>>();
1126
1127 valid_certifications
1128 };
1129
1130 // Get the active delegations / certifications.
1131 if last {
1132 // This is the last path segment: we want a
1133 // certification of a specific User ID.
1134
1135 if let Some(ua) = target.cert().userids()
1136 .filter(|ua| ua.userid() == userid)
1137 .next()
1138 {
1139 valid_certifications = parse_component(Some(&ua));
1140 }
1141
1142 if valid_certifications.is_empty() {
1143 let err = PathError::NoCertification(
1144 CertSynopsis::from(issuer),
1145 CertSynopsis::from(target),
1146 userid.clone());
1147 t!(" {}", err);
1148 cl.errors.push(err.into());
1149 }
1150 } else {
1151 // We're looking for a delegation; a certification on
1152 // any User ID will do.
1153
1154 // We use the Cert and to the ValidCert, because
1155 // we want to consider all User IDs and all third
1156 // party certifications, not only those considered
1157 // valid by the current policy.
1158 for ua in target.cert().userids() {
1159 valid_certifications.extend_from_slice(
1160 &parse_component(Some(&ua)))
1161 }
1162 valid_certifications.extend_from_slice(&parse_component(None));
1163
1164 if valid_certifications.is_empty() {
1165 let err = PathError::NoDelegation(
1166 CertSynopsis::from(issuer),
1167 CertSynopsis::from(target));
1168 t!(" {}", err);
1169 cl.errors.push(err.into());
1170 }
1171 }
1172 t!(" Have {} valid certifications",
1173 valid_certifications.len());
1174
1175 // Checks if a certification is adequate.
1176 //
1177 // The first time through, we only look if any
1178 // certification satisfies the requirements. If so, we
1179 // don't want to lint the irrelevant certifications. The
1180 // second time through, we know no active certification
1181 // was sufficient so we do want to lint everything.
1182 let required_depth = if singleton || self.certification_network() {
1183 Depth::from(0)
1184 } else {
1185 Depth::from(certs.len() - 1 - (i + 1))
1186 };
1187
1188 let adequate = |certification: &Certification,
1189 cl: Option<&mut CertificationLints>|
1190 {
1191 // We check that the current certificate's regular
1192 // expressions match the target user ID UNLESS
1193 // (`required_depth == 0`) this is the certification
1194 // that introduces the target user ID.
1195 //
1196 // Consider: Alice delegates to 'Bob <bob@other.org>',
1197 // but only for "some.org". Bob's email address
1198 // (`bob@other.org`) is not in some.org, but that
1199 // doesn't matter when considering Alice's
1200 // introduction of Bob; the regular expressions only
1201 // scopes what user IDs Bob can introduce!
1202 //
1203 // ```
1204 // Alice <alice@example.org>
1205 // |
1206 // | Authorization (depth: 1, amount: 120),
1207 // | Regular expression: some.org
1208 // v
1209 // Bob <bob@other.org>
1210 // / \
1211 // / Certification \ Certification
1212 // v v
1213 // Carol <carol@some.org> Dave <dave@other.org>
1214 // ```
1215 let result = if ! self.certification_network()
1216 && (required_depth > 0.into()
1217 && (! certification.regular_expressions()
1218 .map(|re_set| re_set.matches_userid(&userid))
1219 // Invalid => everything matches.
1220 .unwrap_or(true)))
1221 {
1222 if let Some(cl) = cl {
1223 let err = PathError::RegexMismatch(
1224 certification.clone(), userid.clone());
1225 t!(" {}", err);
1226 cl.errors.push(err.into());
1227 }
1228 false
1229 } else if ! self.certification_network()
1230 && certification.depth() < required_depth
1231 {
1232 if let Some(cl) = cl {
1233 let err = PathError::InsufficientTrustDepth(
1234 certification.clone(), required_depth);
1235 t!(" {}", err);
1236 cl.errors.push(err.into());
1237 }
1238 false
1239 } else if certification.amount() < required_amount {
1240 if let Some(cl) = cl {
1241 let err = PathError::InsufficientTrustAmount(
1242 certification.clone(), required_amount);
1243 t!(" {}", err);
1244 cl.errors.push(err.into());
1245 }
1246 false
1247 } else {
1248 true
1249 };
1250
1251 t!(" Certification is {}adequate: {:?}",
1252 if result { "" } else { "not " },
1253 certification);
1254
1255 result
1256 };
1257
1258 // The CertificationSet returns the active
1259 // certifications, but not in the most convenient
1260 // form.
1261 let cs = CertificationSet::from_certifications(
1262 valid_certifications.clone(), reference_time);
1263 let active_certifications: Vec<Certification> = cs
1264 .into_iter()
1265 .flat_map(|cs| cs.into_certifications())
1266 .collect();
1267 t!("Have {} active certifications", active_certifications.len());
1268 for certification in active_certifications.iter() {
1269 if adequate(&certification, None) {
1270 // We found a good certification /
1271 // delegation. We're done, even if we are
1272 // linting.
1273 *cl = CertificationLints::from_certification(
1274 certification.clone());
1275 continue 'certification;
1276 }
1277 }
1278
1279 let err = if last {
1280 PathError::NoAdequateCertification(
1281 CertSynopsis::from(issuer),
1282 CertSynopsis::from(target),
1283 userid.clone(),
1284 required_amount)
1285 } else {
1286 PathError::NoAdequateDelegation(
1287 CertSynopsis::from(issuer),
1288 CertSynopsis::from(target),
1289 required_depth, required_amount)
1290 };
1291 t!(" {}", err);
1292 if lint {
1293 cl.errors.push(err.into());
1294 } else {
1295 return Err(err.into());
1296 }
1297
1298 // None of the active certifications are adequate.
1299 // Iterate again, but be verbose.
1300 for certification in active_certifications.iter() {
1301 adequate(certification, Some(&mut cl));
1302 }
1303
1304 // Now lint the inactive certifications.
1305 for certification in valid_certifications.iter() {
1306 // Skip the active certifications. We just
1307 // linted them.
1308 if active_certifications.contains(&certification) {
1309 continue;
1310 }
1311
1312 if adequate(certification, Some(&mut cl)) {
1313 let err = PathError::AdequateButNotActive(
1314 certification.clone());
1315 t!(" {}", err);
1316 cl.errors.push(err.into());
1317 }
1318 }
1319
1320 // Finally add the lints for the invalid certifications.
1321 for (certification, userid, err)
1322 in invalid_certifications.into_iter()
1323 {
1324 let certification = Certification::from_signature(
1325 issuer, userid, target, &certification);
1326 if adequate(&certification, Some(&mut cl)) {
1327 let err = PathError::AdequateButNotValid(
1328 certification.clone(), err);
1329 t!(" {}", err);
1330 cl.errors.push(err.into());
1331 }
1332 }
1333 }
1334
1335 Ok(PathLints {
1336 certs: cert_lints,
1337 certifications: certification_lints,
1338 certification_network: self.certification_network(),
1339 })
1340 }
1341}
1342
1343#[cfg(test)]
1344mod tests {
1345 use super::*;
1346
1347 use openpgp::policy::StandardPolicy;
1348
1349 use crate::NetworkBuilder;
1350 use crate::store::CertStore;
1351
1352 // Check that Network::path and Network::lint_path correctly error
1353 // out (and don't panic) when the path is empty.
1354 #[test]
1355 fn empty_path() -> Result<()> {
1356 let p = &StandardPolicy::new();
1357
1358 let store = CertStore::from_certs(Vec::new(), p, None)?;
1359 let network: Network<_> = NetworkBuilder::rootless(&store).build();
1360
1361 let uid = UserID::from("user@example.org");
1362
1363 network.path(&[], &uid, 0, p)
1364 .unwrap_err();
1365
1366 network.lint_path(&[], &uid, 0, p)
1367 .unwrap_err();
1368
1369 Ok(())
1370 }
1371}