Skip to main content

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}